deepseek-harness/docs/module-graph.md

1233 lines
84 KiB
Markdown
Raw Normal View History

<!-- Generated by scripts/gen-module-graph.ts — do not edit by hand.
Run `pnpm run gen-module-graph` to regenerate. -->
# Module dependency graph
2026-07-05 01:25:58 +08:00
Inter-package dependencies among the `@deepseek-ai/dsh-*` harness packages, derived from each package's `peerDependencies` (the canonical runtime-dependency signal) and grouped by the `packages/<group>/<pkg>` hierarchy. An edge `a --> b` means package `a` depends on package `b`. Names have the `@deepseek-ai/dsh-` prefix stripped.
```mermaid
2026-07-05 01:25:58 +08:00
flowchart TD
subgraph group_util["packages/util"]
pkg_atomic_write["atomic-write"]
2026-07-05 01:25:58 +08:00
pkg_brand["brand"]
pkg_native_command["native-command"]
pkg_paths["paths"]
pkg_retention["retention"]
pkg_timeout["timeout"]
2026-07-05 01:25:58 +08:00
end
subgraph group_llm["packages/llm"]
pkg_llm["llm"]
pkg_llm_deepseek["llm-deepseek"]
pkg_llm_pi_ai["llm-pi-ai"]
pkg_llm_retry["llm-retry"]
pkg_token_meter["token-meter"]
2026-07-05 01:25:58 +08:00
end
subgraph group_core["packages/core"]
pkg_agent["agent"]
pkg_agent_loop["agent-loop"]
pkg_scope["scope"]
2026-07-05 01:25:58 +08:00
pkg_session["session"]
pkg_system_prompt["system-prompt"]
pkg_tools["tools"]
end
subgraph group_goal["packages/goal"]
2026-07-19 23:55:33 +08:00
pkg_command_goal["command-goal"]
pkg_goal["goal"]
pkg_goal_session["goal-session"]
pkg_tool_goal["tool-goal"]
end
2026-07-05 01:25:58 +08:00
subgraph group_bash["packages/bash"]
pkg_bash["bash"]
pkg_bash_local["bash-local"]
pkg_bash_sandbox["bash-sandbox"]
2026-07-05 01:25:58 +08:00
pkg_tool_bash["tool-bash"]
end
subgraph group_fs["packages/fs"]
pkg_fs["fs"]
pkg_fs_local["fs-local"]
pkg_fs_policy["fs-policy"]
feat(sandbox): cross-family file sandbox — one policy home, sandboxed fs provider, fs escalation parity Extend SandboxMode enforcement from bash to the filesystem tools, the sandbox RFC's deferred cross-family phase. - dsh-sandbox-policy (new, ctx.sandboxPolicy): the single home for the deployment default mode + workspaceRoot and the per-session override event, renamed bash/sandbox-mode -> sandbox/mode and moved here with its fold/setter. Decouples the bash seam from dsh-session. - dsh-fs-sandbox (new): SandboxedFileSystem extends LocalFileSystem and fences write/edit by the per-call mode (read-only denies, workspace-write contains to the workspace + temp roots via the shared writableRoots, danger passes through); reads pass through. Structured FS_SANDBOX_DENIED; in-lock parent re-canonicalization. A policy fence in trusted code, not a kernel boundary. - dsh-sandbox: the shared escalation kit (writableRoots, the strictly-wider ladder, denial/hint markers, approveEscalation) both tool families use; approveEscalation takes a structural approver so dsh-sandbox gains no approval/agent dependency, and both tools stay duplication-free. - tool-fs: write/edit advertise sandbox_permissions/justification under a confining ctx.fs, map FS_SANDBOX_DENIED to the shared [sandbox: ...] marker, and resolve the same one-approved-wider retry. - examples/acp-agent: composes sandbox-policy + fs-sandbox, drops the gating that disabled the fs stack under confined modes. RFC docs/rfc/implemented/feature/2026-07-14-cross-family-fs-sandbox.md; the old sandbox RFC's In-process/deferred/FAQ sections updated to shipped fact.
2026-07-14 20:05:57 +08:00
pkg_fs_sandbox["fs-sandbox"]
2026-07-05 01:25:58 +08:00
pkg_tool_fs["tool-fs"]
pkg_tool_fs_search["tool-fs-search"]
pkg_tool_str_replace_editor["tool-str-replace-editor"]
2026-07-05 01:25:58 +08:00
end
subgraph group_skill["packages/skill"]
pkg_skill["skill"]
pkg_skill_local["skill-local"]
pkg_tool_skill["tool-skill"]
end
2026-07-05 01:25:58 +08:00
subgraph group_compact["packages/compact"]
pkg_command_compact["command-compact"]
2026-07-05 01:25:58 +08:00
pkg_compact["compact"]
pkg_compact_basic["compact-basic"]
pkg_compact_tool_result_prune["compact-tool-result-prune"]
2026-07-05 01:25:58 +08:00
end
subgraph group_subagent["packages/subagent"]
pkg_subagent["subagent"]
pkg_subagent_acp["subagent-acp"]
pkg_subagent_dsh_sdk["subagent-dsh-sdk"]
2026-07-05 01:25:58 +08:00
pkg_subagent_fork["subagent-fork"]
pkg_subagent_inprocess["subagent-inprocess"]
pkg_subagent_spawn["subagent-spawn"]
pkg_tool_subagent["tool-subagent"]
end
subgraph group_web["packages/web"]
pkg_tool_web["tool-web"]
pkg_web["web"]
pkg_web_fetch_local["web-fetch-local"]
pkg_web_search_deepseek["web-search-deepseek"]
pkg_web_search_exa["web-search-exa"]
pkg_web_search_perplexity["web-search-perplexity"]
end
subgraph group_spill["packages/spill"]
pkg_spill["spill"]
pkg_spill_local["spill-local"]
pkg_spill_policy["spill-policy"]
end
subgraph group_timeout["packages/timeout"]
pkg_timeout_policy["timeout-policy"]
end
2026-07-05 01:25:58 +08:00
subgraph group_todo["packages/todo"]
pkg_tool_todo["tool-todo"]
end
subgraph group_plan["packages/plan"]
pkg_plan_mode["plan-mode"]
feat(mode): the session-mode core — logged per-agent policy state (@deepseek-ai/dsh-mode) Plan mode's stage 1 (RFC 2026-07-07-plan-mode): a new packages/mode/ group with one product package owning the mode/set SessionEventMap vocabulary (log-only, non-surface, whole-value replace), the pure foldMode, and the ctx.modes service (list/get/set). User flips are pending intents flushed at turn/start / step/end — turn enclosure makes an idle append illegal — with one coalesced context/message notice when the flushed mode differs from what the last logged request header told the model; a folded mode the config no longer defines reads as default plus one boundary notice. Enforcement is two covering layers: a system-prompt/assemble wrapper filters the RETURNED assembly's tools to the mode's allowlist (and shows exit_plan_mode IFF the folded mode is plan) beside the mode:policy section at order 50, and a tools/pre-execute gate denies deny-by-default against the same allowlist, judging by the logged mode only. The default mode is the absence of policy — assemblies stay byte-identical to a no-dsh-mode deployment. AgentOptions.mode (declaration-merged) seeds a child's initial mode through the same flush on agent/created; the stdio app gains /mode (print/switch, never sent to the model) over an opportunistic ctx.get('modes'). Config is an explicit resolve step: the built-in plan definition (read-only allowlist; bash/subagent excluded until the sandbox family lands) merges unless overridden, 'default' as a key throws at load, unknown names throw at set() time.
2026-07-10 01:38:39 +08:00
end
subgraph group_cordis["packages/cordis"]
pkg_repository_plugin["repository-plugin"]
pkg_tool_cordis["tool-cordis"]
end
2026-07-05 01:25:58 +08:00
subgraph group_hooks["packages/hooks"]
pkg_hook_protocol["hook-protocol"]
pkg_hooks_claude["hooks-claude"]
pkg_hooks_codex["hooks-codex"]
end
subgraph group_session_persistence["packages/session-persistence"]
pkg_session_checkpoint_policy["session-checkpoint-policy"]
2026-07-05 01:25:58 +08:00
pkg_session_persistence["session-persistence"]
pkg_session_persistence_jsonl["session-persistence-jsonl"]
pkg_session_persistence_sqlite["session-persistence-sqlite"]
end
subgraph group_session_query["packages/session-query"]
pkg_session_query["session-query"]
pkg_session_query_sqlite["session-query-sqlite"]
pkg_tool_session_query["tool-session-query"]
end
subgraph group_session_title["packages/session-title"]
pkg_session_title["session-title"]
pkg_session_title_all_messages_llm["session-title-all-messages-llm"]
pkg_session_title_first_message_llm["session-title-first-message-llm"]
pkg_session_title_llm["session-title-llm"]
end
2026-07-05 01:25:58 +08:00
subgraph group_support["packages/support"]
pkg_acp_snapshot["acp-snapshot"]
pkg_agent_loop_testkit["agent-loop-testkit"]
2026-07-05 01:25:58 +08:00
pkg_invariants["invariants"]
pkg_llm_mock_server["llm-mock-server"]
2026-07-05 01:25:58 +08:00
pkg_llm_replay["llm-replay"]
2026-07-14 05:00:54 +08:00
pkg_loader_smoke["loader-smoke"]
2026-07-05 01:25:58 +08:00
end
subgraph group_acp["packages/acp"]
2026-07-05 01:25:58 +08:00
pkg_acp["acp"]
end
subgraph group_ui["packages/ui"]
pkg_app_boot["app-boot"]
2026-07-19 22:11:59 +08:00
pkg_commands["commands"]
pkg_jsonrpc["jsonrpc"]
feat(permission): user-facing permission presets — one Permissions select over the two knobs A preset names a bundle of the two mechanism knobs — request = workspace-write + ask, yolo = danger-full-access + never — so the editor shows ONE 'Permissions' select where the sandbox-mode and approval-policy tiers stay orthogonal capabilities (the Codex /approvals shape: presets over two dials). ctx.permission (dsh-permission) owns the config-defined table, validates the default preset's bundle against the composed knob defaults at load (fails loud), and writes a switch THROUGH: one log-only permission/preset event (the audit fact reverse-mapping cannot recover — the planned 'agent' preset shares request's knob values and differs only in composed policy) plus each knob event via its own setter, deduped — a net-zero switch appends nothing. Every knob consumer keeps reading its own fold, untouched. The current preset DERIVES from the effective knob values — the fold breaks bundle ties, a knob state outside the table is the reserved 'custom' value (a state, not an error: shown while it holds, switchable FROM, never a target), and defaultPreset disappears (zero-event state reverse-maps from the composition defaults). The ACP bridge drops the two per-knob selects for the one preset select (advertised only when ctx.permission is composed); pending/anchor/no-op semantics carry over unchanged, with the no-op echo acknowledged before vocabulary validation so a client re-pushing a derived 'custom' current never errors. The sandbox variant example composes the service with a workspace-write default; the permission-switching, escalation-approved and escalation-rejected scenarios are re-recorded under it (escalations now target an outside-workspace /tmp path under danger-full-access, self-cleaning) and config-options is re-authored on the single-select wire.
2026-07-12 21:03:41 +08:00
pkg_permission["permission"]
pkg_tool_ask_user["tool-ask-user"]
pkg_tui["tui"]
pkg_user_approval["user-approval"]
pkg_user_interaction["user-interaction"]
2026-07-05 01:25:58 +08:00
end
feat(gui): step1 skeleton — dsc web serves built web UI over booted harness host Five new modules: apps/dsc (bin: parseArgs + node:http static server + signal shutdown), packages/host/apiproxy (programmatic harness core composition, agents:[]), packages/client/web-runtime (React-free browser runtime), packages/client/web-ui (React mount), apps/web (vite build entry producing dist consumed by apps/dsc via package exports). Root wiring: apps/* workspace glob, dsh-* paths for host/client groups, demo:web script, apps/web/dist gitignore. No protocol/API routes yet — contract lands in step2 (see missions/tasks/20260719-1902-apiproxy-api-design). Includes the design + implementation archives (spec v2.1, deepseekchat baseline and harness boot research, implementation run log). Acceptance: 12/12 passed incl. real-key llm.stream smoke (51 chunks). feat(gui): apiproxy — four-quadrant RPC contract + fetch carriers, live end to end Contract layer (src/api/, 14 files): four named wire message types (ClientRequest / ServerResponse / ServerRequest / ClientResponse) as a discriminated union over strict bidirectional rpcId (initiator mints, responder echoes; channel and message fully decoupled — HTTP is the client->server pipe, SSE the reverse); narrow RpcRequest<P>/ RpcResponse<T> signature forms; RpcMethodMap with RequestPayload<K>/ ResponseValue<K> derivation; typed RpcError details map; approval/ question responses modeled as ClientResponse via a single /api/respond endpoint (RpcReceipt carrier ack); zod schemas anchored per Wire<T> against exactOptionalPropertyTypes. impl/api-proxy.ts: describe/list/create, both SSE streams (frame queue pump, subscribed baseline, lifecycle frames, signal cleanup); history pages on message boundaries (tail-back scan, partial included in the tail page); prompt dispatches queue->agent.send / steer->agent.steer with rpcId carried through MessageSource; cancel for attached sessions; cold-session resume deduped via a per-id promise map; host-level provider/model defaults injected at create/resume. fetch/: mechanical UNARY_ROUTES table, two-level parse with path==method check, SSE frames completed to ServerRequest full form; client mints -> narrows -> envelopes outbound, verifies rpcId echo inbound, streams SSE frames, four-quadrant onEnvelope tap (debug panel choke point). Real-browser fixes: URL base resolves to location.origin (hardcoded internal base broke real pages), browser-safe export paths. Design archives: contract design.md v2.0 with decision log, core-coverage audit, comparative studies, step2 impl run log. Probed end to end over real HTTP: prompt -> live model stream -> history returns the finished reply. feat(gui): RpcLog debug panel — fixture-driven milestone, playwright-verified 10/10 web-runtime: rpcLog + ui slices (zustand), four-quadrant RpcLogEntry (client-request / server-response / server-request / client-response), onEnvelope tap -> microtask-batched pump with 500-entry ring buffer, ConnectionController (private state, backoff reconnect), fixture API with fake envelopes (?fixture switch), bootWebRuntime; contract types via temporary local copies (api-types.ts, swapped for real imports when W3 client lands). web-ui: components/panels/RpcLog five-piece set (badge with unread count, floating panel, direction glyphs per quadrant, same-rpcId pair highlighting in two families, JSON payload expand, follow/pause, clear), App shell, utils/formatRelative, light-theme CSS variables with dark placeholders. dsc bin: mime lookup fixed to use the actually-served file (naked '/?query' no longer falls through to octet-stream download); shutdown closes SSE keep-alive connections so SIGTERM actually exits. Acceptance: scripts/verify-rpclog-panel.mjs (chromium headless) ALL PASS 10/10 over design.md §D 1-6. pkg: add web scripts for building feat(gui): session milestone — list + conversation over Session OOP, styled RpcLog v2.1 web-runtime: Session/SessionManager object layer (resident instances, mux frame routing, lineage flattening), foldSurface adapter with padding sentinels for paged windows, chunk accumulator for streaming partials, batched change notification (useSyncExternalStore contract), connection sinks + reconnect fix (the 300ms self-abort reconnect storm that made the session list flap is gone), fixture rewritten as a scripted host (60-turn history, typewriter replay, resident pending approval, child session); temporary contract copies deleted in favor of real apiproxy imports. web-ui: sessions screen (list with lineage indent + selection as container-local state), conversation view (turn grouping, reasoning fold, tool cards, steering, pending interaction cards, upward paging with scroll anchoring), input bar with queue/steer/stop; RpcLog panel restyled per docs/web-styling.md (tokenized palette, quadrant badge glyphs now vertical ↑↓⇟⇞, pair highlighting, floating shadow). docs/web-styling.md: living style guide (tokens, visual baseline, coding rules, evolution log). Acceptance: verify-session.mjs 31/31, verify-session-real.mjs 5/5 (real model streaming), verify-rpclog-panel.mjs 10/10. feat(gui): hostruntime split + repo-wide package prefix rename Package split (design: 20260720-0101-hostruntime-split-design): dsh-host-runtime carries bootHost + createApiProxy + startHost() (RunningHost {api, handler, defaults, ctx, dispose} — the seam Electron and any future shell reuses; ctx is the official front-door mount point); dsh-host-webserver carries the node:http static+API bridge (fixed: abort now keys on res 'close' + writableEnded — req 'close' fires on body end since Node 16 and was killing every SSE stream instantly, the reconnect-storm root cause); apps/dsc is now a thin assembly with web/-p subcommands. dsc -p runs the full isomorphic carrier chain in process (second real protocol consumer; probed end-to-end against the live model). Naming rule (user decree): packages under host/ and client/ carry the directory prefix in their npm name — dsh-host-apiproxy, dsh-client-web-runtime, dsh-client-web-ui renamed repo-wide in one frozen batch; explicit tsconfig paths entries added where the wildcard no longer matches. Acceptance: verify-session 31/31, verify-rpclog-panel 10/10, verify-session-real 7/7 (incl. new 12s connection-stability sentinels), tsc green, dsc web + dsc -p smoke both pass. refactor(gui): AbstractApiClient class hierarchy — OO client with inheritable seams AbstractApiClient (apiproxy) carries every protocol invariant: rpcId minting, four-quadrant envelope wrap/unwrap, zod parsing, SSE frame parsing, the payload-direct IApiClient surface (callers no longer mint rpcIds — the carrier does), and the instance-level envelope observation pump (batched via microtask; moved off module-level globals in rpc-log.ts, which is now a pure subscriber mapping envelopes into store entries — the debug panel observes the connection, it is not part of it). Platform subclasses own two abstract seams (doFetch, onEnvelope) plus three protocol-level virtuals for transportless overrides: InProcessApiClient (apiproxy; dsc -p uses new InProcessApiClient( host.handler)), WebApiClient (web-runtime), FixtureApiClient (fixture now subclasses instead of wrapping). Naming per decree: AbstractApiClient / IApiClient; ApiProxy stays the impl-side narrow-form contract. headless.ts call sites drop rpcRequest wrappers (payload-direct); split-design archive updated with the naming-rule ledger. tsc green; verify-session 31/31, verify-rpclog-panel 10/10, verify-session-real 7/7 (12s connection sentinel count=4); dsc -p smoke CALLER-OK. feat(gui): InputBar final form — bug batch, deepseekchat layout, single primary button, running locks input Squashes the whole InputBar iteration batch: IME/caret/auto-grow/focus/dedup bug fixes, layout aligned to the deepseekchat baseline, single primary button with hover flyout, finalized button semantics with the Codex-style icon circle, and running-state locking where stop is the only mid-turn action. The same batch carried the Chinese-to-English code comment sweep (density pruned), folded in here. docs(gui): purge work-log references from code comments 76 design-doc references cleared across the GUI packages: section pointers inlined as self-contained constraint statements, pure pointer comments dropped, milestone codenames and ruling tags out, and the 14 contract file headers switched to the formal RFC (the only sanctioned external reference). web-styling.md now cites the styling RFC instead of the disposable research archive. grep for work-log reference variants is clean across the GUI packages. docs(gui): file-header comments self-contained — drop RFC filename references RFC renames/reorgs must not require a source sweep (the 2026-07-20 two-way merge proved it). 11 headers lose only the '(RFC …)' tail and stay self-contained; api-proxy.ts keeps its minimal-first note. fix(gui): session streaming — freeze interrupted partials, sweep stale running calls, send force-scrolls Aborted turns never emit the finalizing assistant/message, so the accumulated partial and its running tool cards kept rendering below later messages — the "new message lands above the stopped reply" illusion. turn/end side effects now freeze content-bearing partials into interrupted terminal nodes (fractional seq keeps flow order; the live freeze and history replay converge through applyEventSideEffects, so a refresh reconstructs identical frozen nodes) and turn running tool cards into interrupted terminal cards; only content-free partials are swept outright. ConversationView gains the send-force-scroll rule (own words must be visible) alongside the pre-update atBottom follow flag. Regressions pinned as E2-4a–c (real host) and §E1-11h (fixture). feat(gui): webserver hardening verify script feat(gui): dark-mode toggle pinned to the sidebar bottom Interim home before the Settings page exists (the button re-homes with zero logic change — mechanics live in utils/theme.ts): html[data-theme] flip + dsc.theme localStorage, stored choice wins over the OS prefers-color-scheme default, applied in mount() before first paint so a dark reload never flashes light. Moon/sun inline SVG icon button at the sidebar's pinned bottom row. Pure front-end local concern: no RPC, no Session/store involvement. Dark sweep of list/conversation/input card/RPC panel found no unreadable pairs — no token changes needed. docs(gui): GUI RFCs and web styling handbook Layering+RPC protocol and web client architecture RFCs (post-reorg, developer-facing polish folded in) plus the styling engineering handbook. Mission work logs live in the commit above; PRs can be cut from this commit to include formal docs only. fix(gui): client object-layer hardening — audit timing/reference/resilience batches (S3-S5,C1-C3,C5-C8) fix(gui): carrier error channel + webserver backpressure (audit A1-A5,A7-A10,R2,R5) feat(gui): session persistence surface — cold list, project cwd, legacy no-cwd retirement refactor: rename dsc CLI to dsh — apps/cli, bin name, package scope Includes the root tsconfig project-references fix for host/* and client/web-runtime (originally a separate build fix commit). test(gui): three-tier suite — protocol/object/browser lanes, tier-a fill to per-file 100% test(gui): jsdom lane for web-ui + web-runtime coverage gate entry docs(gui): GUI testing system RFC (zh) feat(gui): tool-card views — contract slot, host-computed delivery, three-level card fallback fix(gui): lint clean across GUI packages — wrap long doc comments, drop dead type args, sync-return methods without awaits docs(gui): doc-sync mechanical fixes — JSDoc on apiproxy/host exports, RFC sketch fences ignore-check, md-wrap paragraphs, drop missions links, web-ui plain-ts entry chore(gui): module-graph regen + knip clean — drop dead re-exports, internalize createFixtureApi, scan web-ui tsx and verify mjs scripts build(gui): wire client/host packages into the lib build shape — tsc references + tsdown (web-ui css-external), lib manifests, cordis peer, apiproxy typed subpaths, vite src aliases test(gui): host-side per-file 100% coverage — apiproxy schema/carrier suites, webserver http-bridge suite, host-runtime composition suite; client/* coverage excluded pending the browser-side testing work item docs(gui): package READMEs for the five GUI packages — model-experience audit entries, limitations sections docs(gui): bilingual RFC pairs + client JSDoc completion — translate the three GUI RFCs to English with i18n records and manifest ratchet, Consequences sections both sides, full client/* export JSDoc, regen doc graphs and RFC index fix(scripts): doc-typecheck built-declarations mode maps /src/* subpath wildcards (apiproxy browser-safe channels) docs(gui): apply dsh rename across pr-gates docs — READMEs, layering RFC en, web-ui entry comment, i18n re-record fix(gui): post-rebase lint reconciliation — wrap main-tree long doc comments, read-through narrowing guards, abortError Error normalization, handleUnary generic justification fix(gui): post-rebase doc/test reconciliation — align host specs with evolved carrier contracts (sentinel rpcId, stream/error surfacing, url-path transport messages, defaults.cwd), Agent Note titles and relocated links, KV Cache effect sections, JSDoc on evolved exports fix(gui): second-rebase reconciliation to 509db0cb3 — restore api panel exports the baseline suites consume, knip workspace entries for jsdom lane and apps/web smokes, hoist result narrowing, align testing.md to the narrowed web-ui exclusion fix(test): vitest-scoped tsconfig maps bare imports for tsx specs — with GUI manifests now pointing at lib, an unmapped importer loaded a second copy of the web-runtime singletons fix(gui): typecheck + lint clean over the tool-card batch — brand callIds and object-form turn/end reason in the view spec, narrow fixture arg stringification, wrap long v8-ignore comments docs(gui): export JSDoc for tool-card surfaces + testing-note pairing header docs: rfc for web testing feat: add tools to host-runtime fix(gui): dispatch agent/error via agentEvents in host-runtime spec — mounted invariants plugin rejects raw ctx.emit without the scope carrier fix(gui): restore GUI knip workspaces + scripts/mjs entries and regenerate lockfile after master rebase fix(gui): post-rebase gate repairs — drop context-node envelope (master unwrapped injected content envelopes), regen event matrix, condense testing.md web-ui exclusion within budget fix(session): browser-safe deep-equal in surface — node:util import broke the vite bundle ci(gates): frontend vite build joins pre-push — node: imports in the client closure pass tsc but break the browser bundle test(tui): drop the checkout-dependent process.cwd() harness default — a long worktree path pushes the footer token counters past the 88-column fake terminal test(gui): jsdom behavior E2E — conversation main path over fixture runtime, reconnect banner lifecycle test(gui): jsdom RPC panel behavior — ledger rows, expand, pairing, pause/clear, follow-pause, payload truncation test(gui): jsdom tier-2 — InputBar guards, reasoning fold, JSON blocks, message variants, theme, create-then-select; act-harden banner case test(gui): jsdom tier-3 — ConversationView states/paging/force-bottom, ToolCallCard arms, PendingCard, list rows test(gui): jsdom tails — view-card variants, LogRow directions, registry hygiene, badge overflow, hook ops, mount glue test(gui): jsdom tails round 2 — call-ref blocks, resume follow, view precedence, failed create, empty-diff arm test(gui): jsdom final arms — anchor compensation, follow-off, interval ticks, view halves, node-over-running precedence test(gui): web-ui joins the per-file 100% coverage gate Annotation-only src changes plus the config swap. The web-ui exclusion is replaced by a single index.tsx entry (stale byte-identical duplicate of mount.tsx, nothing imports it; same entry-glue treatment as bin.ts) and the coverage include gains .tsx. v8-ignore sites (each with its reason inline): - ConversationView 3x ref-null guards; InputBar disabled-click guard - ToolCallCard both-null arms + windowless-custom argsRaw arm - LogRow css-module key fallbacks (start/stop block); RpcLogBody 3x ref-null guards - web-runtime drift from the tool-card batch: fixture presenter catch/str typo-guards, dense-array guards (fold-adapter reset, session rebuild, fixture backscan), live view-present arm (fixture replays are text-only; view vocabulary is covered by the history samples) test(gui): close the PR #443 host-side coverage gaps — apiproxy client abort arms, api-proxy cold/view paths, webserver drain - apiproxy fetch/client.ts: 3 new cases (pre-aborted signal short-circuits before transport + string reason mapping, non-Error/string reason falls to the default AbortError message, signal-less doFetch passthrough) - runtime/api-proxy.ts: one v8-ignore (summarizeCold cwd arm — list() filters cwd-less legacy metas) + api-proxy-cold.spec.ts (cold list merge: mtime source, locate-undefined and vanished-log fallbacks, lineage; no-persistence/no-factory resume → internal) + 2 view cases (history views with meta passthrough and orphan/bad-args/presenterless soft-falls, session/disposed open-call cleanup on the mux stream) - webserver/index.ts: /api/big fixture drives both drain-wait legs (full 8MiB readback after drain, mid-chunk disconnect wakes via 'close') feat: app shell fix: rebase conflicts fix: coverage fix(gui): lint clean after rebase — wrap long v8-ignore comments, unconditional v1 detail-block claim chore(gui): remove browser/probe verify scripts from scripts/ The six GUI acceptance/probe scripts (carrier-errors, rpclog-panel, session, session-real, webserver-backpressure, webserver-hardening) leave the repo's scripts/ tree; the three code comments that pointed at them now describe the coverage lane without naming a script path. fix(webserver): guard the request callback — one malformed request must not kill the process The async handle() had no top-level catch, so any throw inside it (a bad %-escape reaching decodeURIComponent, a client dropping mid-body, a response stream erroring) became an unhandled rejection and took the whole process down (audit R1 must-fix). The guard answers 400 when headers are not out yet, destroys the socket when they are, and reports the failure to onError (the package never prints). Spec covers all three legs: %-escape barrage → 400 + server stays alive, non-Error throw wrapped for onError, mid-stream explosion → socket teardown. feat: client AGENTS.md fix: client/AGENTS.md fix: rebase feat(gui): T0 cut 1 — 12 client package skeletons with contract stubs, dshClient declarations, tsdown client preset, theme token sheets feat(gui): T0 cut 2 — pure git mv migration per v3 §11 (connection six, runtime sessions/kernel, ui-conversation chat, ui-primitives markdown family, web shell + e2e) feat(gui): T0 cuts 3+4 — import rewiring to new package names, .legacy demotion of owner-rewrite files, legacy web-runtime/web-ui/apps-web retired to attic feat(gui): connection 对账刀——index.ts 精确导出清单替换 export *,intents.legacy 溶解删除 feat(client/ui-slots): SlotCore real implementation — kind semantics, sync version + microtask-batched notify, onMutate bridge feat(gui): web shell vite alias — retarget to new client packages, shell static surface only feat(gui): host 侧刀属地半——HostWebPluginRegistry(entries 扫描+internal/plugin 去抖重扫+dshClient 校验+exports./client 解析)、GET /plugins/<id>/client.js 分发端点、GET / 与 SPA fallback 注入 __DSH_BOOT__(webPlugins 可选注入,不传行为不变) feat(web-react): add use-sync-external-store dep + local shim typings feat(web-react): bindSnapshotSelector via uSES with-selector shim feat(gui): ui-layout concession-chain solver — pure computeColumns with contract geometry feat(gui): ui-layout LayoutService — four persisted stores, clamped actions, list-driven prune feat(gui): ui-layout AppFrame styles — grid columns, collapse-safe borders, edge drag handles test(gui): 存量 spec 平移——connection 三件+runtime 六件自 attic 捞回改包名路径全绿;api-helpers 按归属拆分(wire 半留 connection、classifier 半随 conversation.ts 入 runtime);boot-intents/preinit/rpc-log 随 intents/rpc-log 退役不迁(记 v3 §3.2 溶解项) feat(client/ui-primitives): StateDot/Button/Pill/Input/Menu atoms, ConnectionBanner de-legacied to pure props, JsonBlock CSS on --dsw tokens feat(web-react): createSnapshotStore engine (rafFlush batch, persist opt-in, dev freeze) + spec feat(gui): ui-layout AppFrame — grid tracks, pointer-capture drag handles with rAF throttle, frame ResizeObserver feat(web-react): useInvoke (external pending store, stable invoke, concurrency count) + spec test(web-react): bind spec — equality bail, custom eq, zero resubscribe, StrictMode, method sources feat(gui): ui-layout index rewiring — real exports, client apply provides ctx.layout and defines three slots feat(web-react): SessionProvider (renderBody deps) + RootBindingProvider + binding contexts + spec feat(gui): web shell AppRoot boot-page styles — self-contained with neutral token fallbacks feat(gui): web shell AppRoot — boot gate over loader status, fail-loud plugin failure list fix(gui): AppRoot gates on explicit settled signal — status-derived readiness races the incrementally filled table feat(client/ui-theme): ThemeService real implementation — registry with built-in light/dark, apply toggles body[data-ds-dark-theme], third-party token overrides as body inline vars feat(web-react): scopedSlots outlet (kind matrix, inject WeakMap caches, per-entry error boundary) + spec feat(gui): web shell module-table seed — pure-library entities for the loader require surface feat(client/i18n): I18nService real implementation — ns×locale registry, stable bind(ns) reference, zh fallback chain, zh/en skeleton dictionaries feat(gui): web shell assembly closure — layout exports via module table, SessionProvider + scopedSlots + RootBindingProvider feat: client/ui-conversation feat: code codedoc build(gui): root bundle green — web shell excluded from the lib workspace (vite app), ui-primitives lib externalizes css side-effect imports (web-ui precedent) gates(gui): verify-cordis-config follows aggregate tsconfig references (root is a shell over host/client programs); module graph regenerated for the twelve client packages chore(gui): retire legacy migration sources — every owner rewrite landed (t0-checklist §7 ledger honored); orphan css of retired components removed gates(gui): knip green groundwork — e2e/tsx entries for the new packages, loader-runtime deps ignored where loading is by specifier string, fake plugin ids un-bare-named, dead test export dropped chore(client): manifest shape batch A — ui-slots/web-react/ui-primitives invariant companions, files whitelist, cordis+invariants peer/dev, tsconfig refs chore(client): manifest shape batch B — connection/runtime/ui-conversation/ui-trajectory files whitelist, cordis peer+dev, explicit invariant lib entries (clientBundle signature) chore(client): manifest shape batch C — i18n/ui-layout/ui-sidebar/ui-theme invariant companions, files whitelist, invariants peer/dev, tsconfig refs chore(client): manifest shape batch D — web shell gains node-half lib entry + invariant companion + uniform files whitelist chore(client): drop verified-unused deps — dsh-tools from runtime/ui-conversation (types ride /presentation), ui-primitives+clsx from ui-layout gates(gui): doc-gate fixes — theme JSDoc prose, three client type-link exemptions, agent-note paths follow the migration, config catalog regenerated gates(gui): type-equiv manifest follows the types.ts extraction, approval JSDoc keeps its link form, persistence catalog regenerated docs(gui): per-constant JSDoc on the contract geometry exports (export-jsdoc gate) test(gates): loader-composition budget covers cold tsx resolution after the program split (was flaking at the default 5s) docs(gui): README substantiation batch 1 — ui-slots/ui-primitives/web-react/connection: Model Experience short form, real deferred-work ledgers, description accuracy pass fix(client): theme/i18n dual-entry split — service classes + cordis merges move to src/client (host catalog scanner no longer misclassifies client services), node halves keep types + empty apply; catalogs regenerated docs(gui): README substantiation batch 2 — runtime/ui-layout/ui-sidebar/ui-conversation: Model Experience short form, package-owned deferred-work ledgers (unload stub, watch approximation, /client value-import rule, global details state, two-state dots, stats duration gap, single-bundle caches) docs(gui): README substantiation batch 3 — ui-trajectory/ui-theme/i18n/web: Model Experience short form, deferred-work ledgers (placeholder charter, no theme toggle owner, empty locale dictionaries, one-shot rendering); both README gates green test(scripts): purity spec adopts clientBundle two-arg signature (explicit libEntry, no default) gates(gui): knip green — declaration-merge dep ignored, fake plugin id assembled at runtime, invariants dep de-duplicated to peer+dev, stale apps/web section dropped feat(gui): 门禁波次 host 三包 invariant 形状——apiproxy explained-empty 伴生(wire 契约层零事件面)、webserver 真关系伴生(manifest 行必解析出 clientPath,防 __DSH_BOOT__ 广告 404 bundle;apps/cli 发布 webPlugins 键供审计)、runtime 补 files 白名单;三包 exports/files/peer+dev/tsconfig refs 齐 fw-react 形状;constraints+invariants 双 gate 零违规 build(client): ui-layout/ui-sidebar tsdown configs adopt the explicit two-arg clientBundle signature (orphaned follow-up of the manifest shape batch) refactor(gui): shell boot becomes a library face — bootWebShell(el) exported for the apps/web entry; main.ts retired refactor(gui): exports 纪律刀1——ui-theme/i18n node index 收敛为只空 apply(Translate/LocaleDict/ThemeTokens 类型下沉 src/client/),ui-conversation 的 I18nService import 改 /client 子路径 build(typecheck): converge to root host aggregate + tsconfig.client.json — delete tsconfig.host.json, verify-cordis-config seeds both aggregates feat(gui): apps/web restored as the vite application — thin main over bootWebShell; dsh-client-web becomes a plain lib (index exports shell surface, vite files and e2e moved out) chore(gates): knip.json rewritten on the master base — same semantics, minimal diff (formatting churn dropped) docs(gui): 时效清扫②——testing.md 删 web-ui 覆盖豁免残句;web-styling.md 加 token 换代头注(--dsw-* 现行、工程约束条款仍有效并注明收编处) docs(gui): 时效清扫③——四对 GUI Agent Note 加路径更新头注(web-runtime/web-ui/dsh-frontend→现行 12 包结构;设计结论存续声明;双语对同步) docs(gui): 时效清扫③b——四对 note 头注的 i18n 配对哈希重录 build(typecheck): minimal-diff tsconfig shape — drop root files entry (purity spec + preset move to client program), compress comments, drop redundant util/home root ref feat(gui): apps/web restoration follow-through — dsh-frontend package name, cli dist resolve, root build:web filter, tsdown exemption dropped, vitest web lane + knip + client aggregate retargeted, e2e paths rebased refactor(gui): exports 纪律刀2——connection wire 六件 git mv 进 src/client/(wire 即该 dshClient 插件的 client 半),node index=只空 apply,/client 半边整面导出(v3 §3.2 清单原样),包内 tests 改 src/client 直取 refactor(gui): exports 纪律刀3——runtime 实现整体下沉 src/client/(sessions/slots/loader;契约类型与 cordis merge 随迁 client/index),node index=只空 apply;./loader exports 指 client/loader;全消费面(web 壳/ui-sidebar/ui-trajectory/tests)bare→/client 机械跟改;vitest.e2e 换 tsconfig.vitest paths(root tsconfig 排除 client 会把 /client import 掉到 exports 的浏览器 dist bundle) refactor(gui): exports 纪律刀3 补遗——ui-layout 三处 bare runtime import 改 /client(刀3 消费面机械跟改漏提交件;跨属地机械一行×3 报备 ui-shell) test(gui): drop the getSessionManager singleton case — the init/get pair is a dead legacy-boot surface with zero live consumers (SessionsService constructs and holds the manager under the plugin architecture); source removal tracked with rt-core refactor(gui): 删 manager.ts 尾部 initSessionManager/getSessionManager 单例对——旧 boot 直连遗物,插件化下 SessionsService 构造持有 manager,全仓零活消费者(convo-b 测试清扫对表,其测试用例已先行退役 7e2c51898);头注释同步去单例措辞 code refactor
2026-07-19 21:17:57 +08:00
subgraph group_client["packages/client"]
pkg_client_connection["client-connection"]
pkg_client_hmr["client-hmr"]
pkg_client_locale["client-locale"]
pkg_client_modules["client-modules"]
feat(gui): step1 skeleton — dsc web serves built web UI over booted harness host Five new modules: apps/dsc (bin: parseArgs + node:http static server + signal shutdown), packages/host/apiproxy (programmatic harness core composition, agents:[]), packages/client/web-runtime (React-free browser runtime), packages/client/web-ui (React mount), apps/web (vite build entry producing dist consumed by apps/dsc via package exports). Root wiring: apps/* workspace glob, dsh-* paths for host/client groups, demo:web script, apps/web/dist gitignore. No protocol/API routes yet — contract lands in step2 (see missions/tasks/20260719-1902-apiproxy-api-design). Includes the design + implementation archives (spec v2.1, deepseekchat baseline and harness boot research, implementation run log). Acceptance: 12/12 passed incl. real-key llm.stream smoke (51 chunks). feat(gui): apiproxy — four-quadrant RPC contract + fetch carriers, live end to end Contract layer (src/api/, 14 files): four named wire message types (ClientRequest / ServerResponse / ServerRequest / ClientResponse) as a discriminated union over strict bidirectional rpcId (initiator mints, responder echoes; channel and message fully decoupled — HTTP is the client->server pipe, SSE the reverse); narrow RpcRequest<P>/ RpcResponse<T> signature forms; RpcMethodMap with RequestPayload<K>/ ResponseValue<K> derivation; typed RpcError details map; approval/ question responses modeled as ClientResponse via a single /api/respond endpoint (RpcReceipt carrier ack); zod schemas anchored per Wire<T> against exactOptionalPropertyTypes. impl/api-proxy.ts: describe/list/create, both SSE streams (frame queue pump, subscribed baseline, lifecycle frames, signal cleanup); history pages on message boundaries (tail-back scan, partial included in the tail page); prompt dispatches queue->agent.send / steer->agent.steer with rpcId carried through MessageSource; cancel for attached sessions; cold-session resume deduped via a per-id promise map; host-level provider/model defaults injected at create/resume. fetch/: mechanical UNARY_ROUTES table, two-level parse with path==method check, SSE frames completed to ServerRequest full form; client mints -> narrows -> envelopes outbound, verifies rpcId echo inbound, streams SSE frames, four-quadrant onEnvelope tap (debug panel choke point). Real-browser fixes: URL base resolves to location.origin (hardcoded internal base broke real pages), browser-safe export paths. Design archives: contract design.md v2.0 with decision log, core-coverage audit, comparative studies, step2 impl run log. Probed end to end over real HTTP: prompt -> live model stream -> history returns the finished reply. feat(gui): RpcLog debug panel — fixture-driven milestone, playwright-verified 10/10 web-runtime: rpcLog + ui slices (zustand), four-quadrant RpcLogEntry (client-request / server-response / server-request / client-response), onEnvelope tap -> microtask-batched pump with 500-entry ring buffer, ConnectionController (private state, backoff reconnect), fixture API with fake envelopes (?fixture switch), bootWebRuntime; contract types via temporary local copies (api-types.ts, swapped for real imports when W3 client lands). web-ui: components/panels/RpcLog five-piece set (badge with unread count, floating panel, direction glyphs per quadrant, same-rpcId pair highlighting in two families, JSON payload expand, follow/pause, clear), App shell, utils/formatRelative, light-theme CSS variables with dark placeholders. dsc bin: mime lookup fixed to use the actually-served file (naked '/?query' no longer falls through to octet-stream download); shutdown closes SSE keep-alive connections so SIGTERM actually exits. Acceptance: scripts/verify-rpclog-panel.mjs (chromium headless) ALL PASS 10/10 over design.md §D 1-6. pkg: add web scripts for building feat(gui): session milestone — list + conversation over Session OOP, styled RpcLog v2.1 web-runtime: Session/SessionManager object layer (resident instances, mux frame routing, lineage flattening), foldSurface adapter with padding sentinels for paged windows, chunk accumulator for streaming partials, batched change notification (useSyncExternalStore contract), connection sinks + reconnect fix (the 300ms self-abort reconnect storm that made the session list flap is gone), fixture rewritten as a scripted host (60-turn history, typewriter replay, resident pending approval, child session); temporary contract copies deleted in favor of real apiproxy imports. web-ui: sessions screen (list with lineage indent + selection as container-local state), conversation view (turn grouping, reasoning fold, tool cards, steering, pending interaction cards, upward paging with scroll anchoring), input bar with queue/steer/stop; RpcLog panel restyled per docs/web-styling.md (tokenized palette, quadrant badge glyphs now vertical ↑↓⇟⇞, pair highlighting, floating shadow). docs/web-styling.md: living style guide (tokens, visual baseline, coding rules, evolution log). Acceptance: verify-session.mjs 31/31, verify-session-real.mjs 5/5 (real model streaming), verify-rpclog-panel.mjs 10/10. feat(gui): hostruntime split + repo-wide package prefix rename Package split (design: 20260720-0101-hostruntime-split-design): dsh-host-runtime carries bootHost + createApiProxy + startHost() (RunningHost {api, handler, defaults, ctx, dispose} — the seam Electron and any future shell reuses; ctx is the official front-door mount point); dsh-host-webserver carries the node:http static+API bridge (fixed: abort now keys on res 'close' + writableEnded — req 'close' fires on body end since Node 16 and was killing every SSE stream instantly, the reconnect-storm root cause); apps/dsc is now a thin assembly with web/-p subcommands. dsc -p runs the full isomorphic carrier chain in process (second real protocol consumer; probed end-to-end against the live model). Naming rule (user decree): packages under host/ and client/ carry the directory prefix in their npm name — dsh-host-apiproxy, dsh-client-web-runtime, dsh-client-web-ui renamed repo-wide in one frozen batch; explicit tsconfig paths entries added where the wildcard no longer matches. Acceptance: verify-session 31/31, verify-rpclog-panel 10/10, verify-session-real 7/7 (incl. new 12s connection-stability sentinels), tsc green, dsc web + dsc -p smoke both pass. refactor(gui): AbstractApiClient class hierarchy — OO client with inheritable seams AbstractApiClient (apiproxy) carries every protocol invariant: rpcId minting, four-quadrant envelope wrap/unwrap, zod parsing, SSE frame parsing, the payload-direct IApiClient surface (callers no longer mint rpcIds — the carrier does), and the instance-level envelope observation pump (batched via microtask; moved off module-level globals in rpc-log.ts, which is now a pure subscriber mapping envelopes into store entries — the debug panel observes the connection, it is not part of it). Platform subclasses own two abstract seams (doFetch, onEnvelope) plus three protocol-level virtuals for transportless overrides: InProcessApiClient (apiproxy; dsc -p uses new InProcessApiClient( host.handler)), WebApiClient (web-runtime), FixtureApiClient (fixture now subclasses instead of wrapping). Naming per decree: AbstractApiClient / IApiClient; ApiProxy stays the impl-side narrow-form contract. headless.ts call sites drop rpcRequest wrappers (payload-direct); split-design archive updated with the naming-rule ledger. tsc green; verify-session 31/31, verify-rpclog-panel 10/10, verify-session-real 7/7 (12s connection sentinel count=4); dsc -p smoke CALLER-OK. feat(gui): InputBar final form — bug batch, deepseekchat layout, single primary button, running locks input Squashes the whole InputBar iteration batch: IME/caret/auto-grow/focus/dedup bug fixes, layout aligned to the deepseekchat baseline, single primary button with hover flyout, finalized button semantics with the Codex-style icon circle, and running-state locking where stop is the only mid-turn action. The same batch carried the Chinese-to-English code comment sweep (density pruned), folded in here. docs(gui): purge work-log references from code comments 76 design-doc references cleared across the GUI packages: section pointers inlined as self-contained constraint statements, pure pointer comments dropped, milestone codenames and ruling tags out, and the 14 contract file headers switched to the formal RFC (the only sanctioned external reference). web-styling.md now cites the styling RFC instead of the disposable research archive. grep for work-log reference variants is clean across the GUI packages. docs(gui): file-header comments self-contained — drop RFC filename references RFC renames/reorgs must not require a source sweep (the 2026-07-20 two-way merge proved it). 11 headers lose only the '(RFC …)' tail and stay self-contained; api-proxy.ts keeps its minimal-first note. fix(gui): session streaming — freeze interrupted partials, sweep stale running calls, send force-scrolls Aborted turns never emit the finalizing assistant/message, so the accumulated partial and its running tool cards kept rendering below later messages — the "new message lands above the stopped reply" illusion. turn/end side effects now freeze content-bearing partials into interrupted terminal nodes (fractional seq keeps flow order; the live freeze and history replay converge through applyEventSideEffects, so a refresh reconstructs identical frozen nodes) and turn running tool cards into interrupted terminal cards; only content-free partials are swept outright. ConversationView gains the send-force-scroll rule (own words must be visible) alongside the pre-update atBottom follow flag. Regressions pinned as E2-4a–c (real host) and §E1-11h (fixture). feat(gui): webserver hardening verify script feat(gui): dark-mode toggle pinned to the sidebar bottom Interim home before the Settings page exists (the button re-homes with zero logic change — mechanics live in utils/theme.ts): html[data-theme] flip + dsc.theme localStorage, stored choice wins over the OS prefers-color-scheme default, applied in mount() before first paint so a dark reload never flashes light. Moon/sun inline SVG icon button at the sidebar's pinned bottom row. Pure front-end local concern: no RPC, no Session/store involvement. Dark sweep of list/conversation/input card/RPC panel found no unreadable pairs — no token changes needed. docs(gui): GUI RFCs and web styling handbook Layering+RPC protocol and web client architecture RFCs (post-reorg, developer-facing polish folded in) plus the styling engineering handbook. Mission work logs live in the commit above; PRs can be cut from this commit to include formal docs only. fix(gui): client object-layer hardening — audit timing/reference/resilience batches (S3-S5,C1-C3,C5-C8) fix(gui): carrier error channel + webserver backpressure (audit A1-A5,A7-A10,R2,R5) feat(gui): session persistence surface — cold list, project cwd, legacy no-cwd retirement refactor: rename dsc CLI to dsh — apps/cli, bin name, package scope Includes the root tsconfig project-references fix for host/* and client/web-runtime (originally a separate build fix commit). test(gui): three-tier suite — protocol/object/browser lanes, tier-a fill to per-file 100% test(gui): jsdom lane for web-ui + web-runtime coverage gate entry docs(gui): GUI testing system RFC (zh) feat(gui): tool-card views — contract slot, host-computed delivery, three-level card fallback fix(gui): lint clean across GUI packages — wrap long doc comments, drop dead type args, sync-return methods without awaits docs(gui): doc-sync mechanical fixes — JSDoc on apiproxy/host exports, RFC sketch fences ignore-check, md-wrap paragraphs, drop missions links, web-ui plain-ts entry chore(gui): module-graph regen + knip clean — drop dead re-exports, internalize createFixtureApi, scan web-ui tsx and verify mjs scripts build(gui): wire client/host packages into the lib build shape — tsc references + tsdown (web-ui css-external), lib manifests, cordis peer, apiproxy typed subpaths, vite src aliases test(gui): host-side per-file 100% coverage — apiproxy schema/carrier suites, webserver http-bridge suite, host-runtime composition suite; client/* coverage excluded pending the browser-side testing work item docs(gui): package READMEs for the five GUI packages — model-experience audit entries, limitations sections docs(gui): bilingual RFC pairs + client JSDoc completion — translate the three GUI RFCs to English with i18n records and manifest ratchet, Consequences sections both sides, full client/* export JSDoc, regen doc graphs and RFC index fix(scripts): doc-typecheck built-declarations mode maps /src/* subpath wildcards (apiproxy browser-safe channels) docs(gui): apply dsh rename across pr-gates docs — READMEs, layering RFC en, web-ui entry comment, i18n re-record fix(gui): post-rebase lint reconciliation — wrap main-tree long doc comments, read-through narrowing guards, abortError Error normalization, handleUnary generic justification fix(gui): post-rebase doc/test reconciliation — align host specs with evolved carrier contracts (sentinel rpcId, stream/error surfacing, url-path transport messages, defaults.cwd), Agent Note titles and relocated links, KV Cache effect sections, JSDoc on evolved exports fix(gui): second-rebase reconciliation to 509db0cb3 — restore api panel exports the baseline suites consume, knip workspace entries for jsdom lane and apps/web smokes, hoist result narrowing, align testing.md to the narrowed web-ui exclusion fix(test): vitest-scoped tsconfig maps bare imports for tsx specs — with GUI manifests now pointing at lib, an unmapped importer loaded a second copy of the web-runtime singletons fix(gui): typecheck + lint clean over the tool-card batch — brand callIds and object-form turn/end reason in the view spec, narrow fixture arg stringification, wrap long v8-ignore comments docs(gui): export JSDoc for tool-card surfaces + testing-note pairing header docs: rfc for web testing feat: add tools to host-runtime fix(gui): dispatch agent/error via agentEvents in host-runtime spec — mounted invariants plugin rejects raw ctx.emit without the scope carrier fix(gui): restore GUI knip workspaces + scripts/mjs entries and regenerate lockfile after master rebase fix(gui): post-rebase gate repairs — drop context-node envelope (master unwrapped injected content envelopes), regen event matrix, condense testing.md web-ui exclusion within budget fix(session): browser-safe deep-equal in surface — node:util import broke the vite bundle ci(gates): frontend vite build joins pre-push — node: imports in the client closure pass tsc but break the browser bundle test(tui): drop the checkout-dependent process.cwd() harness default — a long worktree path pushes the footer token counters past the 88-column fake terminal test(gui): jsdom behavior E2E — conversation main path over fixture runtime, reconnect banner lifecycle test(gui): jsdom RPC panel behavior — ledger rows, expand, pairing, pause/clear, follow-pause, payload truncation test(gui): jsdom tier-2 — InputBar guards, reasoning fold, JSON blocks, message variants, theme, create-then-select; act-harden banner case test(gui): jsdom tier-3 — ConversationView states/paging/force-bottom, ToolCallCard arms, PendingCard, list rows test(gui): jsdom tails — view-card variants, LogRow directions, registry hygiene, badge overflow, hook ops, mount glue test(gui): jsdom tails round 2 — call-ref blocks, resume follow, view precedence, failed create, empty-diff arm test(gui): jsdom final arms — anchor compensation, follow-off, interval ticks, view halves, node-over-running precedence test(gui): web-ui joins the per-file 100% coverage gate Annotation-only src changes plus the config swap. The web-ui exclusion is replaced by a single index.tsx entry (stale byte-identical duplicate of mount.tsx, nothing imports it; same entry-glue treatment as bin.ts) and the coverage include gains .tsx. v8-ignore sites (each with its reason inline): - ConversationView 3x ref-null guards; InputBar disabled-click guard - ToolCallCard both-null arms + windowless-custom argsRaw arm - LogRow css-module key fallbacks (start/stop block); RpcLogBody 3x ref-null guards - web-runtime drift from the tool-card batch: fixture presenter catch/str typo-guards, dense-array guards (fold-adapter reset, session rebuild, fixture backscan), live view-present arm (fixture replays are text-only; view vocabulary is covered by the history samples) test(gui): close the PR #443 host-side coverage gaps — apiproxy client abort arms, api-proxy cold/view paths, webserver drain - apiproxy fetch/client.ts: 3 new cases (pre-aborted signal short-circuits before transport + string reason mapping, non-Error/string reason falls to the default AbortError message, signal-less doFetch passthrough) - runtime/api-proxy.ts: one v8-ignore (summarizeCold cwd arm — list() filters cwd-less legacy metas) + api-proxy-cold.spec.ts (cold list merge: mtime source, locate-undefined and vanished-log fallbacks, lineage; no-persistence/no-factory resume → internal) + 2 view cases (history views with meta passthrough and orphan/bad-args/presenterless soft-falls, session/disposed open-call cleanup on the mux stream) - webserver/index.ts: /api/big fixture drives both drain-wait legs (full 8MiB readback after drain, mid-chunk disconnect wakes via 'close') feat: app shell fix: rebase conflicts fix: coverage fix(gui): lint clean after rebase — wrap long v8-ignore comments, unconditional v1 detail-block claim chore(gui): remove browser/probe verify scripts from scripts/ The six GUI acceptance/probe scripts (carrier-errors, rpclog-panel, session, session-real, webserver-backpressure, webserver-hardening) leave the repo's scripts/ tree; the three code comments that pointed at them now describe the coverage lane without naming a script path. fix(webserver): guard the request callback — one malformed request must not kill the process The async handle() had no top-level catch, so any throw inside it (a bad %-escape reaching decodeURIComponent, a client dropping mid-body, a response stream erroring) became an unhandled rejection and took the whole process down (audit R1 must-fix). The guard answers 400 when headers are not out yet, destroys the socket when they are, and reports the failure to onError (the package never prints). Spec covers all three legs: %-escape barrage → 400 + server stays alive, non-Error throw wrapped for onError, mid-stream explosion → socket teardown. feat: client AGENTS.md fix: client/AGENTS.md fix: rebase feat(gui): T0 cut 1 — 12 client package skeletons with contract stubs, dshClient declarations, tsdown client preset, theme token sheets feat(gui): T0 cut 2 — pure git mv migration per v3 §11 (connection six, runtime sessions/kernel, ui-conversation chat, ui-primitives markdown family, web shell + e2e) feat(gui): T0 cuts 3+4 — import rewiring to new package names, .legacy demotion of owner-rewrite files, legacy web-runtime/web-ui/apps-web retired to attic feat(gui): connection 对账刀——index.ts 精确导出清单替换 export *,intents.legacy 溶解删除 feat(client/ui-slots): SlotCore real implementation — kind semantics, sync version + microtask-batched notify, onMutate bridge feat(gui): web shell vite alias — retarget to new client packages, shell static surface only feat(gui): host 侧刀属地半——HostWebPluginRegistry(entries 扫描+internal/plugin 去抖重扫+dshClient 校验+exports./client 解析)、GET /plugins/<id>/client.js 分发端点、GET / 与 SPA fallback 注入 __DSH_BOOT__(webPlugins 可选注入,不传行为不变) feat(web-react): add use-sync-external-store dep + local shim typings feat(web-react): bindSnapshotSelector via uSES with-selector shim feat(gui): ui-layout concession-chain solver — pure computeColumns with contract geometry feat(gui): ui-layout LayoutService — four persisted stores, clamped actions, list-driven prune feat(gui): ui-layout AppFrame styles — grid columns, collapse-safe borders, edge drag handles test(gui): 存量 spec 平移——connection 三件+runtime 六件自 attic 捞回改包名路径全绿;api-helpers 按归属拆分(wire 半留 connection、classifier 半随 conversation.ts 入 runtime);boot-intents/preinit/rpc-log 随 intents/rpc-log 退役不迁(记 v3 §3.2 溶解项) feat(client/ui-primitives): StateDot/Button/Pill/Input/Menu atoms, ConnectionBanner de-legacied to pure props, JsonBlock CSS on --dsw tokens feat(web-react): createSnapshotStore engine (rafFlush batch, persist opt-in, dev freeze) + spec feat(gui): ui-layout AppFrame — grid tracks, pointer-capture drag handles with rAF throttle, frame ResizeObserver feat(web-react): useInvoke (external pending store, stable invoke, concurrency count) + spec test(web-react): bind spec — equality bail, custom eq, zero resubscribe, StrictMode, method sources feat(gui): ui-layout index rewiring — real exports, client apply provides ctx.layout and defines three slots feat(web-react): SessionProvider (renderBody deps) + RootBindingProvider + binding contexts + spec feat(gui): web shell AppRoot boot-page styles — self-contained with neutral token fallbacks feat(gui): web shell AppRoot — boot gate over loader status, fail-loud plugin failure list fix(gui): AppRoot gates on explicit settled signal — status-derived readiness races the incrementally filled table feat(client/ui-theme): ThemeService real implementation — registry with built-in light/dark, apply toggles body[data-ds-dark-theme], third-party token overrides as body inline vars feat(web-react): scopedSlots outlet (kind matrix, inject WeakMap caches, per-entry error boundary) + spec feat(gui): web shell module-table seed — pure-library entities for the loader require surface feat(client/i18n): I18nService real implementation — ns×locale registry, stable bind(ns) reference, zh fallback chain, zh/en skeleton dictionaries feat(gui): web shell assembly closure — layout exports via module table, SessionProvider + scopedSlots + RootBindingProvider feat: client/ui-conversation feat: code codedoc build(gui): root bundle green — web shell excluded from the lib workspace (vite app), ui-primitives lib externalizes css side-effect imports (web-ui precedent) gates(gui): verify-cordis-config follows aggregate tsconfig references (root is a shell over host/client programs); module graph regenerated for the twelve client packages chore(gui): retire legacy migration sources — every owner rewrite landed (t0-checklist §7 ledger honored); orphan css of retired components removed gates(gui): knip green groundwork — e2e/tsx entries for the new packages, loader-runtime deps ignored where loading is by specifier string, fake plugin ids un-bare-named, dead test export dropped chore(client): manifest shape batch A — ui-slots/web-react/ui-primitives invariant companions, files whitelist, cordis+invariants peer/dev, tsconfig refs chore(client): manifest shape batch B — connection/runtime/ui-conversation/ui-trajectory files whitelist, cordis peer+dev, explicit invariant lib entries (clientBundle signature) chore(client): manifest shape batch C — i18n/ui-layout/ui-sidebar/ui-theme invariant companions, files whitelist, invariants peer/dev, tsconfig refs chore(client): manifest shape batch D — web shell gains node-half lib entry + invariant companion + uniform files whitelist chore(client): drop verified-unused deps — dsh-tools from runtime/ui-conversation (types ride /presentation), ui-primitives+clsx from ui-layout gates(gui): doc-gate fixes — theme JSDoc prose, three client type-link exemptions, agent-note paths follow the migration, config catalog regenerated gates(gui): type-equiv manifest follows the types.ts extraction, approval JSDoc keeps its link form, persistence catalog regenerated docs(gui): per-constant JSDoc on the contract geometry exports (export-jsdoc gate) test(gates): loader-composition budget covers cold tsx resolution after the program split (was flaking at the default 5s) docs(gui): README substantiation batch 1 — ui-slots/ui-primitives/web-react/connection: Model Experience short form, real deferred-work ledgers, description accuracy pass fix(client): theme/i18n dual-entry split — service classes + cordis merges move to src/client (host catalog scanner no longer misclassifies client services), node halves keep types + empty apply; catalogs regenerated docs(gui): README substantiation batch 2 — runtime/ui-layout/ui-sidebar/ui-conversation: Model Experience short form, package-owned deferred-work ledgers (unload stub, watch approximation, /client value-import rule, global details state, two-state dots, stats duration gap, single-bundle caches) docs(gui): README substantiation batch 3 — ui-trajectory/ui-theme/i18n/web: Model Experience short form, deferred-work ledgers (placeholder charter, no theme toggle owner, empty locale dictionaries, one-shot rendering); both README gates green test(scripts): purity spec adopts clientBundle two-arg signature (explicit libEntry, no default) gates(gui): knip green — declaration-merge dep ignored, fake plugin id assembled at runtime, invariants dep de-duplicated to peer+dev, stale apps/web section dropped feat(gui): 门禁波次 host 三包 invariant 形状——apiproxy explained-empty 伴生(wire 契约层零事件面)、webserver 真关系伴生(manifest 行必解析出 clientPath,防 __DSH_BOOT__ 广告 404 bundle;apps/cli 发布 webPlugins 键供审计)、runtime 补 files 白名单;三包 exports/files/peer+dev/tsconfig refs 齐 fw-react 形状;constraints+invariants 双 gate 零违规 build(client): ui-layout/ui-sidebar tsdown configs adopt the explicit two-arg clientBundle signature (orphaned follow-up of the manifest shape batch) refactor(gui): shell boot becomes a library face — bootWebShell(el) exported for the apps/web entry; main.ts retired refactor(gui): exports 纪律刀1——ui-theme/i18n node index 收敛为只空 apply(Translate/LocaleDict/ThemeTokens 类型下沉 src/client/),ui-conversation 的 I18nService import 改 /client 子路径 build(typecheck): converge to root host aggregate + tsconfig.client.json — delete tsconfig.host.json, verify-cordis-config seeds both aggregates feat(gui): apps/web restored as the vite application — thin main over bootWebShell; dsh-client-web becomes a plain lib (index exports shell surface, vite files and e2e moved out) chore(gates): knip.json rewritten on the master base — same semantics, minimal diff (formatting churn dropped) docs(gui): 时效清扫②——testing.md 删 web-ui 覆盖豁免残句;web-styling.md 加 token 换代头注(--dsw-* 现行、工程约束条款仍有效并注明收编处) docs(gui): 时效清扫③——四对 GUI Agent Note 加路径更新头注(web-runtime/web-ui/dsh-frontend→现行 12 包结构;设计结论存续声明;双语对同步) docs(gui): 时效清扫③b——四对 note 头注的 i18n 配对哈希重录 build(typecheck): minimal-diff tsconfig shape — drop root files entry (purity spec + preset move to client program), compress comments, drop redundant util/home root ref feat(gui): apps/web restoration follow-through — dsh-frontend package name, cli dist resolve, root build:web filter, tsdown exemption dropped, vitest web lane + knip + client aggregate retargeted, e2e paths rebased refactor(gui): exports 纪律刀2——connection wire 六件 git mv 进 src/client/(wire 即该 dshClient 插件的 client 半),node index=只空 apply,/client 半边整面导出(v3 §3.2 清单原样),包内 tests 改 src/client 直取 refactor(gui): exports 纪律刀3——runtime 实现整体下沉 src/client/(sessions/slots/loader;契约类型与 cordis merge 随迁 client/index),node index=只空 apply;./loader exports 指 client/loader;全消费面(web 壳/ui-sidebar/ui-trajectory/tests)bare→/client 机械跟改;vitest.e2e 换 tsconfig.vitest paths(root tsconfig 排除 client 会把 /client import 掉到 exports 的浏览器 dist bundle) refactor(gui): exports 纪律刀3 补遗——ui-layout 三处 bare runtime import 改 /client(刀3 消费面机械跟改漏提交件;跨属地机械一行×3 报备 ui-shell) test(gui): drop the getSessionManager singleton case — the init/get pair is a dead legacy-boot surface with zero live consumers (SessionsService constructs and holds the manager under the plugin architecture); source removal tracked with rt-core refactor(gui): 删 manager.ts 尾部 initSessionManager/getSessionManager 单例对——旧 boot 直连遗物,插件化下 SessionsService 构造持有 manager,全仓零活消费者(convo-b 测试清扫对表,其测试用例已先行退役 7e2c51898);头注释同步去单例措辞 code refactor
2026-07-19 21:17:57 +08:00
pkg_client_runtime["client-runtime"]
pkg_client_schema_form["client-schema-form"]
pkg_client_test_runtime["client-test-runtime"]
pkg_client_ui_command["client-ui-command"]
feat(gui): step1 skeleton — dsc web serves built web UI over booted harness host Five new modules: apps/dsc (bin: parseArgs + node:http static server + signal shutdown), packages/host/apiproxy (programmatic harness core composition, agents:[]), packages/client/web-runtime (React-free browser runtime), packages/client/web-ui (React mount), apps/web (vite build entry producing dist consumed by apps/dsc via package exports). Root wiring: apps/* workspace glob, dsh-* paths for host/client groups, demo:web script, apps/web/dist gitignore. No protocol/API routes yet — contract lands in step2 (see missions/tasks/20260719-1902-apiproxy-api-design). Includes the design + implementation archives (spec v2.1, deepseekchat baseline and harness boot research, implementation run log). Acceptance: 12/12 passed incl. real-key llm.stream smoke (51 chunks). feat(gui): apiproxy — four-quadrant RPC contract + fetch carriers, live end to end Contract layer (src/api/, 14 files): four named wire message types (ClientRequest / ServerResponse / ServerRequest / ClientResponse) as a discriminated union over strict bidirectional rpcId (initiator mints, responder echoes; channel and message fully decoupled — HTTP is the client->server pipe, SSE the reverse); narrow RpcRequest<P>/ RpcResponse<T> signature forms; RpcMethodMap with RequestPayload<K>/ ResponseValue<K> derivation; typed RpcError details map; approval/ question responses modeled as ClientResponse via a single /api/respond endpoint (RpcReceipt carrier ack); zod schemas anchored per Wire<T> against exactOptionalPropertyTypes. impl/api-proxy.ts: describe/list/create, both SSE streams (frame queue pump, subscribed baseline, lifecycle frames, signal cleanup); history pages on message boundaries (tail-back scan, partial included in the tail page); prompt dispatches queue->agent.send / steer->agent.steer with rpcId carried through MessageSource; cancel for attached sessions; cold-session resume deduped via a per-id promise map; host-level provider/model defaults injected at create/resume. fetch/: mechanical UNARY_ROUTES table, two-level parse with path==method check, SSE frames completed to ServerRequest full form; client mints -> narrows -> envelopes outbound, verifies rpcId echo inbound, streams SSE frames, four-quadrant onEnvelope tap (debug panel choke point). Real-browser fixes: URL base resolves to location.origin (hardcoded internal base broke real pages), browser-safe export paths. Design archives: contract design.md v2.0 with decision log, core-coverage audit, comparative studies, step2 impl run log. Probed end to end over real HTTP: prompt -> live model stream -> history returns the finished reply. feat(gui): RpcLog debug panel — fixture-driven milestone, playwright-verified 10/10 web-runtime: rpcLog + ui slices (zustand), four-quadrant RpcLogEntry (client-request / server-response / server-request / client-response), onEnvelope tap -> microtask-batched pump with 500-entry ring buffer, ConnectionController (private state, backoff reconnect), fixture API with fake envelopes (?fixture switch), bootWebRuntime; contract types via temporary local copies (api-types.ts, swapped for real imports when W3 client lands). web-ui: components/panels/RpcLog five-piece set (badge with unread count, floating panel, direction glyphs per quadrant, same-rpcId pair highlighting in two families, JSON payload expand, follow/pause, clear), App shell, utils/formatRelative, light-theme CSS variables with dark placeholders. dsc bin: mime lookup fixed to use the actually-served file (naked '/?query' no longer falls through to octet-stream download); shutdown closes SSE keep-alive connections so SIGTERM actually exits. Acceptance: scripts/verify-rpclog-panel.mjs (chromium headless) ALL PASS 10/10 over design.md §D 1-6. pkg: add web scripts for building feat(gui): session milestone — list + conversation over Session OOP, styled RpcLog v2.1 web-runtime: Session/SessionManager object layer (resident instances, mux frame routing, lineage flattening), foldSurface adapter with padding sentinels for paged windows, chunk accumulator for streaming partials, batched change notification (useSyncExternalStore contract), connection sinks + reconnect fix (the 300ms self-abort reconnect storm that made the session list flap is gone), fixture rewritten as a scripted host (60-turn history, typewriter replay, resident pending approval, child session); temporary contract copies deleted in favor of real apiproxy imports. web-ui: sessions screen (list with lineage indent + selection as container-local state), conversation view (turn grouping, reasoning fold, tool cards, steering, pending interaction cards, upward paging with scroll anchoring), input bar with queue/steer/stop; RpcLog panel restyled per docs/web-styling.md (tokenized palette, quadrant badge glyphs now vertical ↑↓⇟⇞, pair highlighting, floating shadow). docs/web-styling.md: living style guide (tokens, visual baseline, coding rules, evolution log). Acceptance: verify-session.mjs 31/31, verify-session-real.mjs 5/5 (real model streaming), verify-rpclog-panel.mjs 10/10. feat(gui): hostruntime split + repo-wide package prefix rename Package split (design: 20260720-0101-hostruntime-split-design): dsh-host-runtime carries bootHost + createApiProxy + startHost() (RunningHost {api, handler, defaults, ctx, dispose} — the seam Electron and any future shell reuses; ctx is the official front-door mount point); dsh-host-webserver carries the node:http static+API bridge (fixed: abort now keys on res 'close' + writableEnded — req 'close' fires on body end since Node 16 and was killing every SSE stream instantly, the reconnect-storm root cause); apps/dsc is now a thin assembly with web/-p subcommands. dsc -p runs the full isomorphic carrier chain in process (second real protocol consumer; probed end-to-end against the live model). Naming rule (user decree): packages under host/ and client/ carry the directory prefix in their npm name — dsh-host-apiproxy, dsh-client-web-runtime, dsh-client-web-ui renamed repo-wide in one frozen batch; explicit tsconfig paths entries added where the wildcard no longer matches. Acceptance: verify-session 31/31, verify-rpclog-panel 10/10, verify-session-real 7/7 (incl. new 12s connection-stability sentinels), tsc green, dsc web + dsc -p smoke both pass. refactor(gui): AbstractApiClient class hierarchy — OO client with inheritable seams AbstractApiClient (apiproxy) carries every protocol invariant: rpcId minting, four-quadrant envelope wrap/unwrap, zod parsing, SSE frame parsing, the payload-direct IApiClient surface (callers no longer mint rpcIds — the carrier does), and the instance-level envelope observation pump (batched via microtask; moved off module-level globals in rpc-log.ts, which is now a pure subscriber mapping envelopes into store entries — the debug panel observes the connection, it is not part of it). Platform subclasses own two abstract seams (doFetch, onEnvelope) plus three protocol-level virtuals for transportless overrides: InProcessApiClient (apiproxy; dsc -p uses new InProcessApiClient( host.handler)), WebApiClient (web-runtime), FixtureApiClient (fixture now subclasses instead of wrapping). Naming per decree: AbstractApiClient / IApiClient; ApiProxy stays the impl-side narrow-form contract. headless.ts call sites drop rpcRequest wrappers (payload-direct); split-design archive updated with the naming-rule ledger. tsc green; verify-session 31/31, verify-rpclog-panel 10/10, verify-session-real 7/7 (12s connection sentinel count=4); dsc -p smoke CALLER-OK. feat(gui): InputBar final form — bug batch, deepseekchat layout, single primary button, running locks input Squashes the whole InputBar iteration batch: IME/caret/auto-grow/focus/dedup bug fixes, layout aligned to the deepseekchat baseline, single primary button with hover flyout, finalized button semantics with the Codex-style icon circle, and running-state locking where stop is the only mid-turn action. The same batch carried the Chinese-to-English code comment sweep (density pruned), folded in here. docs(gui): purge work-log references from code comments 76 design-doc references cleared across the GUI packages: section pointers inlined as self-contained constraint statements, pure pointer comments dropped, milestone codenames and ruling tags out, and the 14 contract file headers switched to the formal RFC (the only sanctioned external reference). web-styling.md now cites the styling RFC instead of the disposable research archive. grep for work-log reference variants is clean across the GUI packages. docs(gui): file-header comments self-contained — drop RFC filename references RFC renames/reorgs must not require a source sweep (the 2026-07-20 two-way merge proved it). 11 headers lose only the '(RFC …)' tail and stay self-contained; api-proxy.ts keeps its minimal-first note. fix(gui): session streaming — freeze interrupted partials, sweep stale running calls, send force-scrolls Aborted turns never emit the finalizing assistant/message, so the accumulated partial and its running tool cards kept rendering below later messages — the "new message lands above the stopped reply" illusion. turn/end side effects now freeze content-bearing partials into interrupted terminal nodes (fractional seq keeps flow order; the live freeze and history replay converge through applyEventSideEffects, so a refresh reconstructs identical frozen nodes) and turn running tool cards into interrupted terminal cards; only content-free partials are swept outright. ConversationView gains the send-force-scroll rule (own words must be visible) alongside the pre-update atBottom follow flag. Regressions pinned as E2-4a–c (real host) and §E1-11h (fixture). feat(gui): webserver hardening verify script feat(gui): dark-mode toggle pinned to the sidebar bottom Interim home before the Settings page exists (the button re-homes with zero logic change — mechanics live in utils/theme.ts): html[data-theme] flip + dsc.theme localStorage, stored choice wins over the OS prefers-color-scheme default, applied in mount() before first paint so a dark reload never flashes light. Moon/sun inline SVG icon button at the sidebar's pinned bottom row. Pure front-end local concern: no RPC, no Session/store involvement. Dark sweep of list/conversation/input card/RPC panel found no unreadable pairs — no token changes needed. docs(gui): GUI RFCs and web styling handbook Layering+RPC protocol and web client architecture RFCs (post-reorg, developer-facing polish folded in) plus the styling engineering handbook. Mission work logs live in the commit above; PRs can be cut from this commit to include formal docs only. fix(gui): client object-layer hardening — audit timing/reference/resilience batches (S3-S5,C1-C3,C5-C8) fix(gui): carrier error channel + webserver backpressure (audit A1-A5,A7-A10,R2,R5) feat(gui): session persistence surface — cold list, project cwd, legacy no-cwd retirement refactor: rename dsc CLI to dsh — apps/cli, bin name, package scope Includes the root tsconfig project-references fix for host/* and client/web-runtime (originally a separate build fix commit). test(gui): three-tier suite — protocol/object/browser lanes, tier-a fill to per-file 100% test(gui): jsdom lane for web-ui + web-runtime coverage gate entry docs(gui): GUI testing system RFC (zh) feat(gui): tool-card views — contract slot, host-computed delivery, three-level card fallback fix(gui): lint clean across GUI packages — wrap long doc comments, drop dead type args, sync-return methods without awaits docs(gui): doc-sync mechanical fixes — JSDoc on apiproxy/host exports, RFC sketch fences ignore-check, md-wrap paragraphs, drop missions links, web-ui plain-ts entry chore(gui): module-graph regen + knip clean — drop dead re-exports, internalize createFixtureApi, scan web-ui tsx and verify mjs scripts build(gui): wire client/host packages into the lib build shape — tsc references + tsdown (web-ui css-external), lib manifests, cordis peer, apiproxy typed subpaths, vite src aliases test(gui): host-side per-file 100% coverage — apiproxy schema/carrier suites, webserver http-bridge suite, host-runtime composition suite; client/* coverage excluded pending the browser-side testing work item docs(gui): package READMEs for the five GUI packages — model-experience audit entries, limitations sections docs(gui): bilingual RFC pairs + client JSDoc completion — translate the three GUI RFCs to English with i18n records and manifest ratchet, Consequences sections both sides, full client/* export JSDoc, regen doc graphs and RFC index fix(scripts): doc-typecheck built-declarations mode maps /src/* subpath wildcards (apiproxy browser-safe channels) docs(gui): apply dsh rename across pr-gates docs — READMEs, layering RFC en, web-ui entry comment, i18n re-record fix(gui): post-rebase lint reconciliation — wrap main-tree long doc comments, read-through narrowing guards, abortError Error normalization, handleUnary generic justification fix(gui): post-rebase doc/test reconciliation — align host specs with evolved carrier contracts (sentinel rpcId, stream/error surfacing, url-path transport messages, defaults.cwd), Agent Note titles and relocated links, KV Cache effect sections, JSDoc on evolved exports fix(gui): second-rebase reconciliation to 509db0cb3 — restore api panel exports the baseline suites consume, knip workspace entries for jsdom lane and apps/web smokes, hoist result narrowing, align testing.md to the narrowed web-ui exclusion fix(test): vitest-scoped tsconfig maps bare imports for tsx specs — with GUI manifests now pointing at lib, an unmapped importer loaded a second copy of the web-runtime singletons fix(gui): typecheck + lint clean over the tool-card batch — brand callIds and object-form turn/end reason in the view spec, narrow fixture arg stringification, wrap long v8-ignore comments docs(gui): export JSDoc for tool-card surfaces + testing-note pairing header docs: rfc for web testing feat: add tools to host-runtime fix(gui): dispatch agent/error via agentEvents in host-runtime spec — mounted invariants plugin rejects raw ctx.emit without the scope carrier fix(gui): restore GUI knip workspaces + scripts/mjs entries and regenerate lockfile after master rebase fix(gui): post-rebase gate repairs — drop context-node envelope (master unwrapped injected content envelopes), regen event matrix, condense testing.md web-ui exclusion within budget fix(session): browser-safe deep-equal in surface — node:util import broke the vite bundle ci(gates): frontend vite build joins pre-push — node: imports in the client closure pass tsc but break the browser bundle test(tui): drop the checkout-dependent process.cwd() harness default — a long worktree path pushes the footer token counters past the 88-column fake terminal test(gui): jsdom behavior E2E — conversation main path over fixture runtime, reconnect banner lifecycle test(gui): jsdom RPC panel behavior — ledger rows, expand, pairing, pause/clear, follow-pause, payload truncation test(gui): jsdom tier-2 — InputBar guards, reasoning fold, JSON blocks, message variants, theme, create-then-select; act-harden banner case test(gui): jsdom tier-3 — ConversationView states/paging/force-bottom, ToolCallCard arms, PendingCard, list rows test(gui): jsdom tails — view-card variants, LogRow directions, registry hygiene, badge overflow, hook ops, mount glue test(gui): jsdom tails round 2 — call-ref blocks, resume follow, view precedence, failed create, empty-diff arm test(gui): jsdom final arms — anchor compensation, follow-off, interval ticks, view halves, node-over-running precedence test(gui): web-ui joins the per-file 100% coverage gate Annotation-only src changes plus the config swap. The web-ui exclusion is replaced by a single index.tsx entry (stale byte-identical duplicate of mount.tsx, nothing imports it; same entry-glue treatment as bin.ts) and the coverage include gains .tsx. v8-ignore sites (each with its reason inline): - ConversationView 3x ref-null guards; InputBar disabled-click guard - ToolCallCard both-null arms + windowless-custom argsRaw arm - LogRow css-module key fallbacks (start/stop block); RpcLogBody 3x ref-null guards - web-runtime drift from the tool-card batch: fixture presenter catch/str typo-guards, dense-array guards (fold-adapter reset, session rebuild, fixture backscan), live view-present arm (fixture replays are text-only; view vocabulary is covered by the history samples) test(gui): close the PR #443 host-side coverage gaps — apiproxy client abort arms, api-proxy cold/view paths, webserver drain - apiproxy fetch/client.ts: 3 new cases (pre-aborted signal short-circuits before transport + string reason mapping, non-Error/string reason falls to the default AbortError message, signal-less doFetch passthrough) - runtime/api-proxy.ts: one v8-ignore (summarizeCold cwd arm — list() filters cwd-less legacy metas) + api-proxy-cold.spec.ts (cold list merge: mtime source, locate-undefined and vanished-log fallbacks, lineage; no-persistence/no-factory resume → internal) + 2 view cases (history views with meta passthrough and orphan/bad-args/presenterless soft-falls, session/disposed open-call cleanup on the mux stream) - webserver/index.ts: /api/big fixture drives both drain-wait legs (full 8MiB readback after drain, mid-chunk disconnect wakes via 'close') feat: app shell fix: rebase conflicts fix: coverage fix(gui): lint clean after rebase — wrap long v8-ignore comments, unconditional v1 detail-block claim chore(gui): remove browser/probe verify scripts from scripts/ The six GUI acceptance/probe scripts (carrier-errors, rpclog-panel, session, session-real, webserver-backpressure, webserver-hardening) leave the repo's scripts/ tree; the three code comments that pointed at them now describe the coverage lane without naming a script path. fix(webserver): guard the request callback — one malformed request must not kill the process The async handle() had no top-level catch, so any throw inside it (a bad %-escape reaching decodeURIComponent, a client dropping mid-body, a response stream erroring) became an unhandled rejection and took the whole process down (audit R1 must-fix). The guard answers 400 when headers are not out yet, destroys the socket when they are, and reports the failure to onError (the package never prints). Spec covers all three legs: %-escape barrage → 400 + server stays alive, non-Error throw wrapped for onError, mid-stream explosion → socket teardown. feat: client AGENTS.md fix: client/AGENTS.md fix: rebase feat(gui): T0 cut 1 — 12 client package skeletons with contract stubs, dshClient declarations, tsdown client preset, theme token sheets feat(gui): T0 cut 2 — pure git mv migration per v3 §11 (connection six, runtime sessions/kernel, ui-conversation chat, ui-primitives markdown family, web shell + e2e) feat(gui): T0 cuts 3+4 — import rewiring to new package names, .legacy demotion of owner-rewrite files, legacy web-runtime/web-ui/apps-web retired to attic feat(gui): connection 对账刀——index.ts 精确导出清单替换 export *,intents.legacy 溶解删除 feat(client/ui-slots): SlotCore real implementation — kind semantics, sync version + microtask-batched notify, onMutate bridge feat(gui): web shell vite alias — retarget to new client packages, shell static surface only feat(gui): host 侧刀属地半——HostWebPluginRegistry(entries 扫描+internal/plugin 去抖重扫+dshClient 校验+exports./client 解析)、GET /plugins/<id>/client.js 分发端点、GET / 与 SPA fallback 注入 __DSH_BOOT__(webPlugins 可选注入,不传行为不变) feat(web-react): add use-sync-external-store dep + local shim typings feat(web-react): bindSnapshotSelector via uSES with-selector shim feat(gui): ui-layout concession-chain solver — pure computeColumns with contract geometry feat(gui): ui-layout LayoutService — four persisted stores, clamped actions, list-driven prune feat(gui): ui-layout AppFrame styles — grid columns, collapse-safe borders, edge drag handles test(gui): 存量 spec 平移——connection 三件+runtime 六件自 attic 捞回改包名路径全绿;api-helpers 按归属拆分(wire 半留 connection、classifier 半随 conversation.ts 入 runtime);boot-intents/preinit/rpc-log 随 intents/rpc-log 退役不迁(记 v3 §3.2 溶解项) feat(client/ui-primitives): StateDot/Button/Pill/Input/Menu atoms, ConnectionBanner de-legacied to pure props, JsonBlock CSS on --dsw tokens feat(web-react): createSnapshotStore engine (rafFlush batch, persist opt-in, dev freeze) + spec feat(gui): ui-layout AppFrame — grid tracks, pointer-capture drag handles with rAF throttle, frame ResizeObserver feat(web-react): useInvoke (external pending store, stable invoke, concurrency count) + spec test(web-react): bind spec — equality bail, custom eq, zero resubscribe, StrictMode, method sources feat(gui): ui-layout index rewiring — real exports, client apply provides ctx.layout and defines three slots feat(web-react): SessionProvider (renderBody deps) + RootBindingProvider + binding contexts + spec feat(gui): web shell AppRoot boot-page styles — self-contained with neutral token fallbacks feat(gui): web shell AppRoot — boot gate over loader status, fail-loud plugin failure list fix(gui): AppRoot gates on explicit settled signal — status-derived readiness races the incrementally filled table feat(client/ui-theme): ThemeService real implementation — registry with built-in light/dark, apply toggles body[data-ds-dark-theme], third-party token overrides as body inline vars feat(web-react): scopedSlots outlet (kind matrix, inject WeakMap caches, per-entry error boundary) + spec feat(gui): web shell module-table seed — pure-library entities for the loader require surface feat(client/i18n): I18nService real implementation — ns×locale registry, stable bind(ns) reference, zh fallback chain, zh/en skeleton dictionaries feat(gui): web shell assembly closure — layout exports via module table, SessionProvider + scopedSlots + RootBindingProvider feat: client/ui-conversation feat: code codedoc build(gui): root bundle green — web shell excluded from the lib workspace (vite app), ui-primitives lib externalizes css side-effect imports (web-ui precedent) gates(gui): verify-cordis-config follows aggregate tsconfig references (root is a shell over host/client programs); module graph regenerated for the twelve client packages chore(gui): retire legacy migration sources — every owner rewrite landed (t0-checklist §7 ledger honored); orphan css of retired components removed gates(gui): knip green groundwork — e2e/tsx entries for the new packages, loader-runtime deps ignored where loading is by specifier string, fake plugin ids un-bare-named, dead test export dropped chore(client): manifest shape batch A — ui-slots/web-react/ui-primitives invariant companions, files whitelist, cordis+invariants peer/dev, tsconfig refs chore(client): manifest shape batch B — connection/runtime/ui-conversation/ui-trajectory files whitelist, cordis peer+dev, explicit invariant lib entries (clientBundle signature) chore(client): manifest shape batch C — i18n/ui-layout/ui-sidebar/ui-theme invariant companions, files whitelist, invariants peer/dev, tsconfig refs chore(client): manifest shape batch D — web shell gains node-half lib entry + invariant companion + uniform files whitelist chore(client): drop verified-unused deps — dsh-tools from runtime/ui-conversation (types ride /presentation), ui-primitives+clsx from ui-layout gates(gui): doc-gate fixes — theme JSDoc prose, three client type-link exemptions, agent-note paths follow the migration, config catalog regenerated gates(gui): type-equiv manifest follows the types.ts extraction, approval JSDoc keeps its link form, persistence catalog regenerated docs(gui): per-constant JSDoc on the contract geometry exports (export-jsdoc gate) test(gates): loader-composition budget covers cold tsx resolution after the program split (was flaking at the default 5s) docs(gui): README substantiation batch 1 — ui-slots/ui-primitives/web-react/connection: Model Experience short form, real deferred-work ledgers, description accuracy pass fix(client): theme/i18n dual-entry split — service classes + cordis merges move to src/client (host catalog scanner no longer misclassifies client services), node halves keep types + empty apply; catalogs regenerated docs(gui): README substantiation batch 2 — runtime/ui-layout/ui-sidebar/ui-conversation: Model Experience short form, package-owned deferred-work ledgers (unload stub, watch approximation, /client value-import rule, global details state, two-state dots, stats duration gap, single-bundle caches) docs(gui): README substantiation batch 3 — ui-trajectory/ui-theme/i18n/web: Model Experience short form, deferred-work ledgers (placeholder charter, no theme toggle owner, empty locale dictionaries, one-shot rendering); both README gates green test(scripts): purity spec adopts clientBundle two-arg signature (explicit libEntry, no default) gates(gui): knip green — declaration-merge dep ignored, fake plugin id assembled at runtime, invariants dep de-duplicated to peer+dev, stale apps/web section dropped feat(gui): 门禁波次 host 三包 invariant 形状——apiproxy explained-empty 伴生(wire 契约层零事件面)、webserver 真关系伴生(manifest 行必解析出 clientPath,防 __DSH_BOOT__ 广告 404 bundle;apps/cli 发布 webPlugins 键供审计)、runtime 补 files 白名单;三包 exports/files/peer+dev/tsconfig refs 齐 fw-react 形状;constraints+invariants 双 gate 零违规 build(client): ui-layout/ui-sidebar tsdown configs adopt the explicit two-arg clientBundle signature (orphaned follow-up of the manifest shape batch) refactor(gui): shell boot becomes a library face — bootWebShell(el) exported for the apps/web entry; main.ts retired refactor(gui): exports 纪律刀1——ui-theme/i18n node index 收敛为只空 apply(Translate/LocaleDict/ThemeTokens 类型下沉 src/client/),ui-conversation 的 I18nService import 改 /client 子路径 build(typecheck): converge to root host aggregate + tsconfig.client.json — delete tsconfig.host.json, verify-cordis-config seeds both aggregates feat(gui): apps/web restored as the vite application — thin main over bootWebShell; dsh-client-web becomes a plain lib (index exports shell surface, vite files and e2e moved out) chore(gates): knip.json rewritten on the master base — same semantics, minimal diff (formatting churn dropped) docs(gui): 时效清扫②——testing.md 删 web-ui 覆盖豁免残句;web-styling.md 加 token 换代头注(--dsw-* 现行、工程约束条款仍有效并注明收编处) docs(gui): 时效清扫③——四对 GUI Agent Note 加路径更新头注(web-runtime/web-ui/dsh-frontend→现行 12 包结构;设计结论存续声明;双语对同步) docs(gui): 时效清扫③b——四对 note 头注的 i18n 配对哈希重录 build(typecheck): minimal-diff tsconfig shape — drop root files entry (purity spec + preset move to client program), compress comments, drop redundant util/home root ref feat(gui): apps/web restoration follow-through — dsh-frontend package name, cli dist resolve, root build:web filter, tsdown exemption dropped, vitest web lane + knip + client aggregate retargeted, e2e paths rebased refactor(gui): exports 纪律刀2——connection wire 六件 git mv 进 src/client/(wire 即该 dshClient 插件的 client 半),node index=只空 apply,/client 半边整面导出(v3 §3.2 清单原样),包内 tests 改 src/client 直取 refactor(gui): exports 纪律刀3——runtime 实现整体下沉 src/client/(sessions/slots/loader;契约类型与 cordis merge 随迁 client/index),node index=只空 apply;./loader exports 指 client/loader;全消费面(web 壳/ui-sidebar/ui-trajectory/tests)bare→/client 机械跟改;vitest.e2e 换 tsconfig.vitest paths(root tsconfig 排除 client 会把 /client import 掉到 exports 的浏览器 dist bundle) refactor(gui): exports 纪律刀3 补遗——ui-layout 三处 bare runtime import 改 /client(刀3 消费面机械跟改漏提交件;跨属地机械一行×3 报备 ui-shell) test(gui): drop the getSessionManager singleton case — the init/get pair is a dead legacy-boot surface with zero live consumers (SessionsService constructs and holds the manager under the plugin architecture); source removal tracked with rt-core refactor(gui): 删 manager.ts 尾部 initSessionManager/getSessionManager 单例对——旧 boot 直连遗物,插件化下 SessionsService 构造持有 manager,全仓零活消费者(convo-b 测试清扫对表,其测试用例已先行退役 7e2c51898);头注释同步去单例措辞 code refactor
2026-07-19 21:17:57 +08:00
pkg_client_ui_conversation["client-ui-conversation"]
pkg_client_ui_goal["client-ui-goal"]
feat(gui): step1 skeleton — dsc web serves built web UI over booted harness host Five new modules: apps/dsc (bin: parseArgs + node:http static server + signal shutdown), packages/host/apiproxy (programmatic harness core composition, agents:[]), packages/client/web-runtime (React-free browser runtime), packages/client/web-ui (React mount), apps/web (vite build entry producing dist consumed by apps/dsc via package exports). Root wiring: apps/* workspace glob, dsh-* paths for host/client groups, demo:web script, apps/web/dist gitignore. No protocol/API routes yet — contract lands in step2 (see missions/tasks/20260719-1902-apiproxy-api-design). Includes the design + implementation archives (spec v2.1, deepseekchat baseline and harness boot research, implementation run log). Acceptance: 12/12 passed incl. real-key llm.stream smoke (51 chunks). feat(gui): apiproxy — four-quadrant RPC contract + fetch carriers, live end to end Contract layer (src/api/, 14 files): four named wire message types (ClientRequest / ServerResponse / ServerRequest / ClientResponse) as a discriminated union over strict bidirectional rpcId (initiator mints, responder echoes; channel and message fully decoupled — HTTP is the client->server pipe, SSE the reverse); narrow RpcRequest<P>/ RpcResponse<T> signature forms; RpcMethodMap with RequestPayload<K>/ ResponseValue<K> derivation; typed RpcError details map; approval/ question responses modeled as ClientResponse via a single /api/respond endpoint (RpcReceipt carrier ack); zod schemas anchored per Wire<T> against exactOptionalPropertyTypes. impl/api-proxy.ts: describe/list/create, both SSE streams (frame queue pump, subscribed baseline, lifecycle frames, signal cleanup); history pages on message boundaries (tail-back scan, partial included in the tail page); prompt dispatches queue->agent.send / steer->agent.steer with rpcId carried through MessageSource; cancel for attached sessions; cold-session resume deduped via a per-id promise map; host-level provider/model defaults injected at create/resume. fetch/: mechanical UNARY_ROUTES table, two-level parse with path==method check, SSE frames completed to ServerRequest full form; client mints -> narrows -> envelopes outbound, verifies rpcId echo inbound, streams SSE frames, four-quadrant onEnvelope tap (debug panel choke point). Real-browser fixes: URL base resolves to location.origin (hardcoded internal base broke real pages), browser-safe export paths. Design archives: contract design.md v2.0 with decision log, core-coverage audit, comparative studies, step2 impl run log. Probed end to end over real HTTP: prompt -> live model stream -> history returns the finished reply. feat(gui): RpcLog debug panel — fixture-driven milestone, playwright-verified 10/10 web-runtime: rpcLog + ui slices (zustand), four-quadrant RpcLogEntry (client-request / server-response / server-request / client-response), onEnvelope tap -> microtask-batched pump with 500-entry ring buffer, ConnectionController (private state, backoff reconnect), fixture API with fake envelopes (?fixture switch), bootWebRuntime; contract types via temporary local copies (api-types.ts, swapped for real imports when W3 client lands). web-ui: components/panels/RpcLog five-piece set (badge with unread count, floating panel, direction glyphs per quadrant, same-rpcId pair highlighting in two families, JSON payload expand, follow/pause, clear), App shell, utils/formatRelative, light-theme CSS variables with dark placeholders. dsc bin: mime lookup fixed to use the actually-served file (naked '/?query' no longer falls through to octet-stream download); shutdown closes SSE keep-alive connections so SIGTERM actually exits. Acceptance: scripts/verify-rpclog-panel.mjs (chromium headless) ALL PASS 10/10 over design.md §D 1-6. pkg: add web scripts for building feat(gui): session milestone — list + conversation over Session OOP, styled RpcLog v2.1 web-runtime: Session/SessionManager object layer (resident instances, mux frame routing, lineage flattening), foldSurface adapter with padding sentinels for paged windows, chunk accumulator for streaming partials, batched change notification (useSyncExternalStore contract), connection sinks + reconnect fix (the 300ms self-abort reconnect storm that made the session list flap is gone), fixture rewritten as a scripted host (60-turn history, typewriter replay, resident pending approval, child session); temporary contract copies deleted in favor of real apiproxy imports. web-ui: sessions screen (list with lineage indent + selection as container-local state), conversation view (turn grouping, reasoning fold, tool cards, steering, pending interaction cards, upward paging with scroll anchoring), input bar with queue/steer/stop; RpcLog panel restyled per docs/web-styling.md (tokenized palette, quadrant badge glyphs now vertical ↑↓⇟⇞, pair highlighting, floating shadow). docs/web-styling.md: living style guide (tokens, visual baseline, coding rules, evolution log). Acceptance: verify-session.mjs 31/31, verify-session-real.mjs 5/5 (real model streaming), verify-rpclog-panel.mjs 10/10. feat(gui): hostruntime split + repo-wide package prefix rename Package split (design: 20260720-0101-hostruntime-split-design): dsh-host-runtime carries bootHost + createApiProxy + startHost() (RunningHost {api, handler, defaults, ctx, dispose} — the seam Electron and any future shell reuses; ctx is the official front-door mount point); dsh-host-webserver carries the node:http static+API bridge (fixed: abort now keys on res 'close' + writableEnded — req 'close' fires on body end since Node 16 and was killing every SSE stream instantly, the reconnect-storm root cause); apps/dsc is now a thin assembly with web/-p subcommands. dsc -p runs the full isomorphic carrier chain in process (second real protocol consumer; probed end-to-end against the live model). Naming rule (user decree): packages under host/ and client/ carry the directory prefix in their npm name — dsh-host-apiproxy, dsh-client-web-runtime, dsh-client-web-ui renamed repo-wide in one frozen batch; explicit tsconfig paths entries added where the wildcard no longer matches. Acceptance: verify-session 31/31, verify-rpclog-panel 10/10, verify-session-real 7/7 (incl. new 12s connection-stability sentinels), tsc green, dsc web + dsc -p smoke both pass. refactor(gui): AbstractApiClient class hierarchy — OO client with inheritable seams AbstractApiClient (apiproxy) carries every protocol invariant: rpcId minting, four-quadrant envelope wrap/unwrap, zod parsing, SSE frame parsing, the payload-direct IApiClient surface (callers no longer mint rpcIds — the carrier does), and the instance-level envelope observation pump (batched via microtask; moved off module-level globals in rpc-log.ts, which is now a pure subscriber mapping envelopes into store entries — the debug panel observes the connection, it is not part of it). Platform subclasses own two abstract seams (doFetch, onEnvelope) plus three protocol-level virtuals for transportless overrides: InProcessApiClient (apiproxy; dsc -p uses new InProcessApiClient( host.handler)), WebApiClient (web-runtime), FixtureApiClient (fixture now subclasses instead of wrapping). Naming per decree: AbstractApiClient / IApiClient; ApiProxy stays the impl-side narrow-form contract. headless.ts call sites drop rpcRequest wrappers (payload-direct); split-design archive updated with the naming-rule ledger. tsc green; verify-session 31/31, verify-rpclog-panel 10/10, verify-session-real 7/7 (12s connection sentinel count=4); dsc -p smoke CALLER-OK. feat(gui): InputBar final form — bug batch, deepseekchat layout, single primary button, running locks input Squashes the whole InputBar iteration batch: IME/caret/auto-grow/focus/dedup bug fixes, layout aligned to the deepseekchat baseline, single primary button with hover flyout, finalized button semantics with the Codex-style icon circle, and running-state locking where stop is the only mid-turn action. The same batch carried the Chinese-to-English code comment sweep (density pruned), folded in here. docs(gui): purge work-log references from code comments 76 design-doc references cleared across the GUI packages: section pointers inlined as self-contained constraint statements, pure pointer comments dropped, milestone codenames and ruling tags out, and the 14 contract file headers switched to the formal RFC (the only sanctioned external reference). web-styling.md now cites the styling RFC instead of the disposable research archive. grep for work-log reference variants is clean across the GUI packages. docs(gui): file-header comments self-contained — drop RFC filename references RFC renames/reorgs must not require a source sweep (the 2026-07-20 two-way merge proved it). 11 headers lose only the '(RFC …)' tail and stay self-contained; api-proxy.ts keeps its minimal-first note. fix(gui): session streaming — freeze interrupted partials, sweep stale running calls, send force-scrolls Aborted turns never emit the finalizing assistant/message, so the accumulated partial and its running tool cards kept rendering below later messages — the "new message lands above the stopped reply" illusion. turn/end side effects now freeze content-bearing partials into interrupted terminal nodes (fractional seq keeps flow order; the live freeze and history replay converge through applyEventSideEffects, so a refresh reconstructs identical frozen nodes) and turn running tool cards into interrupted terminal cards; only content-free partials are swept outright. ConversationView gains the send-force-scroll rule (own words must be visible) alongside the pre-update atBottom follow flag. Regressions pinned as E2-4a–c (real host) and §E1-11h (fixture). feat(gui): webserver hardening verify script feat(gui): dark-mode toggle pinned to the sidebar bottom Interim home before the Settings page exists (the button re-homes with zero logic change — mechanics live in utils/theme.ts): html[data-theme] flip + dsc.theme localStorage, stored choice wins over the OS prefers-color-scheme default, applied in mount() before first paint so a dark reload never flashes light. Moon/sun inline SVG icon button at the sidebar's pinned bottom row. Pure front-end local concern: no RPC, no Session/store involvement. Dark sweep of list/conversation/input card/RPC panel found no unreadable pairs — no token changes needed. docs(gui): GUI RFCs and web styling handbook Layering+RPC protocol and web client architecture RFCs (post-reorg, developer-facing polish folded in) plus the styling engineering handbook. Mission work logs live in the commit above; PRs can be cut from this commit to include formal docs only. fix(gui): client object-layer hardening — audit timing/reference/resilience batches (S3-S5,C1-C3,C5-C8) fix(gui): carrier error channel + webserver backpressure (audit A1-A5,A7-A10,R2,R5) feat(gui): session persistence surface — cold list, project cwd, legacy no-cwd retirement refactor: rename dsc CLI to dsh — apps/cli, bin name, package scope Includes the root tsconfig project-references fix for host/* and client/web-runtime (originally a separate build fix commit). test(gui): three-tier suite — protocol/object/browser lanes, tier-a fill to per-file 100% test(gui): jsdom lane for web-ui + web-runtime coverage gate entry docs(gui): GUI testing system RFC (zh) feat(gui): tool-card views — contract slot, host-computed delivery, three-level card fallback fix(gui): lint clean across GUI packages — wrap long doc comments, drop dead type args, sync-return methods without awaits docs(gui): doc-sync mechanical fixes — JSDoc on apiproxy/host exports, RFC sketch fences ignore-check, md-wrap paragraphs, drop missions links, web-ui plain-ts entry chore(gui): module-graph regen + knip clean — drop dead re-exports, internalize createFixtureApi, scan web-ui tsx and verify mjs scripts build(gui): wire client/host packages into the lib build shape — tsc references + tsdown (web-ui css-external), lib manifests, cordis peer, apiproxy typed subpaths, vite src aliases test(gui): host-side per-file 100% coverage — apiproxy schema/carrier suites, webserver http-bridge suite, host-runtime composition suite; client/* coverage excluded pending the browser-side testing work item docs(gui): package READMEs for the five GUI packages — model-experience audit entries, limitations sections docs(gui): bilingual RFC pairs + client JSDoc completion — translate the three GUI RFCs to English with i18n records and manifest ratchet, Consequences sections both sides, full client/* export JSDoc, regen doc graphs and RFC index fix(scripts): doc-typecheck built-declarations mode maps /src/* subpath wildcards (apiproxy browser-safe channels) docs(gui): apply dsh rename across pr-gates docs — READMEs, layering RFC en, web-ui entry comment, i18n re-record fix(gui): post-rebase lint reconciliation — wrap main-tree long doc comments, read-through narrowing guards, abortError Error normalization, handleUnary generic justification fix(gui): post-rebase doc/test reconciliation — align host specs with evolved carrier contracts (sentinel rpcId, stream/error surfacing, url-path transport messages, defaults.cwd), Agent Note titles and relocated links, KV Cache effect sections, JSDoc on evolved exports fix(gui): second-rebase reconciliation to 509db0cb3 — restore api panel exports the baseline suites consume, knip workspace entries for jsdom lane and apps/web smokes, hoist result narrowing, align testing.md to the narrowed web-ui exclusion fix(test): vitest-scoped tsconfig maps bare imports for tsx specs — with GUI manifests now pointing at lib, an unmapped importer loaded a second copy of the web-runtime singletons fix(gui): typecheck + lint clean over the tool-card batch — brand callIds and object-form turn/end reason in the view spec, narrow fixture arg stringification, wrap long v8-ignore comments docs(gui): export JSDoc for tool-card surfaces + testing-note pairing header docs: rfc for web testing feat: add tools to host-runtime fix(gui): dispatch agent/error via agentEvents in host-runtime spec — mounted invariants plugin rejects raw ctx.emit without the scope carrier fix(gui): restore GUI knip workspaces + scripts/mjs entries and regenerate lockfile after master rebase fix(gui): post-rebase gate repairs — drop context-node envelope (master unwrapped injected content envelopes), regen event matrix, condense testing.md web-ui exclusion within budget fix(session): browser-safe deep-equal in surface — node:util import broke the vite bundle ci(gates): frontend vite build joins pre-push — node: imports in the client closure pass tsc but break the browser bundle test(tui): drop the checkout-dependent process.cwd() harness default — a long worktree path pushes the footer token counters past the 88-column fake terminal test(gui): jsdom behavior E2E — conversation main path over fixture runtime, reconnect banner lifecycle test(gui): jsdom RPC panel behavior — ledger rows, expand, pairing, pause/clear, follow-pause, payload truncation test(gui): jsdom tier-2 — InputBar guards, reasoning fold, JSON blocks, message variants, theme, create-then-select; act-harden banner case test(gui): jsdom tier-3 — ConversationView states/paging/force-bottom, ToolCallCard arms, PendingCard, list rows test(gui): jsdom tails — view-card variants, LogRow directions, registry hygiene, badge overflow, hook ops, mount glue test(gui): jsdom tails round 2 — call-ref blocks, resume follow, view precedence, failed create, empty-diff arm test(gui): jsdom final arms — anchor compensation, follow-off, interval ticks, view halves, node-over-running precedence test(gui): web-ui joins the per-file 100% coverage gate Annotation-only src changes plus the config swap. The web-ui exclusion is replaced by a single index.tsx entry (stale byte-identical duplicate of mount.tsx, nothing imports it; same entry-glue treatment as bin.ts) and the coverage include gains .tsx. v8-ignore sites (each with its reason inline): - ConversationView 3x ref-null guards; InputBar disabled-click guard - ToolCallCard both-null arms + windowless-custom argsRaw arm - LogRow css-module key fallbacks (start/stop block); RpcLogBody 3x ref-null guards - web-runtime drift from the tool-card batch: fixture presenter catch/str typo-guards, dense-array guards (fold-adapter reset, session rebuild, fixture backscan), live view-present arm (fixture replays are text-only; view vocabulary is covered by the history samples) test(gui): close the PR #443 host-side coverage gaps — apiproxy client abort arms, api-proxy cold/view paths, webserver drain - apiproxy fetch/client.ts: 3 new cases (pre-aborted signal short-circuits before transport + string reason mapping, non-Error/string reason falls to the default AbortError message, signal-less doFetch passthrough) - runtime/api-proxy.ts: one v8-ignore (summarizeCold cwd arm — list() filters cwd-less legacy metas) + api-proxy-cold.spec.ts (cold list merge: mtime source, locate-undefined and vanished-log fallbacks, lineage; no-persistence/no-factory resume → internal) + 2 view cases (history views with meta passthrough and orphan/bad-args/presenterless soft-falls, session/disposed open-call cleanup on the mux stream) - webserver/index.ts: /api/big fixture drives both drain-wait legs (full 8MiB readback after drain, mid-chunk disconnect wakes via 'close') feat: app shell fix: rebase conflicts fix: coverage fix(gui): lint clean after rebase — wrap long v8-ignore comments, unconditional v1 detail-block claim chore(gui): remove browser/probe verify scripts from scripts/ The six GUI acceptance/probe scripts (carrier-errors, rpclog-panel, session, session-real, webserver-backpressure, webserver-hardening) leave the repo's scripts/ tree; the three code comments that pointed at them now describe the coverage lane without naming a script path. fix(webserver): guard the request callback — one malformed request must not kill the process The async handle() had no top-level catch, so any throw inside it (a bad %-escape reaching decodeURIComponent, a client dropping mid-body, a response stream erroring) became an unhandled rejection and took the whole process down (audit R1 must-fix). The guard answers 400 when headers are not out yet, destroys the socket when they are, and reports the failure to onError (the package never prints). Spec covers all three legs: %-escape barrage → 400 + server stays alive, non-Error throw wrapped for onError, mid-stream explosion → socket teardown. feat: client AGENTS.md fix: client/AGENTS.md fix: rebase feat(gui): T0 cut 1 — 12 client package skeletons with contract stubs, dshClient declarations, tsdown client preset, theme token sheets feat(gui): T0 cut 2 — pure git mv migration per v3 §11 (connection six, runtime sessions/kernel, ui-conversation chat, ui-primitives markdown family, web shell + e2e) feat(gui): T0 cuts 3+4 — import rewiring to new package names, .legacy demotion of owner-rewrite files, legacy web-runtime/web-ui/apps-web retired to attic feat(gui): connection 对账刀——index.ts 精确导出清单替换 export *,intents.legacy 溶解删除 feat(client/ui-slots): SlotCore real implementation — kind semantics, sync version + microtask-batched notify, onMutate bridge feat(gui): web shell vite alias — retarget to new client packages, shell static surface only feat(gui): host 侧刀属地半——HostWebPluginRegistry(entries 扫描+internal/plugin 去抖重扫+dshClient 校验+exports./client 解析)、GET /plugins/<id>/client.js 分发端点、GET / 与 SPA fallback 注入 __DSH_BOOT__(webPlugins 可选注入,不传行为不变) feat(web-react): add use-sync-external-store dep + local shim typings feat(web-react): bindSnapshotSelector via uSES with-selector shim feat(gui): ui-layout concession-chain solver — pure computeColumns with contract geometry feat(gui): ui-layout LayoutService — four persisted stores, clamped actions, list-driven prune feat(gui): ui-layout AppFrame styles — grid columns, collapse-safe borders, edge drag handles test(gui): 存量 spec 平移——connection 三件+runtime 六件自 attic 捞回改包名路径全绿;api-helpers 按归属拆分(wire 半留 connection、classifier 半随 conversation.ts 入 runtime);boot-intents/preinit/rpc-log 随 intents/rpc-log 退役不迁(记 v3 §3.2 溶解项) feat(client/ui-primitives): StateDot/Button/Pill/Input/Menu atoms, ConnectionBanner de-legacied to pure props, JsonBlock CSS on --dsw tokens feat(web-react): createSnapshotStore engine (rafFlush batch, persist opt-in, dev freeze) + spec feat(gui): ui-layout AppFrame — grid tracks, pointer-capture drag handles with rAF throttle, frame ResizeObserver feat(web-react): useInvoke (external pending store, stable invoke, concurrency count) + spec test(web-react): bind spec — equality bail, custom eq, zero resubscribe, StrictMode, method sources feat(gui): ui-layout index rewiring — real exports, client apply provides ctx.layout and defines three slots feat(web-react): SessionProvider (renderBody deps) + RootBindingProvider + binding contexts + spec feat(gui): web shell AppRoot boot-page styles — self-contained with neutral token fallbacks feat(gui): web shell AppRoot — boot gate over loader status, fail-loud plugin failure list fix(gui): AppRoot gates on explicit settled signal — status-derived readiness races the incrementally filled table feat(client/ui-theme): ThemeService real implementation — registry with built-in light/dark, apply toggles body[data-ds-dark-theme], third-party token overrides as body inline vars feat(web-react): scopedSlots outlet (kind matrix, inject WeakMap caches, per-entry error boundary) + spec feat(gui): web shell module-table seed — pure-library entities for the loader require surface feat(client/i18n): I18nService real implementation — ns×locale registry, stable bind(ns) reference, zh fallback chain, zh/en skeleton dictionaries feat(gui): web shell assembly closure — layout exports via module table, SessionProvider + scopedSlots + RootBindingProvider feat: client/ui-conversation feat: code codedoc build(gui): root bundle green — web shell excluded from the lib workspace (vite app), ui-primitives lib externalizes css side-effect imports (web-ui precedent) gates(gui): verify-cordis-config follows aggregate tsconfig references (root is a shell over host/client programs); module graph regenerated for the twelve client packages chore(gui): retire legacy migration sources — every owner rewrite landed (t0-checklist §7 ledger honored); orphan css of retired components removed gates(gui): knip green groundwork — e2e/tsx entries for the new packages, loader-runtime deps ignored where loading is by specifier string, fake plugin ids un-bare-named, dead test export dropped chore(client): manifest shape batch A — ui-slots/web-react/ui-primitives invariant companions, files whitelist, cordis+invariants peer/dev, tsconfig refs chore(client): manifest shape batch B — connection/runtime/ui-conversation/ui-trajectory files whitelist, cordis peer+dev, explicit invariant lib entries (clientBundle signature) chore(client): manifest shape batch C — i18n/ui-layout/ui-sidebar/ui-theme invariant companions, files whitelist, invariants peer/dev, tsconfig refs chore(client): manifest shape batch D — web shell gains node-half lib entry + invariant companion + uniform files whitelist chore(client): drop verified-unused deps — dsh-tools from runtime/ui-conversation (types ride /presentation), ui-primitives+clsx from ui-layout gates(gui): doc-gate fixes — theme JSDoc prose, three client type-link exemptions, agent-note paths follow the migration, config catalog regenerated gates(gui): type-equiv manifest follows the types.ts extraction, approval JSDoc keeps its link form, persistence catalog regenerated docs(gui): per-constant JSDoc on the contract geometry exports (export-jsdoc gate) test(gates): loader-composition budget covers cold tsx resolution after the program split (was flaking at the default 5s) docs(gui): README substantiation batch 1 — ui-slots/ui-primitives/web-react/connection: Model Experience short form, real deferred-work ledgers, description accuracy pass fix(client): theme/i18n dual-entry split — service classes + cordis merges move to src/client (host catalog scanner no longer misclassifies client services), node halves keep types + empty apply; catalogs regenerated docs(gui): README substantiation batch 2 — runtime/ui-layout/ui-sidebar/ui-conversation: Model Experience short form, package-owned deferred-work ledgers (unload stub, watch approximation, /client value-import rule, global details state, two-state dots, stats duration gap, single-bundle caches) docs(gui): README substantiation batch 3 — ui-trajectory/ui-theme/i18n/web: Model Experience short form, deferred-work ledgers (placeholder charter, no theme toggle owner, empty locale dictionaries, one-shot rendering); both README gates green test(scripts): purity spec adopts clientBundle two-arg signature (explicit libEntry, no default) gates(gui): knip green — declaration-merge dep ignored, fake plugin id assembled at runtime, invariants dep de-duplicated to peer+dev, stale apps/web section dropped feat(gui): 门禁波次 host 三包 invariant 形状——apiproxy explained-empty 伴生(wire 契约层零事件面)、webserver 真关系伴生(manifest 行必解析出 clientPath,防 __DSH_BOOT__ 广告 404 bundle;apps/cli 发布 webPlugins 键供审计)、runtime 补 files 白名单;三包 exports/files/peer+dev/tsconfig refs 齐 fw-react 形状;constraints+invariants 双 gate 零违规 build(client): ui-layout/ui-sidebar tsdown configs adopt the explicit two-arg clientBundle signature (orphaned follow-up of the manifest shape batch) refactor(gui): shell boot becomes a library face — bootWebShell(el) exported for the apps/web entry; main.ts retired refactor(gui): exports 纪律刀1——ui-theme/i18n node index 收敛为只空 apply(Translate/LocaleDict/ThemeTokens 类型下沉 src/client/),ui-conversation 的 I18nService import 改 /client 子路径 build(typecheck): converge to root host aggregate + tsconfig.client.json — delete tsconfig.host.json, verify-cordis-config seeds both aggregates feat(gui): apps/web restored as the vite application — thin main over bootWebShell; dsh-client-web becomes a plain lib (index exports shell surface, vite files and e2e moved out) chore(gates): knip.json rewritten on the master base — same semantics, minimal diff (formatting churn dropped) docs(gui): 时效清扫②——testing.md 删 web-ui 覆盖豁免残句;web-styling.md 加 token 换代头注(--dsw-* 现行、工程约束条款仍有效并注明收编处) docs(gui): 时效清扫③——四对 GUI Agent Note 加路径更新头注(web-runtime/web-ui/dsh-frontend→现行 12 包结构;设计结论存续声明;双语对同步) docs(gui): 时效清扫③b——四对 note 头注的 i18n 配对哈希重录 build(typecheck): minimal-diff tsconfig shape — drop root files entry (purity spec + preset move to client program), compress comments, drop redundant util/home root ref feat(gui): apps/web restoration follow-through — dsh-frontend package name, cli dist resolve, root build:web filter, tsdown exemption dropped, vitest web lane + knip + client aggregate retargeted, e2e paths rebased refactor(gui): exports 纪律刀2——connection wire 六件 git mv 进 src/client/(wire 即该 dshClient 插件的 client 半),node index=只空 apply,/client 半边整面导出(v3 §3.2 清单原样),包内 tests 改 src/client 直取 refactor(gui): exports 纪律刀3——runtime 实现整体下沉 src/client/(sessions/slots/loader;契约类型与 cordis merge 随迁 client/index),node index=只空 apply;./loader exports 指 client/loader;全消费面(web 壳/ui-sidebar/ui-trajectory/tests)bare→/client 机械跟改;vitest.e2e 换 tsconfig.vitest paths(root tsconfig 排除 client 会把 /client import 掉到 exports 的浏览器 dist bundle) refactor(gui): exports 纪律刀3 补遗——ui-layout 三处 bare runtime import 改 /client(刀3 消费面机械跟改漏提交件;跨属地机械一行×3 报备 ui-shell) test(gui): drop the getSessionManager singleton case — the init/get pair is a dead legacy-boot surface with zero live consumers (SessionsService constructs and holds the manager under the plugin architecture); source removal tracked with rt-core refactor(gui): 删 manager.ts 尾部 initSessionManager/getSessionManager 单例对——旧 boot 直连遗物,插件化下 SessionsService 构造持有 manager,全仓零活消费者(convo-b 测试清扫对表,其测试用例已先行退役 7e2c51898);头注释同步去单例措辞 code refactor
2026-07-19 21:17:57 +08:00
pkg_client_ui_layout["client-ui-layout"]
2026-07-27 16:12:02 +08:00
pkg_client_ui_model["client-ui-model"]
pkg_client_ui_models["client-ui-models"]
pkg_client_ui_permission["client-ui-permission"]
pkg_client_ui_plan["client-ui-plan"]
feat(gui): step1 skeleton — dsc web serves built web UI over booted harness host Five new modules: apps/dsc (bin: parseArgs + node:http static server + signal shutdown), packages/host/apiproxy (programmatic harness core composition, agents:[]), packages/client/web-runtime (React-free browser runtime), packages/client/web-ui (React mount), apps/web (vite build entry producing dist consumed by apps/dsc via package exports). Root wiring: apps/* workspace glob, dsh-* paths for host/client groups, demo:web script, apps/web/dist gitignore. No protocol/API routes yet — contract lands in step2 (see missions/tasks/20260719-1902-apiproxy-api-design). Includes the design + implementation archives (spec v2.1, deepseekchat baseline and harness boot research, implementation run log). Acceptance: 12/12 passed incl. real-key llm.stream smoke (51 chunks). feat(gui): apiproxy — four-quadrant RPC contract + fetch carriers, live end to end Contract layer (src/api/, 14 files): four named wire message types (ClientRequest / ServerResponse / ServerRequest / ClientResponse) as a discriminated union over strict bidirectional rpcId (initiator mints, responder echoes; channel and message fully decoupled — HTTP is the client->server pipe, SSE the reverse); narrow RpcRequest<P>/ RpcResponse<T> signature forms; RpcMethodMap with RequestPayload<K>/ ResponseValue<K> derivation; typed RpcError details map; approval/ question responses modeled as ClientResponse via a single /api/respond endpoint (RpcReceipt carrier ack); zod schemas anchored per Wire<T> against exactOptionalPropertyTypes. impl/api-proxy.ts: describe/list/create, both SSE streams (frame queue pump, subscribed baseline, lifecycle frames, signal cleanup); history pages on message boundaries (tail-back scan, partial included in the tail page); prompt dispatches queue->agent.send / steer->agent.steer with rpcId carried through MessageSource; cancel for attached sessions; cold-session resume deduped via a per-id promise map; host-level provider/model defaults injected at create/resume. fetch/: mechanical UNARY_ROUTES table, two-level parse with path==method check, SSE frames completed to ServerRequest full form; client mints -> narrows -> envelopes outbound, verifies rpcId echo inbound, streams SSE frames, four-quadrant onEnvelope tap (debug panel choke point). Real-browser fixes: URL base resolves to location.origin (hardcoded internal base broke real pages), browser-safe export paths. Design archives: contract design.md v2.0 with decision log, core-coverage audit, comparative studies, step2 impl run log. Probed end to end over real HTTP: prompt -> live model stream -> history returns the finished reply. feat(gui): RpcLog debug panel — fixture-driven milestone, playwright-verified 10/10 web-runtime: rpcLog + ui slices (zustand), four-quadrant RpcLogEntry (client-request / server-response / server-request / client-response), onEnvelope tap -> microtask-batched pump with 500-entry ring buffer, ConnectionController (private state, backoff reconnect), fixture API with fake envelopes (?fixture switch), bootWebRuntime; contract types via temporary local copies (api-types.ts, swapped for real imports when W3 client lands). web-ui: components/panels/RpcLog five-piece set (badge with unread count, floating panel, direction glyphs per quadrant, same-rpcId pair highlighting in two families, JSON payload expand, follow/pause, clear), App shell, utils/formatRelative, light-theme CSS variables with dark placeholders. dsc bin: mime lookup fixed to use the actually-served file (naked '/?query' no longer falls through to octet-stream download); shutdown closes SSE keep-alive connections so SIGTERM actually exits. Acceptance: scripts/verify-rpclog-panel.mjs (chromium headless) ALL PASS 10/10 over design.md §D 1-6. pkg: add web scripts for building feat(gui): session milestone — list + conversation over Session OOP, styled RpcLog v2.1 web-runtime: Session/SessionManager object layer (resident instances, mux frame routing, lineage flattening), foldSurface adapter with padding sentinels for paged windows, chunk accumulator for streaming partials, batched change notification (useSyncExternalStore contract), connection sinks + reconnect fix (the 300ms self-abort reconnect storm that made the session list flap is gone), fixture rewritten as a scripted host (60-turn history, typewriter replay, resident pending approval, child session); temporary contract copies deleted in favor of real apiproxy imports. web-ui: sessions screen (list with lineage indent + selection as container-local state), conversation view (turn grouping, reasoning fold, tool cards, steering, pending interaction cards, upward paging with scroll anchoring), input bar with queue/steer/stop; RpcLog panel restyled per docs/web-styling.md (tokenized palette, quadrant badge glyphs now vertical ↑↓⇟⇞, pair highlighting, floating shadow). docs/web-styling.md: living style guide (tokens, visual baseline, coding rules, evolution log). Acceptance: verify-session.mjs 31/31, verify-session-real.mjs 5/5 (real model streaming), verify-rpclog-panel.mjs 10/10. feat(gui): hostruntime split + repo-wide package prefix rename Package split (design: 20260720-0101-hostruntime-split-design): dsh-host-runtime carries bootHost + createApiProxy + startHost() (RunningHost {api, handler, defaults, ctx, dispose} — the seam Electron and any future shell reuses; ctx is the official front-door mount point); dsh-host-webserver carries the node:http static+API bridge (fixed: abort now keys on res 'close' + writableEnded — req 'close' fires on body end since Node 16 and was killing every SSE stream instantly, the reconnect-storm root cause); apps/dsc is now a thin assembly with web/-p subcommands. dsc -p runs the full isomorphic carrier chain in process (second real protocol consumer; probed end-to-end against the live model). Naming rule (user decree): packages under host/ and client/ carry the directory prefix in their npm name — dsh-host-apiproxy, dsh-client-web-runtime, dsh-client-web-ui renamed repo-wide in one frozen batch; explicit tsconfig paths entries added where the wildcard no longer matches. Acceptance: verify-session 31/31, verify-rpclog-panel 10/10, verify-session-real 7/7 (incl. new 12s connection-stability sentinels), tsc green, dsc web + dsc -p smoke both pass. refactor(gui): AbstractApiClient class hierarchy — OO client with inheritable seams AbstractApiClient (apiproxy) carries every protocol invariant: rpcId minting, four-quadrant envelope wrap/unwrap, zod parsing, SSE frame parsing, the payload-direct IApiClient surface (callers no longer mint rpcIds — the carrier does), and the instance-level envelope observation pump (batched via microtask; moved off module-level globals in rpc-log.ts, which is now a pure subscriber mapping envelopes into store entries — the debug panel observes the connection, it is not part of it). Platform subclasses own two abstract seams (doFetch, onEnvelope) plus three protocol-level virtuals for transportless overrides: InProcessApiClient (apiproxy; dsc -p uses new InProcessApiClient( host.handler)), WebApiClient (web-runtime), FixtureApiClient (fixture now subclasses instead of wrapping). Naming per decree: AbstractApiClient / IApiClient; ApiProxy stays the impl-side narrow-form contract. headless.ts call sites drop rpcRequest wrappers (payload-direct); split-design archive updated with the naming-rule ledger. tsc green; verify-session 31/31, verify-rpclog-panel 10/10, verify-session-real 7/7 (12s connection sentinel count=4); dsc -p smoke CALLER-OK. feat(gui): InputBar final form — bug batch, deepseekchat layout, single primary button, running locks input Squashes the whole InputBar iteration batch: IME/caret/auto-grow/focus/dedup bug fixes, layout aligned to the deepseekchat baseline, single primary button with hover flyout, finalized button semantics with the Codex-style icon circle, and running-state locking where stop is the only mid-turn action. The same batch carried the Chinese-to-English code comment sweep (density pruned), folded in here. docs(gui): purge work-log references from code comments 76 design-doc references cleared across the GUI packages: section pointers inlined as self-contained constraint statements, pure pointer comments dropped, milestone codenames and ruling tags out, and the 14 contract file headers switched to the formal RFC (the only sanctioned external reference). web-styling.md now cites the styling RFC instead of the disposable research archive. grep for work-log reference variants is clean across the GUI packages. docs(gui): file-header comments self-contained — drop RFC filename references RFC renames/reorgs must not require a source sweep (the 2026-07-20 two-way merge proved it). 11 headers lose only the '(RFC …)' tail and stay self-contained; api-proxy.ts keeps its minimal-first note. fix(gui): session streaming — freeze interrupted partials, sweep stale running calls, send force-scrolls Aborted turns never emit the finalizing assistant/message, so the accumulated partial and its running tool cards kept rendering below later messages — the "new message lands above the stopped reply" illusion. turn/end side effects now freeze content-bearing partials into interrupted terminal nodes (fractional seq keeps flow order; the live freeze and history replay converge through applyEventSideEffects, so a refresh reconstructs identical frozen nodes) and turn running tool cards into interrupted terminal cards; only content-free partials are swept outright. ConversationView gains the send-force-scroll rule (own words must be visible) alongside the pre-update atBottom follow flag. Regressions pinned as E2-4a–c (real host) and §E1-11h (fixture). feat(gui): webserver hardening verify script feat(gui): dark-mode toggle pinned to the sidebar bottom Interim home before the Settings page exists (the button re-homes with zero logic change — mechanics live in utils/theme.ts): html[data-theme] flip + dsc.theme localStorage, stored choice wins over the OS prefers-color-scheme default, applied in mount() before first paint so a dark reload never flashes light. Moon/sun inline SVG icon button at the sidebar's pinned bottom row. Pure front-end local concern: no RPC, no Session/store involvement. Dark sweep of list/conversation/input card/RPC panel found no unreadable pairs — no token changes needed. docs(gui): GUI RFCs and web styling handbook Layering+RPC protocol and web client architecture RFCs (post-reorg, developer-facing polish folded in) plus the styling engineering handbook. Mission work logs live in the commit above; PRs can be cut from this commit to include formal docs only. fix(gui): client object-layer hardening — audit timing/reference/resilience batches (S3-S5,C1-C3,C5-C8) fix(gui): carrier error channel + webserver backpressure (audit A1-A5,A7-A10,R2,R5) feat(gui): session persistence surface — cold list, project cwd, legacy no-cwd retirement refactor: rename dsc CLI to dsh — apps/cli, bin name, package scope Includes the root tsconfig project-references fix for host/* and client/web-runtime (originally a separate build fix commit). test(gui): three-tier suite — protocol/object/browser lanes, tier-a fill to per-file 100% test(gui): jsdom lane for web-ui + web-runtime coverage gate entry docs(gui): GUI testing system RFC (zh) feat(gui): tool-card views — contract slot, host-computed delivery, three-level card fallback fix(gui): lint clean across GUI packages — wrap long doc comments, drop dead type args, sync-return methods without awaits docs(gui): doc-sync mechanical fixes — JSDoc on apiproxy/host exports, RFC sketch fences ignore-check, md-wrap paragraphs, drop missions links, web-ui plain-ts entry chore(gui): module-graph regen + knip clean — drop dead re-exports, internalize createFixtureApi, scan web-ui tsx and verify mjs scripts build(gui): wire client/host packages into the lib build shape — tsc references + tsdown (web-ui css-external), lib manifests, cordis peer, apiproxy typed subpaths, vite src aliases test(gui): host-side per-file 100% coverage — apiproxy schema/carrier suites, webserver http-bridge suite, host-runtime composition suite; client/* coverage excluded pending the browser-side testing work item docs(gui): package READMEs for the five GUI packages — model-experience audit entries, limitations sections docs(gui): bilingual RFC pairs + client JSDoc completion — translate the three GUI RFCs to English with i18n records and manifest ratchet, Consequences sections both sides, full client/* export JSDoc, regen doc graphs and RFC index fix(scripts): doc-typecheck built-declarations mode maps /src/* subpath wildcards (apiproxy browser-safe channels) docs(gui): apply dsh rename across pr-gates docs — READMEs, layering RFC en, web-ui entry comment, i18n re-record fix(gui): post-rebase lint reconciliation — wrap main-tree long doc comments, read-through narrowing guards, abortError Error normalization, handleUnary generic justification fix(gui): post-rebase doc/test reconciliation — align host specs with evolved carrier contracts (sentinel rpcId, stream/error surfacing, url-path transport messages, defaults.cwd), Agent Note titles and relocated links, KV Cache effect sections, JSDoc on evolved exports fix(gui): second-rebase reconciliation to 509db0cb3 — restore api panel exports the baseline suites consume, knip workspace entries for jsdom lane and apps/web smokes, hoist result narrowing, align testing.md to the narrowed web-ui exclusion fix(test): vitest-scoped tsconfig maps bare imports for tsx specs — with GUI manifests now pointing at lib, an unmapped importer loaded a second copy of the web-runtime singletons fix(gui): typecheck + lint clean over the tool-card batch — brand callIds and object-form turn/end reason in the view spec, narrow fixture arg stringification, wrap long v8-ignore comments docs(gui): export JSDoc for tool-card surfaces + testing-note pairing header docs: rfc for web testing feat: add tools to host-runtime fix(gui): dispatch agent/error via agentEvents in host-runtime spec — mounted invariants plugin rejects raw ctx.emit without the scope carrier fix(gui): restore GUI knip workspaces + scripts/mjs entries and regenerate lockfile after master rebase fix(gui): post-rebase gate repairs — drop context-node envelope (master unwrapped injected content envelopes), regen event matrix, condense testing.md web-ui exclusion within budget fix(session): browser-safe deep-equal in surface — node:util import broke the vite bundle ci(gates): frontend vite build joins pre-push — node: imports in the client closure pass tsc but break the browser bundle test(tui): drop the checkout-dependent process.cwd() harness default — a long worktree path pushes the footer token counters past the 88-column fake terminal test(gui): jsdom behavior E2E — conversation main path over fixture runtime, reconnect banner lifecycle test(gui): jsdom RPC panel behavior — ledger rows, expand, pairing, pause/clear, follow-pause, payload truncation test(gui): jsdom tier-2 — InputBar guards, reasoning fold, JSON blocks, message variants, theme, create-then-select; act-harden banner case test(gui): jsdom tier-3 — ConversationView states/paging/force-bottom, ToolCallCard arms, PendingCard, list rows test(gui): jsdom tails — view-card variants, LogRow directions, registry hygiene, badge overflow, hook ops, mount glue test(gui): jsdom tails round 2 — call-ref blocks, resume follow, view precedence, failed create, empty-diff arm test(gui): jsdom final arms — anchor compensation, follow-off, interval ticks, view halves, node-over-running precedence test(gui): web-ui joins the per-file 100% coverage gate Annotation-only src changes plus the config swap. The web-ui exclusion is replaced by a single index.tsx entry (stale byte-identical duplicate of mount.tsx, nothing imports it; same entry-glue treatment as bin.ts) and the coverage include gains .tsx. v8-ignore sites (each with its reason inline): - ConversationView 3x ref-null guards; InputBar disabled-click guard - ToolCallCard both-null arms + windowless-custom argsRaw arm - LogRow css-module key fallbacks (start/stop block); RpcLogBody 3x ref-null guards - web-runtime drift from the tool-card batch: fixture presenter catch/str typo-guards, dense-array guards (fold-adapter reset, session rebuild, fixture backscan), live view-present arm (fixture replays are text-only; view vocabulary is covered by the history samples) test(gui): close the PR #443 host-side coverage gaps — apiproxy client abort arms, api-proxy cold/view paths, webserver drain - apiproxy fetch/client.ts: 3 new cases (pre-aborted signal short-circuits before transport + string reason mapping, non-Error/string reason falls to the default AbortError message, signal-less doFetch passthrough) - runtime/api-proxy.ts: one v8-ignore (summarizeCold cwd arm — list() filters cwd-less legacy metas) + api-proxy-cold.spec.ts (cold list merge: mtime source, locate-undefined and vanished-log fallbacks, lineage; no-persistence/no-factory resume → internal) + 2 view cases (history views with meta passthrough and orphan/bad-args/presenterless soft-falls, session/disposed open-call cleanup on the mux stream) - webserver/index.ts: /api/big fixture drives both drain-wait legs (full 8MiB readback after drain, mid-chunk disconnect wakes via 'close') feat: app shell fix: rebase conflicts fix: coverage fix(gui): lint clean after rebase — wrap long v8-ignore comments, unconditional v1 detail-block claim chore(gui): remove browser/probe verify scripts from scripts/ The six GUI acceptance/probe scripts (carrier-errors, rpclog-panel, session, session-real, webserver-backpressure, webserver-hardening) leave the repo's scripts/ tree; the three code comments that pointed at them now describe the coverage lane without naming a script path. fix(webserver): guard the request callback — one malformed request must not kill the process The async handle() had no top-level catch, so any throw inside it (a bad %-escape reaching decodeURIComponent, a client dropping mid-body, a response stream erroring) became an unhandled rejection and took the whole process down (audit R1 must-fix). The guard answers 400 when headers are not out yet, destroys the socket when they are, and reports the failure to onError (the package never prints). Spec covers all three legs: %-escape barrage → 400 + server stays alive, non-Error throw wrapped for onError, mid-stream explosion → socket teardown. feat: client AGENTS.md fix: client/AGENTS.md fix: rebase feat(gui): T0 cut 1 — 12 client package skeletons with contract stubs, dshClient declarations, tsdown client preset, theme token sheets feat(gui): T0 cut 2 — pure git mv migration per v3 §11 (connection six, runtime sessions/kernel, ui-conversation chat, ui-primitives markdown family, web shell + e2e) feat(gui): T0 cuts 3+4 — import rewiring to new package names, .legacy demotion of owner-rewrite files, legacy web-runtime/web-ui/apps-web retired to attic feat(gui): connection 对账刀——index.ts 精确导出清单替换 export *,intents.legacy 溶解删除 feat(client/ui-slots): SlotCore real implementation — kind semantics, sync version + microtask-batched notify, onMutate bridge feat(gui): web shell vite alias — retarget to new client packages, shell static surface only feat(gui): host 侧刀属地半——HostWebPluginRegistry(entries 扫描+internal/plugin 去抖重扫+dshClient 校验+exports./client 解析)、GET /plugins/<id>/client.js 分发端点、GET / 与 SPA fallback 注入 __DSH_BOOT__(webPlugins 可选注入,不传行为不变) feat(web-react): add use-sync-external-store dep + local shim typings feat(web-react): bindSnapshotSelector via uSES with-selector shim feat(gui): ui-layout concession-chain solver — pure computeColumns with contract geometry feat(gui): ui-layout LayoutService — four persisted stores, clamped actions, list-driven prune feat(gui): ui-layout AppFrame styles — grid columns, collapse-safe borders, edge drag handles test(gui): 存量 spec 平移——connection 三件+runtime 六件自 attic 捞回改包名路径全绿;api-helpers 按归属拆分(wire 半留 connection、classifier 半随 conversation.ts 入 runtime);boot-intents/preinit/rpc-log 随 intents/rpc-log 退役不迁(记 v3 §3.2 溶解项) feat(client/ui-primitives): StateDot/Button/Pill/Input/Menu atoms, ConnectionBanner de-legacied to pure props, JsonBlock CSS on --dsw tokens feat(web-react): createSnapshotStore engine (rafFlush batch, persist opt-in, dev freeze) + spec feat(gui): ui-layout AppFrame — grid tracks, pointer-capture drag handles with rAF throttle, frame ResizeObserver feat(web-react): useInvoke (external pending store, stable invoke, concurrency count) + spec test(web-react): bind spec — equality bail, custom eq, zero resubscribe, StrictMode, method sources feat(gui): ui-layout index rewiring — real exports, client apply provides ctx.layout and defines three slots feat(web-react): SessionProvider (renderBody deps) + RootBindingProvider + binding contexts + spec feat(gui): web shell AppRoot boot-page styles — self-contained with neutral token fallbacks feat(gui): web shell AppRoot — boot gate over loader status, fail-loud plugin failure list fix(gui): AppRoot gates on explicit settled signal — status-derived readiness races the incrementally filled table feat(client/ui-theme): ThemeService real implementation — registry with built-in light/dark, apply toggles body[data-ds-dark-theme], third-party token overrides as body inline vars feat(web-react): scopedSlots outlet (kind matrix, inject WeakMap caches, per-entry error boundary) + spec feat(gui): web shell module-table seed — pure-library entities for the loader require surface feat(client/i18n): I18nService real implementation — ns×locale registry, stable bind(ns) reference, zh fallback chain, zh/en skeleton dictionaries feat(gui): web shell assembly closure — layout exports via module table, SessionProvider + scopedSlots + RootBindingProvider feat: client/ui-conversation feat: code codedoc build(gui): root bundle green — web shell excluded from the lib workspace (vite app), ui-primitives lib externalizes css side-effect imports (web-ui precedent) gates(gui): verify-cordis-config follows aggregate tsconfig references (root is a shell over host/client programs); module graph regenerated for the twelve client packages chore(gui): retire legacy migration sources — every owner rewrite landed (t0-checklist §7 ledger honored); orphan css of retired components removed gates(gui): knip green groundwork — e2e/tsx entries for the new packages, loader-runtime deps ignored where loading is by specifier string, fake plugin ids un-bare-named, dead test export dropped chore(client): manifest shape batch A — ui-slots/web-react/ui-primitives invariant companions, files whitelist, cordis+invariants peer/dev, tsconfig refs chore(client): manifest shape batch B — connection/runtime/ui-conversation/ui-trajectory files whitelist, cordis peer+dev, explicit invariant lib entries (clientBundle signature) chore(client): manifest shape batch C — i18n/ui-layout/ui-sidebar/ui-theme invariant companions, files whitelist, invariants peer/dev, tsconfig refs chore(client): manifest shape batch D — web shell gains node-half lib entry + invariant companion + uniform files whitelist chore(client): drop verified-unused deps — dsh-tools from runtime/ui-conversation (types ride /presentation), ui-primitives+clsx from ui-layout gates(gui): doc-gate fixes — theme JSDoc prose, three client type-link exemptions, agent-note paths follow the migration, config catalog regenerated gates(gui): type-equiv manifest follows the types.ts extraction, approval JSDoc keeps its link form, persistence catalog regenerated docs(gui): per-constant JSDoc on the contract geometry exports (export-jsdoc gate) test(gates): loader-composition budget covers cold tsx resolution after the program split (was flaking at the default 5s) docs(gui): README substantiation batch 1 — ui-slots/ui-primitives/web-react/connection: Model Experience short form, real deferred-work ledgers, description accuracy pass fix(client): theme/i18n dual-entry split — service classes + cordis merges move to src/client (host catalog scanner no longer misclassifies client services), node halves keep types + empty apply; catalogs regenerated docs(gui): README substantiation batch 2 — runtime/ui-layout/ui-sidebar/ui-conversation: Model Experience short form, package-owned deferred-work ledgers (unload stub, watch approximation, /client value-import rule, global details state, two-state dots, stats duration gap, single-bundle caches) docs(gui): README substantiation batch 3 — ui-trajectory/ui-theme/i18n/web: Model Experience short form, deferred-work ledgers (placeholder charter, no theme toggle owner, empty locale dictionaries, one-shot rendering); both README gates green test(scripts): purity spec adopts clientBundle two-arg signature (explicit libEntry, no default) gates(gui): knip green — declaration-merge dep ignored, fake plugin id assembled at runtime, invariants dep de-duplicated to peer+dev, stale apps/web section dropped feat(gui): 门禁波次 host 三包 invariant 形状——apiproxy explained-empty 伴生(wire 契约层零事件面)、webserver 真关系伴生(manifest 行必解析出 clientPath,防 __DSH_BOOT__ 广告 404 bundle;apps/cli 发布 webPlugins 键供审计)、runtime 补 files 白名单;三包 exports/files/peer+dev/tsconfig refs 齐 fw-react 形状;constraints+invariants 双 gate 零违规 build(client): ui-layout/ui-sidebar tsdown configs adopt the explicit two-arg clientBundle signature (orphaned follow-up of the manifest shape batch) refactor(gui): shell boot becomes a library face — bootWebShell(el) exported for the apps/web entry; main.ts retired refactor(gui): exports 纪律刀1——ui-theme/i18n node index 收敛为只空 apply(Translate/LocaleDict/ThemeTokens 类型下沉 src/client/),ui-conversation 的 I18nService import 改 /client 子路径 build(typecheck): converge to root host aggregate + tsconfig.client.json — delete tsconfig.host.json, verify-cordis-config seeds both aggregates feat(gui): apps/web restored as the vite application — thin main over bootWebShell; dsh-client-web becomes a plain lib (index exports shell surface, vite files and e2e moved out) chore(gates): knip.json rewritten on the master base — same semantics, minimal diff (formatting churn dropped) docs(gui): 时效清扫②——testing.md 删 web-ui 覆盖豁免残句;web-styling.md 加 token 换代头注(--dsw-* 现行、工程约束条款仍有效并注明收编处) docs(gui): 时效清扫③——四对 GUI Agent Note 加路径更新头注(web-runtime/web-ui/dsh-frontend→现行 12 包结构;设计结论存续声明;双语对同步) docs(gui): 时效清扫③b——四对 note 头注的 i18n 配对哈希重录 build(typecheck): minimal-diff tsconfig shape — drop root files entry (purity spec + preset move to client program), compress comments, drop redundant util/home root ref feat(gui): apps/web restoration follow-through — dsh-frontend package name, cli dist resolve, root build:web filter, tsdown exemption dropped, vitest web lane + knip + client aggregate retargeted, e2e paths rebased refactor(gui): exports 纪律刀2——connection wire 六件 git mv 进 src/client/(wire 即该 dshClient 插件的 client 半),node index=只空 apply,/client 半边整面导出(v3 §3.2 清单原样),包内 tests 改 src/client 直取 refactor(gui): exports 纪律刀3——runtime 实现整体下沉 src/client/(sessions/slots/loader;契约类型与 cordis merge 随迁 client/index),node index=只空 apply;./loader exports 指 client/loader;全消费面(web 壳/ui-sidebar/ui-trajectory/tests)bare→/client 机械跟改;vitest.e2e 换 tsconfig.vitest paths(root tsconfig 排除 client 会把 /client import 掉到 exports 的浏览器 dist bundle) refactor(gui): exports 纪律刀3 补遗——ui-layout 三处 bare runtime import 改 /client(刀3 消费面机械跟改漏提交件;跨属地机械一行×3 报备 ui-shell) test(gui): drop the getSessionManager singleton case — the init/get pair is a dead legacy-boot surface with zero live consumers (SessionsService constructs and holds the manager under the plugin architecture); source removal tracked with rt-core refactor(gui): 删 manager.ts 尾部 initSessionManager/getSessionManager 单例对——旧 boot 直连遗物,插件化下 SessionsService 构造持有 manager,全仓零活消费者(convo-b 测试清扫对表,其测试用例已先行退役 7e2c51898);头注释同步去单例措辞 code refactor
2026-07-19 21:17:57 +08:00
pkg_client_ui_primitives["client-ui-primitives"]
pkg_client_ui_question["client-ui-question"]
pkg_client_ui_settings["client-ui-settings"]
pkg_client_ui_settings_general["client-ui-settings-general"]
feat(gui): step1 skeleton — dsc web serves built web UI over booted harness host Five new modules: apps/dsc (bin: parseArgs + node:http static server + signal shutdown), packages/host/apiproxy (programmatic harness core composition, agents:[]), packages/client/web-runtime (React-free browser runtime), packages/client/web-ui (React mount), apps/web (vite build entry producing dist consumed by apps/dsc via package exports). Root wiring: apps/* workspace glob, dsh-* paths for host/client groups, demo:web script, apps/web/dist gitignore. No protocol/API routes yet — contract lands in step2 (see missions/tasks/20260719-1902-apiproxy-api-design). Includes the design + implementation archives (spec v2.1, deepseekchat baseline and harness boot research, implementation run log). Acceptance: 12/12 passed incl. real-key llm.stream smoke (51 chunks). feat(gui): apiproxy — four-quadrant RPC contract + fetch carriers, live end to end Contract layer (src/api/, 14 files): four named wire message types (ClientRequest / ServerResponse / ServerRequest / ClientResponse) as a discriminated union over strict bidirectional rpcId (initiator mints, responder echoes; channel and message fully decoupled — HTTP is the client->server pipe, SSE the reverse); narrow RpcRequest<P>/ RpcResponse<T> signature forms; RpcMethodMap with RequestPayload<K>/ ResponseValue<K> derivation; typed RpcError details map; approval/ question responses modeled as ClientResponse via a single /api/respond endpoint (RpcReceipt carrier ack); zod schemas anchored per Wire<T> against exactOptionalPropertyTypes. impl/api-proxy.ts: describe/list/create, both SSE streams (frame queue pump, subscribed baseline, lifecycle frames, signal cleanup); history pages on message boundaries (tail-back scan, partial included in the tail page); prompt dispatches queue->agent.send / steer->agent.steer with rpcId carried through MessageSource; cancel for attached sessions; cold-session resume deduped via a per-id promise map; host-level provider/model defaults injected at create/resume. fetch/: mechanical UNARY_ROUTES table, two-level parse with path==method check, SSE frames completed to ServerRequest full form; client mints -> narrows -> envelopes outbound, verifies rpcId echo inbound, streams SSE frames, four-quadrant onEnvelope tap (debug panel choke point). Real-browser fixes: URL base resolves to location.origin (hardcoded internal base broke real pages), browser-safe export paths. Design archives: contract design.md v2.0 with decision log, core-coverage audit, comparative studies, step2 impl run log. Probed end to end over real HTTP: prompt -> live model stream -> history returns the finished reply. feat(gui): RpcLog debug panel — fixture-driven milestone, playwright-verified 10/10 web-runtime: rpcLog + ui slices (zustand), four-quadrant RpcLogEntry (client-request / server-response / server-request / client-response), onEnvelope tap -> microtask-batched pump with 500-entry ring buffer, ConnectionController (private state, backoff reconnect), fixture API with fake envelopes (?fixture switch), bootWebRuntime; contract types via temporary local copies (api-types.ts, swapped for real imports when W3 client lands). web-ui: components/panels/RpcLog five-piece set (badge with unread count, floating panel, direction glyphs per quadrant, same-rpcId pair highlighting in two families, JSON payload expand, follow/pause, clear), App shell, utils/formatRelative, light-theme CSS variables with dark placeholders. dsc bin: mime lookup fixed to use the actually-served file (naked '/?query' no longer falls through to octet-stream download); shutdown closes SSE keep-alive connections so SIGTERM actually exits. Acceptance: scripts/verify-rpclog-panel.mjs (chromium headless) ALL PASS 10/10 over design.md §D 1-6. pkg: add web scripts for building feat(gui): session milestone — list + conversation over Session OOP, styled RpcLog v2.1 web-runtime: Session/SessionManager object layer (resident instances, mux frame routing, lineage flattening), foldSurface adapter with padding sentinels for paged windows, chunk accumulator for streaming partials, batched change notification (useSyncExternalStore contract), connection sinks + reconnect fix (the 300ms self-abort reconnect storm that made the session list flap is gone), fixture rewritten as a scripted host (60-turn history, typewriter replay, resident pending approval, child session); temporary contract copies deleted in favor of real apiproxy imports. web-ui: sessions screen (list with lineage indent + selection as container-local state), conversation view (turn grouping, reasoning fold, tool cards, steering, pending interaction cards, upward paging with scroll anchoring), input bar with queue/steer/stop; RpcLog panel restyled per docs/web-styling.md (tokenized palette, quadrant badge glyphs now vertical ↑↓⇟⇞, pair highlighting, floating shadow). docs/web-styling.md: living style guide (tokens, visual baseline, coding rules, evolution log). Acceptance: verify-session.mjs 31/31, verify-session-real.mjs 5/5 (real model streaming), verify-rpclog-panel.mjs 10/10. feat(gui): hostruntime split + repo-wide package prefix rename Package split (design: 20260720-0101-hostruntime-split-design): dsh-host-runtime carries bootHost + createApiProxy + startHost() (RunningHost {api, handler, defaults, ctx, dispose} — the seam Electron and any future shell reuses; ctx is the official front-door mount point); dsh-host-webserver carries the node:http static+API bridge (fixed: abort now keys on res 'close' + writableEnded — req 'close' fires on body end since Node 16 and was killing every SSE stream instantly, the reconnect-storm root cause); apps/dsc is now a thin assembly with web/-p subcommands. dsc -p runs the full isomorphic carrier chain in process (second real protocol consumer; probed end-to-end against the live model). Naming rule (user decree): packages under host/ and client/ carry the directory prefix in their npm name — dsh-host-apiproxy, dsh-client-web-runtime, dsh-client-web-ui renamed repo-wide in one frozen batch; explicit tsconfig paths entries added where the wildcard no longer matches. Acceptance: verify-session 31/31, verify-rpclog-panel 10/10, verify-session-real 7/7 (incl. new 12s connection-stability sentinels), tsc green, dsc web + dsc -p smoke both pass. refactor(gui): AbstractApiClient class hierarchy — OO client with inheritable seams AbstractApiClient (apiproxy) carries every protocol invariant: rpcId minting, four-quadrant envelope wrap/unwrap, zod parsing, SSE frame parsing, the payload-direct IApiClient surface (callers no longer mint rpcIds — the carrier does), and the instance-level envelope observation pump (batched via microtask; moved off module-level globals in rpc-log.ts, which is now a pure subscriber mapping envelopes into store entries — the debug panel observes the connection, it is not part of it). Platform subclasses own two abstract seams (doFetch, onEnvelope) plus three protocol-level virtuals for transportless overrides: InProcessApiClient (apiproxy; dsc -p uses new InProcessApiClient( host.handler)), WebApiClient (web-runtime), FixtureApiClient (fixture now subclasses instead of wrapping). Naming per decree: AbstractApiClient / IApiClient; ApiProxy stays the impl-side narrow-form contract. headless.ts call sites drop rpcRequest wrappers (payload-direct); split-design archive updated with the naming-rule ledger. tsc green; verify-session 31/31, verify-rpclog-panel 10/10, verify-session-real 7/7 (12s connection sentinel count=4); dsc -p smoke CALLER-OK. feat(gui): InputBar final form — bug batch, deepseekchat layout, single primary button, running locks input Squashes the whole InputBar iteration batch: IME/caret/auto-grow/focus/dedup bug fixes, layout aligned to the deepseekchat baseline, single primary button with hover flyout, finalized button semantics with the Codex-style icon circle, and running-state locking where stop is the only mid-turn action. The same batch carried the Chinese-to-English code comment sweep (density pruned), folded in here. docs(gui): purge work-log references from code comments 76 design-doc references cleared across the GUI packages: section pointers inlined as self-contained constraint statements, pure pointer comments dropped, milestone codenames and ruling tags out, and the 14 contract file headers switched to the formal RFC (the only sanctioned external reference). web-styling.md now cites the styling RFC instead of the disposable research archive. grep for work-log reference variants is clean across the GUI packages. docs(gui): file-header comments self-contained — drop RFC filename references RFC renames/reorgs must not require a source sweep (the 2026-07-20 two-way merge proved it). 11 headers lose only the '(RFC …)' tail and stay self-contained; api-proxy.ts keeps its minimal-first note. fix(gui): session streaming — freeze interrupted partials, sweep stale running calls, send force-scrolls Aborted turns never emit the finalizing assistant/message, so the accumulated partial and its running tool cards kept rendering below later messages — the "new message lands above the stopped reply" illusion. turn/end side effects now freeze content-bearing partials into interrupted terminal nodes (fractional seq keeps flow order; the live freeze and history replay converge through applyEventSideEffects, so a refresh reconstructs identical frozen nodes) and turn running tool cards into interrupted terminal cards; only content-free partials are swept outright. ConversationView gains the send-force-scroll rule (own words must be visible) alongside the pre-update atBottom follow flag. Regressions pinned as E2-4a–c (real host) and §E1-11h (fixture). feat(gui): webserver hardening verify script feat(gui): dark-mode toggle pinned to the sidebar bottom Interim home before the Settings page exists (the button re-homes with zero logic change — mechanics live in utils/theme.ts): html[data-theme] flip + dsc.theme localStorage, stored choice wins over the OS prefers-color-scheme default, applied in mount() before first paint so a dark reload never flashes light. Moon/sun inline SVG icon button at the sidebar's pinned bottom row. Pure front-end local concern: no RPC, no Session/store involvement. Dark sweep of list/conversation/input card/RPC panel found no unreadable pairs — no token changes needed. docs(gui): GUI RFCs and web styling handbook Layering+RPC protocol and web client architecture RFCs (post-reorg, developer-facing polish folded in) plus the styling engineering handbook. Mission work logs live in the commit above; PRs can be cut from this commit to include formal docs only. fix(gui): client object-layer hardening — audit timing/reference/resilience batches (S3-S5,C1-C3,C5-C8) fix(gui): carrier error channel + webserver backpressure (audit A1-A5,A7-A10,R2,R5) feat(gui): session persistence surface — cold list, project cwd, legacy no-cwd retirement refactor: rename dsc CLI to dsh — apps/cli, bin name, package scope Includes the root tsconfig project-references fix for host/* and client/web-runtime (originally a separate build fix commit). test(gui): three-tier suite — protocol/object/browser lanes, tier-a fill to per-file 100% test(gui): jsdom lane for web-ui + web-runtime coverage gate entry docs(gui): GUI testing system RFC (zh) feat(gui): tool-card views — contract slot, host-computed delivery, three-level card fallback fix(gui): lint clean across GUI packages — wrap long doc comments, drop dead type args, sync-return methods without awaits docs(gui): doc-sync mechanical fixes — JSDoc on apiproxy/host exports, RFC sketch fences ignore-check, md-wrap paragraphs, drop missions links, web-ui plain-ts entry chore(gui): module-graph regen + knip clean — drop dead re-exports, internalize createFixtureApi, scan web-ui tsx and verify mjs scripts build(gui): wire client/host packages into the lib build shape — tsc references + tsdown (web-ui css-external), lib manifests, cordis peer, apiproxy typed subpaths, vite src aliases test(gui): host-side per-file 100% coverage — apiproxy schema/carrier suites, webserver http-bridge suite, host-runtime composition suite; client/* coverage excluded pending the browser-side testing work item docs(gui): package READMEs for the five GUI packages — model-experience audit entries, limitations sections docs(gui): bilingual RFC pairs + client JSDoc completion — translate the three GUI RFCs to English with i18n records and manifest ratchet, Consequences sections both sides, full client/* export JSDoc, regen doc graphs and RFC index fix(scripts): doc-typecheck built-declarations mode maps /src/* subpath wildcards (apiproxy browser-safe channels) docs(gui): apply dsh rename across pr-gates docs — READMEs, layering RFC en, web-ui entry comment, i18n re-record fix(gui): post-rebase lint reconciliation — wrap main-tree long doc comments, read-through narrowing guards, abortError Error normalization, handleUnary generic justification fix(gui): post-rebase doc/test reconciliation — align host specs with evolved carrier contracts (sentinel rpcId, stream/error surfacing, url-path transport messages, defaults.cwd), Agent Note titles and relocated links, KV Cache effect sections, JSDoc on evolved exports fix(gui): second-rebase reconciliation to 509db0cb3 — restore api panel exports the baseline suites consume, knip workspace entries for jsdom lane and apps/web smokes, hoist result narrowing, align testing.md to the narrowed web-ui exclusion fix(test): vitest-scoped tsconfig maps bare imports for tsx specs — with GUI manifests now pointing at lib, an unmapped importer loaded a second copy of the web-runtime singletons fix(gui): typecheck + lint clean over the tool-card batch — brand callIds and object-form turn/end reason in the view spec, narrow fixture arg stringification, wrap long v8-ignore comments docs(gui): export JSDoc for tool-card surfaces + testing-note pairing header docs: rfc for web testing feat: add tools to host-runtime fix(gui): dispatch agent/error via agentEvents in host-runtime spec — mounted invariants plugin rejects raw ctx.emit without the scope carrier fix(gui): restore GUI knip workspaces + scripts/mjs entries and regenerate lockfile after master rebase fix(gui): post-rebase gate repairs — drop context-node envelope (master unwrapped injected content envelopes), regen event matrix, condense testing.md web-ui exclusion within budget fix(session): browser-safe deep-equal in surface — node:util import broke the vite bundle ci(gates): frontend vite build joins pre-push — node: imports in the client closure pass tsc but break the browser bundle test(tui): drop the checkout-dependent process.cwd() harness default — a long worktree path pushes the footer token counters past the 88-column fake terminal test(gui): jsdom behavior E2E — conversation main path over fixture runtime, reconnect banner lifecycle test(gui): jsdom RPC panel behavior — ledger rows, expand, pairing, pause/clear, follow-pause, payload truncation test(gui): jsdom tier-2 — InputBar guards, reasoning fold, JSON blocks, message variants, theme, create-then-select; act-harden banner case test(gui): jsdom tier-3 — ConversationView states/paging/force-bottom, ToolCallCard arms, PendingCard, list rows test(gui): jsdom tails — view-card variants, LogRow directions, registry hygiene, badge overflow, hook ops, mount glue test(gui): jsdom tails round 2 — call-ref blocks, resume follow, view precedence, failed create, empty-diff arm test(gui): jsdom final arms — anchor compensation, follow-off, interval ticks, view halves, node-over-running precedence test(gui): web-ui joins the per-file 100% coverage gate Annotation-only src changes plus the config swap. The web-ui exclusion is replaced by a single index.tsx entry (stale byte-identical duplicate of mount.tsx, nothing imports it; same entry-glue treatment as bin.ts) and the coverage include gains .tsx. v8-ignore sites (each with its reason inline): - ConversationView 3x ref-null guards; InputBar disabled-click guard - ToolCallCard both-null arms + windowless-custom argsRaw arm - LogRow css-module key fallbacks (start/stop block); RpcLogBody 3x ref-null guards - web-runtime drift from the tool-card batch: fixture presenter catch/str typo-guards, dense-array guards (fold-adapter reset, session rebuild, fixture backscan), live view-present arm (fixture replays are text-only; view vocabulary is covered by the history samples) test(gui): close the PR #443 host-side coverage gaps — apiproxy client abort arms, api-proxy cold/view paths, webserver drain - apiproxy fetch/client.ts: 3 new cases (pre-aborted signal short-circuits before transport + string reason mapping, non-Error/string reason falls to the default AbortError message, signal-less doFetch passthrough) - runtime/api-proxy.ts: one v8-ignore (summarizeCold cwd arm — list() filters cwd-less legacy metas) + api-proxy-cold.spec.ts (cold list merge: mtime source, locate-undefined and vanished-log fallbacks, lineage; no-persistence/no-factory resume → internal) + 2 view cases (history views with meta passthrough and orphan/bad-args/presenterless soft-falls, session/disposed open-call cleanup on the mux stream) - webserver/index.ts: /api/big fixture drives both drain-wait legs (full 8MiB readback after drain, mid-chunk disconnect wakes via 'close') feat: app shell fix: rebase conflicts fix: coverage fix(gui): lint clean after rebase — wrap long v8-ignore comments, unconditional v1 detail-block claim chore(gui): remove browser/probe verify scripts from scripts/ The six GUI acceptance/probe scripts (carrier-errors, rpclog-panel, session, session-real, webserver-backpressure, webserver-hardening) leave the repo's scripts/ tree; the three code comments that pointed at them now describe the coverage lane without naming a script path. fix(webserver): guard the request callback — one malformed request must not kill the process The async handle() had no top-level catch, so any throw inside it (a bad %-escape reaching decodeURIComponent, a client dropping mid-body, a response stream erroring) became an unhandled rejection and took the whole process down (audit R1 must-fix). The guard answers 400 when headers are not out yet, destroys the socket when they are, and reports the failure to onError (the package never prints). Spec covers all three legs: %-escape barrage → 400 + server stays alive, non-Error throw wrapped for onError, mid-stream explosion → socket teardown. feat: client AGENTS.md fix: client/AGENTS.md fix: rebase feat(gui): T0 cut 1 — 12 client package skeletons with contract stubs, dshClient declarations, tsdown client preset, theme token sheets feat(gui): T0 cut 2 — pure git mv migration per v3 §11 (connection six, runtime sessions/kernel, ui-conversation chat, ui-primitives markdown family, web shell + e2e) feat(gui): T0 cuts 3+4 — import rewiring to new package names, .legacy demotion of owner-rewrite files, legacy web-runtime/web-ui/apps-web retired to attic feat(gui): connection 对账刀——index.ts 精确导出清单替换 export *,intents.legacy 溶解删除 feat(client/ui-slots): SlotCore real implementation — kind semantics, sync version + microtask-batched notify, onMutate bridge feat(gui): web shell vite alias — retarget to new client packages, shell static surface only feat(gui): host 侧刀属地半——HostWebPluginRegistry(entries 扫描+internal/plugin 去抖重扫+dshClient 校验+exports./client 解析)、GET /plugins/<id>/client.js 分发端点、GET / 与 SPA fallback 注入 __DSH_BOOT__(webPlugins 可选注入,不传行为不变) feat(web-react): add use-sync-external-store dep + local shim typings feat(web-react): bindSnapshotSelector via uSES with-selector shim feat(gui): ui-layout concession-chain solver — pure computeColumns with contract geometry feat(gui): ui-layout LayoutService — four persisted stores, clamped actions, list-driven prune feat(gui): ui-layout AppFrame styles — grid columns, collapse-safe borders, edge drag handles test(gui): 存量 spec 平移——connection 三件+runtime 六件自 attic 捞回改包名路径全绿;api-helpers 按归属拆分(wire 半留 connection、classifier 半随 conversation.ts 入 runtime);boot-intents/preinit/rpc-log 随 intents/rpc-log 退役不迁(记 v3 §3.2 溶解项) feat(client/ui-primitives): StateDot/Button/Pill/Input/Menu atoms, ConnectionBanner de-legacied to pure props, JsonBlock CSS on --dsw tokens feat(web-react): createSnapshotStore engine (rafFlush batch, persist opt-in, dev freeze) + spec feat(gui): ui-layout AppFrame — grid tracks, pointer-capture drag handles with rAF throttle, frame ResizeObserver feat(web-react): useInvoke (external pending store, stable invoke, concurrency count) + spec test(web-react): bind spec — equality bail, custom eq, zero resubscribe, StrictMode, method sources feat(gui): ui-layout index rewiring — real exports, client apply provides ctx.layout and defines three slots feat(web-react): SessionProvider (renderBody deps) + RootBindingProvider + binding contexts + spec feat(gui): web shell AppRoot boot-page styles — self-contained with neutral token fallbacks feat(gui): web shell AppRoot — boot gate over loader status, fail-loud plugin failure list fix(gui): AppRoot gates on explicit settled signal — status-derived readiness races the incrementally filled table feat(client/ui-theme): ThemeService real implementation — registry with built-in light/dark, apply toggles body[data-ds-dark-theme], third-party token overrides as body inline vars feat(web-react): scopedSlots outlet (kind matrix, inject WeakMap caches, per-entry error boundary) + spec feat(gui): web shell module-table seed — pure-library entities for the loader require surface feat(client/i18n): I18nService real implementation — ns×locale registry, stable bind(ns) reference, zh fallback chain, zh/en skeleton dictionaries feat(gui): web shell assembly closure — layout exports via module table, SessionProvider + scopedSlots + RootBindingProvider feat: client/ui-conversation feat: code codedoc build(gui): root bundle green — web shell excluded from the lib workspace (vite app), ui-primitives lib externalizes css side-effect imports (web-ui precedent) gates(gui): verify-cordis-config follows aggregate tsconfig references (root is a shell over host/client programs); module graph regenerated for the twelve client packages chore(gui): retire legacy migration sources — every owner rewrite landed (t0-checklist §7 ledger honored); orphan css of retired components removed gates(gui): knip green groundwork — e2e/tsx entries for the new packages, loader-runtime deps ignored where loading is by specifier string, fake plugin ids un-bare-named, dead test export dropped chore(client): manifest shape batch A — ui-slots/web-react/ui-primitives invariant companions, files whitelist, cordis+invariants peer/dev, tsconfig refs chore(client): manifest shape batch B — connection/runtime/ui-conversation/ui-trajectory files whitelist, cordis peer+dev, explicit invariant lib entries (clientBundle signature) chore(client): manifest shape batch C — i18n/ui-layout/ui-sidebar/ui-theme invariant companions, files whitelist, invariants peer/dev, tsconfig refs chore(client): manifest shape batch D — web shell gains node-half lib entry + invariant companion + uniform files whitelist chore(client): drop verified-unused deps — dsh-tools from runtime/ui-conversation (types ride /presentation), ui-primitives+clsx from ui-layout gates(gui): doc-gate fixes — theme JSDoc prose, three client type-link exemptions, agent-note paths follow the migration, config catalog regenerated gates(gui): type-equiv manifest follows the types.ts extraction, approval JSDoc keeps its link form, persistence catalog regenerated docs(gui): per-constant JSDoc on the contract geometry exports (export-jsdoc gate) test(gates): loader-composition budget covers cold tsx resolution after the program split (was flaking at the default 5s) docs(gui): README substantiation batch 1 — ui-slots/ui-primitives/web-react/connection: Model Experience short form, real deferred-work ledgers, description accuracy pass fix(client): theme/i18n dual-entry split — service classes + cordis merges move to src/client (host catalog scanner no longer misclassifies client services), node halves keep types + empty apply; catalogs regenerated docs(gui): README substantiation batch 2 — runtime/ui-layout/ui-sidebar/ui-conversation: Model Experience short form, package-owned deferred-work ledgers (unload stub, watch approximation, /client value-import rule, global details state, two-state dots, stats duration gap, single-bundle caches) docs(gui): README substantiation batch 3 — ui-trajectory/ui-theme/i18n/web: Model Experience short form, deferred-work ledgers (placeholder charter, no theme toggle owner, empty locale dictionaries, one-shot rendering); both README gates green test(scripts): purity spec adopts clientBundle two-arg signature (explicit libEntry, no default) gates(gui): knip green — declaration-merge dep ignored, fake plugin id assembled at runtime, invariants dep de-duplicated to peer+dev, stale apps/web section dropped feat(gui): 门禁波次 host 三包 invariant 形状——apiproxy explained-empty 伴生(wire 契约层零事件面)、webserver 真关系伴生(manifest 行必解析出 clientPath,防 __DSH_BOOT__ 广告 404 bundle;apps/cli 发布 webPlugins 键供审计)、runtime 补 files 白名单;三包 exports/files/peer+dev/tsconfig refs 齐 fw-react 形状;constraints+invariants 双 gate 零违规 build(client): ui-layout/ui-sidebar tsdown configs adopt the explicit two-arg clientBundle signature (orphaned follow-up of the manifest shape batch) refactor(gui): shell boot becomes a library face — bootWebShell(el) exported for the apps/web entry; main.ts retired refactor(gui): exports 纪律刀1——ui-theme/i18n node index 收敛为只空 apply(Translate/LocaleDict/ThemeTokens 类型下沉 src/client/),ui-conversation 的 I18nService import 改 /client 子路径 build(typecheck): converge to root host aggregate + tsconfig.client.json — delete tsconfig.host.json, verify-cordis-config seeds both aggregates feat(gui): apps/web restored as the vite application — thin main over bootWebShell; dsh-client-web becomes a plain lib (index exports shell surface, vite files and e2e moved out) chore(gates): knip.json rewritten on the master base — same semantics, minimal diff (formatting churn dropped) docs(gui): 时效清扫②——testing.md 删 web-ui 覆盖豁免残句;web-styling.md 加 token 换代头注(--dsw-* 现行、工程约束条款仍有效并注明收编处) docs(gui): 时效清扫③——四对 GUI Agent Note 加路径更新头注(web-runtime/web-ui/dsh-frontend→现行 12 包结构;设计结论存续声明;双语对同步) docs(gui): 时效清扫③b——四对 note 头注的 i18n 配对哈希重录 build(typecheck): minimal-diff tsconfig shape — drop root files entry (purity spec + preset move to client program), compress comments, drop redundant util/home root ref feat(gui): apps/web restoration follow-through — dsh-frontend package name, cli dist resolve, root build:web filter, tsdown exemption dropped, vitest web lane + knip + client aggregate retargeted, e2e paths rebased refactor(gui): exports 纪律刀2——connection wire 六件 git mv 进 src/client/(wire 即该 dshClient 插件的 client 半),node index=只空 apply,/client 半边整面导出(v3 §3.2 清单原样),包内 tests 改 src/client 直取 refactor(gui): exports 纪律刀3——runtime 实现整体下沉 src/client/(sessions/slots/loader;契约类型与 cordis merge 随迁 client/index),node index=只空 apply;./loader exports 指 client/loader;全消费面(web 壳/ui-sidebar/ui-trajectory/tests)bare→/client 机械跟改;vitest.e2e 换 tsconfig.vitest paths(root tsconfig 排除 client 会把 /client import 掉到 exports 的浏览器 dist bundle) refactor(gui): exports 纪律刀3 补遗——ui-layout 三处 bare runtime import 改 /client(刀3 消费面机械跟改漏提交件;跨属地机械一行×3 报备 ui-shell) test(gui): drop the getSessionManager singleton case — the init/get pair is a dead legacy-boot surface with zero live consumers (SessionsService constructs and holds the manager under the plugin architecture); source removal tracked with rt-core refactor(gui): 删 manager.ts 尾部 initSessionManager/getSessionManager 单例对——旧 boot 直连遗物,插件化下 SessionsService 构造持有 manager,全仓零活消费者(convo-b 测试清扫对表,其测试用例已先行退役 7e2c51898);头注释同步去单例措辞 code refactor
2026-07-19 21:17:57 +08:00
pkg_client_ui_sidebar["client-ui-sidebar"]
pkg_client_ui_skill["client-ui-skill"]
pkg_client_ui_slash["client-ui-slash"]
feat(gui): step1 skeleton — dsc web serves built web UI over booted harness host Five new modules: apps/dsc (bin: parseArgs + node:http static server + signal shutdown), packages/host/apiproxy (programmatic harness core composition, agents:[]), packages/client/web-runtime (React-free browser runtime), packages/client/web-ui (React mount), apps/web (vite build entry producing dist consumed by apps/dsc via package exports). Root wiring: apps/* workspace glob, dsh-* paths for host/client groups, demo:web script, apps/web/dist gitignore. No protocol/API routes yet — contract lands in step2 (see missions/tasks/20260719-1902-apiproxy-api-design). Includes the design + implementation archives (spec v2.1, deepseekchat baseline and harness boot research, implementation run log). Acceptance: 12/12 passed incl. real-key llm.stream smoke (51 chunks). feat(gui): apiproxy — four-quadrant RPC contract + fetch carriers, live end to end Contract layer (src/api/, 14 files): four named wire message types (ClientRequest / ServerResponse / ServerRequest / ClientResponse) as a discriminated union over strict bidirectional rpcId (initiator mints, responder echoes; channel and message fully decoupled — HTTP is the client->server pipe, SSE the reverse); narrow RpcRequest<P>/ RpcResponse<T> signature forms; RpcMethodMap with RequestPayload<K>/ ResponseValue<K> derivation; typed RpcError details map; approval/ question responses modeled as ClientResponse via a single /api/respond endpoint (RpcReceipt carrier ack); zod schemas anchored per Wire<T> against exactOptionalPropertyTypes. impl/api-proxy.ts: describe/list/create, both SSE streams (frame queue pump, subscribed baseline, lifecycle frames, signal cleanup); history pages on message boundaries (tail-back scan, partial included in the tail page); prompt dispatches queue->agent.send / steer->agent.steer with rpcId carried through MessageSource; cancel for attached sessions; cold-session resume deduped via a per-id promise map; host-level provider/model defaults injected at create/resume. fetch/: mechanical UNARY_ROUTES table, two-level parse with path==method check, SSE frames completed to ServerRequest full form; client mints -> narrows -> envelopes outbound, verifies rpcId echo inbound, streams SSE frames, four-quadrant onEnvelope tap (debug panel choke point). Real-browser fixes: URL base resolves to location.origin (hardcoded internal base broke real pages), browser-safe export paths. Design archives: contract design.md v2.0 with decision log, core-coverage audit, comparative studies, step2 impl run log. Probed end to end over real HTTP: prompt -> live model stream -> history returns the finished reply. feat(gui): RpcLog debug panel — fixture-driven milestone, playwright-verified 10/10 web-runtime: rpcLog + ui slices (zustand), four-quadrant RpcLogEntry (client-request / server-response / server-request / client-response), onEnvelope tap -> microtask-batched pump with 500-entry ring buffer, ConnectionController (private state, backoff reconnect), fixture API with fake envelopes (?fixture switch), bootWebRuntime; contract types via temporary local copies (api-types.ts, swapped for real imports when W3 client lands). web-ui: components/panels/RpcLog five-piece set (badge with unread count, floating panel, direction glyphs per quadrant, same-rpcId pair highlighting in two families, JSON payload expand, follow/pause, clear), App shell, utils/formatRelative, light-theme CSS variables with dark placeholders. dsc bin: mime lookup fixed to use the actually-served file (naked '/?query' no longer falls through to octet-stream download); shutdown closes SSE keep-alive connections so SIGTERM actually exits. Acceptance: scripts/verify-rpclog-panel.mjs (chromium headless) ALL PASS 10/10 over design.md §D 1-6. pkg: add web scripts for building feat(gui): session milestone — list + conversation over Session OOP, styled RpcLog v2.1 web-runtime: Session/SessionManager object layer (resident instances, mux frame routing, lineage flattening), foldSurface adapter with padding sentinels for paged windows, chunk accumulator for streaming partials, batched change notification (useSyncExternalStore contract), connection sinks + reconnect fix (the 300ms self-abort reconnect storm that made the session list flap is gone), fixture rewritten as a scripted host (60-turn history, typewriter replay, resident pending approval, child session); temporary contract copies deleted in favor of real apiproxy imports. web-ui: sessions screen (list with lineage indent + selection as container-local state), conversation view (turn grouping, reasoning fold, tool cards, steering, pending interaction cards, upward paging with scroll anchoring), input bar with queue/steer/stop; RpcLog panel restyled per docs/web-styling.md (tokenized palette, quadrant badge glyphs now vertical ↑↓⇟⇞, pair highlighting, floating shadow). docs/web-styling.md: living style guide (tokens, visual baseline, coding rules, evolution log). Acceptance: verify-session.mjs 31/31, verify-session-real.mjs 5/5 (real model streaming), verify-rpclog-panel.mjs 10/10. feat(gui): hostruntime split + repo-wide package prefix rename Package split (design: 20260720-0101-hostruntime-split-design): dsh-host-runtime carries bootHost + createApiProxy + startHost() (RunningHost {api, handler, defaults, ctx, dispose} — the seam Electron and any future shell reuses; ctx is the official front-door mount point); dsh-host-webserver carries the node:http static+API bridge (fixed: abort now keys on res 'close' + writableEnded — req 'close' fires on body end since Node 16 and was killing every SSE stream instantly, the reconnect-storm root cause); apps/dsc is now a thin assembly with web/-p subcommands. dsc -p runs the full isomorphic carrier chain in process (second real protocol consumer; probed end-to-end against the live model). Naming rule (user decree): packages under host/ and client/ carry the directory prefix in their npm name — dsh-host-apiproxy, dsh-client-web-runtime, dsh-client-web-ui renamed repo-wide in one frozen batch; explicit tsconfig paths entries added where the wildcard no longer matches. Acceptance: verify-session 31/31, verify-rpclog-panel 10/10, verify-session-real 7/7 (incl. new 12s connection-stability sentinels), tsc green, dsc web + dsc -p smoke both pass. refactor(gui): AbstractApiClient class hierarchy — OO client with inheritable seams AbstractApiClient (apiproxy) carries every protocol invariant: rpcId minting, four-quadrant envelope wrap/unwrap, zod parsing, SSE frame parsing, the payload-direct IApiClient surface (callers no longer mint rpcIds — the carrier does), and the instance-level envelope observation pump (batched via microtask; moved off module-level globals in rpc-log.ts, which is now a pure subscriber mapping envelopes into store entries — the debug panel observes the connection, it is not part of it). Platform subclasses own two abstract seams (doFetch, onEnvelope) plus three protocol-level virtuals for transportless overrides: InProcessApiClient (apiproxy; dsc -p uses new InProcessApiClient( host.handler)), WebApiClient (web-runtime), FixtureApiClient (fixture now subclasses instead of wrapping). Naming per decree: AbstractApiClient / IApiClient; ApiProxy stays the impl-side narrow-form contract. headless.ts call sites drop rpcRequest wrappers (payload-direct); split-design archive updated with the naming-rule ledger. tsc green; verify-session 31/31, verify-rpclog-panel 10/10, verify-session-real 7/7 (12s connection sentinel count=4); dsc -p smoke CALLER-OK. feat(gui): InputBar final form — bug batch, deepseekchat layout, single primary button, running locks input Squashes the whole InputBar iteration batch: IME/caret/auto-grow/focus/dedup bug fixes, layout aligned to the deepseekchat baseline, single primary button with hover flyout, finalized button semantics with the Codex-style icon circle, and running-state locking where stop is the only mid-turn action. The same batch carried the Chinese-to-English code comment sweep (density pruned), folded in here. docs(gui): purge work-log references from code comments 76 design-doc references cleared across the GUI packages: section pointers inlined as self-contained constraint statements, pure pointer comments dropped, milestone codenames and ruling tags out, and the 14 contract file headers switched to the formal RFC (the only sanctioned external reference). web-styling.md now cites the styling RFC instead of the disposable research archive. grep for work-log reference variants is clean across the GUI packages. docs(gui): file-header comments self-contained — drop RFC filename references RFC renames/reorgs must not require a source sweep (the 2026-07-20 two-way merge proved it). 11 headers lose only the '(RFC …)' tail and stay self-contained; api-proxy.ts keeps its minimal-first note. fix(gui): session streaming — freeze interrupted partials, sweep stale running calls, send force-scrolls Aborted turns never emit the finalizing assistant/message, so the accumulated partial and its running tool cards kept rendering below later messages — the "new message lands above the stopped reply" illusion. turn/end side effects now freeze content-bearing partials into interrupted terminal nodes (fractional seq keeps flow order; the live freeze and history replay converge through applyEventSideEffects, so a refresh reconstructs identical frozen nodes) and turn running tool cards into interrupted terminal cards; only content-free partials are swept outright. ConversationView gains the send-force-scroll rule (own words must be visible) alongside the pre-update atBottom follow flag. Regressions pinned as E2-4a–c (real host) and §E1-11h (fixture). feat(gui): webserver hardening verify script feat(gui): dark-mode toggle pinned to the sidebar bottom Interim home before the Settings page exists (the button re-homes with zero logic change — mechanics live in utils/theme.ts): html[data-theme] flip + dsc.theme localStorage, stored choice wins over the OS prefers-color-scheme default, applied in mount() before first paint so a dark reload never flashes light. Moon/sun inline SVG icon button at the sidebar's pinned bottom row. Pure front-end local concern: no RPC, no Session/store involvement. Dark sweep of list/conversation/input card/RPC panel found no unreadable pairs — no token changes needed. docs(gui): GUI RFCs and web styling handbook Layering+RPC protocol and web client architecture RFCs (post-reorg, developer-facing polish folded in) plus the styling engineering handbook. Mission work logs live in the commit above; PRs can be cut from this commit to include formal docs only. fix(gui): client object-layer hardening — audit timing/reference/resilience batches (S3-S5,C1-C3,C5-C8) fix(gui): carrier error channel + webserver backpressure (audit A1-A5,A7-A10,R2,R5) feat(gui): session persistence surface — cold list, project cwd, legacy no-cwd retirement refactor: rename dsc CLI to dsh — apps/cli, bin name, package scope Includes the root tsconfig project-references fix for host/* and client/web-runtime (originally a separate build fix commit). test(gui): three-tier suite — protocol/object/browser lanes, tier-a fill to per-file 100% test(gui): jsdom lane for web-ui + web-runtime coverage gate entry docs(gui): GUI testing system RFC (zh) feat(gui): tool-card views — contract slot, host-computed delivery, three-level card fallback fix(gui): lint clean across GUI packages — wrap long doc comments, drop dead type args, sync-return methods without awaits docs(gui): doc-sync mechanical fixes — JSDoc on apiproxy/host exports, RFC sketch fences ignore-check, md-wrap paragraphs, drop missions links, web-ui plain-ts entry chore(gui): module-graph regen + knip clean — drop dead re-exports, internalize createFixtureApi, scan web-ui tsx and verify mjs scripts build(gui): wire client/host packages into the lib build shape — tsc references + tsdown (web-ui css-external), lib manifests, cordis peer, apiproxy typed subpaths, vite src aliases test(gui): host-side per-file 100% coverage — apiproxy schema/carrier suites, webserver http-bridge suite, host-runtime composition suite; client/* coverage excluded pending the browser-side testing work item docs(gui): package READMEs for the five GUI packages — model-experience audit entries, limitations sections docs(gui): bilingual RFC pairs + client JSDoc completion — translate the three GUI RFCs to English with i18n records and manifest ratchet, Consequences sections both sides, full client/* export JSDoc, regen doc graphs and RFC index fix(scripts): doc-typecheck built-declarations mode maps /src/* subpath wildcards (apiproxy browser-safe channels) docs(gui): apply dsh rename across pr-gates docs — READMEs, layering RFC en, web-ui entry comment, i18n re-record fix(gui): post-rebase lint reconciliation — wrap main-tree long doc comments, read-through narrowing guards, abortError Error normalization, handleUnary generic justification fix(gui): post-rebase doc/test reconciliation — align host specs with evolved carrier contracts (sentinel rpcId, stream/error surfacing, url-path transport messages, defaults.cwd), Agent Note titles and relocated links, KV Cache effect sections, JSDoc on evolved exports fix(gui): second-rebase reconciliation to 509db0cb3 — restore api panel exports the baseline suites consume, knip workspace entries for jsdom lane and apps/web smokes, hoist result narrowing, align testing.md to the narrowed web-ui exclusion fix(test): vitest-scoped tsconfig maps bare imports for tsx specs — with GUI manifests now pointing at lib, an unmapped importer loaded a second copy of the web-runtime singletons fix(gui): typecheck + lint clean over the tool-card batch — brand callIds and object-form turn/end reason in the view spec, narrow fixture arg stringification, wrap long v8-ignore comments docs(gui): export JSDoc for tool-card surfaces + testing-note pairing header docs: rfc for web testing feat: add tools to host-runtime fix(gui): dispatch agent/error via agentEvents in host-runtime spec — mounted invariants plugin rejects raw ctx.emit without the scope carrier fix(gui): restore GUI knip workspaces + scripts/mjs entries and regenerate lockfile after master rebase fix(gui): post-rebase gate repairs — drop context-node envelope (master unwrapped injected content envelopes), regen event matrix, condense testing.md web-ui exclusion within budget fix(session): browser-safe deep-equal in surface — node:util import broke the vite bundle ci(gates): frontend vite build joins pre-push — node: imports in the client closure pass tsc but break the browser bundle test(tui): drop the checkout-dependent process.cwd() harness default — a long worktree path pushes the footer token counters past the 88-column fake terminal test(gui): jsdom behavior E2E — conversation main path over fixture runtime, reconnect banner lifecycle test(gui): jsdom RPC panel behavior — ledger rows, expand, pairing, pause/clear, follow-pause, payload truncation test(gui): jsdom tier-2 — InputBar guards, reasoning fold, JSON blocks, message variants, theme, create-then-select; act-harden banner case test(gui): jsdom tier-3 — ConversationView states/paging/force-bottom, ToolCallCard arms, PendingCard, list rows test(gui): jsdom tails — view-card variants, LogRow directions, registry hygiene, badge overflow, hook ops, mount glue test(gui): jsdom tails round 2 — call-ref blocks, resume follow, view precedence, failed create, empty-diff arm test(gui): jsdom final arms — anchor compensation, follow-off, interval ticks, view halves, node-over-running precedence test(gui): web-ui joins the per-file 100% coverage gate Annotation-only src changes plus the config swap. The web-ui exclusion is replaced by a single index.tsx entry (stale byte-identical duplicate of mount.tsx, nothing imports it; same entry-glue treatment as bin.ts) and the coverage include gains .tsx. v8-ignore sites (each with its reason inline): - ConversationView 3x ref-null guards; InputBar disabled-click guard - ToolCallCard both-null arms + windowless-custom argsRaw arm - LogRow css-module key fallbacks (start/stop block); RpcLogBody 3x ref-null guards - web-runtime drift from the tool-card batch: fixture presenter catch/str typo-guards, dense-array guards (fold-adapter reset, session rebuild, fixture backscan), live view-present arm (fixture replays are text-only; view vocabulary is covered by the history samples) test(gui): close the PR #443 host-side coverage gaps — apiproxy client abort arms, api-proxy cold/view paths, webserver drain - apiproxy fetch/client.ts: 3 new cases (pre-aborted signal short-circuits before transport + string reason mapping, non-Error/string reason falls to the default AbortError message, signal-less doFetch passthrough) - runtime/api-proxy.ts: one v8-ignore (summarizeCold cwd arm — list() filters cwd-less legacy metas) + api-proxy-cold.spec.ts (cold list merge: mtime source, locate-undefined and vanished-log fallbacks, lineage; no-persistence/no-factory resume → internal) + 2 view cases (history views with meta passthrough and orphan/bad-args/presenterless soft-falls, session/disposed open-call cleanup on the mux stream) - webserver/index.ts: /api/big fixture drives both drain-wait legs (full 8MiB readback after drain, mid-chunk disconnect wakes via 'close') feat: app shell fix: rebase conflicts fix: coverage fix(gui): lint clean after rebase — wrap long v8-ignore comments, unconditional v1 detail-block claim chore(gui): remove browser/probe verify scripts from scripts/ The six GUI acceptance/probe scripts (carrier-errors, rpclog-panel, session, session-real, webserver-backpressure, webserver-hardening) leave the repo's scripts/ tree; the three code comments that pointed at them now describe the coverage lane without naming a script path. fix(webserver): guard the request callback — one malformed request must not kill the process The async handle() had no top-level catch, so any throw inside it (a bad %-escape reaching decodeURIComponent, a client dropping mid-body, a response stream erroring) became an unhandled rejection and took the whole process down (audit R1 must-fix). The guard answers 400 when headers are not out yet, destroys the socket when they are, and reports the failure to onError (the package never prints). Spec covers all three legs: %-escape barrage → 400 + server stays alive, non-Error throw wrapped for onError, mid-stream explosion → socket teardown. feat: client AGENTS.md fix: client/AGENTS.md fix: rebase feat(gui): T0 cut 1 — 12 client package skeletons with contract stubs, dshClient declarations, tsdown client preset, theme token sheets feat(gui): T0 cut 2 — pure git mv migration per v3 §11 (connection six, runtime sessions/kernel, ui-conversation chat, ui-primitives markdown family, web shell + e2e) feat(gui): T0 cuts 3+4 — import rewiring to new package names, .legacy demotion of owner-rewrite files, legacy web-runtime/web-ui/apps-web retired to attic feat(gui): connection 对账刀——index.ts 精确导出清单替换 export *,intents.legacy 溶解删除 feat(client/ui-slots): SlotCore real implementation — kind semantics, sync version + microtask-batched notify, onMutate bridge feat(gui): web shell vite alias — retarget to new client packages, shell static surface only feat(gui): host 侧刀属地半——HostWebPluginRegistry(entries 扫描+internal/plugin 去抖重扫+dshClient 校验+exports./client 解析)、GET /plugins/<id>/client.js 分发端点、GET / 与 SPA fallback 注入 __DSH_BOOT__(webPlugins 可选注入,不传行为不变) feat(web-react): add use-sync-external-store dep + local shim typings feat(web-react): bindSnapshotSelector via uSES with-selector shim feat(gui): ui-layout concession-chain solver — pure computeColumns with contract geometry feat(gui): ui-layout LayoutService — four persisted stores, clamped actions, list-driven prune feat(gui): ui-layout AppFrame styles — grid columns, collapse-safe borders, edge drag handles test(gui): 存量 spec 平移——connection 三件+runtime 六件自 attic 捞回改包名路径全绿;api-helpers 按归属拆分(wire 半留 connection、classifier 半随 conversation.ts 入 runtime);boot-intents/preinit/rpc-log 随 intents/rpc-log 退役不迁(记 v3 §3.2 溶解项) feat(client/ui-primitives): StateDot/Button/Pill/Input/Menu atoms, ConnectionBanner de-legacied to pure props, JsonBlock CSS on --dsw tokens feat(web-react): createSnapshotStore engine (rafFlush batch, persist opt-in, dev freeze) + spec feat(gui): ui-layout AppFrame — grid tracks, pointer-capture drag handles with rAF throttle, frame ResizeObserver feat(web-react): useInvoke (external pending store, stable invoke, concurrency count) + spec test(web-react): bind spec — equality bail, custom eq, zero resubscribe, StrictMode, method sources feat(gui): ui-layout index rewiring — real exports, client apply provides ctx.layout and defines three slots feat(web-react): SessionProvider (renderBody deps) + RootBindingProvider + binding contexts + spec feat(gui): web shell AppRoot boot-page styles — self-contained with neutral token fallbacks feat(gui): web shell AppRoot — boot gate over loader status, fail-loud plugin failure list fix(gui): AppRoot gates on explicit settled signal — status-derived readiness races the incrementally filled table feat(client/ui-theme): ThemeService real implementation — registry with built-in light/dark, apply toggles body[data-ds-dark-theme], third-party token overrides as body inline vars feat(web-react): scopedSlots outlet (kind matrix, inject WeakMap caches, per-entry error boundary) + spec feat(gui): web shell module-table seed — pure-library entities for the loader require surface feat(client/i18n): I18nService real implementation — ns×locale registry, stable bind(ns) reference, zh fallback chain, zh/en skeleton dictionaries feat(gui): web shell assembly closure — layout exports via module table, SessionProvider + scopedSlots + RootBindingProvider feat: client/ui-conversation feat: code codedoc build(gui): root bundle green — web shell excluded from the lib workspace (vite app), ui-primitives lib externalizes css side-effect imports (web-ui precedent) gates(gui): verify-cordis-config follows aggregate tsconfig references (root is a shell over host/client programs); module graph regenerated for the twelve client packages chore(gui): retire legacy migration sources — every owner rewrite landed (t0-checklist §7 ledger honored); orphan css of retired components removed gates(gui): knip green groundwork — e2e/tsx entries for the new packages, loader-runtime deps ignored where loading is by specifier string, fake plugin ids un-bare-named, dead test export dropped chore(client): manifest shape batch A — ui-slots/web-react/ui-primitives invariant companions, files whitelist, cordis+invariants peer/dev, tsconfig refs chore(client): manifest shape batch B — connection/runtime/ui-conversation/ui-trajectory files whitelist, cordis peer+dev, explicit invariant lib entries (clientBundle signature) chore(client): manifest shape batch C — i18n/ui-layout/ui-sidebar/ui-theme invariant companions, files whitelist, invariants peer/dev, tsconfig refs chore(client): manifest shape batch D — web shell gains node-half lib entry + invariant companion + uniform files whitelist chore(client): drop verified-unused deps — dsh-tools from runtime/ui-conversation (types ride /presentation), ui-primitives+clsx from ui-layout gates(gui): doc-gate fixes — theme JSDoc prose, three client type-link exemptions, agent-note paths follow the migration, config catalog regenerated gates(gui): type-equiv manifest follows the types.ts extraction, approval JSDoc keeps its link form, persistence catalog regenerated docs(gui): per-constant JSDoc on the contract geometry exports (export-jsdoc gate) test(gates): loader-composition budget covers cold tsx resolution after the program split (was flaking at the default 5s) docs(gui): README substantiation batch 1 — ui-slots/ui-primitives/web-react/connection: Model Experience short form, real deferred-work ledgers, description accuracy pass fix(client): theme/i18n dual-entry split — service classes + cordis merges move to src/client (host catalog scanner no longer misclassifies client services), node halves keep types + empty apply; catalogs regenerated docs(gui): README substantiation batch 2 — runtime/ui-layout/ui-sidebar/ui-conversation: Model Experience short form, package-owned deferred-work ledgers (unload stub, watch approximation, /client value-import rule, global details state, two-state dots, stats duration gap, single-bundle caches) docs(gui): README substantiation batch 3 — ui-trajectory/ui-theme/i18n/web: Model Experience short form, deferred-work ledgers (placeholder charter, no theme toggle owner, empty locale dictionaries, one-shot rendering); both README gates green test(scripts): purity spec adopts clientBundle two-arg signature (explicit libEntry, no default) gates(gui): knip green — declaration-merge dep ignored, fake plugin id assembled at runtime, invariants dep de-duplicated to peer+dev, stale apps/web section dropped feat(gui): 门禁波次 host 三包 invariant 形状——apiproxy explained-empty 伴生(wire 契约层零事件面)、webserver 真关系伴生(manifest 行必解析出 clientPath,防 __DSH_BOOT__ 广告 404 bundle;apps/cli 发布 webPlugins 键供审计)、runtime 补 files 白名单;三包 exports/files/peer+dev/tsconfig refs 齐 fw-react 形状;constraints+invariants 双 gate 零违规 build(client): ui-layout/ui-sidebar tsdown configs adopt the explicit two-arg clientBundle signature (orphaned follow-up of the manifest shape batch) refactor(gui): shell boot becomes a library face — bootWebShell(el) exported for the apps/web entry; main.ts retired refactor(gui): exports 纪律刀1——ui-theme/i18n node index 收敛为只空 apply(Translate/LocaleDict/ThemeTokens 类型下沉 src/client/),ui-conversation 的 I18nService import 改 /client 子路径 build(typecheck): converge to root host aggregate + tsconfig.client.json — delete tsconfig.host.json, verify-cordis-config seeds both aggregates feat(gui): apps/web restored as the vite application — thin main over bootWebShell; dsh-client-web becomes a plain lib (index exports shell surface, vite files and e2e moved out) chore(gates): knip.json rewritten on the master base — same semantics, minimal diff (formatting churn dropped) docs(gui): 时效清扫②——testing.md 删 web-ui 覆盖豁免残句;web-styling.md 加 token 换代头注(--dsw-* 现行、工程约束条款仍有效并注明收编处) docs(gui): 时效清扫③——四对 GUI Agent Note 加路径更新头注(web-runtime/web-ui/dsh-frontend→现行 12 包结构;设计结论存续声明;双语对同步) docs(gui): 时效清扫③b——四对 note 头注的 i18n 配对哈希重录 build(typecheck): minimal-diff tsconfig shape — drop root files entry (purity spec + preset move to client program), compress comments, drop redundant util/home root ref feat(gui): apps/web restoration follow-through — dsh-frontend package name, cli dist resolve, root build:web filter, tsdown exemption dropped, vitest web lane + knip + client aggregate retargeted, e2e paths rebased refactor(gui): exports 纪律刀2——connection wire 六件 git mv 进 src/client/(wire 即该 dshClient 插件的 client 半),node index=只空 apply,/client 半边整面导出(v3 §3.2 清单原样),包内 tests 改 src/client 直取 refactor(gui): exports 纪律刀3——runtime 实现整体下沉 src/client/(sessions/slots/loader;契约类型与 cordis merge 随迁 client/index),node index=只空 apply;./loader exports 指 client/loader;全消费面(web 壳/ui-sidebar/ui-trajectory/tests)bare→/client 机械跟改;vitest.e2e 换 tsconfig.vitest paths(root tsconfig 排除 client 会把 /client import 掉到 exports 的浏览器 dist bundle) refactor(gui): exports 纪律刀3 补遗——ui-layout 三处 bare runtime import 改 /client(刀3 消费面机械跟改漏提交件;跨属地机械一行×3 报备 ui-shell) test(gui): drop the getSessionManager singleton case — the init/get pair is a dead legacy-boot surface with zero live consumers (SessionsService constructs and holds the manager under the plugin architecture); source removal tracked with rt-core refactor(gui): 删 manager.ts 尾部 initSessionManager/getSessionManager 单例对——旧 boot 直连遗物,插件化下 SessionsService 构造持有 manager,全仓零活消费者(convo-b 测试清扫对表,其测试用例已先行退役 7e2c51898);头注释同步去单例措辞 code refactor
2026-07-19 21:17:57 +08:00
pkg_client_ui_slots["client-ui-slots"]
pkg_client_ui_subagent["client-ui-subagent"]
feat(gui): step1 skeleton — dsc web serves built web UI over booted harness host Five new modules: apps/dsc (bin: parseArgs + node:http static server + signal shutdown), packages/host/apiproxy (programmatic harness core composition, agents:[]), packages/client/web-runtime (React-free browser runtime), packages/client/web-ui (React mount), apps/web (vite build entry producing dist consumed by apps/dsc via package exports). Root wiring: apps/* workspace glob, dsh-* paths for host/client groups, demo:web script, apps/web/dist gitignore. No protocol/API routes yet — contract lands in step2 (see missions/tasks/20260719-1902-apiproxy-api-design). Includes the design + implementation archives (spec v2.1, deepseekchat baseline and harness boot research, implementation run log). Acceptance: 12/12 passed incl. real-key llm.stream smoke (51 chunks). feat(gui): apiproxy — four-quadrant RPC contract + fetch carriers, live end to end Contract layer (src/api/, 14 files): four named wire message types (ClientRequest / ServerResponse / ServerRequest / ClientResponse) as a discriminated union over strict bidirectional rpcId (initiator mints, responder echoes; channel and message fully decoupled — HTTP is the client->server pipe, SSE the reverse); narrow RpcRequest<P>/ RpcResponse<T> signature forms; RpcMethodMap with RequestPayload<K>/ ResponseValue<K> derivation; typed RpcError details map; approval/ question responses modeled as ClientResponse via a single /api/respond endpoint (RpcReceipt carrier ack); zod schemas anchored per Wire<T> against exactOptionalPropertyTypes. impl/api-proxy.ts: describe/list/create, both SSE streams (frame queue pump, subscribed baseline, lifecycle frames, signal cleanup); history pages on message boundaries (tail-back scan, partial included in the tail page); prompt dispatches queue->agent.send / steer->agent.steer with rpcId carried through MessageSource; cancel for attached sessions; cold-session resume deduped via a per-id promise map; host-level provider/model defaults injected at create/resume. fetch/: mechanical UNARY_ROUTES table, two-level parse with path==method check, SSE frames completed to ServerRequest full form; client mints -> narrows -> envelopes outbound, verifies rpcId echo inbound, streams SSE frames, four-quadrant onEnvelope tap (debug panel choke point). Real-browser fixes: URL base resolves to location.origin (hardcoded internal base broke real pages), browser-safe export paths. Design archives: contract design.md v2.0 with decision log, core-coverage audit, comparative studies, step2 impl run log. Probed end to end over real HTTP: prompt -> live model stream -> history returns the finished reply. feat(gui): RpcLog debug panel — fixture-driven milestone, playwright-verified 10/10 web-runtime: rpcLog + ui slices (zustand), four-quadrant RpcLogEntry (client-request / server-response / server-request / client-response), onEnvelope tap -> microtask-batched pump with 500-entry ring buffer, ConnectionController (private state, backoff reconnect), fixture API with fake envelopes (?fixture switch), bootWebRuntime; contract types via temporary local copies (api-types.ts, swapped for real imports when W3 client lands). web-ui: components/panels/RpcLog five-piece set (badge with unread count, floating panel, direction glyphs per quadrant, same-rpcId pair highlighting in two families, JSON payload expand, follow/pause, clear), App shell, utils/formatRelative, light-theme CSS variables with dark placeholders. dsc bin: mime lookup fixed to use the actually-served file (naked '/?query' no longer falls through to octet-stream download); shutdown closes SSE keep-alive connections so SIGTERM actually exits. Acceptance: scripts/verify-rpclog-panel.mjs (chromium headless) ALL PASS 10/10 over design.md §D 1-6. pkg: add web scripts for building feat(gui): session milestone — list + conversation over Session OOP, styled RpcLog v2.1 web-runtime: Session/SessionManager object layer (resident instances, mux frame routing, lineage flattening), foldSurface adapter with padding sentinels for paged windows, chunk accumulator for streaming partials, batched change notification (useSyncExternalStore contract), connection sinks + reconnect fix (the 300ms self-abort reconnect storm that made the session list flap is gone), fixture rewritten as a scripted host (60-turn history, typewriter replay, resident pending approval, child session); temporary contract copies deleted in favor of real apiproxy imports. web-ui: sessions screen (list with lineage indent + selection as container-local state), conversation view (turn grouping, reasoning fold, tool cards, steering, pending interaction cards, upward paging with scroll anchoring), input bar with queue/steer/stop; RpcLog panel restyled per docs/web-styling.md (tokenized palette, quadrant badge glyphs now vertical ↑↓⇟⇞, pair highlighting, floating shadow). docs/web-styling.md: living style guide (tokens, visual baseline, coding rules, evolution log). Acceptance: verify-session.mjs 31/31, verify-session-real.mjs 5/5 (real model streaming), verify-rpclog-panel.mjs 10/10. feat(gui): hostruntime split + repo-wide package prefix rename Package split (design: 20260720-0101-hostruntime-split-design): dsh-host-runtime carries bootHost + createApiProxy + startHost() (RunningHost {api, handler, defaults, ctx, dispose} — the seam Electron and any future shell reuses; ctx is the official front-door mount point); dsh-host-webserver carries the node:http static+API bridge (fixed: abort now keys on res 'close' + writableEnded — req 'close' fires on body end since Node 16 and was killing every SSE stream instantly, the reconnect-storm root cause); apps/dsc is now a thin assembly with web/-p subcommands. dsc -p runs the full isomorphic carrier chain in process (second real protocol consumer; probed end-to-end against the live model). Naming rule (user decree): packages under host/ and client/ carry the directory prefix in their npm name — dsh-host-apiproxy, dsh-client-web-runtime, dsh-client-web-ui renamed repo-wide in one frozen batch; explicit tsconfig paths entries added where the wildcard no longer matches. Acceptance: verify-session 31/31, verify-rpclog-panel 10/10, verify-session-real 7/7 (incl. new 12s connection-stability sentinels), tsc green, dsc web + dsc -p smoke both pass. refactor(gui): AbstractApiClient class hierarchy — OO client with inheritable seams AbstractApiClient (apiproxy) carries every protocol invariant: rpcId minting, four-quadrant envelope wrap/unwrap, zod parsing, SSE frame parsing, the payload-direct IApiClient surface (callers no longer mint rpcIds — the carrier does), and the instance-level envelope observation pump (batched via microtask; moved off module-level globals in rpc-log.ts, which is now a pure subscriber mapping envelopes into store entries — the debug panel observes the connection, it is not part of it). Platform subclasses own two abstract seams (doFetch, onEnvelope) plus three protocol-level virtuals for transportless overrides: InProcessApiClient (apiproxy; dsc -p uses new InProcessApiClient( host.handler)), WebApiClient (web-runtime), FixtureApiClient (fixture now subclasses instead of wrapping). Naming per decree: AbstractApiClient / IApiClient; ApiProxy stays the impl-side narrow-form contract. headless.ts call sites drop rpcRequest wrappers (payload-direct); split-design archive updated with the naming-rule ledger. tsc green; verify-session 31/31, verify-rpclog-panel 10/10, verify-session-real 7/7 (12s connection sentinel count=4); dsc -p smoke CALLER-OK. feat(gui): InputBar final form — bug batch, deepseekchat layout, single primary button, running locks input Squashes the whole InputBar iteration batch: IME/caret/auto-grow/focus/dedup bug fixes, layout aligned to the deepseekchat baseline, single primary button with hover flyout, finalized button semantics with the Codex-style icon circle, and running-state locking where stop is the only mid-turn action. The same batch carried the Chinese-to-English code comment sweep (density pruned), folded in here. docs(gui): purge work-log references from code comments 76 design-doc references cleared across the GUI packages: section pointers inlined as self-contained constraint statements, pure pointer comments dropped, milestone codenames and ruling tags out, and the 14 contract file headers switched to the formal RFC (the only sanctioned external reference). web-styling.md now cites the styling RFC instead of the disposable research archive. grep for work-log reference variants is clean across the GUI packages. docs(gui): file-header comments self-contained — drop RFC filename references RFC renames/reorgs must not require a source sweep (the 2026-07-20 two-way merge proved it). 11 headers lose only the '(RFC …)' tail and stay self-contained; api-proxy.ts keeps its minimal-first note. fix(gui): session streaming — freeze interrupted partials, sweep stale running calls, send force-scrolls Aborted turns never emit the finalizing assistant/message, so the accumulated partial and its running tool cards kept rendering below later messages — the "new message lands above the stopped reply" illusion. turn/end side effects now freeze content-bearing partials into interrupted terminal nodes (fractional seq keeps flow order; the live freeze and history replay converge through applyEventSideEffects, so a refresh reconstructs identical frozen nodes) and turn running tool cards into interrupted terminal cards; only content-free partials are swept outright. ConversationView gains the send-force-scroll rule (own words must be visible) alongside the pre-update atBottom follow flag. Regressions pinned as E2-4a–c (real host) and §E1-11h (fixture). feat(gui): webserver hardening verify script feat(gui): dark-mode toggle pinned to the sidebar bottom Interim home before the Settings page exists (the button re-homes with zero logic change — mechanics live in utils/theme.ts): html[data-theme] flip + dsc.theme localStorage, stored choice wins over the OS prefers-color-scheme default, applied in mount() before first paint so a dark reload never flashes light. Moon/sun inline SVG icon button at the sidebar's pinned bottom row. Pure front-end local concern: no RPC, no Session/store involvement. Dark sweep of list/conversation/input card/RPC panel found no unreadable pairs — no token changes needed. docs(gui): GUI RFCs and web styling handbook Layering+RPC protocol and web client architecture RFCs (post-reorg, developer-facing polish folded in) plus the styling engineering handbook. Mission work logs live in the commit above; PRs can be cut from this commit to include formal docs only. fix(gui): client object-layer hardening — audit timing/reference/resilience batches (S3-S5,C1-C3,C5-C8) fix(gui): carrier error channel + webserver backpressure (audit A1-A5,A7-A10,R2,R5) feat(gui): session persistence surface — cold list, project cwd, legacy no-cwd retirement refactor: rename dsc CLI to dsh — apps/cli, bin name, package scope Includes the root tsconfig project-references fix for host/* and client/web-runtime (originally a separate build fix commit). test(gui): three-tier suite — protocol/object/browser lanes, tier-a fill to per-file 100% test(gui): jsdom lane for web-ui + web-runtime coverage gate entry docs(gui): GUI testing system RFC (zh) feat(gui): tool-card views — contract slot, host-computed delivery, three-level card fallback fix(gui): lint clean across GUI packages — wrap long doc comments, drop dead type args, sync-return methods without awaits docs(gui): doc-sync mechanical fixes — JSDoc on apiproxy/host exports, RFC sketch fences ignore-check, md-wrap paragraphs, drop missions links, web-ui plain-ts entry chore(gui): module-graph regen + knip clean — drop dead re-exports, internalize createFixtureApi, scan web-ui tsx and verify mjs scripts build(gui): wire client/host packages into the lib build shape — tsc references + tsdown (web-ui css-external), lib manifests, cordis peer, apiproxy typed subpaths, vite src aliases test(gui): host-side per-file 100% coverage — apiproxy schema/carrier suites, webserver http-bridge suite, host-runtime composition suite; client/* coverage excluded pending the browser-side testing work item docs(gui): package READMEs for the five GUI packages — model-experience audit entries, limitations sections docs(gui): bilingual RFC pairs + client JSDoc completion — translate the three GUI RFCs to English with i18n records and manifest ratchet, Consequences sections both sides, full client/* export JSDoc, regen doc graphs and RFC index fix(scripts): doc-typecheck built-declarations mode maps /src/* subpath wildcards (apiproxy browser-safe channels) docs(gui): apply dsh rename across pr-gates docs — READMEs, layering RFC en, web-ui entry comment, i18n re-record fix(gui): post-rebase lint reconciliation — wrap main-tree long doc comments, read-through narrowing guards, abortError Error normalization, handleUnary generic justification fix(gui): post-rebase doc/test reconciliation — align host specs with evolved carrier contracts (sentinel rpcId, stream/error surfacing, url-path transport messages, defaults.cwd), Agent Note titles and relocated links, KV Cache effect sections, JSDoc on evolved exports fix(gui): second-rebase reconciliation to 509db0cb3 — restore api panel exports the baseline suites consume, knip workspace entries for jsdom lane and apps/web smokes, hoist result narrowing, align testing.md to the narrowed web-ui exclusion fix(test): vitest-scoped tsconfig maps bare imports for tsx specs — with GUI manifests now pointing at lib, an unmapped importer loaded a second copy of the web-runtime singletons fix(gui): typecheck + lint clean over the tool-card batch — brand callIds and object-form turn/end reason in the view spec, narrow fixture arg stringification, wrap long v8-ignore comments docs(gui): export JSDoc for tool-card surfaces + testing-note pairing header docs: rfc for web testing feat: add tools to host-runtime fix(gui): dispatch agent/error via agentEvents in host-runtime spec — mounted invariants plugin rejects raw ctx.emit without the scope carrier fix(gui): restore GUI knip workspaces + scripts/mjs entries and regenerate lockfile after master rebase fix(gui): post-rebase gate repairs — drop context-node envelope (master unwrapped injected content envelopes), regen event matrix, condense testing.md web-ui exclusion within budget fix(session): browser-safe deep-equal in surface — node:util import broke the vite bundle ci(gates): frontend vite build joins pre-push — node: imports in the client closure pass tsc but break the browser bundle test(tui): drop the checkout-dependent process.cwd() harness default — a long worktree path pushes the footer token counters past the 88-column fake terminal test(gui): jsdom behavior E2E — conversation main path over fixture runtime, reconnect banner lifecycle test(gui): jsdom RPC panel behavior — ledger rows, expand, pairing, pause/clear, follow-pause, payload truncation test(gui): jsdom tier-2 — InputBar guards, reasoning fold, JSON blocks, message variants, theme, create-then-select; act-harden banner case test(gui): jsdom tier-3 — ConversationView states/paging/force-bottom, ToolCallCard arms, PendingCard, list rows test(gui): jsdom tails — view-card variants, LogRow directions, registry hygiene, badge overflow, hook ops, mount glue test(gui): jsdom tails round 2 — call-ref blocks, resume follow, view precedence, failed create, empty-diff arm test(gui): jsdom final arms — anchor compensation, follow-off, interval ticks, view halves, node-over-running precedence test(gui): web-ui joins the per-file 100% coverage gate Annotation-only src changes plus the config swap. The web-ui exclusion is replaced by a single index.tsx entry (stale byte-identical duplicate of mount.tsx, nothing imports it; same entry-glue treatment as bin.ts) and the coverage include gains .tsx. v8-ignore sites (each with its reason inline): - ConversationView 3x ref-null guards; InputBar disabled-click guard - ToolCallCard both-null arms + windowless-custom argsRaw arm - LogRow css-module key fallbacks (start/stop block); RpcLogBody 3x ref-null guards - web-runtime drift from the tool-card batch: fixture presenter catch/str typo-guards, dense-array guards (fold-adapter reset, session rebuild, fixture backscan), live view-present arm (fixture replays are text-only; view vocabulary is covered by the history samples) test(gui): close the PR #443 host-side coverage gaps — apiproxy client abort arms, api-proxy cold/view paths, webserver drain - apiproxy fetch/client.ts: 3 new cases (pre-aborted signal short-circuits before transport + string reason mapping, non-Error/string reason falls to the default AbortError message, signal-less doFetch passthrough) - runtime/api-proxy.ts: one v8-ignore (summarizeCold cwd arm — list() filters cwd-less legacy metas) + api-proxy-cold.spec.ts (cold list merge: mtime source, locate-undefined and vanished-log fallbacks, lineage; no-persistence/no-factory resume → internal) + 2 view cases (history views with meta passthrough and orphan/bad-args/presenterless soft-falls, session/disposed open-call cleanup on the mux stream) - webserver/index.ts: /api/big fixture drives both drain-wait legs (full 8MiB readback after drain, mid-chunk disconnect wakes via 'close') feat: app shell fix: rebase conflicts fix: coverage fix(gui): lint clean after rebase — wrap long v8-ignore comments, unconditional v1 detail-block claim chore(gui): remove browser/probe verify scripts from scripts/ The six GUI acceptance/probe scripts (carrier-errors, rpclog-panel, session, session-real, webserver-backpressure, webserver-hardening) leave the repo's scripts/ tree; the three code comments that pointed at them now describe the coverage lane without naming a script path. fix(webserver): guard the request callback — one malformed request must not kill the process The async handle() had no top-level catch, so any throw inside it (a bad %-escape reaching decodeURIComponent, a client dropping mid-body, a response stream erroring) became an unhandled rejection and took the whole process down (audit R1 must-fix). The guard answers 400 when headers are not out yet, destroys the socket when they are, and reports the failure to onError (the package never prints). Spec covers all three legs: %-escape barrage → 400 + server stays alive, non-Error throw wrapped for onError, mid-stream explosion → socket teardown. feat: client AGENTS.md fix: client/AGENTS.md fix: rebase feat(gui): T0 cut 1 — 12 client package skeletons with contract stubs, dshClient declarations, tsdown client preset, theme token sheets feat(gui): T0 cut 2 — pure git mv migration per v3 §11 (connection six, runtime sessions/kernel, ui-conversation chat, ui-primitives markdown family, web shell + e2e) feat(gui): T0 cuts 3+4 — import rewiring to new package names, .legacy demotion of owner-rewrite files, legacy web-runtime/web-ui/apps-web retired to attic feat(gui): connection 对账刀——index.ts 精确导出清单替换 export *,intents.legacy 溶解删除 feat(client/ui-slots): SlotCore real implementation — kind semantics, sync version + microtask-batched notify, onMutate bridge feat(gui): web shell vite alias — retarget to new client packages, shell static surface only feat(gui): host 侧刀属地半——HostWebPluginRegistry(entries 扫描+internal/plugin 去抖重扫+dshClient 校验+exports./client 解析)、GET /plugins/<id>/client.js 分发端点、GET / 与 SPA fallback 注入 __DSH_BOOT__(webPlugins 可选注入,不传行为不变) feat(web-react): add use-sync-external-store dep + local shim typings feat(web-react): bindSnapshotSelector via uSES with-selector shim feat(gui): ui-layout concession-chain solver — pure computeColumns with contract geometry feat(gui): ui-layout LayoutService — four persisted stores, clamped actions, list-driven prune feat(gui): ui-layout AppFrame styles — grid columns, collapse-safe borders, edge drag handles test(gui): 存量 spec 平移——connection 三件+runtime 六件自 attic 捞回改包名路径全绿;api-helpers 按归属拆分(wire 半留 connection、classifier 半随 conversation.ts 入 runtime);boot-intents/preinit/rpc-log 随 intents/rpc-log 退役不迁(记 v3 §3.2 溶解项) feat(client/ui-primitives): StateDot/Button/Pill/Input/Menu atoms, ConnectionBanner de-legacied to pure props, JsonBlock CSS on --dsw tokens feat(web-react): createSnapshotStore engine (rafFlush batch, persist opt-in, dev freeze) + spec feat(gui): ui-layout AppFrame — grid tracks, pointer-capture drag handles with rAF throttle, frame ResizeObserver feat(web-react): useInvoke (external pending store, stable invoke, concurrency count) + spec test(web-react): bind spec — equality bail, custom eq, zero resubscribe, StrictMode, method sources feat(gui): ui-layout index rewiring — real exports, client apply provides ctx.layout and defines three slots feat(web-react): SessionProvider (renderBody deps) + RootBindingProvider + binding contexts + spec feat(gui): web shell AppRoot boot-page styles — self-contained with neutral token fallbacks feat(gui): web shell AppRoot — boot gate over loader status, fail-loud plugin failure list fix(gui): AppRoot gates on explicit settled signal — status-derived readiness races the incrementally filled table feat(client/ui-theme): ThemeService real implementation — registry with built-in light/dark, apply toggles body[data-ds-dark-theme], third-party token overrides as body inline vars feat(web-react): scopedSlots outlet (kind matrix, inject WeakMap caches, per-entry error boundary) + spec feat(gui): web shell module-table seed — pure-library entities for the loader require surface feat(client/i18n): I18nService real implementation — ns×locale registry, stable bind(ns) reference, zh fallback chain, zh/en skeleton dictionaries feat(gui): web shell assembly closure — layout exports via module table, SessionProvider + scopedSlots + RootBindingProvider feat: client/ui-conversation feat: code codedoc build(gui): root bundle green — web shell excluded from the lib workspace (vite app), ui-primitives lib externalizes css side-effect imports (web-ui precedent) gates(gui): verify-cordis-config follows aggregate tsconfig references (root is a shell over host/client programs); module graph regenerated for the twelve client packages chore(gui): retire legacy migration sources — every owner rewrite landed (t0-checklist §7 ledger honored); orphan css of retired components removed gates(gui): knip green groundwork — e2e/tsx entries for the new packages, loader-runtime deps ignored where loading is by specifier string, fake plugin ids un-bare-named, dead test export dropped chore(client): manifest shape batch A — ui-slots/web-react/ui-primitives invariant companions, files whitelist, cordis+invariants peer/dev, tsconfig refs chore(client): manifest shape batch B — connection/runtime/ui-conversation/ui-trajectory files whitelist, cordis peer+dev, explicit invariant lib entries (clientBundle signature) chore(client): manifest shape batch C — i18n/ui-layout/ui-sidebar/ui-theme invariant companions, files whitelist, invariants peer/dev, tsconfig refs chore(client): manifest shape batch D — web shell gains node-half lib entry + invariant companion + uniform files whitelist chore(client): drop verified-unused deps — dsh-tools from runtime/ui-conversation (types ride /presentation), ui-primitives+clsx from ui-layout gates(gui): doc-gate fixes — theme JSDoc prose, three client type-link exemptions, agent-note paths follow the migration, config catalog regenerated gates(gui): type-equiv manifest follows the types.ts extraction, approval JSDoc keeps its link form, persistence catalog regenerated docs(gui): per-constant JSDoc on the contract geometry exports (export-jsdoc gate) test(gates): loader-composition budget covers cold tsx resolution after the program split (was flaking at the default 5s) docs(gui): README substantiation batch 1 — ui-slots/ui-primitives/web-react/connection: Model Experience short form, real deferred-work ledgers, description accuracy pass fix(client): theme/i18n dual-entry split — service classes + cordis merges move to src/client (host catalog scanner no longer misclassifies client services), node halves keep types + empty apply; catalogs regenerated docs(gui): README substantiation batch 2 — runtime/ui-layout/ui-sidebar/ui-conversation: Model Experience short form, package-owned deferred-work ledgers (unload stub, watch approximation, /client value-import rule, global details state, two-state dots, stats duration gap, single-bundle caches) docs(gui): README substantiation batch 3 — ui-trajectory/ui-theme/i18n/web: Model Experience short form, deferred-work ledgers (placeholder charter, no theme toggle owner, empty locale dictionaries, one-shot rendering); both README gates green test(scripts): purity spec adopts clientBundle two-arg signature (explicit libEntry, no default) gates(gui): knip green — declaration-merge dep ignored, fake plugin id assembled at runtime, invariants dep de-duplicated to peer+dev, stale apps/web section dropped feat(gui): 门禁波次 host 三包 invariant 形状——apiproxy explained-empty 伴生(wire 契约层零事件面)、webserver 真关系伴生(manifest 行必解析出 clientPath,防 __DSH_BOOT__ 广告 404 bundle;apps/cli 发布 webPlugins 键供审计)、runtime 补 files 白名单;三包 exports/files/peer+dev/tsconfig refs 齐 fw-react 形状;constraints+invariants 双 gate 零违规 build(client): ui-layout/ui-sidebar tsdown configs adopt the explicit two-arg clientBundle signature (orphaned follow-up of the manifest shape batch) refactor(gui): shell boot becomes a library face — bootWebShell(el) exported for the apps/web entry; main.ts retired refactor(gui): exports 纪律刀1——ui-theme/i18n node index 收敛为只空 apply(Translate/LocaleDict/ThemeTokens 类型下沉 src/client/),ui-conversation 的 I18nService import 改 /client 子路径 build(typecheck): converge to root host aggregate + tsconfig.client.json — delete tsconfig.host.json, verify-cordis-config seeds both aggregates feat(gui): apps/web restored as the vite application — thin main over bootWebShell; dsh-client-web becomes a plain lib (index exports shell surface, vite files and e2e moved out) chore(gates): knip.json rewritten on the master base — same semantics, minimal diff (formatting churn dropped) docs(gui): 时效清扫②——testing.md 删 web-ui 覆盖豁免残句;web-styling.md 加 token 换代头注(--dsw-* 现行、工程约束条款仍有效并注明收编处) docs(gui): 时效清扫③——四对 GUI Agent Note 加路径更新头注(web-runtime/web-ui/dsh-frontend→现行 12 包结构;设计结论存续声明;双语对同步) docs(gui): 时效清扫③b——四对 note 头注的 i18n 配对哈希重录 build(typecheck): minimal-diff tsconfig shape — drop root files entry (purity spec + preset move to client program), compress comments, drop redundant util/home root ref feat(gui): apps/web restoration follow-through — dsh-frontend package name, cli dist resolve, root build:web filter, tsdown exemption dropped, vitest web lane + knip + client aggregate retargeted, e2e paths rebased refactor(gui): exports 纪律刀2——connection wire 六件 git mv 进 src/client/(wire 即该 dshClient 插件的 client 半),node index=只空 apply,/client 半边整面导出(v3 §3.2 清单原样),包内 tests 改 src/client 直取 refactor(gui): exports 纪律刀3——runtime 实现整体下沉 src/client/(sessions/slots/loader;契约类型与 cordis merge 随迁 client/index),node index=只空 apply;./loader exports 指 client/loader;全消费面(web 壳/ui-sidebar/ui-trajectory/tests)bare→/client 机械跟改;vitest.e2e 换 tsconfig.vitest paths(root tsconfig 排除 client 会把 /client import 掉到 exports 的浏览器 dist bundle) refactor(gui): exports 纪律刀3 补遗——ui-layout 三处 bare runtime import 改 /client(刀3 消费面机械跟改漏提交件;跨属地机械一行×3 报备 ui-shell) test(gui): drop the getSessionManager singleton case — the init/get pair is a dead legacy-boot surface with zero live consumers (SessionsService constructs and holds the manager under the plugin architecture); source removal tracked with rt-core refactor(gui): 删 manager.ts 尾部 initSessionManager/getSessionManager 单例对——旧 boot 直连遗物,插件化下 SessionsService 构造持有 manager,全仓零活消费者(convo-b 测试清扫对表,其测试用例已先行退役 7e2c51898);头注释同步去单例措辞 code refactor
2026-07-19 21:17:57 +08:00
pkg_client_ui_theme["client-ui-theme"]
pkg_client_ui_trajectory["client-ui-trajectory"]
pkg_client_ui_workspace["client-ui-workspace"]
feat(gui): step1 skeleton — dsc web serves built web UI over booted harness host Five new modules: apps/dsc (bin: parseArgs + node:http static server + signal shutdown), packages/host/apiproxy (programmatic harness core composition, agents:[]), packages/client/web-runtime (React-free browser runtime), packages/client/web-ui (React mount), apps/web (vite build entry producing dist consumed by apps/dsc via package exports). Root wiring: apps/* workspace glob, dsh-* paths for host/client groups, demo:web script, apps/web/dist gitignore. No protocol/API routes yet — contract lands in step2 (see missions/tasks/20260719-1902-apiproxy-api-design). Includes the design + implementation archives (spec v2.1, deepseekchat baseline and harness boot research, implementation run log). Acceptance: 12/12 passed incl. real-key llm.stream smoke (51 chunks). feat(gui): apiproxy — four-quadrant RPC contract + fetch carriers, live end to end Contract layer (src/api/, 14 files): four named wire message types (ClientRequest / ServerResponse / ServerRequest / ClientResponse) as a discriminated union over strict bidirectional rpcId (initiator mints, responder echoes; channel and message fully decoupled — HTTP is the client->server pipe, SSE the reverse); narrow RpcRequest<P>/ RpcResponse<T> signature forms; RpcMethodMap with RequestPayload<K>/ ResponseValue<K> derivation; typed RpcError details map; approval/ question responses modeled as ClientResponse via a single /api/respond endpoint (RpcReceipt carrier ack); zod schemas anchored per Wire<T> against exactOptionalPropertyTypes. impl/api-proxy.ts: describe/list/create, both SSE streams (frame queue pump, subscribed baseline, lifecycle frames, signal cleanup); history pages on message boundaries (tail-back scan, partial included in the tail page); prompt dispatches queue->agent.send / steer->agent.steer with rpcId carried through MessageSource; cancel for attached sessions; cold-session resume deduped via a per-id promise map; host-level provider/model defaults injected at create/resume. fetch/: mechanical UNARY_ROUTES table, two-level parse with path==method check, SSE frames completed to ServerRequest full form; client mints -> narrows -> envelopes outbound, verifies rpcId echo inbound, streams SSE frames, four-quadrant onEnvelope tap (debug panel choke point). Real-browser fixes: URL base resolves to location.origin (hardcoded internal base broke real pages), browser-safe export paths. Design archives: contract design.md v2.0 with decision log, core-coverage audit, comparative studies, step2 impl run log. Probed end to end over real HTTP: prompt -> live model stream -> history returns the finished reply. feat(gui): RpcLog debug panel — fixture-driven milestone, playwright-verified 10/10 web-runtime: rpcLog + ui slices (zustand), four-quadrant RpcLogEntry (client-request / server-response / server-request / client-response), onEnvelope tap -> microtask-batched pump with 500-entry ring buffer, ConnectionController (private state, backoff reconnect), fixture API with fake envelopes (?fixture switch), bootWebRuntime; contract types via temporary local copies (api-types.ts, swapped for real imports when W3 client lands). web-ui: components/panels/RpcLog five-piece set (badge with unread count, floating panel, direction glyphs per quadrant, same-rpcId pair highlighting in two families, JSON payload expand, follow/pause, clear), App shell, utils/formatRelative, light-theme CSS variables with dark placeholders. dsc bin: mime lookup fixed to use the actually-served file (naked '/?query' no longer falls through to octet-stream download); shutdown closes SSE keep-alive connections so SIGTERM actually exits. Acceptance: scripts/verify-rpclog-panel.mjs (chromium headless) ALL PASS 10/10 over design.md §D 1-6. pkg: add web scripts for building feat(gui): session milestone — list + conversation over Session OOP, styled RpcLog v2.1 web-runtime: Session/SessionManager object layer (resident instances, mux frame routing, lineage flattening), foldSurface adapter with padding sentinels for paged windows, chunk accumulator for streaming partials, batched change notification (useSyncExternalStore contract), connection sinks + reconnect fix (the 300ms self-abort reconnect storm that made the session list flap is gone), fixture rewritten as a scripted host (60-turn history, typewriter replay, resident pending approval, child session); temporary contract copies deleted in favor of real apiproxy imports. web-ui: sessions screen (list with lineage indent + selection as container-local state), conversation view (turn grouping, reasoning fold, tool cards, steering, pending interaction cards, upward paging with scroll anchoring), input bar with queue/steer/stop; RpcLog panel restyled per docs/web-styling.md (tokenized palette, quadrant badge glyphs now vertical ↑↓⇟⇞, pair highlighting, floating shadow). docs/web-styling.md: living style guide (tokens, visual baseline, coding rules, evolution log). Acceptance: verify-session.mjs 31/31, verify-session-real.mjs 5/5 (real model streaming), verify-rpclog-panel.mjs 10/10. feat(gui): hostruntime split + repo-wide package prefix rename Package split (design: 20260720-0101-hostruntime-split-design): dsh-host-runtime carries bootHost + createApiProxy + startHost() (RunningHost {api, handler, defaults, ctx, dispose} — the seam Electron and any future shell reuses; ctx is the official front-door mount point); dsh-host-webserver carries the node:http static+API bridge (fixed: abort now keys on res 'close' + writableEnded — req 'close' fires on body end since Node 16 and was killing every SSE stream instantly, the reconnect-storm root cause); apps/dsc is now a thin assembly with web/-p subcommands. dsc -p runs the full isomorphic carrier chain in process (second real protocol consumer; probed end-to-end against the live model). Naming rule (user decree): packages under host/ and client/ carry the directory prefix in their npm name — dsh-host-apiproxy, dsh-client-web-runtime, dsh-client-web-ui renamed repo-wide in one frozen batch; explicit tsconfig paths entries added where the wildcard no longer matches. Acceptance: verify-session 31/31, verify-rpclog-panel 10/10, verify-session-real 7/7 (incl. new 12s connection-stability sentinels), tsc green, dsc web + dsc -p smoke both pass. refactor(gui): AbstractApiClient class hierarchy — OO client with inheritable seams AbstractApiClient (apiproxy) carries every protocol invariant: rpcId minting, four-quadrant envelope wrap/unwrap, zod parsing, SSE frame parsing, the payload-direct IApiClient surface (callers no longer mint rpcIds — the carrier does), and the instance-level envelope observation pump (batched via microtask; moved off module-level globals in rpc-log.ts, which is now a pure subscriber mapping envelopes into store entries — the debug panel observes the connection, it is not part of it). Platform subclasses own two abstract seams (doFetch, onEnvelope) plus three protocol-level virtuals for transportless overrides: InProcessApiClient (apiproxy; dsc -p uses new InProcessApiClient( host.handler)), WebApiClient (web-runtime), FixtureApiClient (fixture now subclasses instead of wrapping). Naming per decree: AbstractApiClient / IApiClient; ApiProxy stays the impl-side narrow-form contract. headless.ts call sites drop rpcRequest wrappers (payload-direct); split-design archive updated with the naming-rule ledger. tsc green; verify-session 31/31, verify-rpclog-panel 10/10, verify-session-real 7/7 (12s connection sentinel count=4); dsc -p smoke CALLER-OK. feat(gui): InputBar final form — bug batch, deepseekchat layout, single primary button, running locks input Squashes the whole InputBar iteration batch: IME/caret/auto-grow/focus/dedup bug fixes, layout aligned to the deepseekchat baseline, single primary button with hover flyout, finalized button semantics with the Codex-style icon circle, and running-state locking where stop is the only mid-turn action. The same batch carried the Chinese-to-English code comment sweep (density pruned), folded in here. docs(gui): purge work-log references from code comments 76 design-doc references cleared across the GUI packages: section pointers inlined as self-contained constraint statements, pure pointer comments dropped, milestone codenames and ruling tags out, and the 14 contract file headers switched to the formal RFC (the only sanctioned external reference). web-styling.md now cites the styling RFC instead of the disposable research archive. grep for work-log reference variants is clean across the GUI packages. docs(gui): file-header comments self-contained — drop RFC filename references RFC renames/reorgs must not require a source sweep (the 2026-07-20 two-way merge proved it). 11 headers lose only the '(RFC …)' tail and stay self-contained; api-proxy.ts keeps its minimal-first note. fix(gui): session streaming — freeze interrupted partials, sweep stale running calls, send force-scrolls Aborted turns never emit the finalizing assistant/message, so the accumulated partial and its running tool cards kept rendering below later messages — the "new message lands above the stopped reply" illusion. turn/end side effects now freeze content-bearing partials into interrupted terminal nodes (fractional seq keeps flow order; the live freeze and history replay converge through applyEventSideEffects, so a refresh reconstructs identical frozen nodes) and turn running tool cards into interrupted terminal cards; only content-free partials are swept outright. ConversationView gains the send-force-scroll rule (own words must be visible) alongside the pre-update atBottom follow flag. Regressions pinned as E2-4a–c (real host) and §E1-11h (fixture). feat(gui): webserver hardening verify script feat(gui): dark-mode toggle pinned to the sidebar bottom Interim home before the Settings page exists (the button re-homes with zero logic change — mechanics live in utils/theme.ts): html[data-theme] flip + dsc.theme localStorage, stored choice wins over the OS prefers-color-scheme default, applied in mount() before first paint so a dark reload never flashes light. Moon/sun inline SVG icon button at the sidebar's pinned bottom row. Pure front-end local concern: no RPC, no Session/store involvement. Dark sweep of list/conversation/input card/RPC panel found no unreadable pairs — no token changes needed. docs(gui): GUI RFCs and web styling handbook Layering+RPC protocol and web client architecture RFCs (post-reorg, developer-facing polish folded in) plus the styling engineering handbook. Mission work logs live in the commit above; PRs can be cut from this commit to include formal docs only. fix(gui): client object-layer hardening — audit timing/reference/resilience batches (S3-S5,C1-C3,C5-C8) fix(gui): carrier error channel + webserver backpressure (audit A1-A5,A7-A10,R2,R5) feat(gui): session persistence surface — cold list, project cwd, legacy no-cwd retirement refactor: rename dsc CLI to dsh — apps/cli, bin name, package scope Includes the root tsconfig project-references fix for host/* and client/web-runtime (originally a separate build fix commit). test(gui): three-tier suite — protocol/object/browser lanes, tier-a fill to per-file 100% test(gui): jsdom lane for web-ui + web-runtime coverage gate entry docs(gui): GUI testing system RFC (zh) feat(gui): tool-card views — contract slot, host-computed delivery, three-level card fallback fix(gui): lint clean across GUI packages — wrap long doc comments, drop dead type args, sync-return methods without awaits docs(gui): doc-sync mechanical fixes — JSDoc on apiproxy/host exports, RFC sketch fences ignore-check, md-wrap paragraphs, drop missions links, web-ui plain-ts entry chore(gui): module-graph regen + knip clean — drop dead re-exports, internalize createFixtureApi, scan web-ui tsx and verify mjs scripts build(gui): wire client/host packages into the lib build shape — tsc references + tsdown (web-ui css-external), lib manifests, cordis peer, apiproxy typed subpaths, vite src aliases test(gui): host-side per-file 100% coverage — apiproxy schema/carrier suites, webserver http-bridge suite, host-runtime composition suite; client/* coverage excluded pending the browser-side testing work item docs(gui): package READMEs for the five GUI packages — model-experience audit entries, limitations sections docs(gui): bilingual RFC pairs + client JSDoc completion — translate the three GUI RFCs to English with i18n records and manifest ratchet, Consequences sections both sides, full client/* export JSDoc, regen doc graphs and RFC index fix(scripts): doc-typecheck built-declarations mode maps /src/* subpath wildcards (apiproxy browser-safe channels) docs(gui): apply dsh rename across pr-gates docs — READMEs, layering RFC en, web-ui entry comment, i18n re-record fix(gui): post-rebase lint reconciliation — wrap main-tree long doc comments, read-through narrowing guards, abortError Error normalization, handleUnary generic justification fix(gui): post-rebase doc/test reconciliation — align host specs with evolved carrier contracts (sentinel rpcId, stream/error surfacing, url-path transport messages, defaults.cwd), Agent Note titles and relocated links, KV Cache effect sections, JSDoc on evolved exports fix(gui): second-rebase reconciliation to 509db0cb3 — restore api panel exports the baseline suites consume, knip workspace entries for jsdom lane and apps/web smokes, hoist result narrowing, align testing.md to the narrowed web-ui exclusion fix(test): vitest-scoped tsconfig maps bare imports for tsx specs — with GUI manifests now pointing at lib, an unmapped importer loaded a second copy of the web-runtime singletons fix(gui): typecheck + lint clean over the tool-card batch — brand callIds and object-form turn/end reason in the view spec, narrow fixture arg stringification, wrap long v8-ignore comments docs(gui): export JSDoc for tool-card surfaces + testing-note pairing header docs: rfc for web testing feat: add tools to host-runtime fix(gui): dispatch agent/error via agentEvents in host-runtime spec — mounted invariants plugin rejects raw ctx.emit without the scope carrier fix(gui): restore GUI knip workspaces + scripts/mjs entries and regenerate lockfile after master rebase fix(gui): post-rebase gate repairs — drop context-node envelope (master unwrapped injected content envelopes), regen event matrix, condense testing.md web-ui exclusion within budget fix(session): browser-safe deep-equal in surface — node:util import broke the vite bundle ci(gates): frontend vite build joins pre-push — node: imports in the client closure pass tsc but break the browser bundle test(tui): drop the checkout-dependent process.cwd() harness default — a long worktree path pushes the footer token counters past the 88-column fake terminal test(gui): jsdom behavior E2E — conversation main path over fixture runtime, reconnect banner lifecycle test(gui): jsdom RPC panel behavior — ledger rows, expand, pairing, pause/clear, follow-pause, payload truncation test(gui): jsdom tier-2 — InputBar guards, reasoning fold, JSON blocks, message variants, theme, create-then-select; act-harden banner case test(gui): jsdom tier-3 — ConversationView states/paging/force-bottom, ToolCallCard arms, PendingCard, list rows test(gui): jsdom tails — view-card variants, LogRow directions, registry hygiene, badge overflow, hook ops, mount glue test(gui): jsdom tails round 2 — call-ref blocks, resume follow, view precedence, failed create, empty-diff arm test(gui): jsdom final arms — anchor compensation, follow-off, interval ticks, view halves, node-over-running precedence test(gui): web-ui joins the per-file 100% coverage gate Annotation-only src changes plus the config swap. The web-ui exclusion is replaced by a single index.tsx entry (stale byte-identical duplicate of mount.tsx, nothing imports it; same entry-glue treatment as bin.ts) and the coverage include gains .tsx. v8-ignore sites (each with its reason inline): - ConversationView 3x ref-null guards; InputBar disabled-click guard - ToolCallCard both-null arms + windowless-custom argsRaw arm - LogRow css-module key fallbacks (start/stop block); RpcLogBody 3x ref-null guards - web-runtime drift from the tool-card batch: fixture presenter catch/str typo-guards, dense-array guards (fold-adapter reset, session rebuild, fixture backscan), live view-present arm (fixture replays are text-only; view vocabulary is covered by the history samples) test(gui): close the PR #443 host-side coverage gaps — apiproxy client abort arms, api-proxy cold/view paths, webserver drain - apiproxy fetch/client.ts: 3 new cases (pre-aborted signal short-circuits before transport + string reason mapping, non-Error/string reason falls to the default AbortError message, signal-less doFetch passthrough) - runtime/api-proxy.ts: one v8-ignore (summarizeCold cwd arm — list() filters cwd-less legacy metas) + api-proxy-cold.spec.ts (cold list merge: mtime source, locate-undefined and vanished-log fallbacks, lineage; no-persistence/no-factory resume → internal) + 2 view cases (history views with meta passthrough and orphan/bad-args/presenterless soft-falls, session/disposed open-call cleanup on the mux stream) - webserver/index.ts: /api/big fixture drives both drain-wait legs (full 8MiB readback after drain, mid-chunk disconnect wakes via 'close') feat: app shell fix: rebase conflicts fix: coverage fix(gui): lint clean after rebase — wrap long v8-ignore comments, unconditional v1 detail-block claim chore(gui): remove browser/probe verify scripts from scripts/ The six GUI acceptance/probe scripts (carrier-errors, rpclog-panel, session, session-real, webserver-backpressure, webserver-hardening) leave the repo's scripts/ tree; the three code comments that pointed at them now describe the coverage lane without naming a script path. fix(webserver): guard the request callback — one malformed request must not kill the process The async handle() had no top-level catch, so any throw inside it (a bad %-escape reaching decodeURIComponent, a client dropping mid-body, a response stream erroring) became an unhandled rejection and took the whole process down (audit R1 must-fix). The guard answers 400 when headers are not out yet, destroys the socket when they are, and reports the failure to onError (the package never prints). Spec covers all three legs: %-escape barrage → 400 + server stays alive, non-Error throw wrapped for onError, mid-stream explosion → socket teardown. feat: client AGENTS.md fix: client/AGENTS.md fix: rebase feat(gui): T0 cut 1 — 12 client package skeletons with contract stubs, dshClient declarations, tsdown client preset, theme token sheets feat(gui): T0 cut 2 — pure git mv migration per v3 §11 (connection six, runtime sessions/kernel, ui-conversation chat, ui-primitives markdown family, web shell + e2e) feat(gui): T0 cuts 3+4 — import rewiring to new package names, .legacy demotion of owner-rewrite files, legacy web-runtime/web-ui/apps-web retired to attic feat(gui): connection 对账刀——index.ts 精确导出清单替换 export *,intents.legacy 溶解删除 feat(client/ui-slots): SlotCore real implementation — kind semantics, sync version + microtask-batched notify, onMutate bridge feat(gui): web shell vite alias — retarget to new client packages, shell static surface only feat(gui): host 侧刀属地半——HostWebPluginRegistry(entries 扫描+internal/plugin 去抖重扫+dshClient 校验+exports./client 解析)、GET /plugins/<id>/client.js 分发端点、GET / 与 SPA fallback 注入 __DSH_BOOT__(webPlugins 可选注入,不传行为不变) feat(web-react): add use-sync-external-store dep + local shim typings feat(web-react): bindSnapshotSelector via uSES with-selector shim feat(gui): ui-layout concession-chain solver — pure computeColumns with contract geometry feat(gui): ui-layout LayoutService — four persisted stores, clamped actions, list-driven prune feat(gui): ui-layout AppFrame styles — grid columns, collapse-safe borders, edge drag handles test(gui): 存量 spec 平移——connection 三件+runtime 六件自 attic 捞回改包名路径全绿;api-helpers 按归属拆分(wire 半留 connection、classifier 半随 conversation.ts 入 runtime);boot-intents/preinit/rpc-log 随 intents/rpc-log 退役不迁(记 v3 §3.2 溶解项) feat(client/ui-primitives): StateDot/Button/Pill/Input/Menu atoms, ConnectionBanner de-legacied to pure props, JsonBlock CSS on --dsw tokens feat(web-react): createSnapshotStore engine (rafFlush batch, persist opt-in, dev freeze) + spec feat(gui): ui-layout AppFrame — grid tracks, pointer-capture drag handles with rAF throttle, frame ResizeObserver feat(web-react): useInvoke (external pending store, stable invoke, concurrency count) + spec test(web-react): bind spec — equality bail, custom eq, zero resubscribe, StrictMode, method sources feat(gui): ui-layout index rewiring — real exports, client apply provides ctx.layout and defines three slots feat(web-react): SessionProvider (renderBody deps) + RootBindingProvider + binding contexts + spec feat(gui): web shell AppRoot boot-page styles — self-contained with neutral token fallbacks feat(gui): web shell AppRoot — boot gate over loader status, fail-loud plugin failure list fix(gui): AppRoot gates on explicit settled signal — status-derived readiness races the incrementally filled table feat(client/ui-theme): ThemeService real implementation — registry with built-in light/dark, apply toggles body[data-ds-dark-theme], third-party token overrides as body inline vars feat(web-react): scopedSlots outlet (kind matrix, inject WeakMap caches, per-entry error boundary) + spec feat(gui): web shell module-table seed — pure-library entities for the loader require surface feat(client/i18n): I18nService real implementation — ns×locale registry, stable bind(ns) reference, zh fallback chain, zh/en skeleton dictionaries feat(gui): web shell assembly closure — layout exports via module table, SessionProvider + scopedSlots + RootBindingProvider feat: client/ui-conversation feat: code codedoc build(gui): root bundle green — web shell excluded from the lib workspace (vite app), ui-primitives lib externalizes css side-effect imports (web-ui precedent) gates(gui): verify-cordis-config follows aggregate tsconfig references (root is a shell over host/client programs); module graph regenerated for the twelve client packages chore(gui): retire legacy migration sources — every owner rewrite landed (t0-checklist §7 ledger honored); orphan css of retired components removed gates(gui): knip green groundwork — e2e/tsx entries for the new packages, loader-runtime deps ignored where loading is by specifier string, fake plugin ids un-bare-named, dead test export dropped chore(client): manifest shape batch A — ui-slots/web-react/ui-primitives invariant companions, files whitelist, cordis+invariants peer/dev, tsconfig refs chore(client): manifest shape batch B — connection/runtime/ui-conversation/ui-trajectory files whitelist, cordis peer+dev, explicit invariant lib entries (clientBundle signature) chore(client): manifest shape batch C — i18n/ui-layout/ui-sidebar/ui-theme invariant companions, files whitelist, invariants peer/dev, tsconfig refs chore(client): manifest shape batch D — web shell gains node-half lib entry + invariant companion + uniform files whitelist chore(client): drop verified-unused deps — dsh-tools from runtime/ui-conversation (types ride /presentation), ui-primitives+clsx from ui-layout gates(gui): doc-gate fixes — theme JSDoc prose, three client type-link exemptions, agent-note paths follow the migration, config catalog regenerated gates(gui): type-equiv manifest follows the types.ts extraction, approval JSDoc keeps its link form, persistence catalog regenerated docs(gui): per-constant JSDoc on the contract geometry exports (export-jsdoc gate) test(gates): loader-composition budget covers cold tsx resolution after the program split (was flaking at the default 5s) docs(gui): README substantiation batch 1 — ui-slots/ui-primitives/web-react/connection: Model Experience short form, real deferred-work ledgers, description accuracy pass fix(client): theme/i18n dual-entry split — service classes + cordis merges move to src/client (host catalog scanner no longer misclassifies client services), node halves keep types + empty apply; catalogs regenerated docs(gui): README substantiation batch 2 — runtime/ui-layout/ui-sidebar/ui-conversation: Model Experience short form, package-owned deferred-work ledgers (unload stub, watch approximation, /client value-import rule, global details state, two-state dots, stats duration gap, single-bundle caches) docs(gui): README substantiation batch 3 — ui-trajectory/ui-theme/i18n/web: Model Experience short form, deferred-work ledgers (placeholder charter, no theme toggle owner, empty locale dictionaries, one-shot rendering); both README gates green test(scripts): purity spec adopts clientBundle two-arg signature (explicit libEntry, no default) gates(gui): knip green — declaration-merge dep ignored, fake plugin id assembled at runtime, invariants dep de-duplicated to peer+dev, stale apps/web section dropped feat(gui): 门禁波次 host 三包 invariant 形状——apiproxy explained-empty 伴生(wire 契约层零事件面)、webserver 真关系伴生(manifest 行必解析出 clientPath,防 __DSH_BOOT__ 广告 404 bundle;apps/cli 发布 webPlugins 键供审计)、runtime 补 files 白名单;三包 exports/files/peer+dev/tsconfig refs 齐 fw-react 形状;constraints+invariants 双 gate 零违规 build(client): ui-layout/ui-sidebar tsdown configs adopt the explicit two-arg clientBundle signature (orphaned follow-up of the manifest shape batch) refactor(gui): shell boot becomes a library face — bootWebShell(el) exported for the apps/web entry; main.ts retired refactor(gui): exports 纪律刀1——ui-theme/i18n node index 收敛为只空 apply(Translate/LocaleDict/ThemeTokens 类型下沉 src/client/),ui-conversation 的 I18nService import 改 /client 子路径 build(typecheck): converge to root host aggregate + tsconfig.client.json — delete tsconfig.host.json, verify-cordis-config seeds both aggregates feat(gui): apps/web restored as the vite application — thin main over bootWebShell; dsh-client-web becomes a plain lib (index exports shell surface, vite files and e2e moved out) chore(gates): knip.json rewritten on the master base — same semantics, minimal diff (formatting churn dropped) docs(gui): 时效清扫②——testing.md 删 web-ui 覆盖豁免残句;web-styling.md 加 token 换代头注(--dsw-* 现行、工程约束条款仍有效并注明收编处) docs(gui): 时效清扫③——四对 GUI Agent Note 加路径更新头注(web-runtime/web-ui/dsh-frontend→现行 12 包结构;设计结论存续声明;双语对同步) docs(gui): 时效清扫③b——四对 note 头注的 i18n 配对哈希重录 build(typecheck): minimal-diff tsconfig shape — drop root files entry (purity spec + preset move to client program), compress comments, drop redundant util/home root ref feat(gui): apps/web restoration follow-through — dsh-frontend package name, cli dist resolve, root build:web filter, tsdown exemption dropped, vitest web lane + knip + client aggregate retargeted, e2e paths rebased refactor(gui): exports 纪律刀2——connection wire 六件 git mv 进 src/client/(wire 即该 dshClient 插件的 client 半),node index=只空 apply,/client 半边整面导出(v3 §3.2 清单原样),包内 tests 改 src/client 直取 refactor(gui): exports 纪律刀3——runtime 实现整体下沉 src/client/(sessions/slots/loader;契约类型与 cordis merge 随迁 client/index),node index=只空 apply;./loader exports 指 client/loader;全消费面(web 壳/ui-sidebar/ui-trajectory/tests)bare→/client 机械跟改;vitest.e2e 换 tsconfig.vitest paths(root tsconfig 排除 client 会把 /client import 掉到 exports 的浏览器 dist bundle) refactor(gui): exports 纪律刀3 补遗——ui-layout 三处 bare runtime import 改 /client(刀3 消费面机械跟改漏提交件;跨属地机械一行×3 报备 ui-shell) test(gui): drop the getSessionManager singleton case — the init/get pair is a dead legacy-boot surface with zero live consumers (SessionsService constructs and holds the manager under the plugin architecture); source removal tracked with rt-core refactor(gui): 删 manager.ts 尾部 initSessionManager/getSessionManager 单例对——旧 boot 直连遗物,插件化下 SessionsService 构造持有 manager,全仓零活消费者(convo-b 测试清扫对表,其测试用例已先行退役 7e2c51898);头注释同步去单例措辞 code refactor
2026-07-19 21:17:57 +08:00
pkg_client_web["client-web"]
pkg_client_web_react["client-web-react"]
end
subgraph group_code_runtime["packages/code-runtime"]
pkg_code_runtime["code-runtime"]
pkg_code_runtime_worker["code-runtime-worker"]
end
2026-07-14 16:04:34 +08:00
subgraph group_context["packages/context"]
pkg_session_reference["session-reference"]
2026-07-14 16:04:34 +08:00
pkg_time_context["time-context"]
pkg_tmux_context["tmux-context"]
pkg_workspace_context["workspace-context"]
2026-07-14 16:04:34 +08:00
end
subgraph group_credentials["packages/credentials"]
pkg_credentials["credentials"]
pkg_credentials_local["credentials-local"]
end
subgraph group_examples["packages/examples"]
pkg_acp_demo["acp-demo"]
pkg_agent_spine_demo["agent-spine-demo"]
2026-07-15 21:21:24 +08:00
pkg_cli_demo["cli-demo"]
pkg_jsonrpc_demo["jsonrpc-demo"]
end
subgraph group_guard["packages/guard"]
pkg_repeat_tool_guard["repeat-tool-guard"]
end
feat(gui): step1 skeleton — dsc web serves built web UI over booted harness host Five new modules: apps/dsc (bin: parseArgs + node:http static server + signal shutdown), packages/host/apiproxy (programmatic harness core composition, agents:[]), packages/client/web-runtime (React-free browser runtime), packages/client/web-ui (React mount), apps/web (vite build entry producing dist consumed by apps/dsc via package exports). Root wiring: apps/* workspace glob, dsh-* paths for host/client groups, demo:web script, apps/web/dist gitignore. No protocol/API routes yet — contract lands in step2 (see missions/tasks/20260719-1902-apiproxy-api-design). Includes the design + implementation archives (spec v2.1, deepseekchat baseline and harness boot research, implementation run log). Acceptance: 12/12 passed incl. real-key llm.stream smoke (51 chunks). feat(gui): apiproxy — four-quadrant RPC contract + fetch carriers, live end to end Contract layer (src/api/, 14 files): four named wire message types (ClientRequest / ServerResponse / ServerRequest / ClientResponse) as a discriminated union over strict bidirectional rpcId (initiator mints, responder echoes; channel and message fully decoupled — HTTP is the client->server pipe, SSE the reverse); narrow RpcRequest<P>/ RpcResponse<T> signature forms; RpcMethodMap with RequestPayload<K>/ ResponseValue<K> derivation; typed RpcError details map; approval/ question responses modeled as ClientResponse via a single /api/respond endpoint (RpcReceipt carrier ack); zod schemas anchored per Wire<T> against exactOptionalPropertyTypes. impl/api-proxy.ts: describe/list/create, both SSE streams (frame queue pump, subscribed baseline, lifecycle frames, signal cleanup); history pages on message boundaries (tail-back scan, partial included in the tail page); prompt dispatches queue->agent.send / steer->agent.steer with rpcId carried through MessageSource; cancel for attached sessions; cold-session resume deduped via a per-id promise map; host-level provider/model defaults injected at create/resume. fetch/: mechanical UNARY_ROUTES table, two-level parse with path==method check, SSE frames completed to ServerRequest full form; client mints -> narrows -> envelopes outbound, verifies rpcId echo inbound, streams SSE frames, four-quadrant onEnvelope tap (debug panel choke point). Real-browser fixes: URL base resolves to location.origin (hardcoded internal base broke real pages), browser-safe export paths. Design archives: contract design.md v2.0 with decision log, core-coverage audit, comparative studies, step2 impl run log. Probed end to end over real HTTP: prompt -> live model stream -> history returns the finished reply. feat(gui): RpcLog debug panel — fixture-driven milestone, playwright-verified 10/10 web-runtime: rpcLog + ui slices (zustand), four-quadrant RpcLogEntry (client-request / server-response / server-request / client-response), onEnvelope tap -> microtask-batched pump with 500-entry ring buffer, ConnectionController (private state, backoff reconnect), fixture API with fake envelopes (?fixture switch), bootWebRuntime; contract types via temporary local copies (api-types.ts, swapped for real imports when W3 client lands). web-ui: components/panels/RpcLog five-piece set (badge with unread count, floating panel, direction glyphs per quadrant, same-rpcId pair highlighting in two families, JSON payload expand, follow/pause, clear), App shell, utils/formatRelative, light-theme CSS variables with dark placeholders. dsc bin: mime lookup fixed to use the actually-served file (naked '/?query' no longer falls through to octet-stream download); shutdown closes SSE keep-alive connections so SIGTERM actually exits. Acceptance: scripts/verify-rpclog-panel.mjs (chromium headless) ALL PASS 10/10 over design.md §D 1-6. pkg: add web scripts for building feat(gui): session milestone — list + conversation over Session OOP, styled RpcLog v2.1 web-runtime: Session/SessionManager object layer (resident instances, mux frame routing, lineage flattening), foldSurface adapter with padding sentinels for paged windows, chunk accumulator for streaming partials, batched change notification (useSyncExternalStore contract), connection sinks + reconnect fix (the 300ms self-abort reconnect storm that made the session list flap is gone), fixture rewritten as a scripted host (60-turn history, typewriter replay, resident pending approval, child session); temporary contract copies deleted in favor of real apiproxy imports. web-ui: sessions screen (list with lineage indent + selection as container-local state), conversation view (turn grouping, reasoning fold, tool cards, steering, pending interaction cards, upward paging with scroll anchoring), input bar with queue/steer/stop; RpcLog panel restyled per docs/web-styling.md (tokenized palette, quadrant badge glyphs now vertical ↑↓⇟⇞, pair highlighting, floating shadow). docs/web-styling.md: living style guide (tokens, visual baseline, coding rules, evolution log). Acceptance: verify-session.mjs 31/31, verify-session-real.mjs 5/5 (real model streaming), verify-rpclog-panel.mjs 10/10. feat(gui): hostruntime split + repo-wide package prefix rename Package split (design: 20260720-0101-hostruntime-split-design): dsh-host-runtime carries bootHost + createApiProxy + startHost() (RunningHost {api, handler, defaults, ctx, dispose} — the seam Electron and any future shell reuses; ctx is the official front-door mount point); dsh-host-webserver carries the node:http static+API bridge (fixed: abort now keys on res 'close' + writableEnded — req 'close' fires on body end since Node 16 and was killing every SSE stream instantly, the reconnect-storm root cause); apps/dsc is now a thin assembly with web/-p subcommands. dsc -p runs the full isomorphic carrier chain in process (second real protocol consumer; probed end-to-end against the live model). Naming rule (user decree): packages under host/ and client/ carry the directory prefix in their npm name — dsh-host-apiproxy, dsh-client-web-runtime, dsh-client-web-ui renamed repo-wide in one frozen batch; explicit tsconfig paths entries added where the wildcard no longer matches. Acceptance: verify-session 31/31, verify-rpclog-panel 10/10, verify-session-real 7/7 (incl. new 12s connection-stability sentinels), tsc green, dsc web + dsc -p smoke both pass. refactor(gui): AbstractApiClient class hierarchy — OO client with inheritable seams AbstractApiClient (apiproxy) carries every protocol invariant: rpcId minting, four-quadrant envelope wrap/unwrap, zod parsing, SSE frame parsing, the payload-direct IApiClient surface (callers no longer mint rpcIds — the carrier does), and the instance-level envelope observation pump (batched via microtask; moved off module-level globals in rpc-log.ts, which is now a pure subscriber mapping envelopes into store entries — the debug panel observes the connection, it is not part of it). Platform subclasses own two abstract seams (doFetch, onEnvelope) plus three protocol-level virtuals for transportless overrides: InProcessApiClient (apiproxy; dsc -p uses new InProcessApiClient( host.handler)), WebApiClient (web-runtime), FixtureApiClient (fixture now subclasses instead of wrapping). Naming per decree: AbstractApiClient / IApiClient; ApiProxy stays the impl-side narrow-form contract. headless.ts call sites drop rpcRequest wrappers (payload-direct); split-design archive updated with the naming-rule ledger. tsc green; verify-session 31/31, verify-rpclog-panel 10/10, verify-session-real 7/7 (12s connection sentinel count=4); dsc -p smoke CALLER-OK. feat(gui): InputBar final form — bug batch, deepseekchat layout, single primary button, running locks input Squashes the whole InputBar iteration batch: IME/caret/auto-grow/focus/dedup bug fixes, layout aligned to the deepseekchat baseline, single primary button with hover flyout, finalized button semantics with the Codex-style icon circle, and running-state locking where stop is the only mid-turn action. The same batch carried the Chinese-to-English code comment sweep (density pruned), folded in here. docs(gui): purge work-log references from code comments 76 design-doc references cleared across the GUI packages: section pointers inlined as self-contained constraint statements, pure pointer comments dropped, milestone codenames and ruling tags out, and the 14 contract file headers switched to the formal RFC (the only sanctioned external reference). web-styling.md now cites the styling RFC instead of the disposable research archive. grep for work-log reference variants is clean across the GUI packages. docs(gui): file-header comments self-contained — drop RFC filename references RFC renames/reorgs must not require a source sweep (the 2026-07-20 two-way merge proved it). 11 headers lose only the '(RFC …)' tail and stay self-contained; api-proxy.ts keeps its minimal-first note. fix(gui): session streaming — freeze interrupted partials, sweep stale running calls, send force-scrolls Aborted turns never emit the finalizing assistant/message, so the accumulated partial and its running tool cards kept rendering below later messages — the "new message lands above the stopped reply" illusion. turn/end side effects now freeze content-bearing partials into interrupted terminal nodes (fractional seq keeps flow order; the live freeze and history replay converge through applyEventSideEffects, so a refresh reconstructs identical frozen nodes) and turn running tool cards into interrupted terminal cards; only content-free partials are swept outright. ConversationView gains the send-force-scroll rule (own words must be visible) alongside the pre-update atBottom follow flag. Regressions pinned as E2-4a–c (real host) and §E1-11h (fixture). feat(gui): webserver hardening verify script feat(gui): dark-mode toggle pinned to the sidebar bottom Interim home before the Settings page exists (the button re-homes with zero logic change — mechanics live in utils/theme.ts): html[data-theme] flip + dsc.theme localStorage, stored choice wins over the OS prefers-color-scheme default, applied in mount() before first paint so a dark reload never flashes light. Moon/sun inline SVG icon button at the sidebar's pinned bottom row. Pure front-end local concern: no RPC, no Session/store involvement. Dark sweep of list/conversation/input card/RPC panel found no unreadable pairs — no token changes needed. docs(gui): GUI RFCs and web styling handbook Layering+RPC protocol and web client architecture RFCs (post-reorg, developer-facing polish folded in) plus the styling engineering handbook. Mission work logs live in the commit above; PRs can be cut from this commit to include formal docs only. fix(gui): client object-layer hardening — audit timing/reference/resilience batches (S3-S5,C1-C3,C5-C8) fix(gui): carrier error channel + webserver backpressure (audit A1-A5,A7-A10,R2,R5) feat(gui): session persistence surface — cold list, project cwd, legacy no-cwd retirement refactor: rename dsc CLI to dsh — apps/cli, bin name, package scope Includes the root tsconfig project-references fix for host/* and client/web-runtime (originally a separate build fix commit). test(gui): three-tier suite — protocol/object/browser lanes, tier-a fill to per-file 100% test(gui): jsdom lane for web-ui + web-runtime coverage gate entry docs(gui): GUI testing system RFC (zh) feat(gui): tool-card views — contract slot, host-computed delivery, three-level card fallback fix(gui): lint clean across GUI packages — wrap long doc comments, drop dead type args, sync-return methods without awaits docs(gui): doc-sync mechanical fixes — JSDoc on apiproxy/host exports, RFC sketch fences ignore-check, md-wrap paragraphs, drop missions links, web-ui plain-ts entry chore(gui): module-graph regen + knip clean — drop dead re-exports, internalize createFixtureApi, scan web-ui tsx and verify mjs scripts build(gui): wire client/host packages into the lib build shape — tsc references + tsdown (web-ui css-external), lib manifests, cordis peer, apiproxy typed subpaths, vite src aliases test(gui): host-side per-file 100% coverage — apiproxy schema/carrier suites, webserver http-bridge suite, host-runtime composition suite; client/* coverage excluded pending the browser-side testing work item docs(gui): package READMEs for the five GUI packages — model-experience audit entries, limitations sections docs(gui): bilingual RFC pairs + client JSDoc completion — translate the three GUI RFCs to English with i18n records and manifest ratchet, Consequences sections both sides, full client/* export JSDoc, regen doc graphs and RFC index fix(scripts): doc-typecheck built-declarations mode maps /src/* subpath wildcards (apiproxy browser-safe channels) docs(gui): apply dsh rename across pr-gates docs — READMEs, layering RFC en, web-ui entry comment, i18n re-record fix(gui): post-rebase lint reconciliation — wrap main-tree long doc comments, read-through narrowing guards, abortError Error normalization, handleUnary generic justification fix(gui): post-rebase doc/test reconciliation — align host specs with evolved carrier contracts (sentinel rpcId, stream/error surfacing, url-path transport messages, defaults.cwd), Agent Note titles and relocated links, KV Cache effect sections, JSDoc on evolved exports fix(gui): second-rebase reconciliation to 509db0cb3 — restore api panel exports the baseline suites consume, knip workspace entries for jsdom lane and apps/web smokes, hoist result narrowing, align testing.md to the narrowed web-ui exclusion fix(test): vitest-scoped tsconfig maps bare imports for tsx specs — with GUI manifests now pointing at lib, an unmapped importer loaded a second copy of the web-runtime singletons fix(gui): typecheck + lint clean over the tool-card batch — brand callIds and object-form turn/end reason in the view spec, narrow fixture arg stringification, wrap long v8-ignore comments docs(gui): export JSDoc for tool-card surfaces + testing-note pairing header docs: rfc for web testing feat: add tools to host-runtime fix(gui): dispatch agent/error via agentEvents in host-runtime spec — mounted invariants plugin rejects raw ctx.emit without the scope carrier fix(gui): restore GUI knip workspaces + scripts/mjs entries and regenerate lockfile after master rebase fix(gui): post-rebase gate repairs — drop context-node envelope (master unwrapped injected content envelopes), regen event matrix, condense testing.md web-ui exclusion within budget fix(session): browser-safe deep-equal in surface — node:util import broke the vite bundle ci(gates): frontend vite build joins pre-push — node: imports in the client closure pass tsc but break the browser bundle test(tui): drop the checkout-dependent process.cwd() harness default — a long worktree path pushes the footer token counters past the 88-column fake terminal test(gui): jsdom behavior E2E — conversation main path over fixture runtime, reconnect banner lifecycle test(gui): jsdom RPC panel behavior — ledger rows, expand, pairing, pause/clear, follow-pause, payload truncation test(gui): jsdom tier-2 — InputBar guards, reasoning fold, JSON blocks, message variants, theme, create-then-select; act-harden banner case test(gui): jsdom tier-3 — ConversationView states/paging/force-bottom, ToolCallCard arms, PendingCard, list rows test(gui): jsdom tails — view-card variants, LogRow directions, registry hygiene, badge overflow, hook ops, mount glue test(gui): jsdom tails round 2 — call-ref blocks, resume follow, view precedence, failed create, empty-diff arm test(gui): jsdom final arms — anchor compensation, follow-off, interval ticks, view halves, node-over-running precedence test(gui): web-ui joins the per-file 100% coverage gate Annotation-only src changes plus the config swap. The web-ui exclusion is replaced by a single index.tsx entry (stale byte-identical duplicate of mount.tsx, nothing imports it; same entry-glue treatment as bin.ts) and the coverage include gains .tsx. v8-ignore sites (each with its reason inline): - ConversationView 3x ref-null guards; InputBar disabled-click guard - ToolCallCard both-null arms + windowless-custom argsRaw arm - LogRow css-module key fallbacks (start/stop block); RpcLogBody 3x ref-null guards - web-runtime drift from the tool-card batch: fixture presenter catch/str typo-guards, dense-array guards (fold-adapter reset, session rebuild, fixture backscan), live view-present arm (fixture replays are text-only; view vocabulary is covered by the history samples) test(gui): close the PR #443 host-side coverage gaps — apiproxy client abort arms, api-proxy cold/view paths, webserver drain - apiproxy fetch/client.ts: 3 new cases (pre-aborted signal short-circuits before transport + string reason mapping, non-Error/string reason falls to the default AbortError message, signal-less doFetch passthrough) - runtime/api-proxy.ts: one v8-ignore (summarizeCold cwd arm — list() filters cwd-less legacy metas) + api-proxy-cold.spec.ts (cold list merge: mtime source, locate-undefined and vanished-log fallbacks, lineage; no-persistence/no-factory resume → internal) + 2 view cases (history views with meta passthrough and orphan/bad-args/presenterless soft-falls, session/disposed open-call cleanup on the mux stream) - webserver/index.ts: /api/big fixture drives both drain-wait legs (full 8MiB readback after drain, mid-chunk disconnect wakes via 'close') feat: app shell fix: rebase conflicts fix: coverage fix(gui): lint clean after rebase — wrap long v8-ignore comments, unconditional v1 detail-block claim chore(gui): remove browser/probe verify scripts from scripts/ The six GUI acceptance/probe scripts (carrier-errors, rpclog-panel, session, session-real, webserver-backpressure, webserver-hardening) leave the repo's scripts/ tree; the three code comments that pointed at them now describe the coverage lane without naming a script path. fix(webserver): guard the request callback — one malformed request must not kill the process The async handle() had no top-level catch, so any throw inside it (a bad %-escape reaching decodeURIComponent, a client dropping mid-body, a response stream erroring) became an unhandled rejection and took the whole process down (audit R1 must-fix). The guard answers 400 when headers are not out yet, destroys the socket when they are, and reports the failure to onError (the package never prints). Spec covers all three legs: %-escape barrage → 400 + server stays alive, non-Error throw wrapped for onError, mid-stream explosion → socket teardown. feat: client AGENTS.md fix: client/AGENTS.md fix: rebase feat(gui): T0 cut 1 — 12 client package skeletons with contract stubs, dshClient declarations, tsdown client preset, theme token sheets feat(gui): T0 cut 2 — pure git mv migration per v3 §11 (connection six, runtime sessions/kernel, ui-conversation chat, ui-primitives markdown family, web shell + e2e) feat(gui): T0 cuts 3+4 — import rewiring to new package names, .legacy demotion of owner-rewrite files, legacy web-runtime/web-ui/apps-web retired to attic feat(gui): connection 对账刀——index.ts 精确导出清单替换 export *,intents.legacy 溶解删除 feat(client/ui-slots): SlotCore real implementation — kind semantics, sync version + microtask-batched notify, onMutate bridge feat(gui): web shell vite alias — retarget to new client packages, shell static surface only feat(gui): host 侧刀属地半——HostWebPluginRegistry(entries 扫描+internal/plugin 去抖重扫+dshClient 校验+exports./client 解析)、GET /plugins/<id>/client.js 分发端点、GET / 与 SPA fallback 注入 __DSH_BOOT__(webPlugins 可选注入,不传行为不变) feat(web-react): add use-sync-external-store dep + local shim typings feat(web-react): bindSnapshotSelector via uSES with-selector shim feat(gui): ui-layout concession-chain solver — pure computeColumns with contract geometry feat(gui): ui-layout LayoutService — four persisted stores, clamped actions, list-driven prune feat(gui): ui-layout AppFrame styles — grid columns, collapse-safe borders, edge drag handles test(gui): 存量 spec 平移——connection 三件+runtime 六件自 attic 捞回改包名路径全绿;api-helpers 按归属拆分(wire 半留 connection、classifier 半随 conversation.ts 入 runtime);boot-intents/preinit/rpc-log 随 intents/rpc-log 退役不迁(记 v3 §3.2 溶解项) feat(client/ui-primitives): StateDot/Button/Pill/Input/Menu atoms, ConnectionBanner de-legacied to pure props, JsonBlock CSS on --dsw tokens feat(web-react): createSnapshotStore engine (rafFlush batch, persist opt-in, dev freeze) + spec feat(gui): ui-layout AppFrame — grid tracks, pointer-capture drag handles with rAF throttle, frame ResizeObserver feat(web-react): useInvoke (external pending store, stable invoke, concurrency count) + spec test(web-react): bind spec — equality bail, custom eq, zero resubscribe, StrictMode, method sources feat(gui): ui-layout index rewiring — real exports, client apply provides ctx.layout and defines three slots feat(web-react): SessionProvider (renderBody deps) + RootBindingProvider + binding contexts + spec feat(gui): web shell AppRoot boot-page styles — self-contained with neutral token fallbacks feat(gui): web shell AppRoot — boot gate over loader status, fail-loud plugin failure list fix(gui): AppRoot gates on explicit settled signal — status-derived readiness races the incrementally filled table feat(client/ui-theme): ThemeService real implementation — registry with built-in light/dark, apply toggles body[data-ds-dark-theme], third-party token overrides as body inline vars feat(web-react): scopedSlots outlet (kind matrix, inject WeakMap caches, per-entry error boundary) + spec feat(gui): web shell module-table seed — pure-library entities for the loader require surface feat(client/i18n): I18nService real implementation — ns×locale registry, stable bind(ns) reference, zh fallback chain, zh/en skeleton dictionaries feat(gui): web shell assembly closure — layout exports via module table, SessionProvider + scopedSlots + RootBindingProvider feat: client/ui-conversation feat: code codedoc build(gui): root bundle green — web shell excluded from the lib workspace (vite app), ui-primitives lib externalizes css side-effect imports (web-ui precedent) gates(gui): verify-cordis-config follows aggregate tsconfig references (root is a shell over host/client programs); module graph regenerated for the twelve client packages chore(gui): retire legacy migration sources — every owner rewrite landed (t0-checklist §7 ledger honored); orphan css of retired components removed gates(gui): knip green groundwork — e2e/tsx entries for the new packages, loader-runtime deps ignored where loading is by specifier string, fake plugin ids un-bare-named, dead test export dropped chore(client): manifest shape batch A — ui-slots/web-react/ui-primitives invariant companions, files whitelist, cordis+invariants peer/dev, tsconfig refs chore(client): manifest shape batch B — connection/runtime/ui-conversation/ui-trajectory files whitelist, cordis peer+dev, explicit invariant lib entries (clientBundle signature) chore(client): manifest shape batch C — i18n/ui-layout/ui-sidebar/ui-theme invariant companions, files whitelist, invariants peer/dev, tsconfig refs chore(client): manifest shape batch D — web shell gains node-half lib entry + invariant companion + uniform files whitelist chore(client): drop verified-unused deps — dsh-tools from runtime/ui-conversation (types ride /presentation), ui-primitives+clsx from ui-layout gates(gui): doc-gate fixes — theme JSDoc prose, three client type-link exemptions, agent-note paths follow the migration, config catalog regenerated gates(gui): type-equiv manifest follows the types.ts extraction, approval JSDoc keeps its link form, persistence catalog regenerated docs(gui): per-constant JSDoc on the contract geometry exports (export-jsdoc gate) test(gates): loader-composition budget covers cold tsx resolution after the program split (was flaking at the default 5s) docs(gui): README substantiation batch 1 — ui-slots/ui-primitives/web-react/connection: Model Experience short form, real deferred-work ledgers, description accuracy pass fix(client): theme/i18n dual-entry split — service classes + cordis merges move to src/client (host catalog scanner no longer misclassifies client services), node halves keep types + empty apply; catalogs regenerated docs(gui): README substantiation batch 2 — runtime/ui-layout/ui-sidebar/ui-conversation: Model Experience short form, package-owned deferred-work ledgers (unload stub, watch approximation, /client value-import rule, global details state, two-state dots, stats duration gap, single-bundle caches) docs(gui): README substantiation batch 3 — ui-trajectory/ui-theme/i18n/web: Model Experience short form, deferred-work ledgers (placeholder charter, no theme toggle owner, empty locale dictionaries, one-shot rendering); both README gates green test(scripts): purity spec adopts clientBundle two-arg signature (explicit libEntry, no default) gates(gui): knip green — declaration-merge dep ignored, fake plugin id assembled at runtime, invariants dep de-duplicated to peer+dev, stale apps/web section dropped feat(gui): 门禁波次 host 三包 invariant 形状——apiproxy explained-empty 伴生(wire 契约层零事件面)、webserver 真关系伴生(manifest 行必解析出 clientPath,防 __DSH_BOOT__ 广告 404 bundle;apps/cli 发布 webPlugins 键供审计)、runtime 补 files 白名单;三包 exports/files/peer+dev/tsconfig refs 齐 fw-react 形状;constraints+invariants 双 gate 零违规 build(client): ui-layout/ui-sidebar tsdown configs adopt the explicit two-arg clientBundle signature (orphaned follow-up of the manifest shape batch) refactor(gui): shell boot becomes a library face — bootWebShell(el) exported for the apps/web entry; main.ts retired refactor(gui): exports 纪律刀1——ui-theme/i18n node index 收敛为只空 apply(Translate/LocaleDict/ThemeTokens 类型下沉 src/client/),ui-conversation 的 I18nService import 改 /client 子路径 build(typecheck): converge to root host aggregate + tsconfig.client.json — delete tsconfig.host.json, verify-cordis-config seeds both aggregates feat(gui): apps/web restored as the vite application — thin main over bootWebShell; dsh-client-web becomes a plain lib (index exports shell surface, vite files and e2e moved out) chore(gates): knip.json rewritten on the master base — same semantics, minimal diff (formatting churn dropped) docs(gui): 时效清扫②——testing.md 删 web-ui 覆盖豁免残句;web-styling.md 加 token 换代头注(--dsw-* 现行、工程约束条款仍有效并注明收编处) docs(gui): 时效清扫③——四对 GUI Agent Note 加路径更新头注(web-runtime/web-ui/dsh-frontend→现行 12 包结构;设计结论存续声明;双语对同步) docs(gui): 时效清扫③b——四对 note 头注的 i18n 配对哈希重录 build(typecheck): minimal-diff tsconfig shape — drop root files entry (purity spec + preset move to client program), compress comments, drop redundant util/home root ref feat(gui): apps/web restoration follow-through — dsh-frontend package name, cli dist resolve, root build:web filter, tsdown exemption dropped, vitest web lane + knip + client aggregate retargeted, e2e paths rebased refactor(gui): exports 纪律刀2——connection wire 六件 git mv 进 src/client/(wire 即该 dshClient 插件的 client 半),node index=只空 apply,/client 半边整面导出(v3 §3.2 清单原样),包内 tests 改 src/client 直取 refactor(gui): exports 纪律刀3——runtime 实现整体下沉 src/client/(sessions/slots/loader;契约类型与 cordis merge 随迁 client/index),node index=只空 apply;./loader exports 指 client/loader;全消费面(web 壳/ui-sidebar/ui-trajectory/tests)bare→/client 机械跟改;vitest.e2e 换 tsconfig.vitest paths(root tsconfig 排除 client 会把 /client import 掉到 exports 的浏览器 dist bundle) refactor(gui): exports 纪律刀3 补遗——ui-layout 三处 bare runtime import 改 /client(刀3 消费面机械跟改漏提交件;跨属地机械一行×3 报备 ui-shell) test(gui): drop the getSessionManager singleton case — the init/get pair is a dead legacy-boot surface with zero live consumers (SessionsService constructs and holds the manager under the plugin architecture); source removal tracked with rt-core refactor(gui): 删 manager.ts 尾部 initSessionManager/getSessionManager 单例对——旧 boot 直连遗物,插件化下 SessionsService 构造持有 manager,全仓零活消费者(convo-b 测试清扫对表,其测试用例已先行退役 7e2c51898);头注释同步去单例措辞 code refactor
2026-07-19 21:17:57 +08:00
subgraph group_host["packages/host"]
pkg_host_apiproxy["host-apiproxy"]
pkg_host_directory_picker["host-directory-picker"]
pkg_host_directory_picker_auto["host-directory-picker-auto"]
pkg_host_directory_picker_browse["host-directory-picker-browse"]
pkg_host_directory_picker_native["host-directory-picker-native"]
feat(gui): step1 skeleton — dsc web serves built web UI over booted harness host Five new modules: apps/dsc (bin: parseArgs + node:http static server + signal shutdown), packages/host/apiproxy (programmatic harness core composition, agents:[]), packages/client/web-runtime (React-free browser runtime), packages/client/web-ui (React mount), apps/web (vite build entry producing dist consumed by apps/dsc via package exports). Root wiring: apps/* workspace glob, dsh-* paths for host/client groups, demo:web script, apps/web/dist gitignore. No protocol/API routes yet — contract lands in step2 (see missions/tasks/20260719-1902-apiproxy-api-design). Includes the design + implementation archives (spec v2.1, deepseekchat baseline and harness boot research, implementation run log). Acceptance: 12/12 passed incl. real-key llm.stream smoke (51 chunks). feat(gui): apiproxy — four-quadrant RPC contract + fetch carriers, live end to end Contract layer (src/api/, 14 files): four named wire message types (ClientRequest / ServerResponse / ServerRequest / ClientResponse) as a discriminated union over strict bidirectional rpcId (initiator mints, responder echoes; channel and message fully decoupled — HTTP is the client->server pipe, SSE the reverse); narrow RpcRequest<P>/ RpcResponse<T> signature forms; RpcMethodMap with RequestPayload<K>/ ResponseValue<K> derivation; typed RpcError details map; approval/ question responses modeled as ClientResponse via a single /api/respond endpoint (RpcReceipt carrier ack); zod schemas anchored per Wire<T> against exactOptionalPropertyTypes. impl/api-proxy.ts: describe/list/create, both SSE streams (frame queue pump, subscribed baseline, lifecycle frames, signal cleanup); history pages on message boundaries (tail-back scan, partial included in the tail page); prompt dispatches queue->agent.send / steer->agent.steer with rpcId carried through MessageSource; cancel for attached sessions; cold-session resume deduped via a per-id promise map; host-level provider/model defaults injected at create/resume. fetch/: mechanical UNARY_ROUTES table, two-level parse with path==method check, SSE frames completed to ServerRequest full form; client mints -> narrows -> envelopes outbound, verifies rpcId echo inbound, streams SSE frames, four-quadrant onEnvelope tap (debug panel choke point). Real-browser fixes: URL base resolves to location.origin (hardcoded internal base broke real pages), browser-safe export paths. Design archives: contract design.md v2.0 with decision log, core-coverage audit, comparative studies, step2 impl run log. Probed end to end over real HTTP: prompt -> live model stream -> history returns the finished reply. feat(gui): RpcLog debug panel — fixture-driven milestone, playwright-verified 10/10 web-runtime: rpcLog + ui slices (zustand), four-quadrant RpcLogEntry (client-request / server-response / server-request / client-response), onEnvelope tap -> microtask-batched pump with 500-entry ring buffer, ConnectionController (private state, backoff reconnect), fixture API with fake envelopes (?fixture switch), bootWebRuntime; contract types via temporary local copies (api-types.ts, swapped for real imports when W3 client lands). web-ui: components/panels/RpcLog five-piece set (badge with unread count, floating panel, direction glyphs per quadrant, same-rpcId pair highlighting in two families, JSON payload expand, follow/pause, clear), App shell, utils/formatRelative, light-theme CSS variables with dark placeholders. dsc bin: mime lookup fixed to use the actually-served file (naked '/?query' no longer falls through to octet-stream download); shutdown closes SSE keep-alive connections so SIGTERM actually exits. Acceptance: scripts/verify-rpclog-panel.mjs (chromium headless) ALL PASS 10/10 over design.md §D 1-6. pkg: add web scripts for building feat(gui): session milestone — list + conversation over Session OOP, styled RpcLog v2.1 web-runtime: Session/SessionManager object layer (resident instances, mux frame routing, lineage flattening), foldSurface adapter with padding sentinels for paged windows, chunk accumulator for streaming partials, batched change notification (useSyncExternalStore contract), connection sinks + reconnect fix (the 300ms self-abort reconnect storm that made the session list flap is gone), fixture rewritten as a scripted host (60-turn history, typewriter replay, resident pending approval, child session); temporary contract copies deleted in favor of real apiproxy imports. web-ui: sessions screen (list with lineage indent + selection as container-local state), conversation view (turn grouping, reasoning fold, tool cards, steering, pending interaction cards, upward paging with scroll anchoring), input bar with queue/steer/stop; RpcLog panel restyled per docs/web-styling.md (tokenized palette, quadrant badge glyphs now vertical ↑↓⇟⇞, pair highlighting, floating shadow). docs/web-styling.md: living style guide (tokens, visual baseline, coding rules, evolution log). Acceptance: verify-session.mjs 31/31, verify-session-real.mjs 5/5 (real model streaming), verify-rpclog-panel.mjs 10/10. feat(gui): hostruntime split + repo-wide package prefix rename Package split (design: 20260720-0101-hostruntime-split-design): dsh-host-runtime carries bootHost + createApiProxy + startHost() (RunningHost {api, handler, defaults, ctx, dispose} — the seam Electron and any future shell reuses; ctx is the official front-door mount point); dsh-host-webserver carries the node:http static+API bridge (fixed: abort now keys on res 'close' + writableEnded — req 'close' fires on body end since Node 16 and was killing every SSE stream instantly, the reconnect-storm root cause); apps/dsc is now a thin assembly with web/-p subcommands. dsc -p runs the full isomorphic carrier chain in process (second real protocol consumer; probed end-to-end against the live model). Naming rule (user decree): packages under host/ and client/ carry the directory prefix in their npm name — dsh-host-apiproxy, dsh-client-web-runtime, dsh-client-web-ui renamed repo-wide in one frozen batch; explicit tsconfig paths entries added where the wildcard no longer matches. Acceptance: verify-session 31/31, verify-rpclog-panel 10/10, verify-session-real 7/7 (incl. new 12s connection-stability sentinels), tsc green, dsc web + dsc -p smoke both pass. refactor(gui): AbstractApiClient class hierarchy — OO client with inheritable seams AbstractApiClient (apiproxy) carries every protocol invariant: rpcId minting, four-quadrant envelope wrap/unwrap, zod parsing, SSE frame parsing, the payload-direct IApiClient surface (callers no longer mint rpcIds — the carrier does), and the instance-level envelope observation pump (batched via microtask; moved off module-level globals in rpc-log.ts, which is now a pure subscriber mapping envelopes into store entries — the debug panel observes the connection, it is not part of it). Platform subclasses own two abstract seams (doFetch, onEnvelope) plus three protocol-level virtuals for transportless overrides: InProcessApiClient (apiproxy; dsc -p uses new InProcessApiClient( host.handler)), WebApiClient (web-runtime), FixtureApiClient (fixture now subclasses instead of wrapping). Naming per decree: AbstractApiClient / IApiClient; ApiProxy stays the impl-side narrow-form contract. headless.ts call sites drop rpcRequest wrappers (payload-direct); split-design archive updated with the naming-rule ledger. tsc green; verify-session 31/31, verify-rpclog-panel 10/10, verify-session-real 7/7 (12s connection sentinel count=4); dsc -p smoke CALLER-OK. feat(gui): InputBar final form — bug batch, deepseekchat layout, single primary button, running locks input Squashes the whole InputBar iteration batch: IME/caret/auto-grow/focus/dedup bug fixes, layout aligned to the deepseekchat baseline, single primary button with hover flyout, finalized button semantics with the Codex-style icon circle, and running-state locking where stop is the only mid-turn action. The same batch carried the Chinese-to-English code comment sweep (density pruned), folded in here. docs(gui): purge work-log references from code comments 76 design-doc references cleared across the GUI packages: section pointers inlined as self-contained constraint statements, pure pointer comments dropped, milestone codenames and ruling tags out, and the 14 contract file headers switched to the formal RFC (the only sanctioned external reference). web-styling.md now cites the styling RFC instead of the disposable research archive. grep for work-log reference variants is clean across the GUI packages. docs(gui): file-header comments self-contained — drop RFC filename references RFC renames/reorgs must not require a source sweep (the 2026-07-20 two-way merge proved it). 11 headers lose only the '(RFC …)' tail and stay self-contained; api-proxy.ts keeps its minimal-first note. fix(gui): session streaming — freeze interrupted partials, sweep stale running calls, send force-scrolls Aborted turns never emit the finalizing assistant/message, so the accumulated partial and its running tool cards kept rendering below later messages — the "new message lands above the stopped reply" illusion. turn/end side effects now freeze content-bearing partials into interrupted terminal nodes (fractional seq keeps flow order; the live freeze and history replay converge through applyEventSideEffects, so a refresh reconstructs identical frozen nodes) and turn running tool cards into interrupted terminal cards; only content-free partials are swept outright. ConversationView gains the send-force-scroll rule (own words must be visible) alongside the pre-update atBottom follow flag. Regressions pinned as E2-4a–c (real host) and §E1-11h (fixture). feat(gui): webserver hardening verify script feat(gui): dark-mode toggle pinned to the sidebar bottom Interim home before the Settings page exists (the button re-homes with zero logic change — mechanics live in utils/theme.ts): html[data-theme] flip + dsc.theme localStorage, stored choice wins over the OS prefers-color-scheme default, applied in mount() before first paint so a dark reload never flashes light. Moon/sun inline SVG icon button at the sidebar's pinned bottom row. Pure front-end local concern: no RPC, no Session/store involvement. Dark sweep of list/conversation/input card/RPC panel found no unreadable pairs — no token changes needed. docs(gui): GUI RFCs and web styling handbook Layering+RPC protocol and web client architecture RFCs (post-reorg, developer-facing polish folded in) plus the styling engineering handbook. Mission work logs live in the commit above; PRs can be cut from this commit to include formal docs only. fix(gui): client object-layer hardening — audit timing/reference/resilience batches (S3-S5,C1-C3,C5-C8) fix(gui): carrier error channel + webserver backpressure (audit A1-A5,A7-A10,R2,R5) feat(gui): session persistence surface — cold list, project cwd, legacy no-cwd retirement refactor: rename dsc CLI to dsh — apps/cli, bin name, package scope Includes the root tsconfig project-references fix for host/* and client/web-runtime (originally a separate build fix commit). test(gui): three-tier suite — protocol/object/browser lanes, tier-a fill to per-file 100% test(gui): jsdom lane for web-ui + web-runtime coverage gate entry docs(gui): GUI testing system RFC (zh) feat(gui): tool-card views — contract slot, host-computed delivery, three-level card fallback fix(gui): lint clean across GUI packages — wrap long doc comments, drop dead type args, sync-return methods without awaits docs(gui): doc-sync mechanical fixes — JSDoc on apiproxy/host exports, RFC sketch fences ignore-check, md-wrap paragraphs, drop missions links, web-ui plain-ts entry chore(gui): module-graph regen + knip clean — drop dead re-exports, internalize createFixtureApi, scan web-ui tsx and verify mjs scripts build(gui): wire client/host packages into the lib build shape — tsc references + tsdown (web-ui css-external), lib manifests, cordis peer, apiproxy typed subpaths, vite src aliases test(gui): host-side per-file 100% coverage — apiproxy schema/carrier suites, webserver http-bridge suite, host-runtime composition suite; client/* coverage excluded pending the browser-side testing work item docs(gui): package READMEs for the five GUI packages — model-experience audit entries, limitations sections docs(gui): bilingual RFC pairs + client JSDoc completion — translate the three GUI RFCs to English with i18n records and manifest ratchet, Consequences sections both sides, full client/* export JSDoc, regen doc graphs and RFC index fix(scripts): doc-typecheck built-declarations mode maps /src/* subpath wildcards (apiproxy browser-safe channels) docs(gui): apply dsh rename across pr-gates docs — READMEs, layering RFC en, web-ui entry comment, i18n re-record fix(gui): post-rebase lint reconciliation — wrap main-tree long doc comments, read-through narrowing guards, abortError Error normalization, handleUnary generic justification fix(gui): post-rebase doc/test reconciliation — align host specs with evolved carrier contracts (sentinel rpcId, stream/error surfacing, url-path transport messages, defaults.cwd), Agent Note titles and relocated links, KV Cache effect sections, JSDoc on evolved exports fix(gui): second-rebase reconciliation to 509db0cb3 — restore api panel exports the baseline suites consume, knip workspace entries for jsdom lane and apps/web smokes, hoist result narrowing, align testing.md to the narrowed web-ui exclusion fix(test): vitest-scoped tsconfig maps bare imports for tsx specs — with GUI manifests now pointing at lib, an unmapped importer loaded a second copy of the web-runtime singletons fix(gui): typecheck + lint clean over the tool-card batch — brand callIds and object-form turn/end reason in the view spec, narrow fixture arg stringification, wrap long v8-ignore comments docs(gui): export JSDoc for tool-card surfaces + testing-note pairing header docs: rfc for web testing feat: add tools to host-runtime fix(gui): dispatch agent/error via agentEvents in host-runtime spec — mounted invariants plugin rejects raw ctx.emit without the scope carrier fix(gui): restore GUI knip workspaces + scripts/mjs entries and regenerate lockfile after master rebase fix(gui): post-rebase gate repairs — drop context-node envelope (master unwrapped injected content envelopes), regen event matrix, condense testing.md web-ui exclusion within budget fix(session): browser-safe deep-equal in surface — node:util import broke the vite bundle ci(gates): frontend vite build joins pre-push — node: imports in the client closure pass tsc but break the browser bundle test(tui): drop the checkout-dependent process.cwd() harness default — a long worktree path pushes the footer token counters past the 88-column fake terminal test(gui): jsdom behavior E2E — conversation main path over fixture runtime, reconnect banner lifecycle test(gui): jsdom RPC panel behavior — ledger rows, expand, pairing, pause/clear, follow-pause, payload truncation test(gui): jsdom tier-2 — InputBar guards, reasoning fold, JSON blocks, message variants, theme, create-then-select; act-harden banner case test(gui): jsdom tier-3 — ConversationView states/paging/force-bottom, ToolCallCard arms, PendingCard, list rows test(gui): jsdom tails — view-card variants, LogRow directions, registry hygiene, badge overflow, hook ops, mount glue test(gui): jsdom tails round 2 — call-ref blocks, resume follow, view precedence, failed create, empty-diff arm test(gui): jsdom final arms — anchor compensation, follow-off, interval ticks, view halves, node-over-running precedence test(gui): web-ui joins the per-file 100% coverage gate Annotation-only src changes plus the config swap. The web-ui exclusion is replaced by a single index.tsx entry (stale byte-identical duplicate of mount.tsx, nothing imports it; same entry-glue treatment as bin.ts) and the coverage include gains .tsx. v8-ignore sites (each with its reason inline): - ConversationView 3x ref-null guards; InputBar disabled-click guard - ToolCallCard both-null arms + windowless-custom argsRaw arm - LogRow css-module key fallbacks (start/stop block); RpcLogBody 3x ref-null guards - web-runtime drift from the tool-card batch: fixture presenter catch/str typo-guards, dense-array guards (fold-adapter reset, session rebuild, fixture backscan), live view-present arm (fixture replays are text-only; view vocabulary is covered by the history samples) test(gui): close the PR #443 host-side coverage gaps — apiproxy client abort arms, api-proxy cold/view paths, webserver drain - apiproxy fetch/client.ts: 3 new cases (pre-aborted signal short-circuits before transport + string reason mapping, non-Error/string reason falls to the default AbortError message, signal-less doFetch passthrough) - runtime/api-proxy.ts: one v8-ignore (summarizeCold cwd arm — list() filters cwd-less legacy metas) + api-proxy-cold.spec.ts (cold list merge: mtime source, locate-undefined and vanished-log fallbacks, lineage; no-persistence/no-factory resume → internal) + 2 view cases (history views with meta passthrough and orphan/bad-args/presenterless soft-falls, session/disposed open-call cleanup on the mux stream) - webserver/index.ts: /api/big fixture drives both drain-wait legs (full 8MiB readback after drain, mid-chunk disconnect wakes via 'close') feat: app shell fix: rebase conflicts fix: coverage fix(gui): lint clean after rebase — wrap long v8-ignore comments, unconditional v1 detail-block claim chore(gui): remove browser/probe verify scripts from scripts/ The six GUI acceptance/probe scripts (carrier-errors, rpclog-panel, session, session-real, webserver-backpressure, webserver-hardening) leave the repo's scripts/ tree; the three code comments that pointed at them now describe the coverage lane without naming a script path. fix(webserver): guard the request callback — one malformed request must not kill the process The async handle() had no top-level catch, so any throw inside it (a bad %-escape reaching decodeURIComponent, a client dropping mid-body, a response stream erroring) became an unhandled rejection and took the whole process down (audit R1 must-fix). The guard answers 400 when headers are not out yet, destroys the socket when they are, and reports the failure to onError (the package never prints). Spec covers all three legs: %-escape barrage → 400 + server stays alive, non-Error throw wrapped for onError, mid-stream explosion → socket teardown. feat: client AGENTS.md fix: client/AGENTS.md fix: rebase feat(gui): T0 cut 1 — 12 client package skeletons with contract stubs, dshClient declarations, tsdown client preset, theme token sheets feat(gui): T0 cut 2 — pure git mv migration per v3 §11 (connection six, runtime sessions/kernel, ui-conversation chat, ui-primitives markdown family, web shell + e2e) feat(gui): T0 cuts 3+4 — import rewiring to new package names, .legacy demotion of owner-rewrite files, legacy web-runtime/web-ui/apps-web retired to attic feat(gui): connection 对账刀——index.ts 精确导出清单替换 export *,intents.legacy 溶解删除 feat(client/ui-slots): SlotCore real implementation — kind semantics, sync version + microtask-batched notify, onMutate bridge feat(gui): web shell vite alias — retarget to new client packages, shell static surface only feat(gui): host 侧刀属地半——HostWebPluginRegistry(entries 扫描+internal/plugin 去抖重扫+dshClient 校验+exports./client 解析)、GET /plugins/<id>/client.js 分发端点、GET / 与 SPA fallback 注入 __DSH_BOOT__(webPlugins 可选注入,不传行为不变) feat(web-react): add use-sync-external-store dep + local shim typings feat(web-react): bindSnapshotSelector via uSES with-selector shim feat(gui): ui-layout concession-chain solver — pure computeColumns with contract geometry feat(gui): ui-layout LayoutService — four persisted stores, clamped actions, list-driven prune feat(gui): ui-layout AppFrame styles — grid columns, collapse-safe borders, edge drag handles test(gui): 存量 spec 平移——connection 三件+runtime 六件自 attic 捞回改包名路径全绿;api-helpers 按归属拆分(wire 半留 connection、classifier 半随 conversation.ts 入 runtime);boot-intents/preinit/rpc-log 随 intents/rpc-log 退役不迁(记 v3 §3.2 溶解项) feat(client/ui-primitives): StateDot/Button/Pill/Input/Menu atoms, ConnectionBanner de-legacied to pure props, JsonBlock CSS on --dsw tokens feat(web-react): createSnapshotStore engine (rafFlush batch, persist opt-in, dev freeze) + spec feat(gui): ui-layout AppFrame — grid tracks, pointer-capture drag handles with rAF throttle, frame ResizeObserver feat(web-react): useInvoke (external pending store, stable invoke, concurrency count) + spec test(web-react): bind spec — equality bail, custom eq, zero resubscribe, StrictMode, method sources feat(gui): ui-layout index rewiring — real exports, client apply provides ctx.layout and defines three slots feat(web-react): SessionProvider (renderBody deps) + RootBindingProvider + binding contexts + spec feat(gui): web shell AppRoot boot-page styles — self-contained with neutral token fallbacks feat(gui): web shell AppRoot — boot gate over loader status, fail-loud plugin failure list fix(gui): AppRoot gates on explicit settled signal — status-derived readiness races the incrementally filled table feat(client/ui-theme): ThemeService real implementation — registry with built-in light/dark, apply toggles body[data-ds-dark-theme], third-party token overrides as body inline vars feat(web-react): scopedSlots outlet (kind matrix, inject WeakMap caches, per-entry error boundary) + spec feat(gui): web shell module-table seed — pure-library entities for the loader require surface feat(client/i18n): I18nService real implementation — ns×locale registry, stable bind(ns) reference, zh fallback chain, zh/en skeleton dictionaries feat(gui): web shell assembly closure — layout exports via module table, SessionProvider + scopedSlots + RootBindingProvider feat: client/ui-conversation feat: code codedoc build(gui): root bundle green — web shell excluded from the lib workspace (vite app), ui-primitives lib externalizes css side-effect imports (web-ui precedent) gates(gui): verify-cordis-config follows aggregate tsconfig references (root is a shell over host/client programs); module graph regenerated for the twelve client packages chore(gui): retire legacy migration sources — every owner rewrite landed (t0-checklist §7 ledger honored); orphan css of retired components removed gates(gui): knip green groundwork — e2e/tsx entries for the new packages, loader-runtime deps ignored where loading is by specifier string, fake plugin ids un-bare-named, dead test export dropped chore(client): manifest shape batch A — ui-slots/web-react/ui-primitives invariant companions, files whitelist, cordis+invariants peer/dev, tsconfig refs chore(client): manifest shape batch B — connection/runtime/ui-conversation/ui-trajectory files whitelist, cordis peer+dev, explicit invariant lib entries (clientBundle signature) chore(client): manifest shape batch C — i18n/ui-layout/ui-sidebar/ui-theme invariant companions, files whitelist, invariants peer/dev, tsconfig refs chore(client): manifest shape batch D — web shell gains node-half lib entry + invariant companion + uniform files whitelist chore(client): drop verified-unused deps — dsh-tools from runtime/ui-conversation (types ride /presentation), ui-primitives+clsx from ui-layout gates(gui): doc-gate fixes — theme JSDoc prose, three client type-link exemptions, agent-note paths follow the migration, config catalog regenerated gates(gui): type-equiv manifest follows the types.ts extraction, approval JSDoc keeps its link form, persistence catalog regenerated docs(gui): per-constant JSDoc on the contract geometry exports (export-jsdoc gate) test(gates): loader-composition budget covers cold tsx resolution after the program split (was flaking at the default 5s) docs(gui): README substantiation batch 1 — ui-slots/ui-primitives/web-react/connection: Model Experience short form, real deferred-work ledgers, description accuracy pass fix(client): theme/i18n dual-entry split — service classes + cordis merges move to src/client (host catalog scanner no longer misclassifies client services), node halves keep types + empty apply; catalogs regenerated docs(gui): README substantiation batch 2 — runtime/ui-layout/ui-sidebar/ui-conversation: Model Experience short form, package-owned deferred-work ledgers (unload stub, watch approximation, /client value-import rule, global details state, two-state dots, stats duration gap, single-bundle caches) docs(gui): README substantiation batch 3 — ui-trajectory/ui-theme/i18n/web: Model Experience short form, deferred-work ledgers (placeholder charter, no theme toggle owner, empty locale dictionaries, one-shot rendering); both README gates green test(scripts): purity spec adopts clientBundle two-arg signature (explicit libEntry, no default) gates(gui): knip green — declaration-merge dep ignored, fake plugin id assembled at runtime, invariants dep de-duplicated to peer+dev, stale apps/web section dropped feat(gui): 门禁波次 host 三包 invariant 形状——apiproxy explained-empty 伴生(wire 契约层零事件面)、webserver 真关系伴生(manifest 行必解析出 clientPath,防 __DSH_BOOT__ 广告 404 bundle;apps/cli 发布 webPlugins 键供审计)、runtime 补 files 白名单;三包 exports/files/peer+dev/tsconfig refs 齐 fw-react 形状;constraints+invariants 双 gate 零违规 build(client): ui-layout/ui-sidebar tsdown configs adopt the explicit two-arg clientBundle signature (orphaned follow-up of the manifest shape batch) refactor(gui): shell boot becomes a library face — bootWebShell(el) exported for the apps/web entry; main.ts retired refactor(gui): exports 纪律刀1——ui-theme/i18n node index 收敛为只空 apply(Translate/LocaleDict/ThemeTokens 类型下沉 src/client/),ui-conversation 的 I18nService import 改 /client 子路径 build(typecheck): converge to root host aggregate + tsconfig.client.json — delete tsconfig.host.json, verify-cordis-config seeds both aggregates feat(gui): apps/web restored as the vite application — thin main over bootWebShell; dsh-client-web becomes a plain lib (index exports shell surface, vite files and e2e moved out) chore(gates): knip.json rewritten on the master base — same semantics, minimal diff (formatting churn dropped) docs(gui): 时效清扫②——testing.md 删 web-ui 覆盖豁免残句;web-styling.md 加 token 换代头注(--dsw-* 现行、工程约束条款仍有效并注明收编处) docs(gui): 时效清扫③——四对 GUI Agent Note 加路径更新头注(web-runtime/web-ui/dsh-frontend→现行 12 包结构;设计结论存续声明;双语对同步) docs(gui): 时效清扫③b——四对 note 头注的 i18n 配对哈希重录 build(typecheck): minimal-diff tsconfig shape — drop root files entry (purity spec + preset move to client program), compress comments, drop redundant util/home root ref feat(gui): apps/web restoration follow-through — dsh-frontend package name, cli dist resolve, root build:web filter, tsdown exemption dropped, vitest web lane + knip + client aggregate retargeted, e2e paths rebased refactor(gui): exports 纪律刀2——connection wire 六件 git mv 进 src/client/(wire 即该 dshClient 插件的 client 半),node index=只空 apply,/client 半边整面导出(v3 §3.2 清单原样),包内 tests 改 src/client 直取 refactor(gui): exports 纪律刀3——runtime 实现整体下沉 src/client/(sessions/slots/loader;契约类型与 cordis merge 随迁 client/index),node index=只空 apply;./loader exports 指 client/loader;全消费面(web 壳/ui-sidebar/ui-trajectory/tests)bare→/client 机械跟改;vitest.e2e 换 tsconfig.vitest paths(root tsconfig 排除 client 会把 /client import 掉到 exports 的浏览器 dist bundle) refactor(gui): exports 纪律刀3 补遗——ui-layout 三处 bare runtime import 改 /client(刀3 消费面机械跟改漏提交件;跨属地机械一行×3 报备 ui-shell) test(gui): drop the getSessionManager singleton case — the init/get pair is a dead legacy-boot surface with zero live consumers (SessionsService constructs and holds the manager under the plugin architecture); source removal tracked with rt-core refactor(gui): 删 manager.ts 尾部 initSessionManager/getSessionManager 单例对——旧 boot 直连遗物,插件化下 SessionsService 构造持有 manager,全仓零活消费者(convo-b 测试清扫对表,其测试用例已先行退役 7e2c51898);头注释同步去单例措辞 code refactor
2026-07-19 21:17:57 +08:00
pkg_host_webserver["host-webserver"]
end
subgraph group_lsp["packages/lsp"]
pkg_lsp["lsp"]
pkg_lsp_local["lsp-local"]
pkg_tool_lsp["tool-lsp"]
end
subgraph group_mcp["packages/mcp"]
pkg_mcp_client["mcp-client"]
end
2026-07-21 16:01:00 +08:00
subgraph group_pty["packages/pty"]
pkg_pty["pty"]
pkg_pty_local["pty-local"]
pkg_tool_bash_persistent["tool-bash-persistent"]
2026-07-21 16:01:00 +08:00
pkg_tool_pty["tool-pty"]
end
subgraph group_sandbox["packages/sandbox"]
pkg_sandbox["sandbox"]
pkg_sandbox_local["sandbox-local"]
feat(sandbox): cross-family file sandbox — one policy home, sandboxed fs provider, fs escalation parity Extend SandboxMode enforcement from bash to the filesystem tools, the sandbox RFC's deferred cross-family phase. - dsh-sandbox-policy (new, ctx.sandboxPolicy): the single home for the deployment default mode + workspaceRoot and the per-session override event, renamed bash/sandbox-mode -> sandbox/mode and moved here with its fold/setter. Decouples the bash seam from dsh-session. - dsh-fs-sandbox (new): SandboxedFileSystem extends LocalFileSystem and fences write/edit by the per-call mode (read-only denies, workspace-write contains to the workspace + temp roots via the shared writableRoots, danger passes through); reads pass through. Structured FS_SANDBOX_DENIED; in-lock parent re-canonicalization. A policy fence in trusted code, not a kernel boundary. - dsh-sandbox: the shared escalation kit (writableRoots, the strictly-wider ladder, denial/hint markers, approveEscalation) both tool families use; approveEscalation takes a structural approver so dsh-sandbox gains no approval/agent dependency, and both tools stay duplication-free. - tool-fs: write/edit advertise sandbox_permissions/justification under a confining ctx.fs, map FS_SANDBOX_DENIED to the shared [sandbox: ...] marker, and resolve the same one-approved-wider retry. - examples/acp-agent: composes sandbox-policy + fs-sandbox, drops the gating that disabled the fs stack under confined modes. RFC docs/rfc/implemented/feature/2026-07-14-cross-family-fs-sandbox.md; the old sandbox RFC's In-process/deferred/FAQ sections updated to shipped fact.
2026-07-14 20:05:57 +08:00
pkg_sandbox_policy["sandbox-policy"]
end
subgraph group_sdk["packages/sdk"]
pkg_helper["helper"]
pkg_scripts["scripts"]
pkg_sdk_client["sdk-client"]
pkg_sdk_protocol["sdk-protocol"]
pkg_telemetry["telemetry"]
end
subgraph group_session_projection["packages/session-projection"]
pkg_session_projection["session-projection"]
pkg_session_projection_cache["session-projection-cache"]
end
subgraph group_settings["packages/settings"]
pkg_settings["settings"]
pkg_settings_local["settings-local"]
end
subgraph group_storage["packages/storage"]
pkg_storage["storage"]
pkg_storage_domain["storage-domain"]
pkg_storage_json["storage-json"]
pkg_storage_sqlite["storage-sqlite"]
end
subgraph group_subprocess["packages/subprocess"]
pkg_subprocess["subprocess"]
pkg_subprocess_local["subprocess-local"]
end
subgraph group_tasks["packages/tasks"]
pkg_tasks["tasks"]
pkg_tasks_local["tasks-local"]
pkg_tool_tasks["tool-tasks"]
end
subgraph group_telemetry["packages/telemetry"]
pkg_session_telemetry["session-telemetry"]
pkg_session_telemetry_otel["session-telemetry-otel"]
end
subgraph group_typert["packages/typert"]
pkg_typert_generator["typert-generator"]
pkg_typert_loader["typert-loader"]
pkg_typert_registry["typert-registry"]
end
Merge remote-tracking branch 'origin/master' into worktree-dynamic-workflows Beyond the mechanical conflicts (provider capability lines vs master's new inheritsParentContext field; generated catalogs regenerated rather than hand-merged; knip/lockfile), three master-side reworks required semantic adaptation of this branch: - The persona rework removed AgentOptions.systemPrompt, which was the structured-output instruction's channel. The instruction now rides the SAME final-request enforcement listener that injects the schema'd tool: appended per request to final.system (per-request wire state, not agent prompt state). Tests assert the wire request (adapter.requests) instead of child.options; the bare-direct-dispatch test pins the no-system arm. - Tool guidance moved out of deployment prompts into per-tool prompt sections; the examples' workflow paragraph became a tool:<toolName> section contributed by dsh-tool-workflow (explicit-ask-only policy), and both example personas resolve to master's minimal identity+behavior form. tool-workflow gains inject: systemPrompt (+ peer dep, tsconfig ref); the export-shape guard updated. - The uniform-RFC-format gate: the dynamic-workflows RFC restructured to the implemented/ skeleton (bare Status line; Proposal -> Decision; What-was-rejected -> Alternatives considered; new Consequences), and the overall-run-timeout deferral is now recorded in the RFC's Deferred list. The doc-graphs atlas classification gains the workflows seam (workflow-vm implementation, tool-workflow consumer). Master's harness-identity section made "empty assembled prompt" states unreachable through the loop, so the instruction-append is a plain undefined-ternary and the structured tests assert append-not-replace. All snapshot goldens (including workflow-run) replay unchanged. Full local CI-equivalent gate sequence green on the merged tree.
2026-07-06 03:14:07 +08:00
subgraph group_workflow["packages/workflow"]
pkg_tool_ralph["tool-ralph"]
Merge remote-tracking branch 'origin/master' into worktree-dynamic-workflows Beyond the mechanical conflicts (provider capability lines vs master's new inheritsParentContext field; generated catalogs regenerated rather than hand-merged; knip/lockfile), three master-side reworks required semantic adaptation of this branch: - The persona rework removed AgentOptions.systemPrompt, which was the structured-output instruction's channel. The instruction now rides the SAME final-request enforcement listener that injects the schema'd tool: appended per request to final.system (per-request wire state, not agent prompt state). Tests assert the wire request (adapter.requests) instead of child.options; the bare-direct-dispatch test pins the no-system arm. - Tool guidance moved out of deployment prompts into per-tool prompt sections; the examples' workflow paragraph became a tool:<toolName> section contributed by dsh-tool-workflow (explicit-ask-only policy), and both example personas resolve to master's minimal identity+behavior form. tool-workflow gains inject: systemPrompt (+ peer dep, tsconfig ref); the export-shape guard updated. - The uniform-RFC-format gate: the dynamic-workflows RFC restructured to the implemented/ skeleton (bare Status line; Proposal -> Decision; What-was-rejected -> Alternatives considered; new Consequences), and the overall-run-timeout deferral is now recorded in the RFC's Deferred list. The doc-graphs atlas classification gains the workflows seam (workflow-vm implementation, tool-workflow consumer). Master's harness-identity section made "empty assembled prompt" states unreachable through the loop, so the instruction-append is a plain undefined-ternary and the structured tests assert append-not-replace. All snapshot goldens (including workflow-run) replay unchanged. Full local CI-equivalent gate sequence green on the merged tree.
2026-07-06 03:14:07 +08:00
pkg_tool_workflow["tool-workflow"]
pkg_workflow["workflow"]
pkg_workflow_workerthread["workflow-workerthread"]
Merge remote-tracking branch 'origin/master' into worktree-dynamic-workflows Beyond the mechanical conflicts (provider capability lines vs master's new inheritsParentContext field; generated catalogs regenerated rather than hand-merged; knip/lockfile), three master-side reworks required semantic adaptation of this branch: - The persona rework removed AgentOptions.systemPrompt, which was the structured-output instruction's channel. The instruction now rides the SAME final-request enforcement listener that injects the schema'd tool: appended per request to final.system (per-request wire state, not agent prompt state). Tests assert the wire request (adapter.requests) instead of child.options; the bare-direct-dispatch test pins the no-system arm. - Tool guidance moved out of deployment prompts into per-tool prompt sections; the examples' workflow paragraph became a tool:<toolName> section contributed by dsh-tool-workflow (explicit-ask-only policy), and both example personas resolve to master's minimal identity+behavior form. tool-workflow gains inject: systemPrompt (+ peer dep, tsconfig ref); the export-shape guard updated. - The uniform-RFC-format gate: the dynamic-workflows RFC restructured to the implemented/ skeleton (bare Status line; Proposal -> Decision; What-was-rejected -> Alternatives considered; new Consequences), and the overall-run-timeout deferral is now recorded in the RFC's Deferred list. The doc-graphs atlas classification gains the workflows seam (workflow-vm implementation, tool-workflow consumer). Master's harness-identity section made "empty assembled prompt" states unreachable through the loop, so the instruction-append is a plain undefined-ternary and the structured tests assert append-not-replace. All snapshot goldens (including workflow-run) replay unchanged. Full local CI-equivalent gate sequence green on the merged tree.
2026-07-06 03:14:07 +08:00
end
subgraph group_workspace["packages/workspace"]
pkg_workspace["workspace"]
end
pkg_atomic_write --> pkg_invariants
pkg_brand --> pkg_invariants
pkg_native_command --> pkg_invariants
pkg_paths --> pkg_invariants
pkg_retention --> pkg_invariants
pkg_timeout --> pkg_invariants
pkg_scope --> pkg_invariants
pkg_skill --> pkg_invariants
pkg_acp_snapshot --> pkg_invariants
pkg_llm_mock_server --> pkg_invariants
pkg_loader_smoke --> pkg_invariants
pkg_client_modules --> pkg_invariants
feat(gui): step1 skeleton — dsc web serves built web UI over booted harness host Five new modules: apps/dsc (bin: parseArgs + node:http static server + signal shutdown), packages/host/apiproxy (programmatic harness core composition, agents:[]), packages/client/web-runtime (React-free browser runtime), packages/client/web-ui (React mount), apps/web (vite build entry producing dist consumed by apps/dsc via package exports). Root wiring: apps/* workspace glob, dsh-* paths for host/client groups, demo:web script, apps/web/dist gitignore. No protocol/API routes yet — contract lands in step2 (see missions/tasks/20260719-1902-apiproxy-api-design). Includes the design + implementation archives (spec v2.1, deepseekchat baseline and harness boot research, implementation run log). Acceptance: 12/12 passed incl. real-key llm.stream smoke (51 chunks). feat(gui): apiproxy — four-quadrant RPC contract + fetch carriers, live end to end Contract layer (src/api/, 14 files): four named wire message types (ClientRequest / ServerResponse / ServerRequest / ClientResponse) as a discriminated union over strict bidirectional rpcId (initiator mints, responder echoes; channel and message fully decoupled — HTTP is the client->server pipe, SSE the reverse); narrow RpcRequest<P>/ RpcResponse<T> signature forms; RpcMethodMap with RequestPayload<K>/ ResponseValue<K> derivation; typed RpcError details map; approval/ question responses modeled as ClientResponse via a single /api/respond endpoint (RpcReceipt carrier ack); zod schemas anchored per Wire<T> against exactOptionalPropertyTypes. impl/api-proxy.ts: describe/list/create, both SSE streams (frame queue pump, subscribed baseline, lifecycle frames, signal cleanup); history pages on message boundaries (tail-back scan, partial included in the tail page); prompt dispatches queue->agent.send / steer->agent.steer with rpcId carried through MessageSource; cancel for attached sessions; cold-session resume deduped via a per-id promise map; host-level provider/model defaults injected at create/resume. fetch/: mechanical UNARY_ROUTES table, two-level parse with path==method check, SSE frames completed to ServerRequest full form; client mints -> narrows -> envelopes outbound, verifies rpcId echo inbound, streams SSE frames, four-quadrant onEnvelope tap (debug panel choke point). Real-browser fixes: URL base resolves to location.origin (hardcoded internal base broke real pages), browser-safe export paths. Design archives: contract design.md v2.0 with decision log, core-coverage audit, comparative studies, step2 impl run log. Probed end to end over real HTTP: prompt -> live model stream -> history returns the finished reply. feat(gui): RpcLog debug panel — fixture-driven milestone, playwright-verified 10/10 web-runtime: rpcLog + ui slices (zustand), four-quadrant RpcLogEntry (client-request / server-response / server-request / client-response), onEnvelope tap -> microtask-batched pump with 500-entry ring buffer, ConnectionController (private state, backoff reconnect), fixture API with fake envelopes (?fixture switch), bootWebRuntime; contract types via temporary local copies (api-types.ts, swapped for real imports when W3 client lands). web-ui: components/panels/RpcLog five-piece set (badge with unread count, floating panel, direction glyphs per quadrant, same-rpcId pair highlighting in two families, JSON payload expand, follow/pause, clear), App shell, utils/formatRelative, light-theme CSS variables with dark placeholders. dsc bin: mime lookup fixed to use the actually-served file (naked '/?query' no longer falls through to octet-stream download); shutdown closes SSE keep-alive connections so SIGTERM actually exits. Acceptance: scripts/verify-rpclog-panel.mjs (chromium headless) ALL PASS 10/10 over design.md §D 1-6. pkg: add web scripts for building feat(gui): session milestone — list + conversation over Session OOP, styled RpcLog v2.1 web-runtime: Session/SessionManager object layer (resident instances, mux frame routing, lineage flattening), foldSurface adapter with padding sentinels for paged windows, chunk accumulator for streaming partials, batched change notification (useSyncExternalStore contract), connection sinks + reconnect fix (the 300ms self-abort reconnect storm that made the session list flap is gone), fixture rewritten as a scripted host (60-turn history, typewriter replay, resident pending approval, child session); temporary contract copies deleted in favor of real apiproxy imports. web-ui: sessions screen (list with lineage indent + selection as container-local state), conversation view (turn grouping, reasoning fold, tool cards, steering, pending interaction cards, upward paging with scroll anchoring), input bar with queue/steer/stop; RpcLog panel restyled per docs/web-styling.md (tokenized palette, quadrant badge glyphs now vertical ↑↓⇟⇞, pair highlighting, floating shadow). docs/web-styling.md: living style guide (tokens, visual baseline, coding rules, evolution log). Acceptance: verify-session.mjs 31/31, verify-session-real.mjs 5/5 (real model streaming), verify-rpclog-panel.mjs 10/10. feat(gui): hostruntime split + repo-wide package prefix rename Package split (design: 20260720-0101-hostruntime-split-design): dsh-host-runtime carries bootHost + createApiProxy + startHost() (RunningHost {api, handler, defaults, ctx, dispose} — the seam Electron and any future shell reuses; ctx is the official front-door mount point); dsh-host-webserver carries the node:http static+API bridge (fixed: abort now keys on res 'close' + writableEnded — req 'close' fires on body end since Node 16 and was killing every SSE stream instantly, the reconnect-storm root cause); apps/dsc is now a thin assembly with web/-p subcommands. dsc -p runs the full isomorphic carrier chain in process (second real protocol consumer; probed end-to-end against the live model). Naming rule (user decree): packages under host/ and client/ carry the directory prefix in their npm name — dsh-host-apiproxy, dsh-client-web-runtime, dsh-client-web-ui renamed repo-wide in one frozen batch; explicit tsconfig paths entries added where the wildcard no longer matches. Acceptance: verify-session 31/31, verify-rpclog-panel 10/10, verify-session-real 7/7 (incl. new 12s connection-stability sentinels), tsc green, dsc web + dsc -p smoke both pass. refactor(gui): AbstractApiClient class hierarchy — OO client with inheritable seams AbstractApiClient (apiproxy) carries every protocol invariant: rpcId minting, four-quadrant envelope wrap/unwrap, zod parsing, SSE frame parsing, the payload-direct IApiClient surface (callers no longer mint rpcIds — the carrier does), and the instance-level envelope observation pump (batched via microtask; moved off module-level globals in rpc-log.ts, which is now a pure subscriber mapping envelopes into store entries — the debug panel observes the connection, it is not part of it). Platform subclasses own two abstract seams (doFetch, onEnvelope) plus three protocol-level virtuals for transportless overrides: InProcessApiClient (apiproxy; dsc -p uses new InProcessApiClient( host.handler)), WebApiClient (web-runtime), FixtureApiClient (fixture now subclasses instead of wrapping). Naming per decree: AbstractApiClient / IApiClient; ApiProxy stays the impl-side narrow-form contract. headless.ts call sites drop rpcRequest wrappers (payload-direct); split-design archive updated with the naming-rule ledger. tsc green; verify-session 31/31, verify-rpclog-panel 10/10, verify-session-real 7/7 (12s connection sentinel count=4); dsc -p smoke CALLER-OK. feat(gui): InputBar final form — bug batch, deepseekchat layout, single primary button, running locks input Squashes the whole InputBar iteration batch: IME/caret/auto-grow/focus/dedup bug fixes, layout aligned to the deepseekchat baseline, single primary button with hover flyout, finalized button semantics with the Codex-style icon circle, and running-state locking where stop is the only mid-turn action. The same batch carried the Chinese-to-English code comment sweep (density pruned), folded in here. docs(gui): purge work-log references from code comments 76 design-doc references cleared across the GUI packages: section pointers inlined as self-contained constraint statements, pure pointer comments dropped, milestone codenames and ruling tags out, and the 14 contract file headers switched to the formal RFC (the only sanctioned external reference). web-styling.md now cites the styling RFC instead of the disposable research archive. grep for work-log reference variants is clean across the GUI packages. docs(gui): file-header comments self-contained — drop RFC filename references RFC renames/reorgs must not require a source sweep (the 2026-07-20 two-way merge proved it). 11 headers lose only the '(RFC …)' tail and stay self-contained; api-proxy.ts keeps its minimal-first note. fix(gui): session streaming — freeze interrupted partials, sweep stale running calls, send force-scrolls Aborted turns never emit the finalizing assistant/message, so the accumulated partial and its running tool cards kept rendering below later messages — the "new message lands above the stopped reply" illusion. turn/end side effects now freeze content-bearing partials into interrupted terminal nodes (fractional seq keeps flow order; the live freeze and history replay converge through applyEventSideEffects, so a refresh reconstructs identical frozen nodes) and turn running tool cards into interrupted terminal cards; only content-free partials are swept outright. ConversationView gains the send-force-scroll rule (own words must be visible) alongside the pre-update atBottom follow flag. Regressions pinned as E2-4a–c (real host) and §E1-11h (fixture). feat(gui): webserver hardening verify script feat(gui): dark-mode toggle pinned to the sidebar bottom Interim home before the Settings page exists (the button re-homes with zero logic change — mechanics live in utils/theme.ts): html[data-theme] flip + dsc.theme localStorage, stored choice wins over the OS prefers-color-scheme default, applied in mount() before first paint so a dark reload never flashes light. Moon/sun inline SVG icon button at the sidebar's pinned bottom row. Pure front-end local concern: no RPC, no Session/store involvement. Dark sweep of list/conversation/input card/RPC panel found no unreadable pairs — no token changes needed. docs(gui): GUI RFCs and web styling handbook Layering+RPC protocol and web client architecture RFCs (post-reorg, developer-facing polish folded in) plus the styling engineering handbook. Mission work logs live in the commit above; PRs can be cut from this commit to include formal docs only. fix(gui): client object-layer hardening — audit timing/reference/resilience batches (S3-S5,C1-C3,C5-C8) fix(gui): carrier error channel + webserver backpressure (audit A1-A5,A7-A10,R2,R5) feat(gui): session persistence surface — cold list, project cwd, legacy no-cwd retirement refactor: rename dsc CLI to dsh — apps/cli, bin name, package scope Includes the root tsconfig project-references fix for host/* and client/web-runtime (originally a separate build fix commit). test(gui): three-tier suite — protocol/object/browser lanes, tier-a fill to per-file 100% test(gui): jsdom lane for web-ui + web-runtime coverage gate entry docs(gui): GUI testing system RFC (zh) feat(gui): tool-card views — contract slot, host-computed delivery, three-level card fallback fix(gui): lint clean across GUI packages — wrap long doc comments, drop dead type args, sync-return methods without awaits docs(gui): doc-sync mechanical fixes — JSDoc on apiproxy/host exports, RFC sketch fences ignore-check, md-wrap paragraphs, drop missions links, web-ui plain-ts entry chore(gui): module-graph regen + knip clean — drop dead re-exports, internalize createFixtureApi, scan web-ui tsx and verify mjs scripts build(gui): wire client/host packages into the lib build shape — tsc references + tsdown (web-ui css-external), lib manifests, cordis peer, apiproxy typed subpaths, vite src aliases test(gui): host-side per-file 100% coverage — apiproxy schema/carrier suites, webserver http-bridge suite, host-runtime composition suite; client/* coverage excluded pending the browser-side testing work item docs(gui): package READMEs for the five GUI packages — model-experience audit entries, limitations sections docs(gui): bilingual RFC pairs + client JSDoc completion — translate the three GUI RFCs to English with i18n records and manifest ratchet, Consequences sections both sides, full client/* export JSDoc, regen doc graphs and RFC index fix(scripts): doc-typecheck built-declarations mode maps /src/* subpath wildcards (apiproxy browser-safe channels) docs(gui): apply dsh rename across pr-gates docs — READMEs, layering RFC en, web-ui entry comment, i18n re-record fix(gui): post-rebase lint reconciliation — wrap main-tree long doc comments, read-through narrowing guards, abortError Error normalization, handleUnary generic justification fix(gui): post-rebase doc/test reconciliation — align host specs with evolved carrier contracts (sentinel rpcId, stream/error surfacing, url-path transport messages, defaults.cwd), Agent Note titles and relocated links, KV Cache effect sections, JSDoc on evolved exports fix(gui): second-rebase reconciliation to 509db0cb3 — restore api panel exports the baseline suites consume, knip workspace entries for jsdom lane and apps/web smokes, hoist result narrowing, align testing.md to the narrowed web-ui exclusion fix(test): vitest-scoped tsconfig maps bare imports for tsx specs — with GUI manifests now pointing at lib, an unmapped importer loaded a second copy of the web-runtime singletons fix(gui): typecheck + lint clean over the tool-card batch — brand callIds and object-form turn/end reason in the view spec, narrow fixture arg stringification, wrap long v8-ignore comments docs(gui): export JSDoc for tool-card surfaces + testing-note pairing header docs: rfc for web testing feat: add tools to host-runtime fix(gui): dispatch agent/error via agentEvents in host-runtime spec — mounted invariants plugin rejects raw ctx.emit without the scope carrier fix(gui): restore GUI knip workspaces + scripts/mjs entries and regenerate lockfile after master rebase fix(gui): post-rebase gate repairs — drop context-node envelope (master unwrapped injected content envelopes), regen event matrix, condense testing.md web-ui exclusion within budget fix(session): browser-safe deep-equal in surface — node:util import broke the vite bundle ci(gates): frontend vite build joins pre-push — node: imports in the client closure pass tsc but break the browser bundle test(tui): drop the checkout-dependent process.cwd() harness default — a long worktree path pushes the footer token counters past the 88-column fake terminal test(gui): jsdom behavior E2E — conversation main path over fixture runtime, reconnect banner lifecycle test(gui): jsdom RPC panel behavior — ledger rows, expand, pairing, pause/clear, follow-pause, payload truncation test(gui): jsdom tier-2 — InputBar guards, reasoning fold, JSON blocks, message variants, theme, create-then-select; act-harden banner case test(gui): jsdom tier-3 — ConversationView states/paging/force-bottom, ToolCallCard arms, PendingCard, list rows test(gui): jsdom tails — view-card variants, LogRow directions, registry hygiene, badge overflow, hook ops, mount glue test(gui): jsdom tails round 2 — call-ref blocks, resume follow, view precedence, failed create, empty-diff arm test(gui): jsdom final arms — anchor compensation, follow-off, interval ticks, view halves, node-over-running precedence test(gui): web-ui joins the per-file 100% coverage gate Annotation-only src changes plus the config swap. The web-ui exclusion is replaced by a single index.tsx entry (stale byte-identical duplicate of mount.tsx, nothing imports it; same entry-glue treatment as bin.ts) and the coverage include gains .tsx. v8-ignore sites (each with its reason inline): - ConversationView 3x ref-null guards; InputBar disabled-click guard - ToolCallCard both-null arms + windowless-custom argsRaw arm - LogRow css-module key fallbacks (start/stop block); RpcLogBody 3x ref-null guards - web-runtime drift from the tool-card batch: fixture presenter catch/str typo-guards, dense-array guards (fold-adapter reset, session rebuild, fixture backscan), live view-present arm (fixture replays are text-only; view vocabulary is covered by the history samples) test(gui): close the PR #443 host-side coverage gaps — apiproxy client abort arms, api-proxy cold/view paths, webserver drain - apiproxy fetch/client.ts: 3 new cases (pre-aborted signal short-circuits before transport + string reason mapping, non-Error/string reason falls to the default AbortError message, signal-less doFetch passthrough) - runtime/api-proxy.ts: one v8-ignore (summarizeCold cwd arm — list() filters cwd-less legacy metas) + api-proxy-cold.spec.ts (cold list merge: mtime source, locate-undefined and vanished-log fallbacks, lineage; no-persistence/no-factory resume → internal) + 2 view cases (history views with meta passthrough and orphan/bad-args/presenterless soft-falls, session/disposed open-call cleanup on the mux stream) - webserver/index.ts: /api/big fixture drives both drain-wait legs (full 8MiB readback after drain, mid-chunk disconnect wakes via 'close') feat: app shell fix: rebase conflicts fix: coverage fix(gui): lint clean after rebase — wrap long v8-ignore comments, unconditional v1 detail-block claim chore(gui): remove browser/probe verify scripts from scripts/ The six GUI acceptance/probe scripts (carrier-errors, rpclog-panel, session, session-real, webserver-backpressure, webserver-hardening) leave the repo's scripts/ tree; the three code comments that pointed at them now describe the coverage lane without naming a script path. fix(webserver): guard the request callback — one malformed request must not kill the process The async handle() had no top-level catch, so any throw inside it (a bad %-escape reaching decodeURIComponent, a client dropping mid-body, a response stream erroring) became an unhandled rejection and took the whole process down (audit R1 must-fix). The guard answers 400 when headers are not out yet, destroys the socket when they are, and reports the failure to onError (the package never prints). Spec covers all three legs: %-escape barrage → 400 + server stays alive, non-Error throw wrapped for onError, mid-stream explosion → socket teardown. feat: client AGENTS.md fix: client/AGENTS.md fix: rebase feat(gui): T0 cut 1 — 12 client package skeletons with contract stubs, dshClient declarations, tsdown client preset, theme token sheets feat(gui): T0 cut 2 — pure git mv migration per v3 §11 (connection six, runtime sessions/kernel, ui-conversation chat, ui-primitives markdown family, web shell + e2e) feat(gui): T0 cuts 3+4 — import rewiring to new package names, .legacy demotion of owner-rewrite files, legacy web-runtime/web-ui/apps-web retired to attic feat(gui): connection 对账刀——index.ts 精确导出清单替换 export *,intents.legacy 溶解删除 feat(client/ui-slots): SlotCore real implementation — kind semantics, sync version + microtask-batched notify, onMutate bridge feat(gui): web shell vite alias — retarget to new client packages, shell static surface only feat(gui): host 侧刀属地半——HostWebPluginRegistry(entries 扫描+internal/plugin 去抖重扫+dshClient 校验+exports./client 解析)、GET /plugins/<id>/client.js 分发端点、GET / 与 SPA fallback 注入 __DSH_BOOT__(webPlugins 可选注入,不传行为不变) feat(web-react): add use-sync-external-store dep + local shim typings feat(web-react): bindSnapshotSelector via uSES with-selector shim feat(gui): ui-layout concession-chain solver — pure computeColumns with contract geometry feat(gui): ui-layout LayoutService — four persisted stores, clamped actions, list-driven prune feat(gui): ui-layout AppFrame styles — grid columns, collapse-safe borders, edge drag handles test(gui): 存量 spec 平移——connection 三件+runtime 六件自 attic 捞回改包名路径全绿;api-helpers 按归属拆分(wire 半留 connection、classifier 半随 conversation.ts 入 runtime);boot-intents/preinit/rpc-log 随 intents/rpc-log 退役不迁(记 v3 §3.2 溶解项) feat(client/ui-primitives): StateDot/Button/Pill/Input/Menu atoms, ConnectionBanner de-legacied to pure props, JsonBlock CSS on --dsw tokens feat(web-react): createSnapshotStore engine (rafFlush batch, persist opt-in, dev freeze) + spec feat(gui): ui-layout AppFrame — grid tracks, pointer-capture drag handles with rAF throttle, frame ResizeObserver feat(web-react): useInvoke (external pending store, stable invoke, concurrency count) + spec test(web-react): bind spec — equality bail, custom eq, zero resubscribe, StrictMode, method sources feat(gui): ui-layout index rewiring — real exports, client apply provides ctx.layout and defines three slots feat(web-react): SessionProvider (renderBody deps) + RootBindingProvider + binding contexts + spec feat(gui): web shell AppRoot boot-page styles — self-contained with neutral token fallbacks feat(gui): web shell AppRoot — boot gate over loader status, fail-loud plugin failure list fix(gui): AppRoot gates on explicit settled signal — status-derived readiness races the incrementally filled table feat(client/ui-theme): ThemeService real implementation — registry with built-in light/dark, apply toggles body[data-ds-dark-theme], third-party token overrides as body inline vars feat(web-react): scopedSlots outlet (kind matrix, inject WeakMap caches, per-entry error boundary) + spec feat(gui): web shell module-table seed — pure-library entities for the loader require surface feat(client/i18n): I18nService real implementation — ns×locale registry, stable bind(ns) reference, zh fallback chain, zh/en skeleton dictionaries feat(gui): web shell assembly closure — layout exports via module table, SessionProvider + scopedSlots + RootBindingProvider feat: client/ui-conversation feat: code codedoc build(gui): root bundle green — web shell excluded from the lib workspace (vite app), ui-primitives lib externalizes css side-effect imports (web-ui precedent) gates(gui): verify-cordis-config follows aggregate tsconfig references (root is a shell over host/client programs); module graph regenerated for the twelve client packages chore(gui): retire legacy migration sources — every owner rewrite landed (t0-checklist §7 ledger honored); orphan css of retired components removed gates(gui): knip green groundwork — e2e/tsx entries for the new packages, loader-runtime deps ignored where loading is by specifier string, fake plugin ids un-bare-named, dead test export dropped chore(client): manifest shape batch A — ui-slots/web-react/ui-primitives invariant companions, files whitelist, cordis+invariants peer/dev, tsconfig refs chore(client): manifest shape batch B — connection/runtime/ui-conversation/ui-trajectory files whitelist, cordis peer+dev, explicit invariant lib entries (clientBundle signature) chore(client): manifest shape batch C — i18n/ui-layout/ui-sidebar/ui-theme invariant companions, files whitelist, invariants peer/dev, tsconfig refs chore(client): manifest shape batch D — web shell gains node-half lib entry + invariant companion + uniform files whitelist chore(client): drop verified-unused deps — dsh-tools from runtime/ui-conversation (types ride /presentation), ui-primitives+clsx from ui-layout gates(gui): doc-gate fixes — theme JSDoc prose, three client type-link exemptions, agent-note paths follow the migration, config catalog regenerated gates(gui): type-equiv manifest follows the types.ts extraction, approval JSDoc keeps its link form, persistence catalog regenerated docs(gui): per-constant JSDoc on the contract geometry exports (export-jsdoc gate) test(gates): loader-composition budget covers cold tsx resolution after the program split (was flaking at the default 5s) docs(gui): README substantiation batch 1 — ui-slots/ui-primitives/web-react/connection: Model Experience short form, real deferred-work ledgers, description accuracy pass fix(client): theme/i18n dual-entry split — service classes + cordis merges move to src/client (host catalog scanner no longer misclassifies client services), node halves keep types + empty apply; catalogs regenerated docs(gui): README substantiation batch 2 — runtime/ui-layout/ui-sidebar/ui-conversation: Model Experience short form, package-owned deferred-work ledgers (unload stub, watch approximation, /client value-import rule, global details state, two-state dots, stats duration gap, single-bundle caches) docs(gui): README substantiation batch 3 — ui-trajectory/ui-theme/i18n/web: Model Experience short form, deferred-work ledgers (placeholder charter, no theme toggle owner, empty locale dictionaries, one-shot rendering); both README gates green test(scripts): purity spec adopts clientBundle two-arg signature (explicit libEntry, no default) gates(gui): knip green — declaration-merge dep ignored, fake plugin id assembled at runtime, invariants dep de-duplicated to peer+dev, stale apps/web section dropped feat(gui): 门禁波次 host 三包 invariant 形状——apiproxy explained-empty 伴生(wire 契约层零事件面)、webserver 真关系伴生(manifest 行必解析出 clientPath,防 __DSH_BOOT__ 广告 404 bundle;apps/cli 发布 webPlugins 键供审计)、runtime 补 files 白名单;三包 exports/files/peer+dev/tsconfig refs 齐 fw-react 形状;constraints+invariants 双 gate 零违规 build(client): ui-layout/ui-sidebar tsdown configs adopt the explicit two-arg clientBundle signature (orphaned follow-up of the manifest shape batch) refactor(gui): shell boot becomes a library face — bootWebShell(el) exported for the apps/web entry; main.ts retired refactor(gui): exports 纪律刀1——ui-theme/i18n node index 收敛为只空 apply(Translate/LocaleDict/ThemeTokens 类型下沉 src/client/),ui-conversation 的 I18nService import 改 /client 子路径 build(typecheck): converge to root host aggregate + tsconfig.client.json — delete tsconfig.host.json, verify-cordis-config seeds both aggregates feat(gui): apps/web restored as the vite application — thin main over bootWebShell; dsh-client-web becomes a plain lib (index exports shell surface, vite files and e2e moved out) chore(gates): knip.json rewritten on the master base — same semantics, minimal diff (formatting churn dropped) docs(gui): 时效清扫②——testing.md 删 web-ui 覆盖豁免残句;web-styling.md 加 token 换代头注(--dsw-* 现行、工程约束条款仍有效并注明收编处) docs(gui): 时效清扫③——四对 GUI Agent Note 加路径更新头注(web-runtime/web-ui/dsh-frontend→现行 12 包结构;设计结论存续声明;双语对同步) docs(gui): 时效清扫③b——四对 note 头注的 i18n 配对哈希重录 build(typecheck): minimal-diff tsconfig shape — drop root files entry (purity spec + preset move to client program), compress comments, drop redundant util/home root ref feat(gui): apps/web restoration follow-through — dsh-frontend package name, cli dist resolve, root build:web filter, tsdown exemption dropped, vitest web lane + knip + client aggregate retargeted, e2e paths rebased refactor(gui): exports 纪律刀2——connection wire 六件 git mv 进 src/client/(wire 即该 dshClient 插件的 client 半),node index=只空 apply,/client 半边整面导出(v3 §3.2 清单原样),包内 tests 改 src/client 直取 refactor(gui): exports 纪律刀3——runtime 实现整体下沉 src/client/(sessions/slots/loader;契约类型与 cordis merge 随迁 client/index),node index=只空 apply;./loader exports 指 client/loader;全消费面(web 壳/ui-sidebar/ui-trajectory/tests)bare→/client 机械跟改;vitest.e2e 换 tsconfig.vitest paths(root tsconfig 排除 client 会把 /client import 掉到 exports 的浏览器 dist bundle) refactor(gui): exports 纪律刀3 补遗——ui-layout 三处 bare runtime import 改 /client(刀3 消费面机械跟改漏提交件;跨属地机械一行×3 报备 ui-shell) test(gui): drop the getSessionManager singleton case — the init/get pair is a dead legacy-boot surface with zero live consumers (SessionsService constructs and holds the manager under the plugin architecture); source removal tracked with rt-core refactor(gui): 删 manager.ts 尾部 initSessionManager/getSessionManager 单例对——旧 boot 直连遗物,插件化下 SessionsService 构造持有 manager,全仓零活消费者(convo-b 测试清扫对表,其测试用例已先行退役 7e2c51898);头注释同步去单例措辞 code refactor
2026-07-19 21:17:57 +08:00
pkg_client_runtime --> pkg_invariants
pkg_client_schema_form --> pkg_invariants
feat(gui): step1 skeleton — dsc web serves built web UI over booted harness host Five new modules: apps/dsc (bin: parseArgs + node:http static server + signal shutdown), packages/host/apiproxy (programmatic harness core composition, agents:[]), packages/client/web-runtime (React-free browser runtime), packages/client/web-ui (React mount), apps/web (vite build entry producing dist consumed by apps/dsc via package exports). Root wiring: apps/* workspace glob, dsh-* paths for host/client groups, demo:web script, apps/web/dist gitignore. No protocol/API routes yet — contract lands in step2 (see missions/tasks/20260719-1902-apiproxy-api-design). Includes the design + implementation archives (spec v2.1, deepseekchat baseline and harness boot research, implementation run log). Acceptance: 12/12 passed incl. real-key llm.stream smoke (51 chunks). feat(gui): apiproxy — four-quadrant RPC contract + fetch carriers, live end to end Contract layer (src/api/, 14 files): four named wire message types (ClientRequest / ServerResponse / ServerRequest / ClientResponse) as a discriminated union over strict bidirectional rpcId (initiator mints, responder echoes; channel and message fully decoupled — HTTP is the client->server pipe, SSE the reverse); narrow RpcRequest<P>/ RpcResponse<T> signature forms; RpcMethodMap with RequestPayload<K>/ ResponseValue<K> derivation; typed RpcError details map; approval/ question responses modeled as ClientResponse via a single /api/respond endpoint (RpcReceipt carrier ack); zod schemas anchored per Wire<T> against exactOptionalPropertyTypes. impl/api-proxy.ts: describe/list/create, both SSE streams (frame queue pump, subscribed baseline, lifecycle frames, signal cleanup); history pages on message boundaries (tail-back scan, partial included in the tail page); prompt dispatches queue->agent.send / steer->agent.steer with rpcId carried through MessageSource; cancel for attached sessions; cold-session resume deduped via a per-id promise map; host-level provider/model defaults injected at create/resume. fetch/: mechanical UNARY_ROUTES table, two-level parse with path==method check, SSE frames completed to ServerRequest full form; client mints -> narrows -> envelopes outbound, verifies rpcId echo inbound, streams SSE frames, four-quadrant onEnvelope tap (debug panel choke point). Real-browser fixes: URL base resolves to location.origin (hardcoded internal base broke real pages), browser-safe export paths. Design archives: contract design.md v2.0 with decision log, core-coverage audit, comparative studies, step2 impl run log. Probed end to end over real HTTP: prompt -> live model stream -> history returns the finished reply. feat(gui): RpcLog debug panel — fixture-driven milestone, playwright-verified 10/10 web-runtime: rpcLog + ui slices (zustand), four-quadrant RpcLogEntry (client-request / server-response / server-request / client-response), onEnvelope tap -> microtask-batched pump with 500-entry ring buffer, ConnectionController (private state, backoff reconnect), fixture API with fake envelopes (?fixture switch), bootWebRuntime; contract types via temporary local copies (api-types.ts, swapped for real imports when W3 client lands). web-ui: components/panels/RpcLog five-piece set (badge with unread count, floating panel, direction glyphs per quadrant, same-rpcId pair highlighting in two families, JSON payload expand, follow/pause, clear), App shell, utils/formatRelative, light-theme CSS variables with dark placeholders. dsc bin: mime lookup fixed to use the actually-served file (naked '/?query' no longer falls through to octet-stream download); shutdown closes SSE keep-alive connections so SIGTERM actually exits. Acceptance: scripts/verify-rpclog-panel.mjs (chromium headless) ALL PASS 10/10 over design.md §D 1-6. pkg: add web scripts for building feat(gui): session milestone — list + conversation over Session OOP, styled RpcLog v2.1 web-runtime: Session/SessionManager object layer (resident instances, mux frame routing, lineage flattening), foldSurface adapter with padding sentinels for paged windows, chunk accumulator for streaming partials, batched change notification (useSyncExternalStore contract), connection sinks + reconnect fix (the 300ms self-abort reconnect storm that made the session list flap is gone), fixture rewritten as a scripted host (60-turn history, typewriter replay, resident pending approval, child session); temporary contract copies deleted in favor of real apiproxy imports. web-ui: sessions screen (list with lineage indent + selection as container-local state), conversation view (turn grouping, reasoning fold, tool cards, steering, pending interaction cards, upward paging with scroll anchoring), input bar with queue/steer/stop; RpcLog panel restyled per docs/web-styling.md (tokenized palette, quadrant badge glyphs now vertical ↑↓⇟⇞, pair highlighting, floating shadow). docs/web-styling.md: living style guide (tokens, visual baseline, coding rules, evolution log). Acceptance: verify-session.mjs 31/31, verify-session-real.mjs 5/5 (real model streaming), verify-rpclog-panel.mjs 10/10. feat(gui): hostruntime split + repo-wide package prefix rename Package split (design: 20260720-0101-hostruntime-split-design): dsh-host-runtime carries bootHost + createApiProxy + startHost() (RunningHost {api, handler, defaults, ctx, dispose} — the seam Electron and any future shell reuses; ctx is the official front-door mount point); dsh-host-webserver carries the node:http static+API bridge (fixed: abort now keys on res 'close' + writableEnded — req 'close' fires on body end since Node 16 and was killing every SSE stream instantly, the reconnect-storm root cause); apps/dsc is now a thin assembly with web/-p subcommands. dsc -p runs the full isomorphic carrier chain in process (second real protocol consumer; probed end-to-end against the live model). Naming rule (user decree): packages under host/ and client/ carry the directory prefix in their npm name — dsh-host-apiproxy, dsh-client-web-runtime, dsh-client-web-ui renamed repo-wide in one frozen batch; explicit tsconfig paths entries added where the wildcard no longer matches. Acceptance: verify-session 31/31, verify-rpclog-panel 10/10, verify-session-real 7/7 (incl. new 12s connection-stability sentinels), tsc green, dsc web + dsc -p smoke both pass. refactor(gui): AbstractApiClient class hierarchy — OO client with inheritable seams AbstractApiClient (apiproxy) carries every protocol invariant: rpcId minting, four-quadrant envelope wrap/unwrap, zod parsing, SSE frame parsing, the payload-direct IApiClient surface (callers no longer mint rpcIds — the carrier does), and the instance-level envelope observation pump (batched via microtask; moved off module-level globals in rpc-log.ts, which is now a pure subscriber mapping envelopes into store entries — the debug panel observes the connection, it is not part of it). Platform subclasses own two abstract seams (doFetch, onEnvelope) plus three protocol-level virtuals for transportless overrides: InProcessApiClient (apiproxy; dsc -p uses new InProcessApiClient( host.handler)), WebApiClient (web-runtime), FixtureApiClient (fixture now subclasses instead of wrapping). Naming per decree: AbstractApiClient / IApiClient; ApiProxy stays the impl-side narrow-form contract. headless.ts call sites drop rpcRequest wrappers (payload-direct); split-design archive updated with the naming-rule ledger. tsc green; verify-session 31/31, verify-rpclog-panel 10/10, verify-session-real 7/7 (12s connection sentinel count=4); dsc -p smoke CALLER-OK. feat(gui): InputBar final form — bug batch, deepseekchat layout, single primary button, running locks input Squashes the whole InputBar iteration batch: IME/caret/auto-grow/focus/dedup bug fixes, layout aligned to the deepseekchat baseline, single primary button with hover flyout, finalized button semantics with the Codex-style icon circle, and running-state locking where stop is the only mid-turn action. The same batch carried the Chinese-to-English code comment sweep (density pruned), folded in here. docs(gui): purge work-log references from code comments 76 design-doc references cleared across the GUI packages: section pointers inlined as self-contained constraint statements, pure pointer comments dropped, milestone codenames and ruling tags out, and the 14 contract file headers switched to the formal RFC (the only sanctioned external reference). web-styling.md now cites the styling RFC instead of the disposable research archive. grep for work-log reference variants is clean across the GUI packages. docs(gui): file-header comments self-contained — drop RFC filename references RFC renames/reorgs must not require a source sweep (the 2026-07-20 two-way merge proved it). 11 headers lose only the '(RFC …)' tail and stay self-contained; api-proxy.ts keeps its minimal-first note. fix(gui): session streaming — freeze interrupted partials, sweep stale running calls, send force-scrolls Aborted turns never emit the finalizing assistant/message, so the accumulated partial and its running tool cards kept rendering below later messages — the "new message lands above the stopped reply" illusion. turn/end side effects now freeze content-bearing partials into interrupted terminal nodes (fractional seq keeps flow order; the live freeze and history replay converge through applyEventSideEffects, so a refresh reconstructs identical frozen nodes) and turn running tool cards into interrupted terminal cards; only content-free partials are swept outright. ConversationView gains the send-force-scroll rule (own words must be visible) alongside the pre-update atBottom follow flag. Regressions pinned as E2-4a–c (real host) and §E1-11h (fixture). feat(gui): webserver hardening verify script feat(gui): dark-mode toggle pinned to the sidebar bottom Interim home before the Settings page exists (the button re-homes with zero logic change — mechanics live in utils/theme.ts): html[data-theme] flip + dsc.theme localStorage, stored choice wins over the OS prefers-color-scheme default, applied in mount() before first paint so a dark reload never flashes light. Moon/sun inline SVG icon button at the sidebar's pinned bottom row. Pure front-end local concern: no RPC, no Session/store involvement. Dark sweep of list/conversation/input card/RPC panel found no unreadable pairs — no token changes needed. docs(gui): GUI RFCs and web styling handbook Layering+RPC protocol and web client architecture RFCs (post-reorg, developer-facing polish folded in) plus the styling engineering handbook. Mission work logs live in the commit above; PRs can be cut from this commit to include formal docs only. fix(gui): client object-layer hardening — audit timing/reference/resilience batches (S3-S5,C1-C3,C5-C8) fix(gui): carrier error channel + webserver backpressure (audit A1-A5,A7-A10,R2,R5) feat(gui): session persistence surface — cold list, project cwd, legacy no-cwd retirement refactor: rename dsc CLI to dsh — apps/cli, bin name, package scope Includes the root tsconfig project-references fix for host/* and client/web-runtime (originally a separate build fix commit). test(gui): three-tier suite — protocol/object/browser lanes, tier-a fill to per-file 100% test(gui): jsdom lane for web-ui + web-runtime coverage gate entry docs(gui): GUI testing system RFC (zh) feat(gui): tool-card views — contract slot, host-computed delivery, three-level card fallback fix(gui): lint clean across GUI packages — wrap long doc comments, drop dead type args, sync-return methods without awaits docs(gui): doc-sync mechanical fixes — JSDoc on apiproxy/host exports, RFC sketch fences ignore-check, md-wrap paragraphs, drop missions links, web-ui plain-ts entry chore(gui): module-graph regen + knip clean — drop dead re-exports, internalize createFixtureApi, scan web-ui tsx and verify mjs scripts build(gui): wire client/host packages into the lib build shape — tsc references + tsdown (web-ui css-external), lib manifests, cordis peer, apiproxy typed subpaths, vite src aliases test(gui): host-side per-file 100% coverage — apiproxy schema/carrier suites, webserver http-bridge suite, host-runtime composition suite; client/* coverage excluded pending the browser-side testing work item docs(gui): package READMEs for the five GUI packages — model-experience audit entries, limitations sections docs(gui): bilingual RFC pairs + client JSDoc completion — translate the three GUI RFCs to English with i18n records and manifest ratchet, Consequences sections both sides, full client/* export JSDoc, regen doc graphs and RFC index fix(scripts): doc-typecheck built-declarations mode maps /src/* subpath wildcards (apiproxy browser-safe channels) docs(gui): apply dsh rename across pr-gates docs — READMEs, layering RFC en, web-ui entry comment, i18n re-record fix(gui): post-rebase lint reconciliation — wrap main-tree long doc comments, read-through narrowing guards, abortError Error normalization, handleUnary generic justification fix(gui): post-rebase doc/test reconciliation — align host specs with evolved carrier contracts (sentinel rpcId, stream/error surfacing, url-path transport messages, defaults.cwd), Agent Note titles and relocated links, KV Cache effect sections, JSDoc on evolved exports fix(gui): second-rebase reconciliation to 509db0cb3 — restore api panel exports the baseline suites consume, knip workspace entries for jsdom lane and apps/web smokes, hoist result narrowing, align testing.md to the narrowed web-ui exclusion fix(test): vitest-scoped tsconfig maps bare imports for tsx specs — with GUI manifests now pointing at lib, an unmapped importer loaded a second copy of the web-runtime singletons fix(gui): typecheck + lint clean over the tool-card batch — brand callIds and object-form turn/end reason in the view spec, narrow fixture arg stringification, wrap long v8-ignore comments docs(gui): export JSDoc for tool-card surfaces + testing-note pairing header docs: rfc for web testing feat: add tools to host-runtime fix(gui): dispatch agent/error via agentEvents in host-runtime spec — mounted invariants plugin rejects raw ctx.emit without the scope carrier fix(gui): restore GUI knip workspaces + scripts/mjs entries and regenerate lockfile after master rebase fix(gui): post-rebase gate repairs — drop context-node envelope (master unwrapped injected content envelopes), regen event matrix, condense testing.md web-ui exclusion within budget fix(session): browser-safe deep-equal in surface — node:util import broke the vite bundle ci(gates): frontend vite build joins pre-push — node: imports in the client closure pass tsc but break the browser bundle test(tui): drop the checkout-dependent process.cwd() harness default — a long worktree path pushes the footer token counters past the 88-column fake terminal test(gui): jsdom behavior E2E — conversation main path over fixture runtime, reconnect banner lifecycle test(gui): jsdom RPC panel behavior — ledger rows, expand, pairing, pause/clear, follow-pause, payload truncation test(gui): jsdom tier-2 — InputBar guards, reasoning fold, JSON blocks, message variants, theme, create-then-select; act-harden banner case test(gui): jsdom tier-3 — ConversationView states/paging/force-bottom, ToolCallCard arms, PendingCard, list rows test(gui): jsdom tails — view-card variants, LogRow directions, registry hygiene, badge overflow, hook ops, mount glue test(gui): jsdom tails round 2 — call-ref blocks, resume follow, view precedence, failed create, empty-diff arm test(gui): jsdom final arms — anchor compensation, follow-off, interval ticks, view halves, node-over-running precedence test(gui): web-ui joins the per-file 100% coverage gate Annotation-only src changes plus the config swap. The web-ui exclusion is replaced by a single index.tsx entry (stale byte-identical duplicate of mount.tsx, nothing imports it; same entry-glue treatment as bin.ts) and the coverage include gains .tsx. v8-ignore sites (each with its reason inline): - ConversationView 3x ref-null guards; InputBar disabled-click guard - ToolCallCard both-null arms + windowless-custom argsRaw arm - LogRow css-module key fallbacks (start/stop block); RpcLogBody 3x ref-null guards - web-runtime drift from the tool-card batch: fixture presenter catch/str typo-guards, dense-array guards (fold-adapter reset, session rebuild, fixture backscan), live view-present arm (fixture replays are text-only; view vocabulary is covered by the history samples) test(gui): close the PR #443 host-side coverage gaps — apiproxy client abort arms, api-proxy cold/view paths, webserver drain - apiproxy fetch/client.ts: 3 new cases (pre-aborted signal short-circuits before transport + string reason mapping, non-Error/string reason falls to the default AbortError message, signal-less doFetch passthrough) - runtime/api-proxy.ts: one v8-ignore (summarizeCold cwd arm — list() filters cwd-less legacy metas) + api-proxy-cold.spec.ts (cold list merge: mtime source, locate-undefined and vanished-log fallbacks, lineage; no-persistence/no-factory resume → internal) + 2 view cases (history views with meta passthrough and orphan/bad-args/presenterless soft-falls, session/disposed open-call cleanup on the mux stream) - webserver/index.ts: /api/big fixture drives both drain-wait legs (full 8MiB readback after drain, mid-chunk disconnect wakes via 'close') feat: app shell fix: rebase conflicts fix: coverage fix(gui): lint clean after rebase — wrap long v8-ignore comments, unconditional v1 detail-block claim chore(gui): remove browser/probe verify scripts from scripts/ The six GUI acceptance/probe scripts (carrier-errors, rpclog-panel, session, session-real, webserver-backpressure, webserver-hardening) leave the repo's scripts/ tree; the three code comments that pointed at them now describe the coverage lane without naming a script path. fix(webserver): guard the request callback — one malformed request must not kill the process The async handle() had no top-level catch, so any throw inside it (a bad %-escape reaching decodeURIComponent, a client dropping mid-body, a response stream erroring) became an unhandled rejection and took the whole process down (audit R1 must-fix). The guard answers 400 when headers are not out yet, destroys the socket when they are, and reports the failure to onError (the package never prints). Spec covers all three legs: %-escape barrage → 400 + server stays alive, non-Error throw wrapped for onError, mid-stream explosion → socket teardown. feat: client AGENTS.md fix: client/AGENTS.md fix: rebase feat(gui): T0 cut 1 — 12 client package skeletons with contract stubs, dshClient declarations, tsdown client preset, theme token sheets feat(gui): T0 cut 2 — pure git mv migration per v3 §11 (connection six, runtime sessions/kernel, ui-conversation chat, ui-primitives markdown family, web shell + e2e) feat(gui): T0 cuts 3+4 — import rewiring to new package names, .legacy demotion of owner-rewrite files, legacy web-runtime/web-ui/apps-web retired to attic feat(gui): connection 对账刀——index.ts 精确导出清单替换 export *,intents.legacy 溶解删除 feat(client/ui-slots): SlotCore real implementation — kind semantics, sync version + microtask-batched notify, onMutate bridge feat(gui): web shell vite alias — retarget to new client packages, shell static surface only feat(gui): host 侧刀属地半——HostWebPluginRegistry(entries 扫描+internal/plugin 去抖重扫+dshClient 校验+exports./client 解析)、GET /plugins/<id>/client.js 分发端点、GET / 与 SPA fallback 注入 __DSH_BOOT__(webPlugins 可选注入,不传行为不变) feat(web-react): add use-sync-external-store dep + local shim typings feat(web-react): bindSnapshotSelector via uSES with-selector shim feat(gui): ui-layout concession-chain solver — pure computeColumns with contract geometry feat(gui): ui-layout LayoutService — four persisted stores, clamped actions, list-driven prune feat(gui): ui-layout AppFrame styles — grid columns, collapse-safe borders, edge drag handles test(gui): 存量 spec 平移——connection 三件+runtime 六件自 attic 捞回改包名路径全绿;api-helpers 按归属拆分(wire 半留 connection、classifier 半随 conversation.ts 入 runtime);boot-intents/preinit/rpc-log 随 intents/rpc-log 退役不迁(记 v3 §3.2 溶解项) feat(client/ui-primitives): StateDot/Button/Pill/Input/Menu atoms, ConnectionBanner de-legacied to pure props, JsonBlock CSS on --dsw tokens feat(web-react): createSnapshotStore engine (rafFlush batch, persist opt-in, dev freeze) + spec feat(gui): ui-layout AppFrame — grid tracks, pointer-capture drag handles with rAF throttle, frame ResizeObserver feat(web-react): useInvoke (external pending store, stable invoke, concurrency count) + spec test(web-react): bind spec — equality bail, custom eq, zero resubscribe, StrictMode, method sources feat(gui): ui-layout index rewiring — real exports, client apply provides ctx.layout and defines three slots feat(web-react): SessionProvider (renderBody deps) + RootBindingProvider + binding contexts + spec feat(gui): web shell AppRoot boot-page styles — self-contained with neutral token fallbacks feat(gui): web shell AppRoot — boot gate over loader status, fail-loud plugin failure list fix(gui): AppRoot gates on explicit settled signal — status-derived readiness races the incrementally filled table feat(client/ui-theme): ThemeService real implementation — registry with built-in light/dark, apply toggles body[data-ds-dark-theme], third-party token overrides as body inline vars feat(web-react): scopedSlots outlet (kind matrix, inject WeakMap caches, per-entry error boundary) + spec feat(gui): web shell module-table seed — pure-library entities for the loader require surface feat(client/i18n): I18nService real implementation — ns×locale registry, stable bind(ns) reference, zh fallback chain, zh/en skeleton dictionaries feat(gui): web shell assembly closure — layout exports via module table, SessionProvider + scopedSlots + RootBindingProvider feat: client/ui-conversation feat: code codedoc build(gui): root bundle green — web shell excluded from the lib workspace (vite app), ui-primitives lib externalizes css side-effect imports (web-ui precedent) gates(gui): verify-cordis-config follows aggregate tsconfig references (root is a shell over host/client programs); module graph regenerated for the twelve client packages chore(gui): retire legacy migration sources — every owner rewrite landed (t0-checklist §7 ledger honored); orphan css of retired components removed gates(gui): knip green groundwork — e2e/tsx entries for the new packages, loader-runtime deps ignored where loading is by specifier string, fake plugin ids un-bare-named, dead test export dropped chore(client): manifest shape batch A — ui-slots/web-react/ui-primitives invariant companions, files whitelist, cordis+invariants peer/dev, tsconfig refs chore(client): manifest shape batch B — connection/runtime/ui-conversation/ui-trajectory files whitelist, cordis peer+dev, explicit invariant lib entries (clientBundle signature) chore(client): manifest shape batch C — i18n/ui-layout/ui-sidebar/ui-theme invariant companions, files whitelist, invariants peer/dev, tsconfig refs chore(client): manifest shape batch D — web shell gains node-half lib entry + invariant companion + uniform files whitelist chore(client): drop verified-unused deps — dsh-tools from runtime/ui-conversation (types ride /presentation), ui-primitives+clsx from ui-layout gates(gui): doc-gate fixes — theme JSDoc prose, three client type-link exemptions, agent-note paths follow the migration, config catalog regenerated gates(gui): type-equiv manifest follows the types.ts extraction, approval JSDoc keeps its link form, persistence catalog regenerated docs(gui): per-constant JSDoc on the contract geometry exports (export-jsdoc gate) test(gates): loader-composition budget covers cold tsx resolution after the program split (was flaking at the default 5s) docs(gui): README substantiation batch 1 — ui-slots/ui-primitives/web-react/connection: Model Experience short form, real deferred-work ledgers, description accuracy pass fix(client): theme/i18n dual-entry split — service classes + cordis merges move to src/client (host catalog scanner no longer misclassifies client services), node halves keep types + empty apply; catalogs regenerated docs(gui): README substantiation batch 2 — runtime/ui-layout/ui-sidebar/ui-conversation: Model Experience short form, package-owned deferred-work ledgers (unload stub, watch approximation, /client value-import rule, global details state, two-state dots, stats duration gap, single-bundle caches) docs(gui): README substantiation batch 3 — ui-trajectory/ui-theme/i18n/web: Model Experience short form, deferred-work ledgers (placeholder charter, no theme toggle owner, empty locale dictionaries, one-shot rendering); both README gates green test(scripts): purity spec adopts clientBundle two-arg signature (explicit libEntry, no default) gates(gui): knip green — declaration-merge dep ignored, fake plugin id assembled at runtime, invariants dep de-duplicated to peer+dev, stale apps/web section dropped feat(gui): 门禁波次 host 三包 invariant 形状——apiproxy explained-empty 伴生(wire 契约层零事件面)、webserver 真关系伴生(manifest 行必解析出 clientPath,防 __DSH_BOOT__ 广告 404 bundle;apps/cli 发布 webPlugins 键供审计)、runtime 补 files 白名单;三包 exports/files/peer+dev/tsconfig refs 齐 fw-react 形状;constraints+invariants 双 gate 零违规 build(client): ui-layout/ui-sidebar tsdown configs adopt the explicit two-arg clientBundle signature (orphaned follow-up of the manifest shape batch) refactor(gui): shell boot becomes a library face — bootWebShell(el) exported for the apps/web entry; main.ts retired refactor(gui): exports 纪律刀1——ui-theme/i18n node index 收敛为只空 apply(Translate/LocaleDict/ThemeTokens 类型下沉 src/client/),ui-conversation 的 I18nService import 改 /client 子路径 build(typecheck): converge to root host aggregate + tsconfig.client.json — delete tsconfig.host.json, verify-cordis-config seeds both aggregates feat(gui): apps/web restored as the vite application — thin main over bootWebShell; dsh-client-web becomes a plain lib (index exports shell surface, vite files and e2e moved out) chore(gates): knip.json rewritten on the master base — same semantics, minimal diff (formatting churn dropped) docs(gui): 时效清扫②——testing.md 删 web-ui 覆盖豁免残句;web-styling.md 加 token 换代头注(--dsw-* 现行、工程约束条款仍有效并注明收编处) docs(gui): 时效清扫③——四对 GUI Agent Note 加路径更新头注(web-runtime/web-ui/dsh-frontend→现行 12 包结构;设计结论存续声明;双语对同步) docs(gui): 时效清扫③b——四对 note 头注的 i18n 配对哈希重录 build(typecheck): minimal-diff tsconfig shape — drop root files entry (purity spec + preset move to client program), compress comments, drop redundant util/home root ref feat(gui): apps/web restoration follow-through — dsh-frontend package name, cli dist resolve, root build:web filter, tsdown exemption dropped, vitest web lane + knip + client aggregate retargeted, e2e paths rebased refactor(gui): exports 纪律刀2——connection wire 六件 git mv 进 src/client/(wire 即该 dshClient 插件的 client 半),node index=只空 apply,/client 半边整面导出(v3 §3.2 清单原样),包内 tests 改 src/client 直取 refactor(gui): exports 纪律刀3——runtime 实现整体下沉 src/client/(sessions/slots/loader;契约类型与 cordis merge 随迁 client/index),node index=只空 apply;./loader exports 指 client/loader;全消费面(web 壳/ui-sidebar/ui-trajectory/tests)bare→/client 机械跟改;vitest.e2e 换 tsconfig.vitest paths(root tsconfig 排除 client 会把 /client import 掉到 exports 的浏览器 dist bundle) refactor(gui): exports 纪律刀3 补遗——ui-layout 三处 bare runtime import 改 /client(刀3 消费面机械跟改漏提交件;跨属地机械一行×3 报备 ui-shell) test(gui): drop the getSessionManager singleton case — the init/get pair is a dead legacy-boot surface with zero live consumers (SessionsService constructs and holds the manager under the plugin architecture); source removal tracked with rt-core refactor(gui): 删 manager.ts 尾部 initSessionManager/getSessionManager 单例对——旧 boot 直连遗物,插件化下 SessionsService 构造持有 manager,全仓零活消费者(convo-b 测试清扫对表,其测试用例已先行退役 7e2c51898);头注释同步去单例措辞 code refactor
2026-07-19 21:17:57 +08:00
pkg_client_ui_primitives --> pkg_invariants
pkg_client_ui_slots --> pkg_invariants
pkg_client_web --> pkg_invariants
pkg_client_web_react --> pkg_invariants
pkg_code_runtime --> pkg_invariants
pkg_jsonrpc_demo --> pkg_invariants
feat(gui): step1 skeleton — dsc web serves built web UI over booted harness host Five new modules: apps/dsc (bin: parseArgs + node:http static server + signal shutdown), packages/host/apiproxy (programmatic harness core composition, agents:[]), packages/client/web-runtime (React-free browser runtime), packages/client/web-ui (React mount), apps/web (vite build entry producing dist consumed by apps/dsc via package exports). Root wiring: apps/* workspace glob, dsh-* paths for host/client groups, demo:web script, apps/web/dist gitignore. No protocol/API routes yet — contract lands in step2 (see missions/tasks/20260719-1902-apiproxy-api-design). Includes the design + implementation archives (spec v2.1, deepseekchat baseline and harness boot research, implementation run log). Acceptance: 12/12 passed incl. real-key llm.stream smoke (51 chunks). feat(gui): apiproxy — four-quadrant RPC contract + fetch carriers, live end to end Contract layer (src/api/, 14 files): four named wire message types (ClientRequest / ServerResponse / ServerRequest / ClientResponse) as a discriminated union over strict bidirectional rpcId (initiator mints, responder echoes; channel and message fully decoupled — HTTP is the client->server pipe, SSE the reverse); narrow RpcRequest<P>/ RpcResponse<T> signature forms; RpcMethodMap with RequestPayload<K>/ ResponseValue<K> derivation; typed RpcError details map; approval/ question responses modeled as ClientResponse via a single /api/respond endpoint (RpcReceipt carrier ack); zod schemas anchored per Wire<T> against exactOptionalPropertyTypes. impl/api-proxy.ts: describe/list/create, both SSE streams (frame queue pump, subscribed baseline, lifecycle frames, signal cleanup); history pages on message boundaries (tail-back scan, partial included in the tail page); prompt dispatches queue->agent.send / steer->agent.steer with rpcId carried through MessageSource; cancel for attached sessions; cold-session resume deduped via a per-id promise map; host-level provider/model defaults injected at create/resume. fetch/: mechanical UNARY_ROUTES table, two-level parse with path==method check, SSE frames completed to ServerRequest full form; client mints -> narrows -> envelopes outbound, verifies rpcId echo inbound, streams SSE frames, four-quadrant onEnvelope tap (debug panel choke point). Real-browser fixes: URL base resolves to location.origin (hardcoded internal base broke real pages), browser-safe export paths. Design archives: contract design.md v2.0 with decision log, core-coverage audit, comparative studies, step2 impl run log. Probed end to end over real HTTP: prompt -> live model stream -> history returns the finished reply. feat(gui): RpcLog debug panel — fixture-driven milestone, playwright-verified 10/10 web-runtime: rpcLog + ui slices (zustand), four-quadrant RpcLogEntry (client-request / server-response / server-request / client-response), onEnvelope tap -> microtask-batched pump with 500-entry ring buffer, ConnectionController (private state, backoff reconnect), fixture API with fake envelopes (?fixture switch), bootWebRuntime; contract types via temporary local copies (api-types.ts, swapped for real imports when W3 client lands). web-ui: components/panels/RpcLog five-piece set (badge with unread count, floating panel, direction glyphs per quadrant, same-rpcId pair highlighting in two families, JSON payload expand, follow/pause, clear), App shell, utils/formatRelative, light-theme CSS variables with dark placeholders. dsc bin: mime lookup fixed to use the actually-served file (naked '/?query' no longer falls through to octet-stream download); shutdown closes SSE keep-alive connections so SIGTERM actually exits. Acceptance: scripts/verify-rpclog-panel.mjs (chromium headless) ALL PASS 10/10 over design.md §D 1-6. pkg: add web scripts for building feat(gui): session milestone — list + conversation over Session OOP, styled RpcLog v2.1 web-runtime: Session/SessionManager object layer (resident instances, mux frame routing, lineage flattening), foldSurface adapter with padding sentinels for paged windows, chunk accumulator for streaming partials, batched change notification (useSyncExternalStore contract), connection sinks + reconnect fix (the 300ms self-abort reconnect storm that made the session list flap is gone), fixture rewritten as a scripted host (60-turn history, typewriter replay, resident pending approval, child session); temporary contract copies deleted in favor of real apiproxy imports. web-ui: sessions screen (list with lineage indent + selection as container-local state), conversation view (turn grouping, reasoning fold, tool cards, steering, pending interaction cards, upward paging with scroll anchoring), input bar with queue/steer/stop; RpcLog panel restyled per docs/web-styling.md (tokenized palette, quadrant badge glyphs now vertical ↑↓⇟⇞, pair highlighting, floating shadow). docs/web-styling.md: living style guide (tokens, visual baseline, coding rules, evolution log). Acceptance: verify-session.mjs 31/31, verify-session-real.mjs 5/5 (real model streaming), verify-rpclog-panel.mjs 10/10. feat(gui): hostruntime split + repo-wide package prefix rename Package split (design: 20260720-0101-hostruntime-split-design): dsh-host-runtime carries bootHost + createApiProxy + startHost() (RunningHost {api, handler, defaults, ctx, dispose} — the seam Electron and any future shell reuses; ctx is the official front-door mount point); dsh-host-webserver carries the node:http static+API bridge (fixed: abort now keys on res 'close' + writableEnded — req 'close' fires on body end since Node 16 and was killing every SSE stream instantly, the reconnect-storm root cause); apps/dsc is now a thin assembly with web/-p subcommands. dsc -p runs the full isomorphic carrier chain in process (second real protocol consumer; probed end-to-end against the live model). Naming rule (user decree): packages under host/ and client/ carry the directory prefix in their npm name — dsh-host-apiproxy, dsh-client-web-runtime, dsh-client-web-ui renamed repo-wide in one frozen batch; explicit tsconfig paths entries added where the wildcard no longer matches. Acceptance: verify-session 31/31, verify-rpclog-panel 10/10, verify-session-real 7/7 (incl. new 12s connection-stability sentinels), tsc green, dsc web + dsc -p smoke both pass. refactor(gui): AbstractApiClient class hierarchy — OO client with inheritable seams AbstractApiClient (apiproxy) carries every protocol invariant: rpcId minting, four-quadrant envelope wrap/unwrap, zod parsing, SSE frame parsing, the payload-direct IApiClient surface (callers no longer mint rpcIds — the carrier does), and the instance-level envelope observation pump (batched via microtask; moved off module-level globals in rpc-log.ts, which is now a pure subscriber mapping envelopes into store entries — the debug panel observes the connection, it is not part of it). Platform subclasses own two abstract seams (doFetch, onEnvelope) plus three protocol-level virtuals for transportless overrides: InProcessApiClient (apiproxy; dsc -p uses new InProcessApiClient( host.handler)), WebApiClient (web-runtime), FixtureApiClient (fixture now subclasses instead of wrapping). Naming per decree: AbstractApiClient / IApiClient; ApiProxy stays the impl-side narrow-form contract. headless.ts call sites drop rpcRequest wrappers (payload-direct); split-design archive updated with the naming-rule ledger. tsc green; verify-session 31/31, verify-rpclog-panel 10/10, verify-session-real 7/7 (12s connection sentinel count=4); dsc -p smoke CALLER-OK. feat(gui): InputBar final form — bug batch, deepseekchat layout, single primary button, running locks input Squashes the whole InputBar iteration batch: IME/caret/auto-grow/focus/dedup bug fixes, layout aligned to the deepseekchat baseline, single primary button with hover flyout, finalized button semantics with the Codex-style icon circle, and running-state locking where stop is the only mid-turn action. The same batch carried the Chinese-to-English code comment sweep (density pruned), folded in here. docs(gui): purge work-log references from code comments 76 design-doc references cleared across the GUI packages: section pointers inlined as self-contained constraint statements, pure pointer comments dropped, milestone codenames and ruling tags out, and the 14 contract file headers switched to the formal RFC (the only sanctioned external reference). web-styling.md now cites the styling RFC instead of the disposable research archive. grep for work-log reference variants is clean across the GUI packages. docs(gui): file-header comments self-contained — drop RFC filename references RFC renames/reorgs must not require a source sweep (the 2026-07-20 two-way merge proved it). 11 headers lose only the '(RFC …)' tail and stay self-contained; api-proxy.ts keeps its minimal-first note. fix(gui): session streaming — freeze interrupted partials, sweep stale running calls, send force-scrolls Aborted turns never emit the finalizing assistant/message, so the accumulated partial and its running tool cards kept rendering below later messages — the "new message lands above the stopped reply" illusion. turn/end side effects now freeze content-bearing partials into interrupted terminal nodes (fractional seq keeps flow order; the live freeze and history replay converge through applyEventSideEffects, so a refresh reconstructs identical frozen nodes) and turn running tool cards into interrupted terminal cards; only content-free partials are swept outright. ConversationView gains the send-force-scroll rule (own words must be visible) alongside the pre-update atBottom follow flag. Regressions pinned as E2-4a–c (real host) and §E1-11h (fixture). feat(gui): webserver hardening verify script feat(gui): dark-mode toggle pinned to the sidebar bottom Interim home before the Settings page exists (the button re-homes with zero logic change — mechanics live in utils/theme.ts): html[data-theme] flip + dsc.theme localStorage, stored choice wins over the OS prefers-color-scheme default, applied in mount() before first paint so a dark reload never flashes light. Moon/sun inline SVG icon button at the sidebar's pinned bottom row. Pure front-end local concern: no RPC, no Session/store involvement. Dark sweep of list/conversation/input card/RPC panel found no unreadable pairs — no token changes needed. docs(gui): GUI RFCs and web styling handbook Layering+RPC protocol and web client architecture RFCs (post-reorg, developer-facing polish folded in) plus the styling engineering handbook. Mission work logs live in the commit above; PRs can be cut from this commit to include formal docs only. fix(gui): client object-layer hardening — audit timing/reference/resilience batches (S3-S5,C1-C3,C5-C8) fix(gui): carrier error channel + webserver backpressure (audit A1-A5,A7-A10,R2,R5) feat(gui): session persistence surface — cold list, project cwd, legacy no-cwd retirement refactor: rename dsc CLI to dsh — apps/cli, bin name, package scope Includes the root tsconfig project-references fix for host/* and client/web-runtime (originally a separate build fix commit). test(gui): three-tier suite — protocol/object/browser lanes, tier-a fill to per-file 100% test(gui): jsdom lane for web-ui + web-runtime coverage gate entry docs(gui): GUI testing system RFC (zh) feat(gui): tool-card views — contract slot, host-computed delivery, three-level card fallback fix(gui): lint clean across GUI packages — wrap long doc comments, drop dead type args, sync-return methods without awaits docs(gui): doc-sync mechanical fixes — JSDoc on apiproxy/host exports, RFC sketch fences ignore-check, md-wrap paragraphs, drop missions links, web-ui plain-ts entry chore(gui): module-graph regen + knip clean — drop dead re-exports, internalize createFixtureApi, scan web-ui tsx and verify mjs scripts build(gui): wire client/host packages into the lib build shape — tsc references + tsdown (web-ui css-external), lib manifests, cordis peer, apiproxy typed subpaths, vite src aliases test(gui): host-side per-file 100% coverage — apiproxy schema/carrier suites, webserver http-bridge suite, host-runtime composition suite; client/* coverage excluded pending the browser-side testing work item docs(gui): package READMEs for the five GUI packages — model-experience audit entries, limitations sections docs(gui): bilingual RFC pairs + client JSDoc completion — translate the three GUI RFCs to English with i18n records and manifest ratchet, Consequences sections both sides, full client/* export JSDoc, regen doc graphs and RFC index fix(scripts): doc-typecheck built-declarations mode maps /src/* subpath wildcards (apiproxy browser-safe channels) docs(gui): apply dsh rename across pr-gates docs — READMEs, layering RFC en, web-ui entry comment, i18n re-record fix(gui): post-rebase lint reconciliation — wrap main-tree long doc comments, read-through narrowing guards, abortError Error normalization, handleUnary generic justification fix(gui): post-rebase doc/test reconciliation — align host specs with evolved carrier contracts (sentinel rpcId, stream/error surfacing, url-path transport messages, defaults.cwd), Agent Note titles and relocated links, KV Cache effect sections, JSDoc on evolved exports fix(gui): second-rebase reconciliation to 509db0cb3 — restore api panel exports the baseline suites consume, knip workspace entries for jsdom lane and apps/web smokes, hoist result narrowing, align testing.md to the narrowed web-ui exclusion fix(test): vitest-scoped tsconfig maps bare imports for tsx specs — with GUI manifests now pointing at lib, an unmapped importer loaded a second copy of the web-runtime singletons fix(gui): typecheck + lint clean over the tool-card batch — brand callIds and object-form turn/end reason in the view spec, narrow fixture arg stringification, wrap long v8-ignore comments docs(gui): export JSDoc for tool-card surfaces + testing-note pairing header docs: rfc for web testing feat: add tools to host-runtime fix(gui): dispatch agent/error via agentEvents in host-runtime spec — mounted invariants plugin rejects raw ctx.emit without the scope carrier fix(gui): restore GUI knip workspaces + scripts/mjs entries and regenerate lockfile after master rebase fix(gui): post-rebase gate repairs — drop context-node envelope (master unwrapped injected content envelopes), regen event matrix, condense testing.md web-ui exclusion within budget fix(session): browser-safe deep-equal in surface — node:util import broke the vite bundle ci(gates): frontend vite build joins pre-push — node: imports in the client closure pass tsc but break the browser bundle test(tui): drop the checkout-dependent process.cwd() harness default — a long worktree path pushes the footer token counters past the 88-column fake terminal test(gui): jsdom behavior E2E — conversation main path over fixture runtime, reconnect banner lifecycle test(gui): jsdom RPC panel behavior — ledger rows, expand, pairing, pause/clear, follow-pause, payload truncation test(gui): jsdom tier-2 — InputBar guards, reasoning fold, JSON blocks, message variants, theme, create-then-select; act-harden banner case test(gui): jsdom tier-3 — ConversationView states/paging/force-bottom, ToolCallCard arms, PendingCard, list rows test(gui): jsdom tails — view-card variants, LogRow directions, registry hygiene, badge overflow, hook ops, mount glue test(gui): jsdom tails round 2 — call-ref blocks, resume follow, view precedence, failed create, empty-diff arm test(gui): jsdom final arms — anchor compensation, follow-off, interval ticks, view halves, node-over-running precedence test(gui): web-ui joins the per-file 100% coverage gate Annotation-only src changes plus the config swap. The web-ui exclusion is replaced by a single index.tsx entry (stale byte-identical duplicate of mount.tsx, nothing imports it; same entry-glue treatment as bin.ts) and the coverage include gains .tsx. v8-ignore sites (each with its reason inline): - ConversationView 3x ref-null guards; InputBar disabled-click guard - ToolCallCard both-null arms + windowless-custom argsRaw arm - LogRow css-module key fallbacks (start/stop block); RpcLogBody 3x ref-null guards - web-runtime drift from the tool-card batch: fixture presenter catch/str typo-guards, dense-array guards (fold-adapter reset, session rebuild, fixture backscan), live view-present arm (fixture replays are text-only; view vocabulary is covered by the history samples) test(gui): close the PR #443 host-side coverage gaps — apiproxy client abort arms, api-proxy cold/view paths, webserver drain - apiproxy fetch/client.ts: 3 new cases (pre-aborted signal short-circuits before transport + string reason mapping, non-Error/string reason falls to the default AbortError message, signal-less doFetch passthrough) - runtime/api-proxy.ts: one v8-ignore (summarizeCold cwd arm — list() filters cwd-less legacy metas) + api-proxy-cold.spec.ts (cold list merge: mtime source, locate-undefined and vanished-log fallbacks, lineage; no-persistence/no-factory resume → internal) + 2 view cases (history views with meta passthrough and orphan/bad-args/presenterless soft-falls, session/disposed open-call cleanup on the mux stream) - webserver/index.ts: /api/big fixture drives both drain-wait legs (full 8MiB readback after drain, mid-chunk disconnect wakes via 'close') feat: app shell fix: rebase conflicts fix: coverage fix(gui): lint clean after rebase — wrap long v8-ignore comments, unconditional v1 detail-block claim chore(gui): remove browser/probe verify scripts from scripts/ The six GUI acceptance/probe scripts (carrier-errors, rpclog-panel, session, session-real, webserver-backpressure, webserver-hardening) leave the repo's scripts/ tree; the three code comments that pointed at them now describe the coverage lane without naming a script path. fix(webserver): guard the request callback — one malformed request must not kill the process The async handle() had no top-level catch, so any throw inside it (a bad %-escape reaching decodeURIComponent, a client dropping mid-body, a response stream erroring) became an unhandled rejection and took the whole process down (audit R1 must-fix). The guard answers 400 when headers are not out yet, destroys the socket when they are, and reports the failure to onError (the package never prints). Spec covers all three legs: %-escape barrage → 400 + server stays alive, non-Error throw wrapped for onError, mid-stream explosion → socket teardown. feat: client AGENTS.md fix: client/AGENTS.md fix: rebase feat(gui): T0 cut 1 — 12 client package skeletons with contract stubs, dshClient declarations, tsdown client preset, theme token sheets feat(gui): T0 cut 2 — pure git mv migration per v3 §11 (connection six, runtime sessions/kernel, ui-conversation chat, ui-primitives markdown family, web shell + e2e) feat(gui): T0 cuts 3+4 — import rewiring to new package names, .legacy demotion of owner-rewrite files, legacy web-runtime/web-ui/apps-web retired to attic feat(gui): connection 对账刀——index.ts 精确导出清单替换 export *,intents.legacy 溶解删除 feat(client/ui-slots): SlotCore real implementation — kind semantics, sync version + microtask-batched notify, onMutate bridge feat(gui): web shell vite alias — retarget to new client packages, shell static surface only feat(gui): host 侧刀属地半——HostWebPluginRegistry(entries 扫描+internal/plugin 去抖重扫+dshClient 校验+exports./client 解析)、GET /plugins/<id>/client.js 分发端点、GET / 与 SPA fallback 注入 __DSH_BOOT__(webPlugins 可选注入,不传行为不变) feat(web-react): add use-sync-external-store dep + local shim typings feat(web-react): bindSnapshotSelector via uSES with-selector shim feat(gui): ui-layout concession-chain solver — pure computeColumns with contract geometry feat(gui): ui-layout LayoutService — four persisted stores, clamped actions, list-driven prune feat(gui): ui-layout AppFrame styles — grid columns, collapse-safe borders, edge drag handles test(gui): 存量 spec 平移——connection 三件+runtime 六件自 attic 捞回改包名路径全绿;api-helpers 按归属拆分(wire 半留 connection、classifier 半随 conversation.ts 入 runtime);boot-intents/preinit/rpc-log 随 intents/rpc-log 退役不迁(记 v3 §3.2 溶解项) feat(client/ui-primitives): StateDot/Button/Pill/Input/Menu atoms, ConnectionBanner de-legacied to pure props, JsonBlock CSS on --dsw tokens feat(web-react): createSnapshotStore engine (rafFlush batch, persist opt-in, dev freeze) + spec feat(gui): ui-layout AppFrame — grid tracks, pointer-capture drag handles with rAF throttle, frame ResizeObserver feat(web-react): useInvoke (external pending store, stable invoke, concurrency count) + spec test(web-react): bind spec — equality bail, custom eq, zero resubscribe, StrictMode, method sources feat(gui): ui-layout index rewiring — real exports, client apply provides ctx.layout and defines three slots feat(web-react): SessionProvider (renderBody deps) + RootBindingProvider + binding contexts + spec feat(gui): web shell AppRoot boot-page styles — self-contained with neutral token fallbacks feat(gui): web shell AppRoot — boot gate over loader status, fail-loud plugin failure list fix(gui): AppRoot gates on explicit settled signal — status-derived readiness races the incrementally filled table feat(client/ui-theme): ThemeService real implementation — registry with built-in light/dark, apply toggles body[data-ds-dark-theme], third-party token overrides as body inline vars feat(web-react): scopedSlots outlet (kind matrix, inject WeakMap caches, per-entry error boundary) + spec feat(gui): web shell module-table seed — pure-library entities for the loader require surface feat(client/i18n): I18nService real implementation — ns×locale registry, stable bind(ns) reference, zh fallback chain, zh/en skeleton dictionaries feat(gui): web shell assembly closure — layout exports via module table, SessionProvider + scopedSlots + RootBindingProvider feat: client/ui-conversation feat: code codedoc build(gui): root bundle green — web shell excluded from the lib workspace (vite app), ui-primitives lib externalizes css side-effect imports (web-ui precedent) gates(gui): verify-cordis-config follows aggregate tsconfig references (root is a shell over host/client programs); module graph regenerated for the twelve client packages chore(gui): retire legacy migration sources — every owner rewrite landed (t0-checklist §7 ledger honored); orphan css of retired components removed gates(gui): knip green groundwork — e2e/tsx entries for the new packages, loader-runtime deps ignored where loading is by specifier string, fake plugin ids un-bare-named, dead test export dropped chore(client): manifest shape batch A — ui-slots/web-react/ui-primitives invariant companions, files whitelist, cordis+invariants peer/dev, tsconfig refs chore(client): manifest shape batch B — connection/runtime/ui-conversation/ui-trajectory files whitelist, cordis peer+dev, explicit invariant lib entries (clientBundle signature) chore(client): manifest shape batch C — i18n/ui-layout/ui-sidebar/ui-theme invariant companions, files whitelist, invariants peer/dev, tsconfig refs chore(client): manifest shape batch D — web shell gains node-half lib entry + invariant companion + uniform files whitelist chore(client): drop verified-unused deps — dsh-tools from runtime/ui-conversation (types ride /presentation), ui-primitives+clsx from ui-layout gates(gui): doc-gate fixes — theme JSDoc prose, three client type-link exemptions, agent-note paths follow the migration, config catalog regenerated gates(gui): type-equiv manifest follows the types.ts extraction, approval JSDoc keeps its link form, persistence catalog regenerated docs(gui): per-constant JSDoc on the contract geometry exports (export-jsdoc gate) test(gates): loader-composition budget covers cold tsx resolution after the program split (was flaking at the default 5s) docs(gui): README substantiation batch 1 — ui-slots/ui-primitives/web-react/connection: Model Experience short form, real deferred-work ledgers, description accuracy pass fix(client): theme/i18n dual-entry split — service classes + cordis merges move to src/client (host catalog scanner no longer misclassifies client services), node halves keep types + empty apply; catalogs regenerated docs(gui): README substantiation batch 2 — runtime/ui-layout/ui-sidebar/ui-conversation: Model Experience short form, package-owned deferred-work ledgers (unload stub, watch approximation, /client value-import rule, global details state, two-state dots, stats duration gap, single-bundle caches) docs(gui): README substantiation batch 3 — ui-trajectory/ui-theme/i18n/web: Model Experience short form, deferred-work ledgers (placeholder charter, no theme toggle owner, empty locale dictionaries, one-shot rendering); both README gates green test(scripts): purity spec adopts clientBundle two-arg signature (explicit libEntry, no default) gates(gui): knip green — declaration-merge dep ignored, fake plugin id assembled at runtime, invariants dep de-duplicated to peer+dev, stale apps/web section dropped feat(gui): 门禁波次 host 三包 invariant 形状——apiproxy explained-empty 伴生(wire 契约层零事件面)、webserver 真关系伴生(manifest 行必解析出 clientPath,防 __DSH_BOOT__ 广告 404 bundle;apps/cli 发布 webPlugins 键供审计)、runtime 补 files 白名单;三包 exports/files/peer+dev/tsconfig refs 齐 fw-react 形状;constraints+invariants 双 gate 零违规 build(client): ui-layout/ui-sidebar tsdown configs adopt the explicit two-arg clientBundle signature (orphaned follow-up of the manifest shape batch) refactor(gui): shell boot becomes a library face — bootWebShell(el) exported for the apps/web entry; main.ts retired refactor(gui): exports 纪律刀1——ui-theme/i18n node index 收敛为只空 apply(Translate/LocaleDict/ThemeTokens 类型下沉 src/client/),ui-conversation 的 I18nService import 改 /client 子路径 build(typecheck): converge to root host aggregate + tsconfig.client.json — delete tsconfig.host.json, verify-cordis-config seeds both aggregates feat(gui): apps/web restored as the vite application — thin main over bootWebShell; dsh-client-web becomes a plain lib (index exports shell surface, vite files and e2e moved out) chore(gates): knip.json rewritten on the master base — same semantics, minimal diff (formatting churn dropped) docs(gui): 时效清扫②——testing.md 删 web-ui 覆盖豁免残句;web-styling.md 加 token 换代头注(--dsw-* 现行、工程约束条款仍有效并注明收编处) docs(gui): 时效清扫③——四对 GUI Agent Note 加路径更新头注(web-runtime/web-ui/dsh-frontend→现行 12 包结构;设计结论存续声明;双语对同步) docs(gui): 时效清扫③b——四对 note 头注的 i18n 配对哈希重录 build(typecheck): minimal-diff tsconfig shape — drop root files entry (purity spec + preset move to client program), compress comments, drop redundant util/home root ref feat(gui): apps/web restoration follow-through — dsh-frontend package name, cli dist resolve, root build:web filter, tsdown exemption dropped, vitest web lane + knip + client aggregate retargeted, e2e paths rebased refactor(gui): exports 纪律刀2——connection wire 六件 git mv 进 src/client/(wire 即该 dshClient 插件的 client 半),node index=只空 apply,/client 半边整面导出(v3 §3.2 清单原样),包内 tests 改 src/client 直取 refactor(gui): exports 纪律刀3——runtime 实现整体下沉 src/client/(sessions/slots/loader;契约类型与 cordis merge 随迁 client/index),node index=只空 apply;./loader exports 指 client/loader;全消费面(web 壳/ui-sidebar/ui-trajectory/tests)bare→/client 机械跟改;vitest.e2e 换 tsconfig.vitest paths(root tsconfig 排除 client 会把 /client import 掉到 exports 的浏览器 dist bundle) refactor(gui): exports 纪律刀3 补遗——ui-layout 三处 bare runtime import 改 /client(刀3 消费面机械跟改漏提交件;跨属地机械一行×3 报备 ui-shell) test(gui): drop the getSessionManager singleton case — the init/get pair is a dead legacy-boot surface with zero live consumers (SessionsService constructs and holds the manager under the plugin architecture); source removal tracked with rt-core refactor(gui): 删 manager.ts 尾部 initSessionManager/getSessionManager 单例对——旧 boot 直连遗物,插件化下 SessionsService 构造持有 manager,全仓零活消费者(convo-b 测试清扫对表,其测试用例已先行退役 7e2c51898);头注释同步去单例措辞 code refactor
2026-07-19 21:17:57 +08:00
pkg_host_apiproxy --> pkg_invariants
pkg_host_directory_picker --> pkg_invariants
feat(gui): step1 skeleton — dsc web serves built web UI over booted harness host Five new modules: apps/dsc (bin: parseArgs + node:http static server + signal shutdown), packages/host/apiproxy (programmatic harness core composition, agents:[]), packages/client/web-runtime (React-free browser runtime), packages/client/web-ui (React mount), apps/web (vite build entry producing dist consumed by apps/dsc via package exports). Root wiring: apps/* workspace glob, dsh-* paths for host/client groups, demo:web script, apps/web/dist gitignore. No protocol/API routes yet — contract lands in step2 (see missions/tasks/20260719-1902-apiproxy-api-design). Includes the design + implementation archives (spec v2.1, deepseekchat baseline and harness boot research, implementation run log). Acceptance: 12/12 passed incl. real-key llm.stream smoke (51 chunks). feat(gui): apiproxy — four-quadrant RPC contract + fetch carriers, live end to end Contract layer (src/api/, 14 files): four named wire message types (ClientRequest / ServerResponse / ServerRequest / ClientResponse) as a discriminated union over strict bidirectional rpcId (initiator mints, responder echoes; channel and message fully decoupled — HTTP is the client->server pipe, SSE the reverse); narrow RpcRequest<P>/ RpcResponse<T> signature forms; RpcMethodMap with RequestPayload<K>/ ResponseValue<K> derivation; typed RpcError details map; approval/ question responses modeled as ClientResponse via a single /api/respond endpoint (RpcReceipt carrier ack); zod schemas anchored per Wire<T> against exactOptionalPropertyTypes. impl/api-proxy.ts: describe/list/create, both SSE streams (frame queue pump, subscribed baseline, lifecycle frames, signal cleanup); history pages on message boundaries (tail-back scan, partial included in the tail page); prompt dispatches queue->agent.send / steer->agent.steer with rpcId carried through MessageSource; cancel for attached sessions; cold-session resume deduped via a per-id promise map; host-level provider/model defaults injected at create/resume. fetch/: mechanical UNARY_ROUTES table, two-level parse with path==method check, SSE frames completed to ServerRequest full form; client mints -> narrows -> envelopes outbound, verifies rpcId echo inbound, streams SSE frames, four-quadrant onEnvelope tap (debug panel choke point). Real-browser fixes: URL base resolves to location.origin (hardcoded internal base broke real pages), browser-safe export paths. Design archives: contract design.md v2.0 with decision log, core-coverage audit, comparative studies, step2 impl run log. Probed end to end over real HTTP: prompt -> live model stream -> history returns the finished reply. feat(gui): RpcLog debug panel — fixture-driven milestone, playwright-verified 10/10 web-runtime: rpcLog + ui slices (zustand), four-quadrant RpcLogEntry (client-request / server-response / server-request / client-response), onEnvelope tap -> microtask-batched pump with 500-entry ring buffer, ConnectionController (private state, backoff reconnect), fixture API with fake envelopes (?fixture switch), bootWebRuntime; contract types via temporary local copies (api-types.ts, swapped for real imports when W3 client lands). web-ui: components/panels/RpcLog five-piece set (badge with unread count, floating panel, direction glyphs per quadrant, same-rpcId pair highlighting in two families, JSON payload expand, follow/pause, clear), App shell, utils/formatRelative, light-theme CSS variables with dark placeholders. dsc bin: mime lookup fixed to use the actually-served file (naked '/?query' no longer falls through to octet-stream download); shutdown closes SSE keep-alive connections so SIGTERM actually exits. Acceptance: scripts/verify-rpclog-panel.mjs (chromium headless) ALL PASS 10/10 over design.md §D 1-6. pkg: add web scripts for building feat(gui): session milestone — list + conversation over Session OOP, styled RpcLog v2.1 web-runtime: Session/SessionManager object layer (resident instances, mux frame routing, lineage flattening), foldSurface adapter with padding sentinels for paged windows, chunk accumulator for streaming partials, batched change notification (useSyncExternalStore contract), connection sinks + reconnect fix (the 300ms self-abort reconnect storm that made the session list flap is gone), fixture rewritten as a scripted host (60-turn history, typewriter replay, resident pending approval, child session); temporary contract copies deleted in favor of real apiproxy imports. web-ui: sessions screen (list with lineage indent + selection as container-local state), conversation view (turn grouping, reasoning fold, tool cards, steering, pending interaction cards, upward paging with scroll anchoring), input bar with queue/steer/stop; RpcLog panel restyled per docs/web-styling.md (tokenized palette, quadrant badge glyphs now vertical ↑↓⇟⇞, pair highlighting, floating shadow). docs/web-styling.md: living style guide (tokens, visual baseline, coding rules, evolution log). Acceptance: verify-session.mjs 31/31, verify-session-real.mjs 5/5 (real model streaming), verify-rpclog-panel.mjs 10/10. feat(gui): hostruntime split + repo-wide package prefix rename Package split (design: 20260720-0101-hostruntime-split-design): dsh-host-runtime carries bootHost + createApiProxy + startHost() (RunningHost {api, handler, defaults, ctx, dispose} — the seam Electron and any future shell reuses; ctx is the official front-door mount point); dsh-host-webserver carries the node:http static+API bridge (fixed: abort now keys on res 'close' + writableEnded — req 'close' fires on body end since Node 16 and was killing every SSE stream instantly, the reconnect-storm root cause); apps/dsc is now a thin assembly with web/-p subcommands. dsc -p runs the full isomorphic carrier chain in process (second real protocol consumer; probed end-to-end against the live model). Naming rule (user decree): packages under host/ and client/ carry the directory prefix in their npm name — dsh-host-apiproxy, dsh-client-web-runtime, dsh-client-web-ui renamed repo-wide in one frozen batch; explicit tsconfig paths entries added where the wildcard no longer matches. Acceptance: verify-session 31/31, verify-rpclog-panel 10/10, verify-session-real 7/7 (incl. new 12s connection-stability sentinels), tsc green, dsc web + dsc -p smoke both pass. refactor(gui): AbstractApiClient class hierarchy — OO client with inheritable seams AbstractApiClient (apiproxy) carries every protocol invariant: rpcId minting, four-quadrant envelope wrap/unwrap, zod parsing, SSE frame parsing, the payload-direct IApiClient surface (callers no longer mint rpcIds — the carrier does), and the instance-level envelope observation pump (batched via microtask; moved off module-level globals in rpc-log.ts, which is now a pure subscriber mapping envelopes into store entries — the debug panel observes the connection, it is not part of it). Platform subclasses own two abstract seams (doFetch, onEnvelope) plus three protocol-level virtuals for transportless overrides: InProcessApiClient (apiproxy; dsc -p uses new InProcessApiClient( host.handler)), WebApiClient (web-runtime), FixtureApiClient (fixture now subclasses instead of wrapping). Naming per decree: AbstractApiClient / IApiClient; ApiProxy stays the impl-side narrow-form contract. headless.ts call sites drop rpcRequest wrappers (payload-direct); split-design archive updated with the naming-rule ledger. tsc green; verify-session 31/31, verify-rpclog-panel 10/10, verify-session-real 7/7 (12s connection sentinel count=4); dsc -p smoke CALLER-OK. feat(gui): InputBar final form — bug batch, deepseekchat layout, single primary button, running locks input Squashes the whole InputBar iteration batch: IME/caret/auto-grow/focus/dedup bug fixes, layout aligned to the deepseekchat baseline, single primary button with hover flyout, finalized button semantics with the Codex-style icon circle, and running-state locking where stop is the only mid-turn action. The same batch carried the Chinese-to-English code comment sweep (density pruned), folded in here. docs(gui): purge work-log references from code comments 76 design-doc references cleared across the GUI packages: section pointers inlined as self-contained constraint statements, pure pointer comments dropped, milestone codenames and ruling tags out, and the 14 contract file headers switched to the formal RFC (the only sanctioned external reference). web-styling.md now cites the styling RFC instead of the disposable research archive. grep for work-log reference variants is clean across the GUI packages. docs(gui): file-header comments self-contained — drop RFC filename references RFC renames/reorgs must not require a source sweep (the 2026-07-20 two-way merge proved it). 11 headers lose only the '(RFC …)' tail and stay self-contained; api-proxy.ts keeps its minimal-first note. fix(gui): session streaming — freeze interrupted partials, sweep stale running calls, send force-scrolls Aborted turns never emit the finalizing assistant/message, so the accumulated partial and its running tool cards kept rendering below later messages — the "new message lands above the stopped reply" illusion. turn/end side effects now freeze content-bearing partials into interrupted terminal nodes (fractional seq keeps flow order; the live freeze and history replay converge through applyEventSideEffects, so a refresh reconstructs identical frozen nodes) and turn running tool cards into interrupted terminal cards; only content-free partials are swept outright. ConversationView gains the send-force-scroll rule (own words must be visible) alongside the pre-update atBottom follow flag. Regressions pinned as E2-4a–c (real host) and §E1-11h (fixture). feat(gui): webserver hardening verify script feat(gui): dark-mode toggle pinned to the sidebar bottom Interim home before the Settings page exists (the button re-homes with zero logic change — mechanics live in utils/theme.ts): html[data-theme] flip + dsc.theme localStorage, stored choice wins over the OS prefers-color-scheme default, applied in mount() before first paint so a dark reload never flashes light. Moon/sun inline SVG icon button at the sidebar's pinned bottom row. Pure front-end local concern: no RPC, no Session/store involvement. Dark sweep of list/conversation/input card/RPC panel found no unreadable pairs — no token changes needed. docs(gui): GUI RFCs and web styling handbook Layering+RPC protocol and web client architecture RFCs (post-reorg, developer-facing polish folded in) plus the styling engineering handbook. Mission work logs live in the commit above; PRs can be cut from this commit to include formal docs only. fix(gui): client object-layer hardening — audit timing/reference/resilience batches (S3-S5,C1-C3,C5-C8) fix(gui): carrier error channel + webserver backpressure (audit A1-A5,A7-A10,R2,R5) feat(gui): session persistence surface — cold list, project cwd, legacy no-cwd retirement refactor: rename dsc CLI to dsh — apps/cli, bin name, package scope Includes the root tsconfig project-references fix for host/* and client/web-runtime (originally a separate build fix commit). test(gui): three-tier suite — protocol/object/browser lanes, tier-a fill to per-file 100% test(gui): jsdom lane for web-ui + web-runtime coverage gate entry docs(gui): GUI testing system RFC (zh) feat(gui): tool-card views — contract slot, host-computed delivery, three-level card fallback fix(gui): lint clean across GUI packages — wrap long doc comments, drop dead type args, sync-return methods without awaits docs(gui): doc-sync mechanical fixes — JSDoc on apiproxy/host exports, RFC sketch fences ignore-check, md-wrap paragraphs, drop missions links, web-ui plain-ts entry chore(gui): module-graph regen + knip clean — drop dead re-exports, internalize createFixtureApi, scan web-ui tsx and verify mjs scripts build(gui): wire client/host packages into the lib build shape — tsc references + tsdown (web-ui css-external), lib manifests, cordis peer, apiproxy typed subpaths, vite src aliases test(gui): host-side per-file 100% coverage — apiproxy schema/carrier suites, webserver http-bridge suite, host-runtime composition suite; client/* coverage excluded pending the browser-side testing work item docs(gui): package READMEs for the five GUI packages — model-experience audit entries, limitations sections docs(gui): bilingual RFC pairs + client JSDoc completion — translate the three GUI RFCs to English with i18n records and manifest ratchet, Consequences sections both sides, full client/* export JSDoc, regen doc graphs and RFC index fix(scripts): doc-typecheck built-declarations mode maps /src/* subpath wildcards (apiproxy browser-safe channels) docs(gui): apply dsh rename across pr-gates docs — READMEs, layering RFC en, web-ui entry comment, i18n re-record fix(gui): post-rebase lint reconciliation — wrap main-tree long doc comments, read-through narrowing guards, abortError Error normalization, handleUnary generic justification fix(gui): post-rebase doc/test reconciliation — align host specs with evolved carrier contracts (sentinel rpcId, stream/error surfacing, url-path transport messages, defaults.cwd), Agent Note titles and relocated links, KV Cache effect sections, JSDoc on evolved exports fix(gui): second-rebase reconciliation to 509db0cb3 — restore api panel exports the baseline suites consume, knip workspace entries for jsdom lane and apps/web smokes, hoist result narrowing, align testing.md to the narrowed web-ui exclusion fix(test): vitest-scoped tsconfig maps bare imports for tsx specs — with GUI manifests now pointing at lib, an unmapped importer loaded a second copy of the web-runtime singletons fix(gui): typecheck + lint clean over the tool-card batch — brand callIds and object-form turn/end reason in the view spec, narrow fixture arg stringification, wrap long v8-ignore comments docs(gui): export JSDoc for tool-card surfaces + testing-note pairing header docs: rfc for web testing feat: add tools to host-runtime fix(gui): dispatch agent/error via agentEvents in host-runtime spec — mounted invariants plugin rejects raw ctx.emit without the scope carrier fix(gui): restore GUI knip workspaces + scripts/mjs entries and regenerate lockfile after master rebase fix(gui): post-rebase gate repairs — drop context-node envelope (master unwrapped injected content envelopes), regen event matrix, condense testing.md web-ui exclusion within budget fix(session): browser-safe deep-equal in surface — node:util import broke the vite bundle ci(gates): frontend vite build joins pre-push — node: imports in the client closure pass tsc but break the browser bundle test(tui): drop the checkout-dependent process.cwd() harness default — a long worktree path pushes the footer token counters past the 88-column fake terminal test(gui): jsdom behavior E2E — conversation main path over fixture runtime, reconnect banner lifecycle test(gui): jsdom RPC panel behavior — ledger rows, expand, pairing, pause/clear, follow-pause, payload truncation test(gui): jsdom tier-2 — InputBar guards, reasoning fold, JSON blocks, message variants, theme, create-then-select; act-harden banner case test(gui): jsdom tier-3 — ConversationView states/paging/force-bottom, ToolCallCard arms, PendingCard, list rows test(gui): jsdom tails — view-card variants, LogRow directions, registry hygiene, badge overflow, hook ops, mount glue test(gui): jsdom tails round 2 — call-ref blocks, resume follow, view precedence, failed create, empty-diff arm test(gui): jsdom final arms — anchor compensation, follow-off, interval ticks, view halves, node-over-running precedence test(gui): web-ui joins the per-file 100% coverage gate Annotation-only src changes plus the config swap. The web-ui exclusion is replaced by a single index.tsx entry (stale byte-identical duplicate of mount.tsx, nothing imports it; same entry-glue treatment as bin.ts) and the coverage include gains .tsx. v8-ignore sites (each with its reason inline): - ConversationView 3x ref-null guards; InputBar disabled-click guard - ToolCallCard both-null arms + windowless-custom argsRaw arm - LogRow css-module key fallbacks (start/stop block); RpcLogBody 3x ref-null guards - web-runtime drift from the tool-card batch: fixture presenter catch/str typo-guards, dense-array guards (fold-adapter reset, session rebuild, fixture backscan), live view-present arm (fixture replays are text-only; view vocabulary is covered by the history samples) test(gui): close the PR #443 host-side coverage gaps — apiproxy client abort arms, api-proxy cold/view paths, webserver drain - apiproxy fetch/client.ts: 3 new cases (pre-aborted signal short-circuits before transport + string reason mapping, non-Error/string reason falls to the default AbortError message, signal-less doFetch passthrough) - runtime/api-proxy.ts: one v8-ignore (summarizeCold cwd arm — list() filters cwd-less legacy metas) + api-proxy-cold.spec.ts (cold list merge: mtime source, locate-undefined and vanished-log fallbacks, lineage; no-persistence/no-factory resume → internal) + 2 view cases (history views with meta passthrough and orphan/bad-args/presenterless soft-falls, session/disposed open-call cleanup on the mux stream) - webserver/index.ts: /api/big fixture drives both drain-wait legs (full 8MiB readback after drain, mid-chunk disconnect wakes via 'close') feat: app shell fix: rebase conflicts fix: coverage fix(gui): lint clean after rebase — wrap long v8-ignore comments, unconditional v1 detail-block claim chore(gui): remove browser/probe verify scripts from scripts/ The six GUI acceptance/probe scripts (carrier-errors, rpclog-panel, session, session-real, webserver-backpressure, webserver-hardening) leave the repo's scripts/ tree; the three code comments that pointed at them now describe the coverage lane without naming a script path. fix(webserver): guard the request callback — one malformed request must not kill the process The async handle() had no top-level catch, so any throw inside it (a bad %-escape reaching decodeURIComponent, a client dropping mid-body, a response stream erroring) became an unhandled rejection and took the whole process down (audit R1 must-fix). The guard answers 400 when headers are not out yet, destroys the socket when they are, and reports the failure to onError (the package never prints). Spec covers all three legs: %-escape barrage → 400 + server stays alive, non-Error throw wrapped for onError, mid-stream explosion → socket teardown. feat: client AGENTS.md fix: client/AGENTS.md fix: rebase feat(gui): T0 cut 1 — 12 client package skeletons with contract stubs, dshClient declarations, tsdown client preset, theme token sheets feat(gui): T0 cut 2 — pure git mv migration per v3 §11 (connection six, runtime sessions/kernel, ui-conversation chat, ui-primitives markdown family, web shell + e2e) feat(gui): T0 cuts 3+4 — import rewiring to new package names, .legacy demotion of owner-rewrite files, legacy web-runtime/web-ui/apps-web retired to attic feat(gui): connection 对账刀——index.ts 精确导出清单替换 export *,intents.legacy 溶解删除 feat(client/ui-slots): SlotCore real implementation — kind semantics, sync version + microtask-batched notify, onMutate bridge feat(gui): web shell vite alias — retarget to new client packages, shell static surface only feat(gui): host 侧刀属地半——HostWebPluginRegistry(entries 扫描+internal/plugin 去抖重扫+dshClient 校验+exports./client 解析)、GET /plugins/<id>/client.js 分发端点、GET / 与 SPA fallback 注入 __DSH_BOOT__(webPlugins 可选注入,不传行为不变) feat(web-react): add use-sync-external-store dep + local shim typings feat(web-react): bindSnapshotSelector via uSES with-selector shim feat(gui): ui-layout concession-chain solver — pure computeColumns with contract geometry feat(gui): ui-layout LayoutService — four persisted stores, clamped actions, list-driven prune feat(gui): ui-layout AppFrame styles — grid columns, collapse-safe borders, edge drag handles test(gui): 存量 spec 平移——connection 三件+runtime 六件自 attic 捞回改包名路径全绿;api-helpers 按归属拆分(wire 半留 connection、classifier 半随 conversation.ts 入 runtime);boot-intents/preinit/rpc-log 随 intents/rpc-log 退役不迁(记 v3 §3.2 溶解项) feat(client/ui-primitives): StateDot/Button/Pill/Input/Menu atoms, ConnectionBanner de-legacied to pure props, JsonBlock CSS on --dsw tokens feat(web-react): createSnapshotStore engine (rafFlush batch, persist opt-in, dev freeze) + spec feat(gui): ui-layout AppFrame — grid tracks, pointer-capture drag handles with rAF throttle, frame ResizeObserver feat(web-react): useInvoke (external pending store, stable invoke, concurrency count) + spec test(web-react): bind spec — equality bail, custom eq, zero resubscribe, StrictMode, method sources feat(gui): ui-layout index rewiring — real exports, client apply provides ctx.layout and defines three slots feat(web-react): SessionProvider (renderBody deps) + RootBindingProvider + binding contexts + spec feat(gui): web shell AppRoot boot-page styles — self-contained with neutral token fallbacks feat(gui): web shell AppRoot — boot gate over loader status, fail-loud plugin failure list fix(gui): AppRoot gates on explicit settled signal — status-derived readiness races the incrementally filled table feat(client/ui-theme): ThemeService real implementation — registry with built-in light/dark, apply toggles body[data-ds-dark-theme], third-party token overrides as body inline vars feat(web-react): scopedSlots outlet (kind matrix, inject WeakMap caches, per-entry error boundary) + spec feat(gui): web shell module-table seed — pure-library entities for the loader require surface feat(client/i18n): I18nService real implementation — ns×locale registry, stable bind(ns) reference, zh fallback chain, zh/en skeleton dictionaries feat(gui): web shell assembly closure — layout exports via module table, SessionProvider + scopedSlots + RootBindingProvider feat: client/ui-conversation feat: code codedoc build(gui): root bundle green — web shell excluded from the lib workspace (vite app), ui-primitives lib externalizes css side-effect imports (web-ui precedent) gates(gui): verify-cordis-config follows aggregate tsconfig references (root is a shell over host/client programs); module graph regenerated for the twelve client packages chore(gui): retire legacy migration sources — every owner rewrite landed (t0-checklist §7 ledger honored); orphan css of retired components removed gates(gui): knip green groundwork — e2e/tsx entries for the new packages, loader-runtime deps ignored where loading is by specifier string, fake plugin ids un-bare-named, dead test export dropped chore(client): manifest shape batch A — ui-slots/web-react/ui-primitives invariant companions, files whitelist, cordis+invariants peer/dev, tsconfig refs chore(client): manifest shape batch B — connection/runtime/ui-conversation/ui-trajectory files whitelist, cordis peer+dev, explicit invariant lib entries (clientBundle signature) chore(client): manifest shape batch C — i18n/ui-layout/ui-sidebar/ui-theme invariant companions, files whitelist, invariants peer/dev, tsconfig refs chore(client): manifest shape batch D — web shell gains node-half lib entry + invariant companion + uniform files whitelist chore(client): drop verified-unused deps — dsh-tools from runtime/ui-conversation (types ride /presentation), ui-primitives+clsx from ui-layout gates(gui): doc-gate fixes — theme JSDoc prose, three client type-link exemptions, agent-note paths follow the migration, config catalog regenerated gates(gui): type-equiv manifest follows the types.ts extraction, approval JSDoc keeps its link form, persistence catalog regenerated docs(gui): per-constant JSDoc on the contract geometry exports (export-jsdoc gate) test(gates): loader-composition budget covers cold tsx resolution after the program split (was flaking at the default 5s) docs(gui): README substantiation batch 1 — ui-slots/ui-primitives/web-react/connection: Model Experience short form, real deferred-work ledgers, description accuracy pass fix(client): theme/i18n dual-entry split — service classes + cordis merges move to src/client (host catalog scanner no longer misclassifies client services), node halves keep types + empty apply; catalogs regenerated docs(gui): README substantiation batch 2 — runtime/ui-layout/ui-sidebar/ui-conversation: Model Experience short form, package-owned deferred-work ledgers (unload stub, watch approximation, /client value-import rule, global details state, two-state dots, stats duration gap, single-bundle caches) docs(gui): README substantiation batch 3 — ui-trajectory/ui-theme/i18n/web: Model Experience short form, deferred-work ledgers (placeholder charter, no theme toggle owner, empty locale dictionaries, one-shot rendering); both README gates green test(scripts): purity spec adopts clientBundle two-arg signature (explicit libEntry, no default) gates(gui): knip green — declaration-merge dep ignored, fake plugin id assembled at runtime, invariants dep de-duplicated to peer+dev, stale apps/web section dropped feat(gui): 门禁波次 host 三包 invariant 形状——apiproxy explained-empty 伴生(wire 契约层零事件面)、webserver 真关系伴生(manifest 行必解析出 clientPath,防 __DSH_BOOT__ 广告 404 bundle;apps/cli 发布 webPlugins 键供审计)、runtime 补 files 白名单;三包 exports/files/peer+dev/tsconfig refs 齐 fw-react 形状;constraints+invariants 双 gate 零违规 build(client): ui-layout/ui-sidebar tsdown configs adopt the explicit two-arg clientBundle signature (orphaned follow-up of the manifest shape batch) refactor(gui): shell boot becomes a library face — bootWebShell(el) exported for the apps/web entry; main.ts retired refactor(gui): exports 纪律刀1——ui-theme/i18n node index 收敛为只空 apply(Translate/LocaleDict/ThemeTokens 类型下沉 src/client/),ui-conversation 的 I18nService import 改 /client 子路径 build(typecheck): converge to root host aggregate + tsconfig.client.json — delete tsconfig.host.json, verify-cordis-config seeds both aggregates feat(gui): apps/web restored as the vite application — thin main over bootWebShell; dsh-client-web becomes a plain lib (index exports shell surface, vite files and e2e moved out) chore(gates): knip.json rewritten on the master base — same semantics, minimal diff (formatting churn dropped) docs(gui): 时效清扫②——testing.md 删 web-ui 覆盖豁免残句;web-styling.md 加 token 换代头注(--dsw-* 现行、工程约束条款仍有效并注明收编处) docs(gui): 时效清扫③——四对 GUI Agent Note 加路径更新头注(web-runtime/web-ui/dsh-frontend→现行 12 包结构;设计结论存续声明;双语对同步) docs(gui): 时效清扫③b——四对 note 头注的 i18n 配对哈希重录 build(typecheck): minimal-diff tsconfig shape — drop root files entry (purity spec + preset move to client program), compress comments, drop redundant util/home root ref feat(gui): apps/web restoration follow-through — dsh-frontend package name, cli dist resolve, root build:web filter, tsdown exemption dropped, vitest web lane + knip + client aggregate retargeted, e2e paths rebased refactor(gui): exports 纪律刀2——connection wire 六件 git mv 进 src/client/(wire 即该 dshClient 插件的 client 半),node index=只空 apply,/client 半边整面导出(v3 §3.2 清单原样),包内 tests 改 src/client 直取 refactor(gui): exports 纪律刀3——runtime 实现整体下沉 src/client/(sessions/slots/loader;契约类型与 cordis merge 随迁 client/index),node index=只空 apply;./loader exports 指 client/loader;全消费面(web 壳/ui-sidebar/ui-trajectory/tests)bare→/client 机械跟改;vitest.e2e 换 tsconfig.vitest paths(root tsconfig 排除 client 会把 /client import 掉到 exports 的浏览器 dist bundle) refactor(gui): exports 纪律刀3 补遗——ui-layout 三处 bare runtime import 改 /client(刀3 消费面机械跟改漏提交件;跨属地机械一行×3 报备 ui-shell) test(gui): drop the getSessionManager singleton case — the init/get pair is a dead legacy-boot surface with zero live consumers (SessionsService constructs and holds the manager under the plugin architecture); source removal tracked with rt-core refactor(gui): 删 manager.ts 尾部 initSessionManager/getSessionManager 单例对——旧 boot 直连遗物,插件化下 SessionsService 构造持有 manager,全仓零活消费者(convo-b 测试清扫对表,其测试用例已先行退役 7e2c51898);头注释同步去单例措辞 code refactor
2026-07-19 21:17:57 +08:00
pkg_host_webserver --> pkg_invariants
pkg_storage --> pkg_invariants
pkg_subprocess --> pkg_invariants
pkg_typert_generator --> pkg_invariants
pkg_typert_registry --> pkg_invariants
2026-07-05 01:25:58 +08:00
pkg_llm --> pkg_brand
pkg_llm --> pkg_invariants
2026-07-25 10:38:42 +08:00
pkg_llm --> pkg_timeout
pkg_client_connection --> pkg_host_webserver
pkg_client_connection --> pkg_invariants
pkg_client_hmr --> pkg_client_modules
pkg_client_hmr --> pkg_host_webserver
pkg_client_hmr --> pkg_invariants
pkg_client_locale --> pkg_client_runtime
pkg_client_locale --> pkg_client_ui_primitives
pkg_client_locale --> pkg_client_ui_slots
pkg_client_locale --> pkg_invariants
pkg_client_test_runtime --> pkg_client_runtime
pkg_client_test_runtime --> pkg_client_ui_slots
pkg_client_test_runtime --> pkg_client_web_react
pkg_client_test_runtime --> pkg_host_apiproxy
pkg_client_test_runtime --> pkg_invariants
pkg_client_ui_settings --> pkg_client_runtime
pkg_client_ui_settings --> pkg_client_ui_primitives
pkg_client_ui_settings --> pkg_client_ui_slots
pkg_client_ui_settings --> pkg_invariants
pkg_client_ui_trajectory --> pkg_client_runtime
2026-07-28 10:34:47 +08:00
pkg_client_ui_trajectory --> pkg_client_ui_primitives
pkg_client_ui_trajectory --> pkg_invariants
pkg_credentials --> pkg_brand
pkg_credentials --> pkg_invariants
pkg_helper --> pkg_brand
pkg_helper --> pkg_invariants
feat(subprocess): migrate lsp-local, subagent-acp, and the env scrubs onto the seam Review direction (tianyicui, PR #660): in a stacked PR, change all other process-running places to use the new service. - lsp-local: LspConnection spawns through ctx.subprocess (piped protocol streams + a no-spill collected stderr tail); its private process-tree helpers (POSIX group signalling, Windows taskkill, liveness polling) are deleted in favor of the seam's handle verbs, and its buildChildEnv now rides scrubbedParentEnv (LSP children also stop inheriting stale DSH_*). The plugin injects 'subprocess'; compositions/tests mount dsh-subprocess-local. - subagent-acp: the ACP child spawns through the seam (piped ndjson streams, inherited stderr); spawn failure surfaces through done-rejection into the same startup race; disposal is handle.dispose with the plugin's configured graces. dsh-subagent-subprocess is DELETED — its dispose ladder and scrub are the seam's, and the isolated-config-dir helper had no consumer. - mcp-client, pty-local, sdk-helper: adopt scrubbedParentEnv as the one scrub definition (their spawns stay put by ownership: the MCP SDK and node-pty own those calls; the SDK wizard runs outside any composition). - Coverage: per-file 100% over every touched src file, with each v8 ignore carrying a platform or contract reason; new suites cover stdio dispositions, the dispose ladder tiers, injected-win32 tree semantics, waitForExit, settled-kill/terminate no-ops, and spawn-failure disposal. - Docs: consumer-migration Agent Note (en; zh follows in this PR), seam note updated in place, subprocess.md rewritten for the reshaped vocabulary (type-equiv re-registered), READMEs and SERVICE_ROLES updated, taskkill added to knip ignoreBinaries.
2026-07-26 15:27:59 +08:00
pkg_helper --> pkg_subprocess
pkg_telemetry --> pkg_brand
pkg_telemetry --> pkg_invariants
pkg_telemetry --> pkg_paths
pkg_settings --> pkg_brand
pkg_settings --> pkg_invariants
pkg_storage_domain --> pkg_invariants
pkg_storage_domain --> pkg_storage
pkg_storage_json --> pkg_invariants
pkg_storage_json --> pkg_storage
pkg_storage_sqlite --> pkg_invariants
pkg_storage_sqlite --> pkg_storage
pkg_subprocess_local --> pkg_invariants
pkg_subprocess_local --> pkg_subprocess
pkg_typert_loader --> pkg_invariants
pkg_typert_loader --> pkg_typert_registry
pkg_llm_deepseek --> pkg_credentials
pkg_llm_deepseek --> pkg_invariants
2026-07-05 01:25:58 +08:00
pkg_llm_deepseek --> pkg_llm
pkg_llm_deepseek --> pkg_settings
pkg_llm_deepseek --> pkg_timeout
pkg_llm_pi_ai --> pkg_credentials
pkg_llm_pi_ai --> pkg_invariants
2026-07-05 01:25:58 +08:00
pkg_llm_pi_ai --> pkg_llm
pkg_llm_pi_ai --> pkg_settings
pkg_llm_pi_ai --> pkg_timeout
2026-07-05 01:25:58 +08:00
pkg_session --> pkg_brand
pkg_session --> pkg_invariants
2026-07-05 01:25:58 +08:00
pkg_session --> pkg_llm
pkg_session --> pkg_scope
pkg_system_prompt --> pkg_invariants
2026-07-05 01:25:58 +08:00
pkg_system_prompt --> pkg_llm
pkg_system_prompt --> pkg_scope
pkg_web --> pkg_invariants
2026-07-05 01:25:58 +08:00
pkg_web --> pkg_llm
pkg_client_ui_models --> pkg_client_connection
pkg_client_ui_models --> pkg_client_runtime
pkg_client_ui_models --> pkg_client_schema_form
pkg_client_ui_models --> pkg_client_ui_primitives
pkg_client_ui_models --> pkg_client_ui_slots
pkg_client_ui_models --> pkg_client_web_react
pkg_client_ui_models --> pkg_invariants
pkg_client_ui_question --> pkg_client_locale
pkg_client_ui_question --> pkg_invariants
pkg_client_ui_settings_general --> pkg_client_connection
pkg_client_ui_settings_general --> pkg_client_locale
pkg_client_ui_settings_general --> pkg_client_runtime
pkg_client_ui_settings_general --> pkg_client_ui_primitives
pkg_client_ui_settings_general --> pkg_client_ui_settings
pkg_client_ui_settings_general --> pkg_client_ui_slots
pkg_client_ui_settings_general --> pkg_client_web_react
pkg_client_ui_settings_general --> pkg_invariants
pkg_client_ui_sidebar --> pkg_client_locale
pkg_client_ui_sidebar --> pkg_client_runtime
pkg_client_ui_sidebar --> pkg_client_ui_primitives
pkg_client_ui_sidebar --> pkg_client_ui_slots
pkg_client_ui_sidebar --> pkg_invariants
2026-07-29 21:40:45 +08:00
pkg_client_ui_slash --> pkg_client_locale
pkg_client_ui_slash --> pkg_client_runtime
pkg_client_ui_slash --> pkg_client_ui_primitives
pkg_client_ui_slash --> pkg_client_ui_slots
pkg_client_ui_slash --> pkg_invariants
pkg_client_ui_theme --> pkg_client_locale
pkg_client_ui_theme --> pkg_client_runtime
pkg_client_ui_theme --> pkg_client_ui_primitives
pkg_client_ui_theme --> pkg_client_ui_slots
pkg_client_ui_theme --> pkg_invariants
2026-07-30 15:56:41 +08:00
pkg_client_ui_workspace --> pkg_client_locale
pkg_client_ui_workspace --> pkg_client_runtime
pkg_client_ui_workspace --> pkg_client_ui_primitives
pkg_client_ui_workspace --> pkg_client_ui_slots
pkg_client_ui_workspace --> pkg_invariants
pkg_credentials_local --> pkg_atomic_write
pkg_credentials_local --> pkg_credentials
pkg_credentials_local --> pkg_invariants
pkg_credentials_local --> pkg_paths
pkg_lsp --> pkg_brand
pkg_lsp --> pkg_invariants
pkg_lsp --> pkg_llm
pkg_sandbox --> pkg_invariants
pkg_sandbox --> pkg_llm
pkg_settings_local --> pkg_atomic_write
pkg_settings_local --> pkg_invariants
pkg_settings_local --> pkg_paths
pkg_settings_local --> pkg_settings
2026-07-05 01:25:58 +08:00
pkg_agent --> pkg_brand
pkg_agent --> pkg_invariants
2026-07-05 01:25:58 +08:00
pkg_agent --> pkg_llm
pkg_agent --> pkg_scope
2026-07-05 01:25:58 +08:00
pkg_agent --> pkg_session
pkg_agent --> pkg_system_prompt
pkg_bash --> pkg_invariants
pkg_bash --> pkg_sandbox
pkg_bash --> pkg_subprocess
feat(sandbox): cross-family file sandbox — one policy home, sandboxed fs provider, fs escalation parity Extend SandboxMode enforcement from bash to the filesystem tools, the sandbox RFC's deferred cross-family phase. - dsh-sandbox-policy (new, ctx.sandboxPolicy): the single home for the deployment default mode + workspaceRoot and the per-session override event, renamed bash/sandbox-mode -> sandbox/mode and moved here with its fold/setter. Decouples the bash seam from dsh-session. - dsh-fs-sandbox (new): SandboxedFileSystem extends LocalFileSystem and fences write/edit by the per-call mode (read-only denies, workspace-write contains to the workspace + temp roots via the shared writableRoots, danger passes through); reads pass through. Structured FS_SANDBOX_DENIED; in-lock parent re-canonicalization. A policy fence in trusted code, not a kernel boundary. - dsh-sandbox: the shared escalation kit (writableRoots, the strictly-wider ladder, denial/hint markers, approveEscalation) both tool families use; approveEscalation takes a structural approver so dsh-sandbox gains no approval/agent dependency, and both tools stay duplication-free. - tool-fs: write/edit advertise sandbox_permissions/justification under a confining ctx.fs, map FS_SANDBOX_DENIED to the shared [sandbox: ...] marker, and resolve the same one-approved-wider retry. - examples/acp-agent: composes sandbox-policy + fs-sandbox, drops the gating that disabled the fs stack under confined modes. RFC docs/rfc/implemented/feature/2026-07-14-cross-family-fs-sandbox.md; the old sandbox RFC's In-process/deferred/FAQ sections updated to shipped fact.
2026-07-14 20:05:57 +08:00
pkg_fs --> pkg_brand
pkg_fs --> pkg_invariants
feat(sandbox): cross-family file sandbox — one policy home, sandboxed fs provider, fs escalation parity Extend SandboxMode enforcement from bash to the filesystem tools, the sandbox RFC's deferred cross-family phase. - dsh-sandbox-policy (new, ctx.sandboxPolicy): the single home for the deployment default mode + workspaceRoot and the per-session override event, renamed bash/sandbox-mode -> sandbox/mode and moved here with its fold/setter. Decouples the bash seam from dsh-session. - dsh-fs-sandbox (new): SandboxedFileSystem extends LocalFileSystem and fences write/edit by the per-call mode (read-only denies, workspace-write contains to the workspace + temp roots via the shared writableRoots, danger passes through); reads pass through. Structured FS_SANDBOX_DENIED; in-lock parent re-canonicalization. A policy fence in trusted code, not a kernel boundary. - dsh-sandbox: the shared escalation kit (writableRoots, the strictly-wider ladder, denial/hint markers, approveEscalation) both tool families use; approveEscalation takes a structural approver so dsh-sandbox gains no approval/agent dependency, and both tools stay duplication-free. - tool-fs: write/edit advertise sandbox_permissions/justification under a confining ctx.fs, map FS_SANDBOX_DENIED to the shared [sandbox: ...] marker, and resolve the same one-approved-wider retry. - examples/acp-agent: composes sandbox-policy + fs-sandbox, drops the gating that disabled the fs stack under confined modes. RFC docs/rfc/implemented/feature/2026-07-14-cross-family-fs-sandbox.md; the old sandbox RFC's In-process/deferred/FAQ sections updated to shipped fact.
2026-07-14 20:05:57 +08:00
pkg_fs --> pkg_llm
pkg_fs --> pkg_sandbox
pkg_compact --> pkg_invariants
2026-07-05 01:25:58 +08:00
pkg_compact --> pkg_llm
pkg_compact --> pkg_session
pkg_compact_tool_result_prune --> pkg_invariants
pkg_compact_tool_result_prune --> pkg_llm
pkg_compact_tool_result_prune --> pkg_session
pkg_web_fetch_local --> pkg_invariants
pkg_web_fetch_local --> pkg_timeout
2026-07-05 01:25:58 +08:00
pkg_web_fetch_local --> pkg_web
pkg_web_search_exa --> pkg_invariants
2026-07-05 01:25:58 +08:00
pkg_web_search_exa --> pkg_web
pkg_web_search_perplexity --> pkg_invariants
2026-07-05 01:25:58 +08:00
pkg_web_search_perplexity --> pkg_web
pkg_spill --> pkg_brand
pkg_spill --> pkg_invariants
pkg_spill --> pkg_llm
pkg_spill --> pkg_session
pkg_session_persistence --> pkg_brand
pkg_session_persistence --> pkg_invariants
2026-07-05 01:25:58 +08:00
pkg_session_persistence --> pkg_session
pkg_llm_replay --> pkg_invariants
2026-07-05 01:25:58 +08:00
pkg_llm_replay --> pkg_llm
pkg_llm_replay --> pkg_session
pkg_app_boot --> pkg_invariants
pkg_app_boot --> pkg_paths
pkg_app_boot --> pkg_system_prompt
pkg_client_ui_layout --> pkg_client_runtime
pkg_client_ui_layout --> pkg_client_ui_slots
pkg_client_ui_layout --> pkg_client_ui_theme
pkg_client_ui_layout --> pkg_invariants
2026-07-29 21:40:45 +08:00
pkg_client_ui_skill --> pkg_client_connection
pkg_client_ui_skill --> pkg_client_runtime
pkg_client_ui_skill --> pkg_client_ui_slash
pkg_client_ui_skill --> pkg_client_ui_slots
pkg_client_ui_skill --> pkg_invariants
pkg_client_ui_subagent --> pkg_client_runtime
pkg_client_ui_subagent --> pkg_client_ui_slash
pkg_client_ui_subagent --> pkg_client_ui_slots
pkg_client_ui_subagent --> pkg_invariants
pkg_code_runtime_worker --> pkg_code_runtime
pkg_code_runtime_worker --> pkg_invariants
pkg_code_runtime_worker --> pkg_session
pkg_code_runtime_worker --> pkg_timeout
2026-07-30 15:56:41 +08:00
pkg_host_directory_picker_browse --> pkg_client_locale
pkg_host_directory_picker_browse --> pkg_client_runtime
pkg_host_directory_picker_browse --> pkg_client_ui_primitives
pkg_host_directory_picker_browse --> pkg_client_ui_slots
pkg_host_directory_picker_browse --> pkg_client_ui_workspace
pkg_host_directory_picker_browse --> pkg_invariants
pkg_host_directory_picker_native --> pkg_client_runtime
pkg_host_directory_picker_native --> pkg_client_ui_slots
pkg_host_directory_picker_native --> pkg_client_ui_workspace
pkg_host_directory_picker_native --> pkg_invariants
pkg_lsp_local --> pkg_brand
pkg_lsp_local --> pkg_invariants
pkg_lsp_local --> pkg_llm
pkg_lsp_local --> pkg_lsp
feat(subprocess): migrate lsp-local, subagent-acp, and the env scrubs onto the seam Review direction (tianyicui, PR #660): in a stacked PR, change all other process-running places to use the new service. - lsp-local: LspConnection spawns through ctx.subprocess (piped protocol streams + a no-spill collected stderr tail); its private process-tree helpers (POSIX group signalling, Windows taskkill, liveness polling) are deleted in favor of the seam's handle verbs, and its buildChildEnv now rides scrubbedParentEnv (LSP children also stop inheriting stale DSH_*). The plugin injects 'subprocess'; compositions/tests mount dsh-subprocess-local. - subagent-acp: the ACP child spawns through the seam (piped ndjson streams, inherited stderr); spawn failure surfaces through done-rejection into the same startup race; disposal is handle.dispose with the plugin's configured graces. dsh-subagent-subprocess is DELETED — its dispose ladder and scrub are the seam's, and the isolated-config-dir helper had no consumer. - mcp-client, pty-local, sdk-helper: adopt scrubbedParentEnv as the one scrub definition (their spawns stay put by ownership: the MCP SDK and node-pty own those calls; the SDK wizard runs outside any composition). - Coverage: per-file 100% over every touched src file, with each v8 ignore carrying a platform or contract reason; new suites cover stdio dispositions, the dispose ladder tiers, injected-win32 tree semantics, waitForExit, settled-kill/terminate no-ops, and spawn-failure disposal. - Docs: consumer-migration Agent Note (en; zh follows in this PR), seam note updated in place, subprocess.md rewritten for the reshaped vocabulary (type-equiv re-registered), READMEs and SERVICE_ROLES updated, taskkill added to knip ignoreBinaries.
2026-07-26 15:27:59 +08:00
pkg_lsp_local --> pkg_subprocess
pkg_lsp_local --> pkg_timeout
pkg_sandbox_local --> pkg_invariants
pkg_sandbox_local --> pkg_llm
pkg_sandbox_local --> pkg_sandbox
pkg_session_projection --> pkg_invariants
pkg_session_projection --> pkg_session
pkg_llm_retry --> pkg_agent
pkg_llm_retry --> pkg_invariants
pkg_llm_retry --> pkg_llm
pkg_llm_retry --> pkg_session
pkg_llm_retry --> pkg_timeout
feat(web): show durable token usage and context occupancy in the stats line The chat stats line took its token totals from the loaded conversation nodes, so paging changed them and compaction erased the billing behind replaced content. It also had no way to show context occupancy: the numerator and capacity never reached the browser. Both now come from token-meter session projections read through the standard useProjection seat. Window nodes keep supplying turn and step counts plus LLM and tool wall times, which are correctly window-scoped facts about what is on screen; accounting no longer comes from there. `tokenUsage` supplies billing and cache hit. `contextPressure` supplies occupancy, pairing the newest provider-reported prompt size with the newest capacity recorded by `request/context`. Deployments without token-meter drop the token groups; a route whose adapter advertises no capacity drops the occupancy group rather than rendering a placeholder. Occupancy is deliberately approximate: the numerator and capacity are independent last-wins fields, not one atomic request observation, so switching models pairs a fresh capacity with the prior route's pressure until the next request reports usage. It is a user-facing reference figure that nothing in the harness makes decisions from, and it matches how the TUI status line has always computed occupancy. The Agent Note and token-meter README state this as a decision, including why the atomic alternative was implemented and rejected, so it is not re-litigated as a defect. Snapshot delta is one added `Context N% of 128K` segment across eight web goldens; the preceding commit absorbed master's pre-existing golden drift.
2026-07-30 14:48:19 +08:00
pkg_token_meter --> pkg_invariants
pkg_token_meter --> pkg_llm
pkg_token_meter --> pkg_session
pkg_token_meter --> pkg_session_projection
pkg_goal --> pkg_agent
pkg_goal --> pkg_brand
pkg_goal --> pkg_invariants
pkg_goal --> pkg_llm
pkg_goal --> pkg_scope
pkg_goal --> pkg_session
pkg_goal --> pkg_session_projection
pkg_bash_local --> pkg_bash
pkg_bash_local --> pkg_invariants
pkg_bash_local --> pkg_subprocess
pkg_bash_local --> pkg_timeout
feat(sandbox): cross-family file sandbox — one policy home, sandboxed fs provider, fs escalation parity Extend SandboxMode enforcement from bash to the filesystem tools, the sandbox RFC's deferred cross-family phase. - dsh-sandbox-policy (new, ctx.sandboxPolicy): the single home for the deployment default mode + workspaceRoot and the per-session override event, renamed bash/sandbox-mode -> sandbox/mode and moved here with its fold/setter. Decouples the bash seam from dsh-session. - dsh-fs-sandbox (new): SandboxedFileSystem extends LocalFileSystem and fences write/edit by the per-call mode (read-only denies, workspace-write contains to the workspace + temp roots via the shared writableRoots, danger passes through); reads pass through. Structured FS_SANDBOX_DENIED; in-lock parent re-canonicalization. A policy fence in trusted code, not a kernel boundary. - dsh-sandbox: the shared escalation kit (writableRoots, the strictly-wider ladder, denial/hint markers, approveEscalation) both tool families use; approveEscalation takes a structural approver so dsh-sandbox gains no approval/agent dependency, and both tools stay duplication-free. - tool-fs: write/edit advertise sandbox_permissions/justification under a confining ctx.fs, map FS_SANDBOX_DENIED to the shared [sandbox: ...] marker, and resolve the same one-approved-wider retry. - examples/acp-agent: composes sandbox-policy + fs-sandbox, drops the gating that disabled the fs stack under confined modes. RFC docs/rfc/implemented/feature/2026-07-14-cross-family-fs-sandbox.md; the old sandbox RFC's In-process/deferred/FAQ sections updated to shipped fact.
2026-07-14 20:05:57 +08:00
pkg_fs_local --> pkg_fs
pkg_fs_local --> pkg_invariants
feat(sandbox): cross-family file sandbox — one policy home, sandboxed fs provider, fs escalation parity Extend SandboxMode enforcement from bash to the filesystem tools, the sandbox RFC's deferred cross-family phase. - dsh-sandbox-policy (new, ctx.sandboxPolicy): the single home for the deployment default mode + workspaceRoot and the per-session override event, renamed bash/sandbox-mode -> sandbox/mode and moved here with its fold/setter. Decouples the bash seam from dsh-session. - dsh-fs-sandbox (new): SandboxedFileSystem extends LocalFileSystem and fences write/edit by the per-call mode (read-only denies, workspace-write contains to the workspace + temp roots via the shared writableRoots, danger passes through); reads pass through. Structured FS_SANDBOX_DENIED; in-lock parent re-canonicalization. A policy fence in trusted code, not a kernel boundary. - dsh-sandbox: the shared escalation kit (writableRoots, the strictly-wider ladder, denial/hint markers, approveEscalation) both tool families use; approveEscalation takes a structural approver so dsh-sandbox gains no approval/agent dependency, and both tools stay duplication-free. - tool-fs: write/edit advertise sandbox_permissions/justification under a confining ctx.fs, map FS_SANDBOX_DENIED to the shared [sandbox: ...] marker, and resolve the same one-approved-wider retry. - examples/acp-agent: composes sandbox-policy + fs-sandbox, drops the gating that disabled the fs stack under confined modes. RFC docs/rfc/implemented/feature/2026-07-14-cross-family-fs-sandbox.md; the old sandbox RFC's In-process/deferred/FAQ sections updated to shipped fact.
2026-07-14 20:05:57 +08:00
pkg_fs_policy --> pkg_fs
pkg_fs_policy --> pkg_invariants
feat(sandbox): cross-family file sandbox — one policy home, sandboxed fs provider, fs escalation parity Extend SandboxMode enforcement from bash to the filesystem tools, the sandbox RFC's deferred cross-family phase. - dsh-sandbox-policy (new, ctx.sandboxPolicy): the single home for the deployment default mode + workspaceRoot and the per-session override event, renamed bash/sandbox-mode -> sandbox/mode and moved here with its fold/setter. Decouples the bash seam from dsh-session. - dsh-fs-sandbox (new): SandboxedFileSystem extends LocalFileSystem and fences write/edit by the per-call mode (read-only denies, workspace-write contains to the workspace + temp roots via the shared writableRoots, danger passes through); reads pass through. Structured FS_SANDBOX_DENIED; in-lock parent re-canonicalization. A policy fence in trusted code, not a kernel boundary. - dsh-sandbox: the shared escalation kit (writableRoots, the strictly-wider ladder, denial/hint markers, approveEscalation) both tool families use; approveEscalation takes a structural approver so dsh-sandbox gains no approval/agent dependency, and both tools stay duplication-free. - tool-fs: write/edit advertise sandbox_permissions/justification under a confining ctx.fs, map FS_SANDBOX_DENIED to the shared [sandbox: ...] marker, and resolve the same one-approved-wider retry. - examples/acp-agent: composes sandbox-policy + fs-sandbox, drops the gating that disabled the fs stack under confined modes. RFC docs/rfc/implemented/feature/2026-07-14-cross-family-fs-sandbox.md; the old sandbox RFC's In-process/deferred/FAQ sections updated to shipped fact.
2026-07-14 20:05:57 +08:00
pkg_skill_local --> pkg_fs
pkg_skill_local --> pkg_invariants
pkg_skill_local --> pkg_paths
feat(sandbox): cross-family file sandbox — one policy home, sandboxed fs provider, fs escalation parity Extend SandboxMode enforcement from bash to the filesystem tools, the sandbox RFC's deferred cross-family phase. - dsh-sandbox-policy (new, ctx.sandboxPolicy): the single home for the deployment default mode + workspaceRoot and the per-session override event, renamed bash/sandbox-mode -> sandbox/mode and moved here with its fold/setter. Decouples the bash seam from dsh-session. - dsh-fs-sandbox (new): SandboxedFileSystem extends LocalFileSystem and fences write/edit by the per-call mode (read-only denies, workspace-write contains to the workspace + temp roots via the shared writableRoots, danger passes through); reads pass through. Structured FS_SANDBOX_DENIED; in-lock parent re-canonicalization. A policy fence in trusted code, not a kernel boundary. - dsh-sandbox: the shared escalation kit (writableRoots, the strictly-wider ladder, denial/hint markers, approveEscalation) both tool families use; approveEscalation takes a structural approver so dsh-sandbox gains no approval/agent dependency, and both tools stay duplication-free. - tool-fs: write/edit advertise sandbox_permissions/justification under a confining ctx.fs, map FS_SANDBOX_DENIED to the shared [sandbox: ...] marker, and resolve the same one-approved-wider retry. - examples/acp-agent: composes sandbox-policy + fs-sandbox, drops the gating that disabled the fs stack under confined modes. RFC docs/rfc/implemented/feature/2026-07-14-cross-family-fs-sandbox.md; the old sandbox RFC's In-process/deferred/FAQ sections updated to shipped fact.
2026-07-14 20:05:57 +08:00
pkg_skill_local --> pkg_skill
pkg_web_search_deepseek --> pkg_agent
pkg_web_search_deepseek --> pkg_credentials
pkg_web_search_deepseek --> pkg_invariants
pkg_web_search_deepseek --> pkg_session
pkg_web_search_deepseek --> pkg_web
pkg_spill_local --> pkg_invariants
pkg_spill_local --> pkg_spill
pkg_hook_protocol --> pkg_bash
pkg_hook_protocol --> pkg_invariants
pkg_hook_protocol --> pkg_session
pkg_session_persistence_jsonl --> pkg_invariants
2026-07-05 01:25:58 +08:00
pkg_session_persistence_jsonl --> pkg_session
pkg_session_persistence_jsonl --> pkg_session_persistence
pkg_session_persistence_sqlite --> pkg_invariants
2026-07-05 01:25:58 +08:00
pkg_session_persistence_sqlite --> pkg_session
pkg_session_persistence_sqlite --> pkg_session_persistence
pkg_session_title --> pkg_brand
pkg_session_title --> pkg_invariants
pkg_session_title --> pkg_llm
pkg_session_title --> pkg_session
pkg_session_title --> pkg_session_projection
2026-07-19 22:11:59 +08:00
pkg_commands --> pkg_agent
pkg_commands --> pkg_brand
pkg_commands --> pkg_invariants
2026-07-19 22:11:59 +08:00
pkg_commands --> pkg_scope
pkg_commands --> pkg_session
pkg_user_approval --> pkg_agent
pkg_user_approval --> pkg_brand
pkg_user_approval --> pkg_invariants
pkg_user_approval --> pkg_llm
pkg_user_approval --> pkg_scope
pkg_user_approval --> pkg_session
pkg_user_approval --> pkg_system_prompt
pkg_user_interaction --> pkg_agent
pkg_user_interaction --> pkg_invariants
pkg_user_interaction --> pkg_llm
2026-07-14 16:04:34 +08:00
pkg_time_context --> pkg_agent
pkg_time_context --> pkg_invariants
pkg_time_context --> pkg_session
pkg_tmux_context --> pkg_agent
pkg_tmux_context --> pkg_bash
pkg_tmux_context --> pkg_invariants
pkg_tmux_context --> pkg_session
2026-07-30 15:56:41 +08:00
pkg_host_directory_picker_auto --> pkg_host_directory_picker_browse
pkg_host_directory_picker_auto --> pkg_host_directory_picker_native
pkg_host_directory_picker_auto --> pkg_host_webserver
pkg_host_directory_picker_auto --> pkg_invariants
2026-07-21 16:01:00 +08:00
pkg_pty --> pkg_agent
pkg_pty --> pkg_brand
pkg_pty --> pkg_invariants
pkg_sandbox_policy --> pkg_agent
pkg_sandbox_policy --> pkg_invariants
pkg_sandbox_policy --> pkg_sandbox
pkg_sandbox_policy --> pkg_session
pkg_sandbox_policy --> pkg_system_prompt
pkg_scripts --> pkg_app_boot
pkg_scripts --> pkg_invariants
pkg_session_projection_cache --> pkg_invariants
pkg_session_projection_cache --> pkg_session
pkg_session_projection_cache --> pkg_session_persistence
pkg_session_projection_cache --> pkg_session_projection
pkg_session_projection_cache --> pkg_storage_domain
pkg_tasks --> pkg_agent
pkg_tasks --> pkg_brand
pkg_tasks --> pkg_invariants
pkg_tasks --> pkg_session
pkg_session_telemetry --> pkg_agent
pkg_session_telemetry --> pkg_invariants
pkg_session_telemetry --> pkg_session
Merge remote-tracking branch 'origin/master' into worktree-dynamic-workflows Beyond the mechanical conflicts (provider capability lines vs master's new inheritsParentContext field; generated catalogs regenerated rather than hand-merged; knip/lockfile), three master-side reworks required semantic adaptation of this branch: - The persona rework removed AgentOptions.systemPrompt, which was the structured-output instruction's channel. The instruction now rides the SAME final-request enforcement listener that injects the schema'd tool: appended per request to final.system (per-request wire state, not agent prompt state). Tests assert the wire request (adapter.requests) instead of child.options; the bare-direct-dispatch test pins the no-system arm. - Tool guidance moved out of deployment prompts into per-tool prompt sections; the examples' workflow paragraph became a tool:<toolName> section contributed by dsh-tool-workflow (explicit-ask-only policy), and both example personas resolve to master's minimal identity+behavior form. tool-workflow gains inject: systemPrompt (+ peer dep, tsconfig ref); the export-shape guard updated. - The uniform-RFC-format gate: the dynamic-workflows RFC restructured to the implemented/ skeleton (bare Status line; Proposal -> Decision; What-was-rejected -> Alternatives considered; new Consequences), and the overall-run-timeout deferral is now recorded in the RFC's Deferred list. The doc-graphs atlas classification gains the workflows seam (workflow-vm implementation, tool-workflow consumer). Master's harness-identity section made "empty assembled prompt" states unreachable through the loop, so the instruction-append is a plain undefined-ternary and the structured tests assert append-not-replace. All snapshot goldens (including workflow-run) replay unchanged. Full local CI-equivalent gate sequence green on the merged tree.
2026-07-06 03:14:07 +08:00
pkg_workflow --> pkg_agent
pkg_workflow --> pkg_brand
pkg_workflow --> pkg_invariants
Merge remote-tracking branch 'origin/master' into worktree-dynamic-workflows Beyond the mechanical conflicts (provider capability lines vs master's new inheritsParentContext field; generated catalogs regenerated rather than hand-merged; knip/lockfile), three master-side reworks required semantic adaptation of this branch: - The persona rework removed AgentOptions.systemPrompt, which was the structured-output instruction's channel. The instruction now rides the SAME final-request enforcement listener that injects the schema'd tool: appended per request to final.system (per-request wire state, not agent prompt state). Tests assert the wire request (adapter.requests) instead of child.options; the bare-direct-dispatch test pins the no-system arm. - Tool guidance moved out of deployment prompts into per-tool prompt sections; the examples' workflow paragraph became a tool:<toolName> section contributed by dsh-tool-workflow (explicit-ask-only policy), and both example personas resolve to master's minimal identity+behavior form. tool-workflow gains inject: systemPrompt (+ peer dep, tsconfig ref); the export-shape guard updated. - The uniform-RFC-format gate: the dynamic-workflows RFC restructured to the implemented/ skeleton (bare Status line; Proposal -> Decision; What-was-rejected -> Alternatives considered; new Consequences), and the overall-run-timeout deferral is now recorded in the RFC's Deferred list. The doc-graphs atlas classification gains the workflows seam (workflow-vm implementation, tool-workflow consumer). Master's harness-identity section made "empty assembled prompt" states unreachable through the loop, so the instruction-append is a plain undefined-ternary and the structured tests assert append-not-replace. All snapshot goldens (including workflow-run) replay unchanged. Full local CI-equivalent gate sequence green on the merged tree.
2026-07-06 03:14:07 +08:00
pkg_workflow --> pkg_llm
pkg_workflow --> pkg_session
pkg_workspace --> pkg_brand
pkg_workspace --> pkg_invariants
pkg_workspace --> pkg_session
pkg_workspace --> pkg_session_persistence
pkg_workspace --> pkg_storage
pkg_workspace --> pkg_storage_domain
pkg_tools --> pkg_agent
pkg_tools --> pkg_code_runtime
pkg_tools --> pkg_invariants
pkg_tools --> pkg_llm
pkg_tools --> pkg_scope
pkg_tools --> pkg_session
pkg_tools --> pkg_system_prompt
pkg_tools --> pkg_user_approval
2026-07-19 23:55:33 +08:00
pkg_command_goal --> pkg_commands
pkg_command_goal --> pkg_goal
pkg_command_goal --> pkg_invariants
pkg_goal_session --> pkg_agent
pkg_goal_session --> pkg_goal
pkg_goal_session --> pkg_invariants
pkg_goal_session --> pkg_llm
pkg_goal_session --> pkg_session
pkg_bash_sandbox --> pkg_bash
pkg_bash_sandbox --> pkg_bash_local
pkg_bash_sandbox --> pkg_invariants
pkg_bash_sandbox --> pkg_sandbox
feat(sandbox): cross-family file sandbox — one policy home, sandboxed fs provider, fs escalation parity Extend SandboxMode enforcement from bash to the filesystem tools, the sandbox RFC's deferred cross-family phase. - dsh-sandbox-policy (new, ctx.sandboxPolicy): the single home for the deployment default mode + workspaceRoot and the per-session override event, renamed bash/sandbox-mode -> sandbox/mode and moved here with its fold/setter. Decouples the bash seam from dsh-session. - dsh-fs-sandbox (new): SandboxedFileSystem extends LocalFileSystem and fences write/edit by the per-call mode (read-only denies, workspace-write contains to the workspace + temp roots via the shared writableRoots, danger passes through); reads pass through. Structured FS_SANDBOX_DENIED; in-lock parent re-canonicalization. A policy fence in trusted code, not a kernel boundary. - dsh-sandbox: the shared escalation kit (writableRoots, the strictly-wider ladder, denial/hint markers, approveEscalation) both tool families use; approveEscalation takes a structural approver so dsh-sandbox gains no approval/agent dependency, and both tools stay duplication-free. - tool-fs: write/edit advertise sandbox_permissions/justification under a confining ctx.fs, map FS_SANDBOX_DENIED to the shared [sandbox: ...] marker, and resolve the same one-approved-wider retry. - examples/acp-agent: composes sandbox-policy + fs-sandbox, drops the gating that disabled the fs stack under confined modes. RFC docs/rfc/implemented/feature/2026-07-14-cross-family-fs-sandbox.md; the old sandbox RFC's In-process/deferred/FAQ sections updated to shipped fact.
2026-07-14 20:05:57 +08:00
pkg_bash_sandbox --> pkg_sandbox_policy
pkg_fs_sandbox --> pkg_fs
pkg_fs_sandbox --> pkg_fs_local
pkg_fs_sandbox --> pkg_invariants
feat(sandbox): cross-family file sandbox — one policy home, sandboxed fs provider, fs escalation parity Extend SandboxMode enforcement from bash to the filesystem tools, the sandbox RFC's deferred cross-family phase. - dsh-sandbox-policy (new, ctx.sandboxPolicy): the single home for the deployment default mode + workspaceRoot and the per-session override event, renamed bash/sandbox-mode -> sandbox/mode and moved here with its fold/setter. Decouples the bash seam from dsh-session. - dsh-fs-sandbox (new): SandboxedFileSystem extends LocalFileSystem and fences write/edit by the per-call mode (read-only denies, workspace-write contains to the workspace + temp roots via the shared writableRoots, danger passes through); reads pass through. Structured FS_SANDBOX_DENIED; in-lock parent re-canonicalization. A policy fence in trusted code, not a kernel boundary. - dsh-sandbox: the shared escalation kit (writableRoots, the strictly-wider ladder, denial/hint markers, approveEscalation) both tool families use; approveEscalation takes a structural approver so dsh-sandbox gains no approval/agent dependency, and both tools stay duplication-free. - tool-fs: write/edit advertise sandbox_permissions/justification under a confining ctx.fs, map FS_SANDBOX_DENIED to the shared [sandbox: ...] marker, and resolve the same one-approved-wider retry. - examples/acp-agent: composes sandbox-policy + fs-sandbox, drops the gating that disabled the fs stack under confined modes. RFC docs/rfc/implemented/feature/2026-07-14-cross-family-fs-sandbox.md; the old sandbox RFC's In-process/deferred/FAQ sections updated to shipped fact.
2026-07-14 20:05:57 +08:00
pkg_fs_sandbox --> pkg_sandbox
pkg_fs_sandbox --> pkg_sandbox_policy
pkg_command_compact --> pkg_commands
pkg_command_compact --> pkg_compact
pkg_command_compact --> pkg_invariants
feat(web): show durable token usage and context occupancy in the stats line The chat stats line took its token totals from the loaded conversation nodes, so paging changed them and compaction erased the billing behind replaced content. It also had no way to show context occupancy: the numerator and capacity never reached the browser. Both now come from token-meter session projections read through the standard useProjection seat. Window nodes keep supplying turn and step counts plus LLM and tool wall times, which are correctly window-scoped facts about what is on screen; accounting no longer comes from there. `tokenUsage` supplies billing and cache hit. `contextPressure` supplies occupancy, pairing the newest provider-reported prompt size with the newest capacity recorded by `request/context`. Deployments without token-meter drop the token groups; a route whose adapter advertises no capacity drops the occupancy group rather than rendering a placeholder. Occupancy is deliberately approximate: the numerator and capacity are independent last-wins fields, not one atomic request observation, so switching models pairs a fresh capacity with the prior route's pressure until the next request reports usage. It is a user-facing reference figure that nothing in the harness makes decisions from, and it matches how the TUI status line has always computed occupancy. The Agent Note and token-meter README state this as a decision, including why the atomic alternative was implemented and rejected, so it is not re-litigated as a defect. Snapshot delta is one added `Context N% of 128K` segment across eight web goldens; the preceding commit absorbed master's pre-existing golden drift.
2026-07-30 14:48:19 +08:00
pkg_compact_basic --> pkg_agent
pkg_compact_basic --> pkg_compact
pkg_compact_basic --> pkg_compact_tool_result_prune
pkg_compact_basic --> pkg_invariants
pkg_compact_basic --> pkg_llm
pkg_compact_basic --> pkg_session
pkg_compact_basic --> pkg_token_meter
pkg_session_query --> pkg_brand
pkg_session_query --> pkg_invariants
pkg_session_query --> pkg_llm
pkg_session_query --> pkg_session
pkg_session_query --> pkg_session_persistence
pkg_session_query --> pkg_session_title
pkg_session_title_llm --> pkg_invariants
pkg_session_title_llm --> pkg_llm
pkg_session_title_llm --> pkg_session
pkg_session_title_llm --> pkg_session_title
pkg_session_title_llm --> pkg_timeout
pkg_acp --> pkg_agent
pkg_acp --> pkg_invariants
pkg_acp --> pkg_session
pkg_acp --> pkg_user_approval
feat(permission): user-facing permission presets — one Permissions select over the two knobs A preset names a bundle of the two mechanism knobs — request = workspace-write + ask, yolo = danger-full-access + never — so the editor shows ONE 'Permissions' select where the sandbox-mode and approval-policy tiers stay orthogonal capabilities (the Codex /approvals shape: presets over two dials). ctx.permission (dsh-permission) owns the config-defined table, validates the default preset's bundle against the composed knob defaults at load (fails loud), and writes a switch THROUGH: one log-only permission/preset event (the audit fact reverse-mapping cannot recover — the planned 'agent' preset shares request's knob values and differs only in composed policy) plus each knob event via its own setter, deduped — a net-zero switch appends nothing. Every knob consumer keeps reading its own fold, untouched. The current preset DERIVES from the effective knob values — the fold breaks bundle ties, a knob state outside the table is the reserved 'custom' value (a state, not an error: shown while it holds, switchable FROM, never a target), and defaultPreset disappears (zero-event state reverse-maps from the composition defaults). The ACP bridge drops the two per-knob selects for the one preset select (advertised only when ctx.permission is composed); pending/anchor/no-op semantics carry over unchanged, with the no-op echo acknowledged before vocabulary validation so a client re-pushing a derived 'custom' current never errors. The sandbox variant example composes the service with a workspace-write default; the permission-switching, escalation-approved and escalation-rejected scenarios are re-recorded under it (escalations now target an outside-workspace /tmp path under danger-full-access, self-cleaning) and config-options is re-authored on the single-select wire.
2026-07-12 21:03:41 +08:00
pkg_permission --> pkg_bash
pkg_permission --> pkg_commands
pkg_permission --> pkg_invariants
feat(permission): user-facing permission presets — one Permissions select over the two knobs A preset names a bundle of the two mechanism knobs — request = workspace-write + ask, yolo = danger-full-access + never — so the editor shows ONE 'Permissions' select where the sandbox-mode and approval-policy tiers stay orthogonal capabilities (the Codex /approvals shape: presets over two dials). ctx.permission (dsh-permission) owns the config-defined table, validates the default preset's bundle against the composed knob defaults at load (fails loud), and writes a switch THROUGH: one log-only permission/preset event (the audit fact reverse-mapping cannot recover — the planned 'agent' preset shares request's knob values and differs only in composed policy) plus each knob event via its own setter, deduped — a net-zero switch appends nothing. Every knob consumer keeps reading its own fold, untouched. The current preset DERIVES from the effective knob values — the fold breaks bundle ties, a knob state outside the table is the reserved 'custom' value (a state, not an error: shown while it holds, switchable FROM, never a target), and defaultPreset disappears (zero-event state reverse-maps from the composition defaults). The ACP bridge drops the two per-knob selects for the one preset select (advertised only when ctx.permission is composed); pending/anchor/no-op semantics carry over unchanged, with the no-op echo acknowledged before vocabulary validation so a client re-pushing a derived 'custom' current never errors. The sandbox variant example composes the service with a workspace-write default; the permission-switching, escalation-approved and escalation-rejected scenarios are re-recorded under it (escalations now target an outside-workspace /tmp path under danger-full-access, self-cleaning) and config-options is re-authored on the single-select wire.
2026-07-12 21:03:41 +08:00
pkg_permission --> pkg_sandbox
feat(sandbox): cross-family file sandbox — one policy home, sandboxed fs provider, fs escalation parity Extend SandboxMode enforcement from bash to the filesystem tools, the sandbox RFC's deferred cross-family phase. - dsh-sandbox-policy (new, ctx.sandboxPolicy): the single home for the deployment default mode + workspaceRoot and the per-session override event, renamed bash/sandbox-mode -> sandbox/mode and moved here with its fold/setter. Decouples the bash seam from dsh-session. - dsh-fs-sandbox (new): SandboxedFileSystem extends LocalFileSystem and fences write/edit by the per-call mode (read-only denies, workspace-write contains to the workspace + temp roots via the shared writableRoots, danger passes through); reads pass through. Structured FS_SANDBOX_DENIED; in-lock parent re-canonicalization. A policy fence in trusted code, not a kernel boundary. - dsh-sandbox: the shared escalation kit (writableRoots, the strictly-wider ladder, denial/hint markers, approveEscalation) both tool families use; approveEscalation takes a structural approver so dsh-sandbox gains no approval/agent dependency, and both tools stay duplication-free. - tool-fs: write/edit advertise sandbox_permissions/justification under a confining ctx.fs, map FS_SANDBOX_DENIED to the shared [sandbox: ...] marker, and resolve the same one-approved-wider retry. - examples/acp-agent: composes sandbox-policy + fs-sandbox, drops the gating that disabled the fs stack under confined modes. RFC docs/rfc/implemented/feature/2026-07-14-cross-family-fs-sandbox.md; the old sandbox RFC's In-process/deferred/FAQ sections updated to shipped fact.
2026-07-14 20:05:57 +08:00
pkg_permission --> pkg_sandbox_policy
feat(permission): user-facing permission presets — one Permissions select over the two knobs A preset names a bundle of the two mechanism knobs — request = workspace-write + ask, yolo = danger-full-access + never — so the editor shows ONE 'Permissions' select where the sandbox-mode and approval-policy tiers stay orthogonal capabilities (the Codex /approvals shape: presets over two dials). ctx.permission (dsh-permission) owns the config-defined table, validates the default preset's bundle against the composed knob defaults at load (fails loud), and writes a switch THROUGH: one log-only permission/preset event (the audit fact reverse-mapping cannot recover — the planned 'agent' preset shares request's knob values and differs only in composed policy) plus each knob event via its own setter, deduped — a net-zero switch appends nothing. Every knob consumer keeps reading its own fold, untouched. The current preset DERIVES from the effective knob values — the fold breaks bundle ties, a knob state outside the table is the reserved 'custom' value (a state, not an error: shown while it holds, switchable FROM, never a target), and defaultPreset disappears (zero-event state reverse-maps from the composition defaults). The ACP bridge drops the two per-knob selects for the one preset select (advertised only when ctx.permission is composed); pending/anchor/no-op semantics carry over unchanged, with the no-op echo acknowledged before vocabulary validation so a client re-pushing a derived 'custom' current never errors. The sandbox variant example composes the service with a workspace-write default; the permission-switching, escalation-approved and escalation-rejected scenarios are re-recorded under it (escalations now target an outside-workspace /tmp path under danger-full-access, self-cleaning) and config-options is re-authored on the single-select wire.
2026-07-12 21:03:41 +08:00
pkg_permission --> pkg_session
pkg_permission --> pkg_session_projection
pkg_permission --> pkg_settings
feat(permission): user-facing permission presets — one Permissions select over the two knobs A preset names a bundle of the two mechanism knobs — request = workspace-write + ask, yolo = danger-full-access + never — so the editor shows ONE 'Permissions' select where the sandbox-mode and approval-policy tiers stay orthogonal capabilities (the Codex /approvals shape: presets over two dials). ctx.permission (dsh-permission) owns the config-defined table, validates the default preset's bundle against the composed knob defaults at load (fails loud), and writes a switch THROUGH: one log-only permission/preset event (the audit fact reverse-mapping cannot recover — the planned 'agent' preset shares request's knob values and differs only in composed policy) plus each knob event via its own setter, deduped — a net-zero switch appends nothing. Every knob consumer keeps reading its own fold, untouched. The current preset DERIVES from the effective knob values — the fold breaks bundle ties, a knob state outside the table is the reserved 'custom' value (a state, not an error: shown while it holds, switchable FROM, never a target), and defaultPreset disappears (zero-event state reverse-maps from the composition defaults). The ACP bridge drops the two per-knob selects for the one preset select (advertised only when ctx.permission is composed); pending/anchor/no-op semantics carry over unchanged, with the no-op echo acknowledged before vocabulary validation so a client re-pushing a derived 'custom' current never errors. The sandbox variant example composes the service with a workspace-write default; the permission-switching, escalation-approved and escalation-rejected scenarios are re-recorded under it (escalations now target an outside-workspace /tmp path under danger-full-access, self-cleaning) and config-options is re-authored on the single-select wire.
2026-07-12 21:03:41 +08:00
pkg_permission --> pkg_user_approval
feat(web): show durable token usage and context occupancy in the stats line The chat stats line took its token totals from the loaded conversation nodes, so paging changed them and compaction erased the billing behind replaced content. It also had no way to show context occupancy: the numerator and capacity never reached the browser. Both now come from token-meter session projections read through the standard useProjection seat. Window nodes keep supplying turn and step counts plus LLM and tool wall times, which are correctly window-scoped facts about what is on screen; accounting no longer comes from there. `tokenUsage` supplies billing and cache hit. `contextPressure` supplies occupancy, pairing the newest provider-reported prompt size with the newest capacity recorded by `request/context`. Deployments without token-meter drop the token groups; a route whose adapter advertises no capacity drops the occupancy group rather than rendering a placeholder. Occupancy is deliberately approximate: the numerator and capacity are independent last-wins fields, not one atomic request observation, so switching models pairs a fresh capacity with the prior route's pressure until the next request reports usage. It is a user-facing reference figure that nothing in the harness makes decisions from, and it matches how the TUI status line has always computed occupancy. The Agent Note and token-meter README state this as a decision, including why the atomic alternative was implemented and rejected, so it is not re-litigated as a defect. Snapshot delta is one added `Context N% of 128K` segment across eight web goldens; the preceding commit absorbed master's pre-existing golden drift.
2026-07-30 14:48:19 +08:00
pkg_client_ui_conversation --> pkg_client_locale
pkg_client_ui_conversation --> pkg_client_runtime
pkg_client_ui_conversation --> pkg_client_ui_primitives
pkg_client_ui_conversation --> pkg_client_ui_slash
pkg_client_ui_conversation --> pkg_client_ui_slots
pkg_client_ui_conversation --> pkg_invariants
pkg_client_ui_conversation --> pkg_token_meter
2026-07-22 22:37:20 +08:00
pkg_pty_local --> pkg_agent
pkg_pty_local --> pkg_invariants
2026-07-21 16:01:00 +08:00
pkg_pty_local --> pkg_pty
pkg_pty_local --> pkg_sandbox
pkg_pty_local --> pkg_sandbox_policy
2026-07-22 22:37:20 +08:00
pkg_pty_local --> pkg_session
feat(subprocess): migrate lsp-local, subagent-acp, and the env scrubs onto the seam Review direction (tianyicui, PR #660): in a stacked PR, change all other process-running places to use the new service. - lsp-local: LspConnection spawns through ctx.subprocess (piped protocol streams + a no-spill collected stderr tail); its private process-tree helpers (POSIX group signalling, Windows taskkill, liveness polling) are deleted in favor of the seam's handle verbs, and its buildChildEnv now rides scrubbedParentEnv (LSP children also stop inheriting stale DSH_*). The plugin injects 'subprocess'; compositions/tests mount dsh-subprocess-local. - subagent-acp: the ACP child spawns through the seam (piped ndjson streams, inherited stderr); spawn failure surfaces through done-rejection into the same startup race; disposal is handle.dispose with the plugin's configured graces. dsh-subagent-subprocess is DELETED — its dispose ladder and scrub are the seam's, and the isolated-config-dir helper had no consumer. - mcp-client, pty-local, sdk-helper: adopt scrubbedParentEnv as the one scrub definition (their spawns stay put by ownership: the MCP SDK and node-pty own those calls; the SDK wizard runs outside any composition). - Coverage: per-file 100% over every touched src file, with each v8 ignore carrying a platform or contract reason; new suites cover stdio dispositions, the dispose ladder tiers, injected-win32 tree semantics, waitForExit, settled-kill/terminate no-ops, and spawn-failure disposal. - Docs: consumer-migration Agent Note (en; zh follows in this PR), seam note updated in place, subprocess.md rewritten for the reshaped vocabulary (type-equiv re-registered), READMEs and SERVICE_ROLES updated, taskkill added to knip ignoreBinaries.
2026-07-26 15:27:59 +08:00
pkg_pty_local --> pkg_subprocess
pkg_tasks_local --> pkg_agent
pkg_tasks_local --> pkg_invariants
pkg_tasks_local --> pkg_tasks
pkg_tasks_local --> pkg_timeout
2026-07-31 14:33:39 +08:00
pkg_session_telemetry_otel --> pkg_brand
pkg_session_telemetry_otel --> pkg_invariants
pkg_session_telemetry_otel --> pkg_llm
2026-07-31 14:33:39 +08:00
pkg_session_telemetry_otel --> pkg_paths
pkg_session_telemetry_otel --> pkg_session
pkg_session_telemetry_otel --> pkg_session_telemetry
2026-07-05 01:25:58 +08:00
pkg_agent_loop --> pkg_agent
pkg_agent_loop --> pkg_invariants
2026-07-05 01:25:58 +08:00
pkg_agent_loop --> pkg_llm
pkg_agent_loop --> pkg_scope
2026-07-05 01:25:58 +08:00
pkg_agent_loop --> pkg_session
pkg_agent_loop --> pkg_session_persistence
pkg_agent_loop --> pkg_system_prompt
pkg_agent_loop --> pkg_tools
pkg_tool_goal --> pkg_agent
pkg_tool_goal --> pkg_goal
pkg_tool_goal --> pkg_invariants
pkg_tool_goal --> pkg_llm
pkg_tool_goal --> pkg_session
pkg_tool_goal --> pkg_system_prompt
pkg_tool_goal --> pkg_tools
2026-07-05 01:25:58 +08:00
pkg_tool_bash --> pkg_agent
pkg_tool_bash --> pkg_bash
pkg_tool_bash --> pkg_invariants
2026-07-05 01:25:58 +08:00
pkg_tool_bash --> pkg_llm
pkg_tool_bash --> pkg_paths
pkg_tool_bash --> pkg_sandbox
feat(sandbox): cross-family file sandbox — one policy home, sandboxed fs provider, fs escalation parity Extend SandboxMode enforcement from bash to the filesystem tools, the sandbox RFC's deferred cross-family phase. - dsh-sandbox-policy (new, ctx.sandboxPolicy): the single home for the deployment default mode + workspaceRoot and the per-session override event, renamed bash/sandbox-mode -> sandbox/mode and moved here with its fold/setter. Decouples the bash seam from dsh-session. - dsh-fs-sandbox (new): SandboxedFileSystem extends LocalFileSystem and fences write/edit by the per-call mode (read-only denies, workspace-write contains to the workspace + temp roots via the shared writableRoots, danger passes through); reads pass through. Structured FS_SANDBOX_DENIED; in-lock parent re-canonicalization. A policy fence in trusted code, not a kernel boundary. - dsh-sandbox: the shared escalation kit (writableRoots, the strictly-wider ladder, denial/hint markers, approveEscalation) both tool families use; approveEscalation takes a structural approver so dsh-sandbox gains no approval/agent dependency, and both tools stay duplication-free. - tool-fs: write/edit advertise sandbox_permissions/justification under a confining ctx.fs, map FS_SANDBOX_DENIED to the shared [sandbox: ...] marker, and resolve the same one-approved-wider retry. - examples/acp-agent: composes sandbox-policy + fs-sandbox, drops the gating that disabled the fs stack under confined modes. RFC docs/rfc/implemented/feature/2026-07-14-cross-family-fs-sandbox.md; the old sandbox RFC's In-process/deferred/FAQ sections updated to shipped fact.
2026-07-14 20:05:57 +08:00
pkg_tool_bash --> pkg_sandbox_policy
pkg_tool_bash --> pkg_session_persistence
pkg_tool_bash --> pkg_system_prompt
pkg_tool_bash --> pkg_tasks
2026-07-05 01:25:58 +08:00
pkg_tool_bash --> pkg_tools
pkg_tool_bash --> pkg_user_approval
2026-07-05 01:25:58 +08:00
pkg_tool_fs --> pkg_fs
pkg_tool_fs --> pkg_invariants
2026-07-05 01:25:58 +08:00
pkg_tool_fs --> pkg_llm
feat(sandbox): cross-family file sandbox — one policy home, sandboxed fs provider, fs escalation parity Extend SandboxMode enforcement from bash to the filesystem tools, the sandbox RFC's deferred cross-family phase. - dsh-sandbox-policy (new, ctx.sandboxPolicy): the single home for the deployment default mode + workspaceRoot and the per-session override event, renamed bash/sandbox-mode -> sandbox/mode and moved here with its fold/setter. Decouples the bash seam from dsh-session. - dsh-fs-sandbox (new): SandboxedFileSystem extends LocalFileSystem and fences write/edit by the per-call mode (read-only denies, workspace-write contains to the workspace + temp roots via the shared writableRoots, danger passes through); reads pass through. Structured FS_SANDBOX_DENIED; in-lock parent re-canonicalization. A policy fence in trusted code, not a kernel boundary. - dsh-sandbox: the shared escalation kit (writableRoots, the strictly-wider ladder, denial/hint markers, approveEscalation) both tool families use; approveEscalation takes a structural approver so dsh-sandbox gains no approval/agent dependency, and both tools stay duplication-free. - tool-fs: write/edit advertise sandbox_permissions/justification under a confining ctx.fs, map FS_SANDBOX_DENIED to the shared [sandbox: ...] marker, and resolve the same one-approved-wider retry. - examples/acp-agent: composes sandbox-policy + fs-sandbox, drops the gating that disabled the fs stack under confined modes. RFC docs/rfc/implemented/feature/2026-07-14-cross-family-fs-sandbox.md; the old sandbox RFC's In-process/deferred/FAQ sections updated to shipped fact.
2026-07-14 20:05:57 +08:00
pkg_tool_fs --> pkg_sandbox
pkg_tool_fs --> pkg_sandbox_policy
2026-07-05 01:25:58 +08:00
pkg_tool_fs --> pkg_session
pkg_tool_fs --> pkg_system_prompt
pkg_tool_fs --> pkg_tools
feat(sandbox): cross-family file sandbox — one policy home, sandboxed fs provider, fs escalation parity Extend SandboxMode enforcement from bash to the filesystem tools, the sandbox RFC's deferred cross-family phase. - dsh-sandbox-policy (new, ctx.sandboxPolicy): the single home for the deployment default mode + workspaceRoot and the per-session override event, renamed bash/sandbox-mode -> sandbox/mode and moved here with its fold/setter. Decouples the bash seam from dsh-session. - dsh-fs-sandbox (new): SandboxedFileSystem extends LocalFileSystem and fences write/edit by the per-call mode (read-only denies, workspace-write contains to the workspace + temp roots via the shared writableRoots, danger passes through); reads pass through. Structured FS_SANDBOX_DENIED; in-lock parent re-canonicalization. A policy fence in trusted code, not a kernel boundary. - dsh-sandbox: the shared escalation kit (writableRoots, the strictly-wider ladder, denial/hint markers, approveEscalation) both tool families use; approveEscalation takes a structural approver so dsh-sandbox gains no approval/agent dependency, and both tools stay duplication-free. - tool-fs: write/edit advertise sandbox_permissions/justification under a confining ctx.fs, map FS_SANDBOX_DENIED to the shared [sandbox: ...] marker, and resolve the same one-approved-wider retry. - examples/acp-agent: composes sandbox-policy + fs-sandbox, drops the gating that disabled the fs stack under confined modes. RFC docs/rfc/implemented/feature/2026-07-14-cross-family-fs-sandbox.md; the old sandbox RFC's In-process/deferred/FAQ sections updated to shipped fact.
2026-07-14 20:05:57 +08:00
pkg_tool_fs --> pkg_user_approval
pkg_tool_fs_search --> pkg_bash
pkg_tool_fs_search --> pkg_invariants
pkg_tool_fs_search --> pkg_llm
pkg_tool_fs_search --> pkg_retention
pkg_tool_fs_search --> pkg_session
pkg_tool_fs_search --> pkg_spill
pkg_tool_fs_search --> pkg_system_prompt
pkg_tool_fs_search --> pkg_tools
pkg_tool_str_replace_editor --> pkg_fs
pkg_tool_str_replace_editor --> pkg_invariants
pkg_tool_str_replace_editor --> pkg_sandbox
pkg_tool_str_replace_editor --> pkg_sandbox_policy
pkg_tool_str_replace_editor --> pkg_tools
pkg_tool_skill --> pkg_agent
pkg_tool_skill --> pkg_invariants
pkg_tool_skill --> pkg_llm
pkg_tool_skill --> pkg_skill
pkg_tool_skill --> pkg_tools
2026-07-05 01:25:58 +08:00
pkg_subagent --> pkg_agent
pkg_subagent --> pkg_brand
pkg_subagent --> pkg_invariants
2026-07-05 01:25:58 +08:00
pkg_subagent --> pkg_llm
pkg_subagent --> pkg_scope
pkg_subagent --> pkg_session
2026-07-05 01:25:58 +08:00
pkg_subagent --> pkg_tools
pkg_tool_web --> pkg_invariants
2026-07-05 01:25:58 +08:00
pkg_tool_web --> pkg_llm
pkg_tool_web --> pkg_system_prompt
pkg_tool_web --> pkg_tools
pkg_tool_web --> pkg_web
pkg_spill_policy --> pkg_invariants
pkg_spill_policy --> pkg_llm
pkg_spill_policy --> pkg_retention
pkg_spill_policy --> pkg_session
pkg_spill_policy --> pkg_spill
pkg_spill_policy --> pkg_tools
pkg_timeout_policy --> pkg_invariants
pkg_timeout_policy --> pkg_llm
pkg_timeout_policy --> pkg_timeout
pkg_timeout_policy --> pkg_tools
2026-07-05 01:25:58 +08:00
pkg_tool_todo --> pkg_agent
pkg_tool_todo --> pkg_invariants
2026-07-05 01:25:58 +08:00
pkg_tool_todo --> pkg_session
pkg_tool_todo --> pkg_session_projection
2026-07-05 01:25:58 +08:00
pkg_tool_todo --> pkg_tools
pkg_plan_mode --> pkg_agent
pkg_plan_mode --> pkg_commands
pkg_plan_mode --> pkg_invariants
pkg_plan_mode --> pkg_llm
pkg_plan_mode --> pkg_session
pkg_plan_mode --> pkg_session_projection
pkg_plan_mode --> pkg_system_prompt
pkg_plan_mode --> pkg_tools
pkg_plan_mode --> pkg_user_interaction
pkg_tool_cordis --> pkg_invariants
Merge origin/master: scope-aware fusion of the tools/execute seam, session-prefix, and tool-cordis Master brought 50 commits (the tool-cordis group, dsh-code-runtime + worker, the tools/execute around-dispatch seam + timeout-policy, repeat-tool-guard, agent/session-prefix, the ui reorganization). Beyond the ten textual conflicts, the merge reconciles master's new seams with this branch's scoped-registration world: - tools/execute (new waterfall around core dispatch): dispatched with the SAME exec.agent carrier as the pre/post waterfalls — an agent.ctx wrapper times/retries only its own agent's calls — and its base thunk resolves the tool through the caller's visible view (get(exec.name, exec.agent)), so a scoped/shadowed tool dispatches and a restricted-away global stays UNKNOWN_TOOL. Declared this: Scoped<ToolRegistry> with the scope-filtered doc sentence; invariants table + verify-scoped-dispatch pin it (21 events). - agent/session-prefix (new waterfall, once per loop instance): composed via the fused agentEvents dispatcher (scope-filtered like every agent-subject event), declared this: Scoped<Agent>, table-pinned. agent/pre-step keeps master's new sessionPrefix parameter with this branch's Scoped this. - timeout-policy reads the budget through the caller's visible view (get(exec.name, exec.agent)): a scoped tool's own timeoutMs governs its calls; a global name-twin's budget is never misapplied to a shadowing per-agent variant. - tool-cordis: cordis_inspect's tools section lists the CALLING agent's view (its description promises "what you can call"); the sandbox tool façade's reads resolve through the mount's own scope, mirroring where its register lands writes; sandboxRegisterTool's return type carries the exact-disposer union honestly. dsh-scope declared as peer+dev with the project reference. - doc-sync chain unions master's verify-cordis-api with this branch's verify-scoped-dispatch; the generated catalogs, event matrix (the zero-dispatcher guard passes over master's new events), module graph, and the cordis api-catalog are regenerated on the merged surface. Full gate sequence green on the merged tree: typecheck, lint, per-file 100% coverage (2668 tests), snapshots (38), doc-sync, module graph, build, hygiene, demo smoke.
2026-07-09 23:24:42 +08:00
pkg_tool_cordis --> pkg_scope
pkg_tool_cordis --> pkg_tools
2026-07-05 01:25:58 +08:00
pkg_hooks_codex --> pkg_agent
pkg_hooks_codex --> pkg_hook_protocol
pkg_hooks_codex --> pkg_invariants
2026-07-05 01:25:58 +08:00
pkg_hooks_codex --> pkg_llm
pkg_hooks_codex --> pkg_session
pkg_hooks_codex --> pkg_session_persistence
2026-07-05 01:25:58 +08:00
pkg_hooks_codex --> pkg_tools
pkg_session_checkpoint_policy --> pkg_agent
pkg_session_checkpoint_policy --> pkg_invariants
pkg_session_checkpoint_policy --> pkg_llm
pkg_session_checkpoint_policy --> pkg_session
pkg_session_checkpoint_policy --> pkg_session_persistence
pkg_session_checkpoint_policy --> pkg_tools
pkg_session_query_sqlite --> pkg_invariants
pkg_session_query_sqlite --> pkg_session
pkg_session_query_sqlite --> pkg_session_persistence
pkg_session_query_sqlite --> pkg_session_query
pkg_tool_session_query --> pkg_invariants
pkg_tool_session_query --> pkg_llm
pkg_tool_session_query --> pkg_session
pkg_tool_session_query --> pkg_session_query
pkg_tool_session_query --> pkg_system_prompt
pkg_tool_session_query --> pkg_timeout
pkg_tool_session_query --> pkg_tools
pkg_session_title_all_messages_llm --> pkg_invariants
pkg_session_title_all_messages_llm --> pkg_llm
pkg_session_title_all_messages_llm --> pkg_session
pkg_session_title_all_messages_llm --> pkg_session_title
pkg_session_title_all_messages_llm --> pkg_session_title_llm
pkg_session_title_first_message_llm --> pkg_invariants
pkg_session_title_first_message_llm --> pkg_llm
pkg_session_title_first_message_llm --> pkg_session
pkg_session_title_first_message_llm --> pkg_session_title
pkg_session_title_first_message_llm --> pkg_session_title_llm
pkg_agent_loop_testkit --> pkg_agent
pkg_agent_loop_testkit --> pkg_invariants
pkg_agent_loop_testkit --> pkg_llm
pkg_agent_loop_testkit --> pkg_session
pkg_agent_loop_testkit --> pkg_system_prompt
pkg_agent_loop_testkit --> pkg_tools
pkg_tool_ask_user --> pkg_agent
pkg_tool_ask_user --> pkg_invariants
pkg_tool_ask_user --> pkg_tools
pkg_tool_ask_user --> pkg_user_interaction
feat(web): show durable token usage and context occupancy in the stats line The chat stats line took its token totals from the loaded conversation nodes, so paging changed them and compaction erased the billing behind replaced content. It also had no way to show context occupancy: the numerator and capacity never reached the browser. Both now come from token-meter session projections read through the standard useProjection seat. Window nodes keep supplying turn and step counts plus LLM and tool wall times, which are correctly window-scoped facts about what is on screen; accounting no longer comes from there. `tokenUsage` supplies billing and cache hit. `contextPressure` supplies occupancy, pairing the newest provider-reported prompt size with the newest capacity recorded by `request/context`. Deployments without token-meter drop the token groups; a route whose adapter advertises no capacity drops the occupancy group rather than rendering a placeholder. Occupancy is deliberately approximate: the numerator and capacity are independent last-wins fields, not one atomic request observation, so switching models pairs a fresh capacity with the prior route's pressure until the next request reports usage. It is a user-facing reference figure that nothing in the harness makes decisions from, and it matches how the TUI status line has always computed occupancy. The Agent Note and token-meter README state this as a decision, including why the atomic alternative was implemented and rejected, so it is not re-litigated as a defect. Snapshot delta is one added `Context N% of 128K` segment across eight web goldens; the preceding commit absorbed master's pre-existing golden drift.
2026-07-30 14:48:19 +08:00
pkg_client_ui_command --> pkg_client_connection
pkg_client_ui_command --> pkg_client_locale
feat(web): show durable token usage and context occupancy in the stats line The chat stats line took its token totals from the loaded conversation nodes, so paging changed them and compaction erased the billing behind replaced content. It also had no way to show context occupancy: the numerator and capacity never reached the browser. Both now come from token-meter session projections read through the standard useProjection seat. Window nodes keep supplying turn and step counts plus LLM and tool wall times, which are correctly window-scoped facts about what is on screen; accounting no longer comes from there. `tokenUsage` supplies billing and cache hit. `contextPressure` supplies occupancy, pairing the newest provider-reported prompt size with the newest capacity recorded by `request/context`. Deployments without token-meter drop the token groups; a route whose adapter advertises no capacity drops the occupancy group rather than rendering a placeholder. Occupancy is deliberately approximate: the numerator and capacity are independent last-wins fields, not one atomic request observation, so switching models pairs a fresh capacity with the prior route's pressure until the next request reports usage. It is a user-facing reference figure that nothing in the harness makes decisions from, and it matches how the TUI status line has always computed occupancy. The Agent Note and token-meter README state this as a decision, including why the atomic alternative was implemented and rejected, so it is not re-litigated as a defect. Snapshot delta is one added `Context N% of 128K` segment across eight web goldens; the preceding commit absorbed master's pre-existing golden drift.
2026-07-30 14:48:19 +08:00
pkg_client_ui_command --> pkg_client_runtime
pkg_client_ui_command --> pkg_client_ui_conversation
pkg_client_ui_command --> pkg_client_ui_primitives
pkg_client_ui_command --> pkg_client_ui_slash
pkg_client_ui_command --> pkg_client_ui_slots
pkg_client_ui_command --> pkg_invariants
pkg_client_ui_goal --> pkg_client_connection
pkg_client_ui_goal --> pkg_client_locale
feat(web): show durable token usage and context occupancy in the stats line The chat stats line took its token totals from the loaded conversation nodes, so paging changed them and compaction erased the billing behind replaced content. It also had no way to show context occupancy: the numerator and capacity never reached the browser. Both now come from token-meter session projections read through the standard useProjection seat. Window nodes keep supplying turn and step counts plus LLM and tool wall times, which are correctly window-scoped facts about what is on screen; accounting no longer comes from there. `tokenUsage` supplies billing and cache hit. `contextPressure` supplies occupancy, pairing the newest provider-reported prompt size with the newest capacity recorded by `request/context`. Deployments without token-meter drop the token groups; a route whose adapter advertises no capacity drops the occupancy group rather than rendering a placeholder. Occupancy is deliberately approximate: the numerator and capacity are independent last-wins fields, not one atomic request observation, so switching models pairs a fresh capacity with the prior route's pressure until the next request reports usage. It is a user-facing reference figure that nothing in the harness makes decisions from, and it matches how the TUI status line has always computed occupancy. The Agent Note and token-meter README state this as a decision, including why the atomic alternative was implemented and rejected, so it is not re-litigated as a defect. Snapshot delta is one added `Context N% of 128K` segment across eight web goldens; the preceding commit absorbed master's pre-existing golden drift.
2026-07-30 14:48:19 +08:00
pkg_client_ui_goal --> pkg_client_runtime
pkg_client_ui_goal --> pkg_client_ui_conversation
pkg_client_ui_goal --> pkg_client_ui_primitives
pkg_client_ui_goal --> pkg_client_ui_slots
pkg_client_ui_goal --> pkg_goal
pkg_client_ui_goal --> pkg_invariants
pkg_session_reference --> pkg_agent
pkg_session_reference --> pkg_compact
pkg_session_reference --> pkg_invariants
pkg_session_reference --> pkg_llm
pkg_session_reference --> pkg_retention
pkg_session_reference --> pkg_session
pkg_session_reference --> pkg_session_query
pkg_workspace_context --> pkg_agent
pkg_workspace_context --> pkg_fs
pkg_workspace_context --> pkg_invariants
pkg_workspace_context --> pkg_llm
pkg_workspace_context --> pkg_paths
pkg_workspace_context --> pkg_session
pkg_workspace_context --> pkg_tools
pkg_repeat_tool_guard --> pkg_agent
pkg_repeat_tool_guard --> pkg_invariants
pkg_repeat_tool_guard --> pkg_tools
pkg_tool_lsp --> pkg_invariants
pkg_tool_lsp --> pkg_llm
pkg_tool_lsp --> pkg_lsp
pkg_tool_lsp --> pkg_system_prompt
pkg_tool_lsp --> pkg_timeout
pkg_tool_lsp --> pkg_tools
pkg_mcp_client --> pkg_invariants
pkg_mcp_client --> pkg_llm
feat(subprocess): migrate lsp-local, subagent-acp, and the env scrubs onto the seam Review direction (tianyicui, PR #660): in a stacked PR, change all other process-running places to use the new service. - lsp-local: LspConnection spawns through ctx.subprocess (piped protocol streams + a no-spill collected stderr tail); its private process-tree helpers (POSIX group signalling, Windows taskkill, liveness polling) are deleted in favor of the seam's handle verbs, and its buildChildEnv now rides scrubbedParentEnv (LSP children also stop inheriting stale DSH_*). The plugin injects 'subprocess'; compositions/tests mount dsh-subprocess-local. - subagent-acp: the ACP child spawns through the seam (piped ndjson streams, inherited stderr); spawn failure surfaces through done-rejection into the same startup race; disposal is handle.dispose with the plugin's configured graces. dsh-subagent-subprocess is DELETED — its dispose ladder and scrub are the seam's, and the isolated-config-dir helper had no consumer. - mcp-client, pty-local, sdk-helper: adopt scrubbedParentEnv as the one scrub definition (their spawns stay put by ownership: the MCP SDK and node-pty own those calls; the SDK wizard runs outside any composition). - Coverage: per-file 100% over every touched src file, with each v8 ignore carrying a platform or contract reason; new suites cover stdio dispositions, the dispose ladder tiers, injected-win32 tree semantics, waitForExit, settled-kill/terminate no-ops, and spawn-failure disposal. - Docs: consumer-migration Agent Note (en; zh follows in this PR), seam note updated in place, subprocess.md rewritten for the reshaped vocabulary (type-equiv re-registered), READMEs and SERVICE_ROLES updated, taskkill added to knip ignoreBinaries.
2026-07-26 15:27:59 +08:00
pkg_mcp_client --> pkg_subprocess
pkg_mcp_client --> pkg_tools
pkg_tool_bash_persistent --> pkg_agent
pkg_tool_bash_persistent --> pkg_invariants
pkg_tool_bash_persistent --> pkg_pty
pkg_tool_bash_persistent --> pkg_timeout
pkg_tool_bash_persistent --> pkg_tools
2026-07-21 16:01:00 +08:00
pkg_tool_pty --> pkg_agent
pkg_tool_pty --> pkg_invariants
2026-07-21 16:01:00 +08:00
pkg_tool_pty --> pkg_llm
pkg_tool_pty --> pkg_pty
2026-07-22 22:37:20 +08:00
pkg_tool_pty --> pkg_retention
2026-07-21 16:01:00 +08:00
pkg_tool_pty --> pkg_system_prompt
pkg_tool_pty --> pkg_tasks
pkg_tool_pty --> pkg_tools
pkg_tool_tasks --> pkg_agent
pkg_tool_tasks --> pkg_invariants
pkg_tool_tasks --> pkg_llm
2026-07-22 22:37:20 +08:00
pkg_tool_tasks --> pkg_retention
pkg_tool_tasks --> pkg_system_prompt
pkg_tool_tasks --> pkg_tasks
pkg_tool_tasks --> pkg_tools
Merge remote-tracking branch 'origin/master' into worktree-dynamic-workflows Beyond the mechanical conflicts (provider capability lines vs master's new inheritsParentContext field; generated catalogs regenerated rather than hand-merged; knip/lockfile), three master-side reworks required semantic adaptation of this branch: - The persona rework removed AgentOptions.systemPrompt, which was the structured-output instruction's channel. The instruction now rides the SAME final-request enforcement listener that injects the schema'd tool: appended per request to final.system (per-request wire state, not agent prompt state). Tests assert the wire request (adapter.requests) instead of child.options; the bare-direct-dispatch test pins the no-system arm. - Tool guidance moved out of deployment prompts into per-tool prompt sections; the examples' workflow paragraph became a tool:<toolName> section contributed by dsh-tool-workflow (explicit-ask-only policy), and both example personas resolve to master's minimal identity+behavior form. tool-workflow gains inject: systemPrompt (+ peer dep, tsconfig ref); the export-shape guard updated. - The uniform-RFC-format gate: the dynamic-workflows RFC restructured to the implemented/ skeleton (bare Status line; Proposal -> Decision; What-was-rejected -> Alternatives considered; new Consequences), and the overall-run-timeout deferral is now recorded in the RFC's Deferred list. The doc-graphs atlas classification gains the workflows seam (workflow-vm implementation, tool-workflow consumer). Master's harness-identity section made "empty assembled prompt" states unreachable through the loop, so the instruction-append is a plain undefined-ternary and the structured tests assert append-not-replace. All snapshot goldens (including workflow-run) replay unchanged. Full local CI-equivalent gate sequence green on the merged tree.
2026-07-06 03:14:07 +08:00
pkg_tool_workflow --> pkg_agent
pkg_tool_workflow --> pkg_invariants
Merge remote-tracking branch 'origin/master' into worktree-dynamic-workflows Beyond the mechanical conflicts (provider capability lines vs master's new inheritsParentContext field; generated catalogs regenerated rather than hand-merged; knip/lockfile), three master-side reworks required semantic adaptation of this branch: - The persona rework removed AgentOptions.systemPrompt, which was the structured-output instruction's channel. The instruction now rides the SAME final-request enforcement listener that injects the schema'd tool: appended per request to final.system (per-request wire state, not agent prompt state). Tests assert the wire request (adapter.requests) instead of child.options; the bare-direct-dispatch test pins the no-system arm. - Tool guidance moved out of deployment prompts into per-tool prompt sections; the examples' workflow paragraph became a tool:<toolName> section contributed by dsh-tool-workflow (explicit-ask-only policy), and both example personas resolve to master's minimal identity+behavior form. tool-workflow gains inject: systemPrompt (+ peer dep, tsconfig ref); the export-shape guard updated. - The uniform-RFC-format gate: the dynamic-workflows RFC restructured to the implemented/ skeleton (bare Status line; Proposal -> Decision; What-was-rejected -> Alternatives considered; new Consequences), and the overall-run-timeout deferral is now recorded in the RFC's Deferred list. The doc-graphs atlas classification gains the workflows seam (workflow-vm implementation, tool-workflow consumer). Master's harness-identity section made "empty assembled prompt" states unreachable through the loop, so the instruction-append is a plain undefined-ternary and the structured tests assert append-not-replace. All snapshot goldens (including workflow-run) replay unchanged. Full local CI-equivalent gate sequence green on the merged tree.
2026-07-06 03:14:07 +08:00
pkg_tool_workflow --> pkg_llm
pkg_tool_workflow --> pkg_system_prompt
pkg_tool_workflow --> pkg_tools
pkg_tool_workflow --> pkg_workflow
2026-07-05 01:25:58 +08:00
pkg_subagent_acp --> pkg_agent
pkg_subagent_acp --> pkg_invariants
2026-07-05 01:25:58 +08:00
pkg_subagent_acp --> pkg_llm
pkg_subagent_acp --> pkg_session
2026-07-05 01:25:58 +08:00
pkg_subagent_acp --> pkg_subagent
feat(subprocess): migrate lsp-local, subagent-acp, and the env scrubs onto the seam Review direction (tianyicui, PR #660): in a stacked PR, change all other process-running places to use the new service. - lsp-local: LspConnection spawns through ctx.subprocess (piped protocol streams + a no-spill collected stderr tail); its private process-tree helpers (POSIX group signalling, Windows taskkill, liveness polling) are deleted in favor of the seam's handle verbs, and its buildChildEnv now rides scrubbedParentEnv (LSP children also stop inheriting stale DSH_*). The plugin injects 'subprocess'; compositions/tests mount dsh-subprocess-local. - subagent-acp: the ACP child spawns through the seam (piped ndjson streams, inherited stderr); spawn failure surfaces through done-rejection into the same startup race; disposal is handle.dispose with the plugin's configured graces. dsh-subagent-subprocess is DELETED — its dispose ladder and scrub are the seam's, and the isolated-config-dir helper had no consumer. - mcp-client, pty-local, sdk-helper: adopt scrubbedParentEnv as the one scrub definition (their spawns stay put by ownership: the MCP SDK and node-pty own those calls; the SDK wizard runs outside any composition). - Coverage: per-file 100% over every touched src file, with each v8 ignore carrying a platform or contract reason; new suites cover stdio dispositions, the dispose ladder tiers, injected-win32 tree semantics, waitForExit, settled-kill/terminate no-ops, and spawn-failure disposal. - Docs: consumer-migration Agent Note (en; zh follows in this PR), seam note updated in place, subprocess.md rewritten for the reshaped vocabulary (type-equiv re-registered), READMEs and SERVICE_ROLES updated, taskkill added to knip ignoreBinaries.
2026-07-26 15:27:59 +08:00
pkg_subagent_acp --> pkg_subprocess
2026-07-05 01:25:58 +08:00
pkg_subagent_inprocess --> pkg_agent
pkg_subagent_inprocess --> pkg_invariants
2026-07-05 01:25:58 +08:00
pkg_subagent_inprocess --> pkg_llm
pkg_subagent_inprocess --> pkg_sandbox_policy
2026-07-05 01:25:58 +08:00
pkg_subagent_inprocess --> pkg_session
pkg_subagent_inprocess --> pkg_subagent
Structured output on the subagent seam: schema subset, capture runtime, spawn/fork support Carved out of #170 per review feedback — the foundation the workflow tool builds on, now standing alone on master: - dsh-tools: the structured-output JSON Schema subset (StructuredOutputSchema, assertSupportedOutputSchema, validateStructuredValue) — rejects loud outside the enforced subset, listing every violation - dsh-subagent: SubagentStartRequest.outputSchema / SubagentResult.structured become a real capability; the service rejects a schema'd request whose provider lacks it - dsh-subagent-inprocess: the shared structured runtime — one global structured_output capture tool, a prepend final-assembly listener that strips the placeholder for plain agents and swaps in the run's own schema (plus the calling instruction as a trailing section) for structured children, an agent/turn-continuation veto once captured, and the capture/nudge loop in the run driver (structuredNudgeRetries, cancellation honored mid-nudge); lifetime refcounted by backends and live runs - subagent-spawn / subagent-fork flip outputSchema: true One deliberate divergence from the #170 revision: the backends do NOT add 'tools' to their plugin inject. Doing so deferred their apply past the todo plugin, and the delegation tool mirrors provider lifecycle — so the model-visible tool order of every existing prompt changed, invalidating every recorded snapshot fixture. The runtime now gates its capture-tool registration on tools availability itself (sync when live, a scoped inject fiber when the Loader starts the backend first), keeping this PR byte-invisible to existing transcripts: all 35 snapshot scenarios pass against master's fixtures unchanged.
2026-07-06 23:29:08 +08:00
pkg_subagent_inprocess --> pkg_system_prompt
pkg_subagent_inprocess --> pkg_tools
pkg_subagent_inprocess --> pkg_user_approval
2026-07-05 01:25:58 +08:00
pkg_tool_subagent --> pkg_agent
pkg_tool_subagent --> pkg_invariants
2026-07-05 01:25:58 +08:00
pkg_tool_subagent --> pkg_llm
pkg_tool_subagent --> pkg_subagent
pkg_tool_subagent --> pkg_tasks
2026-07-05 01:25:58 +08:00
pkg_tool_subagent --> pkg_tools
pkg_repository_plugin --> pkg_invariants
pkg_repository_plugin --> pkg_mcp_client
pkg_repository_plugin --> pkg_paths
pkg_repository_plugin --> pkg_skill_local
2026-07-05 01:25:58 +08:00
pkg_hooks_claude --> pkg_agent
pkg_hooks_claude --> pkg_hook_protocol
pkg_hooks_claude --> pkg_invariants
2026-07-05 01:25:58 +08:00
pkg_hooks_claude --> pkg_llm
pkg_hooks_claude --> pkg_session
pkg_hooks_claude --> pkg_session_persistence
2026-07-05 01:25:58 +08:00
pkg_hooks_claude --> pkg_subagent
pkg_hooks_claude --> pkg_tools
pkg_tui --> pkg_agent
pkg_tui --> pkg_agent_loop
2026-07-19 22:11:59 +08:00
pkg_tui --> pkg_commands
pkg_tui --> pkg_compact
2026-07-24 12:58:53 +08:00
pkg_tui --> pkg_goal
pkg_tui --> pkg_invariants
pkg_tui --> pkg_llm
pkg_tui --> pkg_llm_retry
pkg_tui --> pkg_session
pkg_tui --> pkg_session_persistence
2026-07-24 12:58:53 +08:00
pkg_tui --> pkg_session_query
pkg_tui --> pkg_session_reference
pkg_tui --> pkg_session_title
pkg_tui --> pkg_skill
pkg_tui --> pkg_subprocess
pkg_tui --> pkg_system_prompt
pkg_tui --> pkg_token_meter
pkg_tui --> pkg_tools
pkg_tui --> pkg_user_interaction
feat(web): show durable token usage and context occupancy in the stats line The chat stats line took its token totals from the loaded conversation nodes, so paging changed them and compaction erased the billing behind replaced content. It also had no way to show context occupancy: the numerator and capacity never reached the browser. Both now come from token-meter session projections read through the standard useProjection seat. Window nodes keep supplying turn and step counts plus LLM and tool wall times, which are correctly window-scoped facts about what is on screen; accounting no longer comes from there. `tokenUsage` supplies billing and cache hit. `contextPressure` supplies occupancy, pairing the newest provider-reported prompt size with the newest capacity recorded by `request/context`. Deployments without token-meter drop the token groups; a route whose adapter advertises no capacity drops the occupancy group rather than rendering a placeholder. Occupancy is deliberately approximate: the numerator and capacity are independent last-wins fields, not one atomic request observation, so switching models pairs a fresh capacity with the prior route's pressure until the next request reports usage. It is a user-facing reference figure that nothing in the harness makes decisions from, and it matches how the TUI status line has always computed occupancy. The Agent Note and token-meter README state this as a decision, including why the atomic alternative was implemented and rejected, so it is not re-litigated as a defect. Snapshot delta is one added `Context N% of 128K` segment across eight web goldens; the preceding commit absorbed master's pre-existing golden drift.
2026-07-30 14:48:19 +08:00
pkg_client_ui_model --> pkg_client_connection
pkg_client_ui_model --> pkg_client_locale
pkg_client_ui_model --> pkg_client_runtime
pkg_client_ui_model --> pkg_client_ui_command
pkg_client_ui_model --> pkg_client_ui_conversation
pkg_client_ui_model --> pkg_client_ui_primitives
pkg_client_ui_model --> pkg_client_ui_slash
pkg_client_ui_model --> pkg_client_ui_slots
pkg_client_ui_model --> pkg_invariants
pkg_client_ui_permission --> pkg_client_connection
pkg_client_ui_permission --> pkg_client_locale
feat(web): show durable token usage and context occupancy in the stats line The chat stats line took its token totals from the loaded conversation nodes, so paging changed them and compaction erased the billing behind replaced content. It also had no way to show context occupancy: the numerator and capacity never reached the browser. Both now come from token-meter session projections read through the standard useProjection seat. Window nodes keep supplying turn and step counts plus LLM and tool wall times, which are correctly window-scoped facts about what is on screen; accounting no longer comes from there. `tokenUsage` supplies billing and cache hit. `contextPressure` supplies occupancy, pairing the newest provider-reported prompt size with the newest capacity recorded by `request/context`. Deployments without token-meter drop the token groups; a route whose adapter advertises no capacity drops the occupancy group rather than rendering a placeholder. Occupancy is deliberately approximate: the numerator and capacity are independent last-wins fields, not one atomic request observation, so switching models pairs a fresh capacity with the prior route's pressure until the next request reports usage. It is a user-facing reference figure that nothing in the harness makes decisions from, and it matches how the TUI status line has always computed occupancy. The Agent Note and token-meter README state this as a decision, including why the atomic alternative was implemented and rejected, so it is not re-litigated as a defect. Snapshot delta is one added `Context N% of 128K` segment across eight web goldens; the preceding commit absorbed master's pre-existing golden drift.
2026-07-30 14:48:19 +08:00
pkg_client_ui_permission --> pkg_client_runtime
pkg_client_ui_permission --> pkg_client_schema_form
feat(web): show durable token usage and context occupancy in the stats line The chat stats line took its token totals from the loaded conversation nodes, so paging changed them and compaction erased the billing behind replaced content. It also had no way to show context occupancy: the numerator and capacity never reached the browser. Both now come from token-meter session projections read through the standard useProjection seat. Window nodes keep supplying turn and step counts plus LLM and tool wall times, which are correctly window-scoped facts about what is on screen; accounting no longer comes from there. `tokenUsage` supplies billing and cache hit. `contextPressure` supplies occupancy, pairing the newest provider-reported prompt size with the newest capacity recorded by `request/context`. Deployments without token-meter drop the token groups; a route whose adapter advertises no capacity drops the occupancy group rather than rendering a placeholder. Occupancy is deliberately approximate: the numerator and capacity are independent last-wins fields, not one atomic request observation, so switching models pairs a fresh capacity with the prior route's pressure until the next request reports usage. It is a user-facing reference figure that nothing in the harness makes decisions from, and it matches how the TUI status line has always computed occupancy. The Agent Note and token-meter README state this as a decision, including why the atomic alternative was implemented and rejected, so it is not re-litigated as a defect. Snapshot delta is one added `Context N% of 128K` segment across eight web goldens; the preceding commit absorbed master's pre-existing golden drift.
2026-07-30 14:48:19 +08:00
pkg_client_ui_permission --> pkg_client_ui_command
pkg_client_ui_permission --> pkg_client_ui_primitives
feat(web): show durable token usage and context occupancy in the stats line The chat stats line took its token totals from the loaded conversation nodes, so paging changed them and compaction erased the billing behind replaced content. It also had no way to show context occupancy: the numerator and capacity never reached the browser. Both now come from token-meter session projections read through the standard useProjection seat. Window nodes keep supplying turn and step counts plus LLM and tool wall times, which are correctly window-scoped facts about what is on screen; accounting no longer comes from there. `tokenUsage` supplies billing and cache hit. `contextPressure` supplies occupancy, pairing the newest provider-reported prompt size with the newest capacity recorded by `request/context`. Deployments without token-meter drop the token groups; a route whose adapter advertises no capacity drops the occupancy group rather than rendering a placeholder. Occupancy is deliberately approximate: the numerator and capacity are independent last-wins fields, not one atomic request observation, so switching models pairs a fresh capacity with the prior route's pressure until the next request reports usage. It is a user-facing reference figure that nothing in the harness makes decisions from, and it matches how the TUI status line has always computed occupancy. The Agent Note and token-meter README state this as a decision, including why the atomic alternative was implemented and rejected, so it is not re-litigated as a defect. Snapshot delta is one added `Context N% of 128K` segment across eight web goldens; the preceding commit absorbed master's pre-existing golden drift.
2026-07-30 14:48:19 +08:00
pkg_client_ui_permission --> pkg_client_ui_slash
pkg_client_ui_permission --> pkg_client_ui_slots
feat(web): show durable token usage and context occupancy in the stats line The chat stats line took its token totals from the loaded conversation nodes, so paging changed them and compaction erased the billing behind replaced content. It also had no way to show context occupancy: the numerator and capacity never reached the browser. Both now come from token-meter session projections read through the standard useProjection seat. Window nodes keep supplying turn and step counts plus LLM and tool wall times, which are correctly window-scoped facts about what is on screen; accounting no longer comes from there. `tokenUsage` supplies billing and cache hit. `contextPressure` supplies occupancy, pairing the newest provider-reported prompt size with the newest capacity recorded by `request/context`. Deployments without token-meter drop the token groups; a route whose adapter advertises no capacity drops the occupancy group rather than rendering a placeholder. Occupancy is deliberately approximate: the numerator and capacity are independent last-wins fields, not one atomic request observation, so switching models pairs a fresh capacity with the prior route's pressure until the next request reports usage. It is a user-facing reference figure that nothing in the harness makes decisions from, and it matches how the TUI status line has always computed occupancy. The Agent Note and token-meter README state this as a decision, including why the atomic alternative was implemented and rejected, so it is not re-litigated as a defect. Snapshot delta is one added `Context N% of 128K` segment across eight web goldens; the preceding commit absorbed master's pre-existing golden drift.
2026-07-30 14:48:19 +08:00
pkg_client_ui_permission --> pkg_invariants
pkg_client_ui_permission --> pkg_permission
pkg_client_ui_plan --> pkg_client_connection
2026-07-30 15:56:41 +08:00
pkg_client_ui_plan --> pkg_client_locale
pkg_client_ui_plan --> pkg_client_runtime
pkg_client_ui_plan --> pkg_client_ui_conversation
pkg_client_ui_plan --> pkg_client_ui_primitives
pkg_client_ui_plan --> pkg_client_ui_slots
pkg_client_ui_plan --> pkg_invariants
pkg_client_ui_plan --> pkg_plan_mode
pkg_agent_spine_demo --> pkg_agent
pkg_agent_spine_demo --> pkg_agent_loop
2026-07-19 23:55:33 +08:00
pkg_agent_spine_demo --> pkg_goal
pkg_agent_spine_demo --> pkg_goal_session
pkg_agent_spine_demo --> pkg_invariants
pkg_agent_spine_demo --> pkg_llm
pkg_agent_spine_demo --> pkg_llm_retry
pkg_agent_spine_demo --> pkg_paths
pkg_agent_spine_demo --> pkg_scope
pkg_agent_spine_demo --> pkg_session
pkg_agent_spine_demo --> pkg_session_title
pkg_agent_spine_demo --> pkg_skill
pkg_agent_spine_demo --> pkg_skill_local
pkg_agent_spine_demo --> pkg_system_prompt
pkg_agent_spine_demo --> pkg_tasks_local
pkg_agent_spine_demo --> pkg_tool_bash
2026-07-19 23:55:33 +08:00
pkg_agent_spine_demo --> pkg_tool_goal
pkg_agent_spine_demo --> pkg_tool_skill
pkg_agent_spine_demo --> pkg_tool_tasks
pkg_agent_spine_demo --> pkg_tools
pkg_agent_spine_demo --> pkg_workspace_context
pkg_sdk_protocol --> pkg_invariants
pkg_sdk_protocol --> pkg_llm
pkg_sdk_protocol --> pkg_session
pkg_sdk_protocol --> pkg_subagent
pkg_tool_ralph --> pkg_agent
pkg_tool_ralph --> pkg_invariants
pkg_tool_ralph --> pkg_llm
pkg_tool_ralph --> pkg_subagent
pkg_tool_ralph --> pkg_system_prompt
pkg_tool_ralph --> pkg_tools
pkg_tool_ralph --> pkg_workflow
pkg_workflow_workerthread --> pkg_agent
pkg_workflow_workerthread --> pkg_brand
pkg_workflow_workerthread --> pkg_invariants
pkg_workflow_workerthread --> pkg_llm
pkg_workflow_workerthread --> pkg_session
pkg_workflow_workerthread --> pkg_subagent
pkg_workflow_workerthread --> pkg_tools
pkg_workflow_workerthread --> pkg_workflow
2026-07-05 01:25:58 +08:00
pkg_subagent_fork --> pkg_agent
pkg_subagent_fork --> pkg_invariants
2026-07-05 01:25:58 +08:00
pkg_subagent_fork --> pkg_session
pkg_subagent_fork --> pkg_subagent
pkg_subagent_fork --> pkg_subagent_inprocess
pkg_subagent_spawn --> pkg_invariants
2026-07-05 01:25:58 +08:00
pkg_subagent_spawn --> pkg_subagent
pkg_subagent_spawn --> pkg_subagent_inprocess
pkg_jsonrpc --> pkg_agent
pkg_jsonrpc --> pkg_invariants
pkg_jsonrpc --> pkg_llm
pkg_jsonrpc --> pkg_llm_deepseek
pkg_jsonrpc --> pkg_scope
pkg_jsonrpc --> pkg_sdk_protocol
pkg_jsonrpc --> pkg_session
pkg_jsonrpc --> pkg_subagent
pkg_acp_demo --> pkg_acp
pkg_acp_demo --> pkg_agent_spine_demo
pkg_acp_demo --> pkg_app_boot
pkg_acp_demo --> pkg_invariants
pkg_acp_demo --> pkg_session_checkpoint_policy
pkg_acp_demo --> pkg_session_persistence_jsonl
pkg_acp_demo --> pkg_session_query
pkg_acp_demo --> pkg_session_query_sqlite
pkg_acp_demo --> pkg_tools
pkg_acp_demo --> pkg_workspace_context
2026-07-15 21:21:24 +08:00
pkg_cli_demo --> pkg_agent
pkg_cli_demo --> pkg_agent_spine_demo
pkg_cli_demo --> pkg_app_boot
pkg_cli_demo --> pkg_invariants
2026-07-15 21:21:24 +08:00
pkg_cli_demo --> pkg_llm
pkg_cli_demo --> pkg_session
pkg_cli_demo --> pkg_session_checkpoint_policy
2026-07-15 21:21:24 +08:00
pkg_cli_demo --> pkg_session_persistence_jsonl
pkg_cli_demo --> pkg_tools
pkg_cli_demo --> pkg_workspace_context
pkg_sdk_client --> pkg_invariants
pkg_sdk_client --> pkg_llm
pkg_sdk_client --> pkg_sdk_protocol
pkg_sdk_client --> pkg_session
pkg_subagent_dsh_sdk --> pkg_agent
pkg_subagent_dsh_sdk --> pkg_invariants
pkg_subagent_dsh_sdk --> pkg_llm
pkg_subagent_dsh_sdk --> pkg_sdk_client
pkg_subagent_dsh_sdk --> pkg_session
pkg_subagent_dsh_sdk --> pkg_subagent
pkg_subagent_dsh_sdk --> pkg_subprocess
```
2026-07-05 01:25:58 +08:00
| Package | Group | Depends on |
| --- | --- | --- |
| [`invariants`](../packages/support/invariants) | `support` | — |
| [`atomic-write`](../packages/util/atomic-write) | `util` | [`invariants`](../packages/support/invariants) |
| [`brand`](../packages/util/brand) | `util` | [`invariants`](../packages/support/invariants) |
| [`native-command`](../packages/util/native-command) | `util` | [`invariants`](../packages/support/invariants) |
| [`paths`](../packages/util/paths) | `util` | [`invariants`](../packages/support/invariants) |
| [`retention`](../packages/util/retention) | `util` | [`invariants`](../packages/support/invariants) |
| [`timeout`](../packages/util/timeout) | `util` | [`invariants`](../packages/support/invariants) |
| [`scope`](../packages/core/scope) | `core` | [`invariants`](../packages/support/invariants) |
| [`skill`](../packages/skill/skill) | `skill` | [`invariants`](../packages/support/invariants) |
| [`acp-snapshot`](../packages/support/acp-snapshot) | `support` | [`invariants`](../packages/support/invariants) |
| [`llm-mock-server`](../packages/support/llm-mock-server) | `support` | [`invariants`](../packages/support/invariants) |
| [`loader-smoke`](../packages/support/loader-smoke) | `support` | [`invariants`](../packages/support/invariants) |
| [`client-modules`](../packages/client/modules) | `client` | [`invariants`](../packages/support/invariants) |
feat(gui): step1 skeleton — dsc web serves built web UI over booted harness host Five new modules: apps/dsc (bin: parseArgs + node:http static server + signal shutdown), packages/host/apiproxy (programmatic harness core composition, agents:[]), packages/client/web-runtime (React-free browser runtime), packages/client/web-ui (React mount), apps/web (vite build entry producing dist consumed by apps/dsc via package exports). Root wiring: apps/* workspace glob, dsh-* paths for host/client groups, demo:web script, apps/web/dist gitignore. No protocol/API routes yet — contract lands in step2 (see missions/tasks/20260719-1902-apiproxy-api-design). Includes the design + implementation archives (spec v2.1, deepseekchat baseline and harness boot research, implementation run log). Acceptance: 12/12 passed incl. real-key llm.stream smoke (51 chunks). feat(gui): apiproxy — four-quadrant RPC contract + fetch carriers, live end to end Contract layer (src/api/, 14 files): four named wire message types (ClientRequest / ServerResponse / ServerRequest / ClientResponse) as a discriminated union over strict bidirectional rpcId (initiator mints, responder echoes; channel and message fully decoupled — HTTP is the client->server pipe, SSE the reverse); narrow RpcRequest<P>/ RpcResponse<T> signature forms; RpcMethodMap with RequestPayload<K>/ ResponseValue<K> derivation; typed RpcError details map; approval/ question responses modeled as ClientResponse via a single /api/respond endpoint (RpcReceipt carrier ack); zod schemas anchored per Wire<T> against exactOptionalPropertyTypes. impl/api-proxy.ts: describe/list/create, both SSE streams (frame queue pump, subscribed baseline, lifecycle frames, signal cleanup); history pages on message boundaries (tail-back scan, partial included in the tail page); prompt dispatches queue->agent.send / steer->agent.steer with rpcId carried through MessageSource; cancel for attached sessions; cold-session resume deduped via a per-id promise map; host-level provider/model defaults injected at create/resume. fetch/: mechanical UNARY_ROUTES table, two-level parse with path==method check, SSE frames completed to ServerRequest full form; client mints -> narrows -> envelopes outbound, verifies rpcId echo inbound, streams SSE frames, four-quadrant onEnvelope tap (debug panel choke point). Real-browser fixes: URL base resolves to location.origin (hardcoded internal base broke real pages), browser-safe export paths. Design archives: contract design.md v2.0 with decision log, core-coverage audit, comparative studies, step2 impl run log. Probed end to end over real HTTP: prompt -> live model stream -> history returns the finished reply. feat(gui): RpcLog debug panel — fixture-driven milestone, playwright-verified 10/10 web-runtime: rpcLog + ui slices (zustand), four-quadrant RpcLogEntry (client-request / server-response / server-request / client-response), onEnvelope tap -> microtask-batched pump with 500-entry ring buffer, ConnectionController (private state, backoff reconnect), fixture API with fake envelopes (?fixture switch), bootWebRuntime; contract types via temporary local copies (api-types.ts, swapped for real imports when W3 client lands). web-ui: components/panels/RpcLog five-piece set (badge with unread count, floating panel, direction glyphs per quadrant, same-rpcId pair highlighting in two families, JSON payload expand, follow/pause, clear), App shell, utils/formatRelative, light-theme CSS variables with dark placeholders. dsc bin: mime lookup fixed to use the actually-served file (naked '/?query' no longer falls through to octet-stream download); shutdown closes SSE keep-alive connections so SIGTERM actually exits. Acceptance: scripts/verify-rpclog-panel.mjs (chromium headless) ALL PASS 10/10 over design.md §D 1-6. pkg: add web scripts for building feat(gui): session milestone — list + conversation over Session OOP, styled RpcLog v2.1 web-runtime: Session/SessionManager object layer (resident instances, mux frame routing, lineage flattening), foldSurface adapter with padding sentinels for paged windows, chunk accumulator for streaming partials, batched change notification (useSyncExternalStore contract), connection sinks + reconnect fix (the 300ms self-abort reconnect storm that made the session list flap is gone), fixture rewritten as a scripted host (60-turn history, typewriter replay, resident pending approval, child session); temporary contract copies deleted in favor of real apiproxy imports. web-ui: sessions screen (list with lineage indent + selection as container-local state), conversation view (turn grouping, reasoning fold, tool cards, steering, pending interaction cards, upward paging with scroll anchoring), input bar with queue/steer/stop; RpcLog panel restyled per docs/web-styling.md (tokenized palette, quadrant badge glyphs now vertical ↑↓⇟⇞, pair highlighting, floating shadow). docs/web-styling.md: living style guide (tokens, visual baseline, coding rules, evolution log). Acceptance: verify-session.mjs 31/31, verify-session-real.mjs 5/5 (real model streaming), verify-rpclog-panel.mjs 10/10. feat(gui): hostruntime split + repo-wide package prefix rename Package split (design: 20260720-0101-hostruntime-split-design): dsh-host-runtime carries bootHost + createApiProxy + startHost() (RunningHost {api, handler, defaults, ctx, dispose} — the seam Electron and any future shell reuses; ctx is the official front-door mount point); dsh-host-webserver carries the node:http static+API bridge (fixed: abort now keys on res 'close' + writableEnded — req 'close' fires on body end since Node 16 and was killing every SSE stream instantly, the reconnect-storm root cause); apps/dsc is now a thin assembly with web/-p subcommands. dsc -p runs the full isomorphic carrier chain in process (second real protocol consumer; probed end-to-end against the live model). Naming rule (user decree): packages under host/ and client/ carry the directory prefix in their npm name — dsh-host-apiproxy, dsh-client-web-runtime, dsh-client-web-ui renamed repo-wide in one frozen batch; explicit tsconfig paths entries added where the wildcard no longer matches. Acceptance: verify-session 31/31, verify-rpclog-panel 10/10, verify-session-real 7/7 (incl. new 12s connection-stability sentinels), tsc green, dsc web + dsc -p smoke both pass. refactor(gui): AbstractApiClient class hierarchy — OO client with inheritable seams AbstractApiClient (apiproxy) carries every protocol invariant: rpcId minting, four-quadrant envelope wrap/unwrap, zod parsing, SSE frame parsing, the payload-direct IApiClient surface (callers no longer mint rpcIds — the carrier does), and the instance-level envelope observation pump (batched via microtask; moved off module-level globals in rpc-log.ts, which is now a pure subscriber mapping envelopes into store entries — the debug panel observes the connection, it is not part of it). Platform subclasses own two abstract seams (doFetch, onEnvelope) plus three protocol-level virtuals for transportless overrides: InProcessApiClient (apiproxy; dsc -p uses new InProcessApiClient( host.handler)), WebApiClient (web-runtime), FixtureApiClient (fixture now subclasses instead of wrapping). Naming per decree: AbstractApiClient / IApiClient; ApiProxy stays the impl-side narrow-form contract. headless.ts call sites drop rpcRequest wrappers (payload-direct); split-design archive updated with the naming-rule ledger. tsc green; verify-session 31/31, verify-rpclog-panel 10/10, verify-session-real 7/7 (12s connection sentinel count=4); dsc -p smoke CALLER-OK. feat(gui): InputBar final form — bug batch, deepseekchat layout, single primary button, running locks input Squashes the whole InputBar iteration batch: IME/caret/auto-grow/focus/dedup bug fixes, layout aligned to the deepseekchat baseline, single primary button with hover flyout, finalized button semantics with the Codex-style icon circle, and running-state locking where stop is the only mid-turn action. The same batch carried the Chinese-to-English code comment sweep (density pruned), folded in here. docs(gui): purge work-log references from code comments 76 design-doc references cleared across the GUI packages: section pointers inlined as self-contained constraint statements, pure pointer comments dropped, milestone codenames and ruling tags out, and the 14 contract file headers switched to the formal RFC (the only sanctioned external reference). web-styling.md now cites the styling RFC instead of the disposable research archive. grep for work-log reference variants is clean across the GUI packages. docs(gui): file-header comments self-contained — drop RFC filename references RFC renames/reorgs must not require a source sweep (the 2026-07-20 two-way merge proved it). 11 headers lose only the '(RFC …)' tail and stay self-contained; api-proxy.ts keeps its minimal-first note. fix(gui): session streaming — freeze interrupted partials, sweep stale running calls, send force-scrolls Aborted turns never emit the finalizing assistant/message, so the accumulated partial and its running tool cards kept rendering below later messages — the "new message lands above the stopped reply" illusion. turn/end side effects now freeze content-bearing partials into interrupted terminal nodes (fractional seq keeps flow order; the live freeze and history replay converge through applyEventSideEffects, so a refresh reconstructs identical frozen nodes) and turn running tool cards into interrupted terminal cards; only content-free partials are swept outright. ConversationView gains the send-force-scroll rule (own words must be visible) alongside the pre-update atBottom follow flag. Regressions pinned as E2-4a–c (real host) and §E1-11h (fixture). feat(gui): webserver hardening verify script feat(gui): dark-mode toggle pinned to the sidebar bottom Interim home before the Settings page exists (the button re-homes with zero logic change — mechanics live in utils/theme.ts): html[data-theme] flip + dsc.theme localStorage, stored choice wins over the OS prefers-color-scheme default, applied in mount() before first paint so a dark reload never flashes light. Moon/sun inline SVG icon button at the sidebar's pinned bottom row. Pure front-end local concern: no RPC, no Session/store involvement. Dark sweep of list/conversation/input card/RPC panel found no unreadable pairs — no token changes needed. docs(gui): GUI RFCs and web styling handbook Layering+RPC protocol and web client architecture RFCs (post-reorg, developer-facing polish folded in) plus the styling engineering handbook. Mission work logs live in the commit above; PRs can be cut from this commit to include formal docs only. fix(gui): client object-layer hardening — audit timing/reference/resilience batches (S3-S5,C1-C3,C5-C8) fix(gui): carrier error channel + webserver backpressure (audit A1-A5,A7-A10,R2,R5) feat(gui): session persistence surface — cold list, project cwd, legacy no-cwd retirement refactor: rename dsc CLI to dsh — apps/cli, bin name, package scope Includes the root tsconfig project-references fix for host/* and client/web-runtime (originally a separate build fix commit). test(gui): three-tier suite — protocol/object/browser lanes, tier-a fill to per-file 100% test(gui): jsdom lane for web-ui + web-runtime coverage gate entry docs(gui): GUI testing system RFC (zh) feat(gui): tool-card views — contract slot, host-computed delivery, three-level card fallback fix(gui): lint clean across GUI packages — wrap long doc comments, drop dead type args, sync-return methods without awaits docs(gui): doc-sync mechanical fixes — JSDoc on apiproxy/host exports, RFC sketch fences ignore-check, md-wrap paragraphs, drop missions links, web-ui plain-ts entry chore(gui): module-graph regen + knip clean — drop dead re-exports, internalize createFixtureApi, scan web-ui tsx and verify mjs scripts build(gui): wire client/host packages into the lib build shape — tsc references + tsdown (web-ui css-external), lib manifests, cordis peer, apiproxy typed subpaths, vite src aliases test(gui): host-side per-file 100% coverage — apiproxy schema/carrier suites, webserver http-bridge suite, host-runtime composition suite; client/* coverage excluded pending the browser-side testing work item docs(gui): package READMEs for the five GUI packages — model-experience audit entries, limitations sections docs(gui): bilingual RFC pairs + client JSDoc completion — translate the three GUI RFCs to English with i18n records and manifest ratchet, Consequences sections both sides, full client/* export JSDoc, regen doc graphs and RFC index fix(scripts): doc-typecheck built-declarations mode maps /src/* subpath wildcards (apiproxy browser-safe channels) docs(gui): apply dsh rename across pr-gates docs — READMEs, layering RFC en, web-ui entry comment, i18n re-record fix(gui): post-rebase lint reconciliation — wrap main-tree long doc comments, read-through narrowing guards, abortError Error normalization, handleUnary generic justification fix(gui): post-rebase doc/test reconciliation — align host specs with evolved carrier contracts (sentinel rpcId, stream/error surfacing, url-path transport messages, defaults.cwd), Agent Note titles and relocated links, KV Cache effect sections, JSDoc on evolved exports fix(gui): second-rebase reconciliation to 509db0cb3 — restore api panel exports the baseline suites consume, knip workspace entries for jsdom lane and apps/web smokes, hoist result narrowing, align testing.md to the narrowed web-ui exclusion fix(test): vitest-scoped tsconfig maps bare imports for tsx specs — with GUI manifests now pointing at lib, an unmapped importer loaded a second copy of the web-runtime singletons fix(gui): typecheck + lint clean over the tool-card batch — brand callIds and object-form turn/end reason in the view spec, narrow fixture arg stringification, wrap long v8-ignore comments docs(gui): export JSDoc for tool-card surfaces + testing-note pairing header docs: rfc for web testing feat: add tools to host-runtime fix(gui): dispatch agent/error via agentEvents in host-runtime spec — mounted invariants plugin rejects raw ctx.emit without the scope carrier fix(gui): restore GUI knip workspaces + scripts/mjs entries and regenerate lockfile after master rebase fix(gui): post-rebase gate repairs — drop context-node envelope (master unwrapped injected content envelopes), regen event matrix, condense testing.md web-ui exclusion within budget fix(session): browser-safe deep-equal in surface — node:util import broke the vite bundle ci(gates): frontend vite build joins pre-push — node: imports in the client closure pass tsc but break the browser bundle test(tui): drop the checkout-dependent process.cwd() harness default — a long worktree path pushes the footer token counters past the 88-column fake terminal test(gui): jsdom behavior E2E — conversation main path over fixture runtime, reconnect banner lifecycle test(gui): jsdom RPC panel behavior — ledger rows, expand, pairing, pause/clear, follow-pause, payload truncation test(gui): jsdom tier-2 — InputBar guards, reasoning fold, JSON blocks, message variants, theme, create-then-select; act-harden banner case test(gui): jsdom tier-3 — ConversationView states/paging/force-bottom, ToolCallCard arms, PendingCard, list rows test(gui): jsdom tails — view-card variants, LogRow directions, registry hygiene, badge overflow, hook ops, mount glue test(gui): jsdom tails round 2 — call-ref blocks, resume follow, view precedence, failed create, empty-diff arm test(gui): jsdom final arms — anchor compensation, follow-off, interval ticks, view halves, node-over-running precedence test(gui): web-ui joins the per-file 100% coverage gate Annotation-only src changes plus the config swap. The web-ui exclusion is replaced by a single index.tsx entry (stale byte-identical duplicate of mount.tsx, nothing imports it; same entry-glue treatment as bin.ts) and the coverage include gains .tsx. v8-ignore sites (each with its reason inline): - ConversationView 3x ref-null guards; InputBar disabled-click guard - ToolCallCard both-null arms + windowless-custom argsRaw arm - LogRow css-module key fallbacks (start/stop block); RpcLogBody 3x ref-null guards - web-runtime drift from the tool-card batch: fixture presenter catch/str typo-guards, dense-array guards (fold-adapter reset, session rebuild, fixture backscan), live view-present arm (fixture replays are text-only; view vocabulary is covered by the history samples) test(gui): close the PR #443 host-side coverage gaps — apiproxy client abort arms, api-proxy cold/view paths, webserver drain - apiproxy fetch/client.ts: 3 new cases (pre-aborted signal short-circuits before transport + string reason mapping, non-Error/string reason falls to the default AbortError message, signal-less doFetch passthrough) - runtime/api-proxy.ts: one v8-ignore (summarizeCold cwd arm — list() filters cwd-less legacy metas) + api-proxy-cold.spec.ts (cold list merge: mtime source, locate-undefined and vanished-log fallbacks, lineage; no-persistence/no-factory resume → internal) + 2 view cases (history views with meta passthrough and orphan/bad-args/presenterless soft-falls, session/disposed open-call cleanup on the mux stream) - webserver/index.ts: /api/big fixture drives both drain-wait legs (full 8MiB readback after drain, mid-chunk disconnect wakes via 'close') feat: app shell fix: rebase conflicts fix: coverage fix(gui): lint clean after rebase — wrap long v8-ignore comments, unconditional v1 detail-block claim chore(gui): remove browser/probe verify scripts from scripts/ The six GUI acceptance/probe scripts (carrier-errors, rpclog-panel, session, session-real, webserver-backpressure, webserver-hardening) leave the repo's scripts/ tree; the three code comments that pointed at them now describe the coverage lane without naming a script path. fix(webserver): guard the request callback — one malformed request must not kill the process The async handle() had no top-level catch, so any throw inside it (a bad %-escape reaching decodeURIComponent, a client dropping mid-body, a response stream erroring) became an unhandled rejection and took the whole process down (audit R1 must-fix). The guard answers 400 when headers are not out yet, destroys the socket when they are, and reports the failure to onError (the package never prints). Spec covers all three legs: %-escape barrage → 400 + server stays alive, non-Error throw wrapped for onError, mid-stream explosion → socket teardown. feat: client AGENTS.md fix: client/AGENTS.md fix: rebase feat(gui): T0 cut 1 — 12 client package skeletons with contract stubs, dshClient declarations, tsdown client preset, theme token sheets feat(gui): T0 cut 2 — pure git mv migration per v3 §11 (connection six, runtime sessions/kernel, ui-conversation chat, ui-primitives markdown family, web shell + e2e) feat(gui): T0 cuts 3+4 — import rewiring to new package names, .legacy demotion of owner-rewrite files, legacy web-runtime/web-ui/apps-web retired to attic feat(gui): connection 对账刀——index.ts 精确导出清单替换 export *,intents.legacy 溶解删除 feat(client/ui-slots): SlotCore real implementation — kind semantics, sync version + microtask-batched notify, onMutate bridge feat(gui): web shell vite alias — retarget to new client packages, shell static surface only feat(gui): host 侧刀属地半——HostWebPluginRegistry(entries 扫描+internal/plugin 去抖重扫+dshClient 校验+exports./client 解析)、GET /plugins/<id>/client.js 分发端点、GET / 与 SPA fallback 注入 __DSH_BOOT__(webPlugins 可选注入,不传行为不变) feat(web-react): add use-sync-external-store dep + local shim typings feat(web-react): bindSnapshotSelector via uSES with-selector shim feat(gui): ui-layout concession-chain solver — pure computeColumns with contract geometry feat(gui): ui-layout LayoutService — four persisted stores, clamped actions, list-driven prune feat(gui): ui-layout AppFrame styles — grid columns, collapse-safe borders, edge drag handles test(gui): 存量 spec 平移——connection 三件+runtime 六件自 attic 捞回改包名路径全绿;api-helpers 按归属拆分(wire 半留 connection、classifier 半随 conversation.ts 入 runtime);boot-intents/preinit/rpc-log 随 intents/rpc-log 退役不迁(记 v3 §3.2 溶解项) feat(client/ui-primitives): StateDot/Button/Pill/Input/Menu atoms, ConnectionBanner de-legacied to pure props, JsonBlock CSS on --dsw tokens feat(web-react): createSnapshotStore engine (rafFlush batch, persist opt-in, dev freeze) + spec feat(gui): ui-layout AppFrame — grid tracks, pointer-capture drag handles with rAF throttle, frame ResizeObserver feat(web-react): useInvoke (external pending store, stable invoke, concurrency count) + spec test(web-react): bind spec — equality bail, custom eq, zero resubscribe, StrictMode, method sources feat(gui): ui-layout index rewiring — real exports, client apply provides ctx.layout and defines three slots feat(web-react): SessionProvider (renderBody deps) + RootBindingProvider + binding contexts + spec feat(gui): web shell AppRoot boot-page styles — self-contained with neutral token fallbacks feat(gui): web shell AppRoot — boot gate over loader status, fail-loud plugin failure list fix(gui): AppRoot gates on explicit settled signal — status-derived readiness races the incrementally filled table feat(client/ui-theme): ThemeService real implementation — registry with built-in light/dark, apply toggles body[data-ds-dark-theme], third-party token overrides as body inline vars feat(web-react): scopedSlots outlet (kind matrix, inject WeakMap caches, per-entry error boundary) + spec feat(gui): web shell module-table seed — pure-library entities for the loader require surface feat(client/i18n): I18nService real implementation — ns×locale registry, stable bind(ns) reference, zh fallback chain, zh/en skeleton dictionaries feat(gui): web shell assembly closure — layout exports via module table, SessionProvider + scopedSlots + RootBindingProvider feat: client/ui-conversation feat: code codedoc build(gui): root bundle green — web shell excluded from the lib workspace (vite app), ui-primitives lib externalizes css side-effect imports (web-ui precedent) gates(gui): verify-cordis-config follows aggregate tsconfig references (root is a shell over host/client programs); module graph regenerated for the twelve client packages chore(gui): retire legacy migration sources — every owner rewrite landed (t0-checklist §7 ledger honored); orphan css of retired components removed gates(gui): knip green groundwork — e2e/tsx entries for the new packages, loader-runtime deps ignored where loading is by specifier string, fake plugin ids un-bare-named, dead test export dropped chore(client): manifest shape batch A — ui-slots/web-react/ui-primitives invariant companions, files whitelist, cordis+invariants peer/dev, tsconfig refs chore(client): manifest shape batch B — connection/runtime/ui-conversation/ui-trajectory files whitelist, cordis peer+dev, explicit invariant lib entries (clientBundle signature) chore(client): manifest shape batch C — i18n/ui-layout/ui-sidebar/ui-theme invariant companions, files whitelist, invariants peer/dev, tsconfig refs chore(client): manifest shape batch D — web shell gains node-half lib entry + invariant companion + uniform files whitelist chore(client): drop verified-unused deps — dsh-tools from runtime/ui-conversation (types ride /presentation), ui-primitives+clsx from ui-layout gates(gui): doc-gate fixes — theme JSDoc prose, three client type-link exemptions, agent-note paths follow the migration, config catalog regenerated gates(gui): type-equiv manifest follows the types.ts extraction, approval JSDoc keeps its link form, persistence catalog regenerated docs(gui): per-constant JSDoc on the contract geometry exports (export-jsdoc gate) test(gates): loader-composition budget covers cold tsx resolution after the program split (was flaking at the default 5s) docs(gui): README substantiation batch 1 — ui-slots/ui-primitives/web-react/connection: Model Experience short form, real deferred-work ledgers, description accuracy pass fix(client): theme/i18n dual-entry split — service classes + cordis merges move to src/client (host catalog scanner no longer misclassifies client services), node halves keep types + empty apply; catalogs regenerated docs(gui): README substantiation batch 2 — runtime/ui-layout/ui-sidebar/ui-conversation: Model Experience short form, package-owned deferred-work ledgers (unload stub, watch approximation, /client value-import rule, global details state, two-state dots, stats duration gap, single-bundle caches) docs(gui): README substantiation batch 3 — ui-trajectory/ui-theme/i18n/web: Model Experience short form, deferred-work ledgers (placeholder charter, no theme toggle owner, empty locale dictionaries, one-shot rendering); both README gates green test(scripts): purity spec adopts clientBundle two-arg signature (explicit libEntry, no default) gates(gui): knip green — declaration-merge dep ignored, fake plugin id assembled at runtime, invariants dep de-duplicated to peer+dev, stale apps/web section dropped feat(gui): 门禁波次 host 三包 invariant 形状——apiproxy explained-empty 伴生(wire 契约层零事件面)、webserver 真关系伴生(manifest 行必解析出 clientPath,防 __DSH_BOOT__ 广告 404 bundle;apps/cli 发布 webPlugins 键供审计)、runtime 补 files 白名单;三包 exports/files/peer+dev/tsconfig refs 齐 fw-react 形状;constraints+invariants 双 gate 零违规 build(client): ui-layout/ui-sidebar tsdown configs adopt the explicit two-arg clientBundle signature (orphaned follow-up of the manifest shape batch) refactor(gui): shell boot becomes a library face — bootWebShell(el) exported for the apps/web entry; main.ts retired refactor(gui): exports 纪律刀1——ui-theme/i18n node index 收敛为只空 apply(Translate/LocaleDict/ThemeTokens 类型下沉 src/client/),ui-conversation 的 I18nService import 改 /client 子路径 build(typecheck): converge to root host aggregate + tsconfig.client.json — delete tsconfig.host.json, verify-cordis-config seeds both aggregates feat(gui): apps/web restored as the vite application — thin main over bootWebShell; dsh-client-web becomes a plain lib (index exports shell surface, vite files and e2e moved out) chore(gates): knip.json rewritten on the master base — same semantics, minimal diff (formatting churn dropped) docs(gui): 时效清扫②——testing.md 删 web-ui 覆盖豁免残句;web-styling.md 加 token 换代头注(--dsw-* 现行、工程约束条款仍有效并注明收编处) docs(gui): 时效清扫③——四对 GUI Agent Note 加路径更新头注(web-runtime/web-ui/dsh-frontend→现行 12 包结构;设计结论存续声明;双语对同步) docs(gui): 时效清扫③b——四对 note 头注的 i18n 配对哈希重录 build(typecheck): minimal-diff tsconfig shape — drop root files entry (purity spec + preset move to client program), compress comments, drop redundant util/home root ref feat(gui): apps/web restoration follow-through — dsh-frontend package name, cli dist resolve, root build:web filter, tsdown exemption dropped, vitest web lane + knip + client aggregate retargeted, e2e paths rebased refactor(gui): exports 纪律刀2——connection wire 六件 git mv 进 src/client/(wire 即该 dshClient 插件的 client 半),node index=只空 apply,/client 半边整面导出(v3 §3.2 清单原样),包内 tests 改 src/client 直取 refactor(gui): exports 纪律刀3——runtime 实现整体下沉 src/client/(sessions/slots/loader;契约类型与 cordis merge 随迁 client/index),node index=只空 apply;./loader exports 指 client/loader;全消费面(web 壳/ui-sidebar/ui-trajectory/tests)bare→/client 机械跟改;vitest.e2e 换 tsconfig.vitest paths(root tsconfig 排除 client 会把 /client import 掉到 exports 的浏览器 dist bundle) refactor(gui): exports 纪律刀3 补遗——ui-layout 三处 bare runtime import 改 /client(刀3 消费面机械跟改漏提交件;跨属地机械一行×3 报备 ui-shell) test(gui): drop the getSessionManager singleton case — the init/get pair is a dead legacy-boot surface with zero live consumers (SessionsService constructs and holds the manager under the plugin architecture); source removal tracked with rt-core refactor(gui): 删 manager.ts 尾部 initSessionManager/getSessionManager 单例对——旧 boot 直连遗物,插件化下 SessionsService 构造持有 manager,全仓零活消费者(convo-b 测试清扫对表,其测试用例已先行退役 7e2c51898);头注释同步去单例措辞 code refactor
2026-07-19 21:17:57 +08:00
| [`client-runtime`](../packages/client/runtime) | `client` | [`invariants`](../packages/support/invariants) |
| [`client-schema-form`](../packages/client/schema-form) | `client` | [`invariants`](../packages/support/invariants) |
feat(gui): step1 skeleton — dsc web serves built web UI over booted harness host Five new modules: apps/dsc (bin: parseArgs + node:http static server + signal shutdown), packages/host/apiproxy (programmatic harness core composition, agents:[]), packages/client/web-runtime (React-free browser runtime), packages/client/web-ui (React mount), apps/web (vite build entry producing dist consumed by apps/dsc via package exports). Root wiring: apps/* workspace glob, dsh-* paths for host/client groups, demo:web script, apps/web/dist gitignore. No protocol/API routes yet — contract lands in step2 (see missions/tasks/20260719-1902-apiproxy-api-design). Includes the design + implementation archives (spec v2.1, deepseekchat baseline and harness boot research, implementation run log). Acceptance: 12/12 passed incl. real-key llm.stream smoke (51 chunks). feat(gui): apiproxy — four-quadrant RPC contract + fetch carriers, live end to end Contract layer (src/api/, 14 files): four named wire message types (ClientRequest / ServerResponse / ServerRequest / ClientResponse) as a discriminated union over strict bidirectional rpcId (initiator mints, responder echoes; channel and message fully decoupled — HTTP is the client->server pipe, SSE the reverse); narrow RpcRequest<P>/ RpcResponse<T> signature forms; RpcMethodMap with RequestPayload<K>/ ResponseValue<K> derivation; typed RpcError details map; approval/ question responses modeled as ClientResponse via a single /api/respond endpoint (RpcReceipt carrier ack); zod schemas anchored per Wire<T> against exactOptionalPropertyTypes. impl/api-proxy.ts: describe/list/create, both SSE streams (frame queue pump, subscribed baseline, lifecycle frames, signal cleanup); history pages on message boundaries (tail-back scan, partial included in the tail page); prompt dispatches queue->agent.send / steer->agent.steer with rpcId carried through MessageSource; cancel for attached sessions; cold-session resume deduped via a per-id promise map; host-level provider/model defaults injected at create/resume. fetch/: mechanical UNARY_ROUTES table, two-level parse with path==method check, SSE frames completed to ServerRequest full form; client mints -> narrows -> envelopes outbound, verifies rpcId echo inbound, streams SSE frames, four-quadrant onEnvelope tap (debug panel choke point). Real-browser fixes: URL base resolves to location.origin (hardcoded internal base broke real pages), browser-safe export paths. Design archives: contract design.md v2.0 with decision log, core-coverage audit, comparative studies, step2 impl run log. Probed end to end over real HTTP: prompt -> live model stream -> history returns the finished reply. feat(gui): RpcLog debug panel — fixture-driven milestone, playwright-verified 10/10 web-runtime: rpcLog + ui slices (zustand), four-quadrant RpcLogEntry (client-request / server-response / server-request / client-response), onEnvelope tap -> microtask-batched pump with 500-entry ring buffer, ConnectionController (private state, backoff reconnect), fixture API with fake envelopes (?fixture switch), bootWebRuntime; contract types via temporary local copies (api-types.ts, swapped for real imports when W3 client lands). web-ui: components/panels/RpcLog five-piece set (badge with unread count, floating panel, direction glyphs per quadrant, same-rpcId pair highlighting in two families, JSON payload expand, follow/pause, clear), App shell, utils/formatRelative, light-theme CSS variables with dark placeholders. dsc bin: mime lookup fixed to use the actually-served file (naked '/?query' no longer falls through to octet-stream download); shutdown closes SSE keep-alive connections so SIGTERM actually exits. Acceptance: scripts/verify-rpclog-panel.mjs (chromium headless) ALL PASS 10/10 over design.md §D 1-6. pkg: add web scripts for building feat(gui): session milestone — list + conversation over Session OOP, styled RpcLog v2.1 web-runtime: Session/SessionManager object layer (resident instances, mux frame routing, lineage flattening), foldSurface adapter with padding sentinels for paged windows, chunk accumulator for streaming partials, batched change notification (useSyncExternalStore contract), connection sinks + reconnect fix (the 300ms self-abort reconnect storm that made the session list flap is gone), fixture rewritten as a scripted host (60-turn history, typewriter replay, resident pending approval, child session); temporary contract copies deleted in favor of real apiproxy imports. web-ui: sessions screen (list with lineage indent + selection as container-local state), conversation view (turn grouping, reasoning fold, tool cards, steering, pending interaction cards, upward paging with scroll anchoring), input bar with queue/steer/stop; RpcLog panel restyled per docs/web-styling.md (tokenized palette, quadrant badge glyphs now vertical ↑↓⇟⇞, pair highlighting, floating shadow). docs/web-styling.md: living style guide (tokens, visual baseline, coding rules, evolution log). Acceptance: verify-session.mjs 31/31, verify-session-real.mjs 5/5 (real model streaming), verify-rpclog-panel.mjs 10/10. feat(gui): hostruntime split + repo-wide package prefix rename Package split (design: 20260720-0101-hostruntime-split-design): dsh-host-runtime carries bootHost + createApiProxy + startHost() (RunningHost {api, handler, defaults, ctx, dispose} — the seam Electron and any future shell reuses; ctx is the official front-door mount point); dsh-host-webserver carries the node:http static+API bridge (fixed: abort now keys on res 'close' + writableEnded — req 'close' fires on body end since Node 16 and was killing every SSE stream instantly, the reconnect-storm root cause); apps/dsc is now a thin assembly with web/-p subcommands. dsc -p runs the full isomorphic carrier chain in process (second real protocol consumer; probed end-to-end against the live model). Naming rule (user decree): packages under host/ and client/ carry the directory prefix in their npm name — dsh-host-apiproxy, dsh-client-web-runtime, dsh-client-web-ui renamed repo-wide in one frozen batch; explicit tsconfig paths entries added where the wildcard no longer matches. Acceptance: verify-session 31/31, verify-rpclog-panel 10/10, verify-session-real 7/7 (incl. new 12s connection-stability sentinels), tsc green, dsc web + dsc -p smoke both pass. refactor(gui): AbstractApiClient class hierarchy — OO client with inheritable seams AbstractApiClient (apiproxy) carries every protocol invariant: rpcId minting, four-quadrant envelope wrap/unwrap, zod parsing, SSE frame parsing, the payload-direct IApiClient surface (callers no longer mint rpcIds — the carrier does), and the instance-level envelope observation pump (batched via microtask; moved off module-level globals in rpc-log.ts, which is now a pure subscriber mapping envelopes into store entries — the debug panel observes the connection, it is not part of it). Platform subclasses own two abstract seams (doFetch, onEnvelope) plus three protocol-level virtuals for transportless overrides: InProcessApiClient (apiproxy; dsc -p uses new InProcessApiClient( host.handler)), WebApiClient (web-runtime), FixtureApiClient (fixture now subclasses instead of wrapping). Naming per decree: AbstractApiClient / IApiClient; ApiProxy stays the impl-side narrow-form contract. headless.ts call sites drop rpcRequest wrappers (payload-direct); split-design archive updated with the naming-rule ledger. tsc green; verify-session 31/31, verify-rpclog-panel 10/10, verify-session-real 7/7 (12s connection sentinel count=4); dsc -p smoke CALLER-OK. feat(gui): InputBar final form — bug batch, deepseekchat layout, single primary button, running locks input Squashes the whole InputBar iteration batch: IME/caret/auto-grow/focus/dedup bug fixes, layout aligned to the deepseekchat baseline, single primary button with hover flyout, finalized button semantics with the Codex-style icon circle, and running-state locking where stop is the only mid-turn action. The same batch carried the Chinese-to-English code comment sweep (density pruned), folded in here. docs(gui): purge work-log references from code comments 76 design-doc references cleared across the GUI packages: section pointers inlined as self-contained constraint statements, pure pointer comments dropped, milestone codenames and ruling tags out, and the 14 contract file headers switched to the formal RFC (the only sanctioned external reference). web-styling.md now cites the styling RFC instead of the disposable research archive. grep for work-log reference variants is clean across the GUI packages. docs(gui): file-header comments self-contained — drop RFC filename references RFC renames/reorgs must not require a source sweep (the 2026-07-20 two-way merge proved it). 11 headers lose only the '(RFC …)' tail and stay self-contained; api-proxy.ts keeps its minimal-first note. fix(gui): session streaming — freeze interrupted partials, sweep stale running calls, send force-scrolls Aborted turns never emit the finalizing assistant/message, so the accumulated partial and its running tool cards kept rendering below later messages — the "new message lands above the stopped reply" illusion. turn/end side effects now freeze content-bearing partials into interrupted terminal nodes (fractional seq keeps flow order; the live freeze and history replay converge through applyEventSideEffects, so a refresh reconstructs identical frozen nodes) and turn running tool cards into interrupted terminal cards; only content-free partials are swept outright. ConversationView gains the send-force-scroll rule (own words must be visible) alongside the pre-update atBottom follow flag. Regressions pinned as E2-4a–c (real host) and §E1-11h (fixture). feat(gui): webserver hardening verify script feat(gui): dark-mode toggle pinned to the sidebar bottom Interim home before the Settings page exists (the button re-homes with zero logic change — mechanics live in utils/theme.ts): html[data-theme] flip + dsc.theme localStorage, stored choice wins over the OS prefers-color-scheme default, applied in mount() before first paint so a dark reload never flashes light. Moon/sun inline SVG icon button at the sidebar's pinned bottom row. Pure front-end local concern: no RPC, no Session/store involvement. Dark sweep of list/conversation/input card/RPC panel found no unreadable pairs — no token changes needed. docs(gui): GUI RFCs and web styling handbook Layering+RPC protocol and web client architecture RFCs (post-reorg, developer-facing polish folded in) plus the styling engineering handbook. Mission work logs live in the commit above; PRs can be cut from this commit to include formal docs only. fix(gui): client object-layer hardening — audit timing/reference/resilience batches (S3-S5,C1-C3,C5-C8) fix(gui): carrier error channel + webserver backpressure (audit A1-A5,A7-A10,R2,R5) feat(gui): session persistence surface — cold list, project cwd, legacy no-cwd retirement refactor: rename dsc CLI to dsh — apps/cli, bin name, package scope Includes the root tsconfig project-references fix for host/* and client/web-runtime (originally a separate build fix commit). test(gui): three-tier suite — protocol/object/browser lanes, tier-a fill to per-file 100% test(gui): jsdom lane for web-ui + web-runtime coverage gate entry docs(gui): GUI testing system RFC (zh) feat(gui): tool-card views — contract slot, host-computed delivery, three-level card fallback fix(gui): lint clean across GUI packages — wrap long doc comments, drop dead type args, sync-return methods without awaits docs(gui): doc-sync mechanical fixes — JSDoc on apiproxy/host exports, RFC sketch fences ignore-check, md-wrap paragraphs, drop missions links, web-ui plain-ts entry chore(gui): module-graph regen + knip clean — drop dead re-exports, internalize createFixtureApi, scan web-ui tsx and verify mjs scripts build(gui): wire client/host packages into the lib build shape — tsc references + tsdown (web-ui css-external), lib manifests, cordis peer, apiproxy typed subpaths, vite src aliases test(gui): host-side per-file 100% coverage — apiproxy schema/carrier suites, webserver http-bridge suite, host-runtime composition suite; client/* coverage excluded pending the browser-side testing work item docs(gui): package READMEs for the five GUI packages — model-experience audit entries, limitations sections docs(gui): bilingual RFC pairs + client JSDoc completion — translate the three GUI RFCs to English with i18n records and manifest ratchet, Consequences sections both sides, full client/* export JSDoc, regen doc graphs and RFC index fix(scripts): doc-typecheck built-declarations mode maps /src/* subpath wildcards (apiproxy browser-safe channels) docs(gui): apply dsh rename across pr-gates docs — READMEs, layering RFC en, web-ui entry comment, i18n re-record fix(gui): post-rebase lint reconciliation — wrap main-tree long doc comments, read-through narrowing guards, abortError Error normalization, handleUnary generic justification fix(gui): post-rebase doc/test reconciliation — align host specs with evolved carrier contracts (sentinel rpcId, stream/error surfacing, url-path transport messages, defaults.cwd), Agent Note titles and relocated links, KV Cache effect sections, JSDoc on evolved exports fix(gui): second-rebase reconciliation to 509db0cb3 — restore api panel exports the baseline suites consume, knip workspace entries for jsdom lane and apps/web smokes, hoist result narrowing, align testing.md to the narrowed web-ui exclusion fix(test): vitest-scoped tsconfig maps bare imports for tsx specs — with GUI manifests now pointing at lib, an unmapped importer loaded a second copy of the web-runtime singletons fix(gui): typecheck + lint clean over the tool-card batch — brand callIds and object-form turn/end reason in the view spec, narrow fixture arg stringification, wrap long v8-ignore comments docs(gui): export JSDoc for tool-card surfaces + testing-note pairing header docs: rfc for web testing feat: add tools to host-runtime fix(gui): dispatch agent/error via agentEvents in host-runtime spec — mounted invariants plugin rejects raw ctx.emit without the scope carrier fix(gui): restore GUI knip workspaces + scripts/mjs entries and regenerate lockfile after master rebase fix(gui): post-rebase gate repairs — drop context-node envelope (master unwrapped injected content envelopes), regen event matrix, condense testing.md web-ui exclusion within budget fix(session): browser-safe deep-equal in surface — node:util import broke the vite bundle ci(gates): frontend vite build joins pre-push — node: imports in the client closure pass tsc but break the browser bundle test(tui): drop the checkout-dependent process.cwd() harness default — a long worktree path pushes the footer token counters past the 88-column fake terminal test(gui): jsdom behavior E2E — conversation main path over fixture runtime, reconnect banner lifecycle test(gui): jsdom RPC panel behavior — ledger rows, expand, pairing, pause/clear, follow-pause, payload truncation test(gui): jsdom tier-2 — InputBar guards, reasoning fold, JSON blocks, message variants, theme, create-then-select; act-harden banner case test(gui): jsdom tier-3 — ConversationView states/paging/force-bottom, ToolCallCard arms, PendingCard, list rows test(gui): jsdom tails — view-card variants, LogRow directions, registry hygiene, badge overflow, hook ops, mount glue test(gui): jsdom tails round 2 — call-ref blocks, resume follow, view precedence, failed create, empty-diff arm test(gui): jsdom final arms — anchor compensation, follow-off, interval ticks, view halves, node-over-running precedence test(gui): web-ui joins the per-file 100% coverage gate Annotation-only src changes plus the config swap. The web-ui exclusion is replaced by a single index.tsx entry (stale byte-identical duplicate of mount.tsx, nothing imports it; same entry-glue treatment as bin.ts) and the coverage include gains .tsx. v8-ignore sites (each with its reason inline): - ConversationView 3x ref-null guards; InputBar disabled-click guard - ToolCallCard both-null arms + windowless-custom argsRaw arm - LogRow css-module key fallbacks (start/stop block); RpcLogBody 3x ref-null guards - web-runtime drift from the tool-card batch: fixture presenter catch/str typo-guards, dense-array guards (fold-adapter reset, session rebuild, fixture backscan), live view-present arm (fixture replays are text-only; view vocabulary is covered by the history samples) test(gui): close the PR #443 host-side coverage gaps — apiproxy client abort arms, api-proxy cold/view paths, webserver drain - apiproxy fetch/client.ts: 3 new cases (pre-aborted signal short-circuits before transport + string reason mapping, non-Error/string reason falls to the default AbortError message, signal-less doFetch passthrough) - runtime/api-proxy.ts: one v8-ignore (summarizeCold cwd arm — list() filters cwd-less legacy metas) + api-proxy-cold.spec.ts (cold list merge: mtime source, locate-undefined and vanished-log fallbacks, lineage; no-persistence/no-factory resume → internal) + 2 view cases (history views with meta passthrough and orphan/bad-args/presenterless soft-falls, session/disposed open-call cleanup on the mux stream) - webserver/index.ts: /api/big fixture drives both drain-wait legs (full 8MiB readback after drain, mid-chunk disconnect wakes via 'close') feat: app shell fix: rebase conflicts fix: coverage fix(gui): lint clean after rebase — wrap long v8-ignore comments, unconditional v1 detail-block claim chore(gui): remove browser/probe verify scripts from scripts/ The six GUI acceptance/probe scripts (carrier-errors, rpclog-panel, session, session-real, webserver-backpressure, webserver-hardening) leave the repo's scripts/ tree; the three code comments that pointed at them now describe the coverage lane without naming a script path. fix(webserver): guard the request callback — one malformed request must not kill the process The async handle() had no top-level catch, so any throw inside it (a bad %-escape reaching decodeURIComponent, a client dropping mid-body, a response stream erroring) became an unhandled rejection and took the whole process down (audit R1 must-fix). The guard answers 400 when headers are not out yet, destroys the socket when they are, and reports the failure to onError (the package never prints). Spec covers all three legs: %-escape barrage → 400 + server stays alive, non-Error throw wrapped for onError, mid-stream explosion → socket teardown. feat: client AGENTS.md fix: client/AGENTS.md fix: rebase feat(gui): T0 cut 1 — 12 client package skeletons with contract stubs, dshClient declarations, tsdown client preset, theme token sheets feat(gui): T0 cut 2 — pure git mv migration per v3 §11 (connection six, runtime sessions/kernel, ui-conversation chat, ui-primitives markdown family, web shell + e2e) feat(gui): T0 cuts 3+4 — import rewiring to new package names, .legacy demotion of owner-rewrite files, legacy web-runtime/web-ui/apps-web retired to attic feat(gui): connection 对账刀——index.ts 精确导出清单替换 export *,intents.legacy 溶解删除 feat(client/ui-slots): SlotCore real implementation — kind semantics, sync version + microtask-batched notify, onMutate bridge feat(gui): web shell vite alias — retarget to new client packages, shell static surface only feat(gui): host 侧刀属地半——HostWebPluginRegistry(entries 扫描+internal/plugin 去抖重扫+dshClient 校验+exports./client 解析)、GET /plugins/<id>/client.js 分发端点、GET / 与 SPA fallback 注入 __DSH_BOOT__(webPlugins 可选注入,不传行为不变) feat(web-react): add use-sync-external-store dep + local shim typings feat(web-react): bindSnapshotSelector via uSES with-selector shim feat(gui): ui-layout concession-chain solver — pure computeColumns with contract geometry feat(gui): ui-layout LayoutService — four persisted stores, clamped actions, list-driven prune feat(gui): ui-layout AppFrame styles — grid columns, collapse-safe borders, edge drag handles test(gui): 存量 spec 平移——connection 三件+runtime 六件自 attic 捞回改包名路径全绿;api-helpers 按归属拆分(wire 半留 connection、classifier 半随 conversation.ts 入 runtime);boot-intents/preinit/rpc-log 随 intents/rpc-log 退役不迁(记 v3 §3.2 溶解项) feat(client/ui-primitives): StateDot/Button/Pill/Input/Menu atoms, ConnectionBanner de-legacied to pure props, JsonBlock CSS on --dsw tokens feat(web-react): createSnapshotStore engine (rafFlush batch, persist opt-in, dev freeze) + spec feat(gui): ui-layout AppFrame — grid tracks, pointer-capture drag handles with rAF throttle, frame ResizeObserver feat(web-react): useInvoke (external pending store, stable invoke, concurrency count) + spec test(web-react): bind spec — equality bail, custom eq, zero resubscribe, StrictMode, method sources feat(gui): ui-layout index rewiring — real exports, client apply provides ctx.layout and defines three slots feat(web-react): SessionProvider (renderBody deps) + RootBindingProvider + binding contexts + spec feat(gui): web shell AppRoot boot-page styles — self-contained with neutral token fallbacks feat(gui): web shell AppRoot — boot gate over loader status, fail-loud plugin failure list fix(gui): AppRoot gates on explicit settled signal — status-derived readiness races the incrementally filled table feat(client/ui-theme): ThemeService real implementation — registry with built-in light/dark, apply toggles body[data-ds-dark-theme], third-party token overrides as body inline vars feat(web-react): scopedSlots outlet (kind matrix, inject WeakMap caches, per-entry error boundary) + spec feat(gui): web shell module-table seed — pure-library entities for the loader require surface feat(client/i18n): I18nService real implementation — ns×locale registry, stable bind(ns) reference, zh fallback chain, zh/en skeleton dictionaries feat(gui): web shell assembly closure — layout exports via module table, SessionProvider + scopedSlots + RootBindingProvider feat: client/ui-conversation feat: code codedoc build(gui): root bundle green — web shell excluded from the lib workspace (vite app), ui-primitives lib externalizes css side-effect imports (web-ui precedent) gates(gui): verify-cordis-config follows aggregate tsconfig references (root is a shell over host/client programs); module graph regenerated for the twelve client packages chore(gui): retire legacy migration sources — every owner rewrite landed (t0-checklist §7 ledger honored); orphan css of retired components removed gates(gui): knip green groundwork — e2e/tsx entries for the new packages, loader-runtime deps ignored where loading is by specifier string, fake plugin ids un-bare-named, dead test export dropped chore(client): manifest shape batch A — ui-slots/web-react/ui-primitives invariant companions, files whitelist, cordis+invariants peer/dev, tsconfig refs chore(client): manifest shape batch B — connection/runtime/ui-conversation/ui-trajectory files whitelist, cordis peer+dev, explicit invariant lib entries (clientBundle signature) chore(client): manifest shape batch C — i18n/ui-layout/ui-sidebar/ui-theme invariant companions, files whitelist, invariants peer/dev, tsconfig refs chore(client): manifest shape batch D — web shell gains node-half lib entry + invariant companion + uniform files whitelist chore(client): drop verified-unused deps — dsh-tools from runtime/ui-conversation (types ride /presentation), ui-primitives+clsx from ui-layout gates(gui): doc-gate fixes — theme JSDoc prose, three client type-link exemptions, agent-note paths follow the migration, config catalog regenerated gates(gui): type-equiv manifest follows the types.ts extraction, approval JSDoc keeps its link form, persistence catalog regenerated docs(gui): per-constant JSDoc on the contract geometry exports (export-jsdoc gate) test(gates): loader-composition budget covers cold tsx resolution after the program split (was flaking at the default 5s) docs(gui): README substantiation batch 1 — ui-slots/ui-primitives/web-react/connection: Model Experience short form, real deferred-work ledgers, description accuracy pass fix(client): theme/i18n dual-entry split — service classes + cordis merges move to src/client (host catalog scanner no longer misclassifies client services), node halves keep types + empty apply; catalogs regenerated docs(gui): README substantiation batch 2 — runtime/ui-layout/ui-sidebar/ui-conversation: Model Experience short form, package-owned deferred-work ledgers (unload stub, watch approximation, /client value-import rule, global details state, two-state dots, stats duration gap, single-bundle caches) docs(gui): README substantiation batch 3 — ui-trajectory/ui-theme/i18n/web: Model Experience short form, deferred-work ledgers (placeholder charter, no theme toggle owner, empty locale dictionaries, one-shot rendering); both README gates green test(scripts): purity spec adopts clientBundle two-arg signature (explicit libEntry, no default) gates(gui): knip green — declaration-merge dep ignored, fake plugin id assembled at runtime, invariants dep de-duplicated to peer+dev, stale apps/web section dropped feat(gui): 门禁波次 host 三包 invariant 形状——apiproxy explained-empty 伴生(wire 契约层零事件面)、webserver 真关系伴生(manifest 行必解析出 clientPath,防 __DSH_BOOT__ 广告 404 bundle;apps/cli 发布 webPlugins 键供审计)、runtime 补 files 白名单;三包 exports/files/peer+dev/tsconfig refs 齐 fw-react 形状;constraints+invariants 双 gate 零违规 build(client): ui-layout/ui-sidebar tsdown configs adopt the explicit two-arg clientBundle signature (orphaned follow-up of the manifest shape batch) refactor(gui): shell boot becomes a library face — bootWebShell(el) exported for the apps/web entry; main.ts retired refactor(gui): exports 纪律刀1——ui-theme/i18n node index 收敛为只空 apply(Translate/LocaleDict/ThemeTokens 类型下沉 src/client/),ui-conversation 的 I18nService import 改 /client 子路径 build(typecheck): converge to root host aggregate + tsconfig.client.json — delete tsconfig.host.json, verify-cordis-config seeds both aggregates feat(gui): apps/web restored as the vite application — thin main over bootWebShell; dsh-client-web becomes a plain lib (index exports shell surface, vite files and e2e moved out) chore(gates): knip.json rewritten on the master base — same semantics, minimal diff (formatting churn dropped) docs(gui): 时效清扫②——testing.md 删 web-ui 覆盖豁免残句;web-styling.md 加 token 换代头注(--dsw-* 现行、工程约束条款仍有效并注明收编处) docs(gui): 时效清扫③——四对 GUI Agent Note 加路径更新头注(web-runtime/web-ui/dsh-frontend→现行 12 包结构;设计结论存续声明;双语对同步) docs(gui): 时效清扫③b——四对 note 头注的 i18n 配对哈希重录 build(typecheck): minimal-diff tsconfig shape — drop root files entry (purity spec + preset move to client program), compress comments, drop redundant util/home root ref feat(gui): apps/web restoration follow-through — dsh-frontend package name, cli dist resolve, root build:web filter, tsdown exemption dropped, vitest web lane + knip + client aggregate retargeted, e2e paths rebased refactor(gui): exports 纪律刀2——connection wire 六件 git mv 进 src/client/(wire 即该 dshClient 插件的 client 半),node index=只空 apply,/client 半边整面导出(v3 §3.2 清单原样),包内 tests 改 src/client 直取 refactor(gui): exports 纪律刀3——runtime 实现整体下沉 src/client/(sessions/slots/loader;契约类型与 cordis merge 随迁 client/index),node index=只空 apply;./loader exports 指 client/loader;全消费面(web 壳/ui-sidebar/ui-trajectory/tests)bare→/client 机械跟改;vitest.e2e 换 tsconfig.vitest paths(root tsconfig 排除 client 会把 /client import 掉到 exports 的浏览器 dist bundle) refactor(gui): exports 纪律刀3 补遗——ui-layout 三处 bare runtime import 改 /client(刀3 消费面机械跟改漏提交件;跨属地机械一行×3 报备 ui-shell) test(gui): drop the getSessionManager singleton case — the init/get pair is a dead legacy-boot surface with zero live consumers (SessionsService constructs and holds the manager under the plugin architecture); source removal tracked with rt-core refactor(gui): 删 manager.ts 尾部 initSessionManager/getSessionManager 单例对——旧 boot 直连遗物,插件化下 SessionsService 构造持有 manager,全仓零活消费者(convo-b 测试清扫对表,其测试用例已先行退役 7e2c51898);头注释同步去单例措辞 code refactor
2026-07-19 21:17:57 +08:00
| [`client-ui-primitives`](../packages/client/ui-primitives) | `client` | [`invariants`](../packages/support/invariants) |
| [`client-ui-slots`](../packages/client/ui-slots) | `client` | [`invariants`](../packages/support/invariants) |
| [`client-web`](../packages/client/web) | `client` | [`invariants`](../packages/support/invariants) |
| [`client-web-react`](../packages/client/web-react) | `client` | [`invariants`](../packages/support/invariants) |
| [`code-runtime`](../packages/code-runtime/code-runtime) | `code-runtime` | [`invariants`](../packages/support/invariants) |
| [`jsonrpc-demo`](../packages/examples/jsonrpc-demo) | `examples` | [`invariants`](../packages/support/invariants) |
feat(gui): step1 skeleton — dsc web serves built web UI over booted harness host Five new modules: apps/dsc (bin: parseArgs + node:http static server + signal shutdown), packages/host/apiproxy (programmatic harness core composition, agents:[]), packages/client/web-runtime (React-free browser runtime), packages/client/web-ui (React mount), apps/web (vite build entry producing dist consumed by apps/dsc via package exports). Root wiring: apps/* workspace glob, dsh-* paths for host/client groups, demo:web script, apps/web/dist gitignore. No protocol/API routes yet — contract lands in step2 (see missions/tasks/20260719-1902-apiproxy-api-design). Includes the design + implementation archives (spec v2.1, deepseekchat baseline and harness boot research, implementation run log). Acceptance: 12/12 passed incl. real-key llm.stream smoke (51 chunks). feat(gui): apiproxy — four-quadrant RPC contract + fetch carriers, live end to end Contract layer (src/api/, 14 files): four named wire message types (ClientRequest / ServerResponse / ServerRequest / ClientResponse) as a discriminated union over strict bidirectional rpcId (initiator mints, responder echoes; channel and message fully decoupled — HTTP is the client->server pipe, SSE the reverse); narrow RpcRequest<P>/ RpcResponse<T> signature forms; RpcMethodMap with RequestPayload<K>/ ResponseValue<K> derivation; typed RpcError details map; approval/ question responses modeled as ClientResponse via a single /api/respond endpoint (RpcReceipt carrier ack); zod schemas anchored per Wire<T> against exactOptionalPropertyTypes. impl/api-proxy.ts: describe/list/create, both SSE streams (frame queue pump, subscribed baseline, lifecycle frames, signal cleanup); history pages on message boundaries (tail-back scan, partial included in the tail page); prompt dispatches queue->agent.send / steer->agent.steer with rpcId carried through MessageSource; cancel for attached sessions; cold-session resume deduped via a per-id promise map; host-level provider/model defaults injected at create/resume. fetch/: mechanical UNARY_ROUTES table, two-level parse with path==method check, SSE frames completed to ServerRequest full form; client mints -> narrows -> envelopes outbound, verifies rpcId echo inbound, streams SSE frames, four-quadrant onEnvelope tap (debug panel choke point). Real-browser fixes: URL base resolves to location.origin (hardcoded internal base broke real pages), browser-safe export paths. Design archives: contract design.md v2.0 with decision log, core-coverage audit, comparative studies, step2 impl run log. Probed end to end over real HTTP: prompt -> live model stream -> history returns the finished reply. feat(gui): RpcLog debug panel — fixture-driven milestone, playwright-verified 10/10 web-runtime: rpcLog + ui slices (zustand), four-quadrant RpcLogEntry (client-request / server-response / server-request / client-response), onEnvelope tap -> microtask-batched pump with 500-entry ring buffer, ConnectionController (private state, backoff reconnect), fixture API with fake envelopes (?fixture switch), bootWebRuntime; contract types via temporary local copies (api-types.ts, swapped for real imports when W3 client lands). web-ui: components/panels/RpcLog five-piece set (badge with unread count, floating panel, direction glyphs per quadrant, same-rpcId pair highlighting in two families, JSON payload expand, follow/pause, clear), App shell, utils/formatRelative, light-theme CSS variables with dark placeholders. dsc bin: mime lookup fixed to use the actually-served file (naked '/?query' no longer falls through to octet-stream download); shutdown closes SSE keep-alive connections so SIGTERM actually exits. Acceptance: scripts/verify-rpclog-panel.mjs (chromium headless) ALL PASS 10/10 over design.md §D 1-6. pkg: add web scripts for building feat(gui): session milestone — list + conversation over Session OOP, styled RpcLog v2.1 web-runtime: Session/SessionManager object layer (resident instances, mux frame routing, lineage flattening), foldSurface adapter with padding sentinels for paged windows, chunk accumulator for streaming partials, batched change notification (useSyncExternalStore contract), connection sinks + reconnect fix (the 300ms self-abort reconnect storm that made the session list flap is gone), fixture rewritten as a scripted host (60-turn history, typewriter replay, resident pending approval, child session); temporary contract copies deleted in favor of real apiproxy imports. web-ui: sessions screen (list with lineage indent + selection as container-local state), conversation view (turn grouping, reasoning fold, tool cards, steering, pending interaction cards, upward paging with scroll anchoring), input bar with queue/steer/stop; RpcLog panel restyled per docs/web-styling.md (tokenized palette, quadrant badge glyphs now vertical ↑↓⇟⇞, pair highlighting, floating shadow). docs/web-styling.md: living style guide (tokens, visual baseline, coding rules, evolution log). Acceptance: verify-session.mjs 31/31, verify-session-real.mjs 5/5 (real model streaming), verify-rpclog-panel.mjs 10/10. feat(gui): hostruntime split + repo-wide package prefix rename Package split (design: 20260720-0101-hostruntime-split-design): dsh-host-runtime carries bootHost + createApiProxy + startHost() (RunningHost {api, handler, defaults, ctx, dispose} — the seam Electron and any future shell reuses; ctx is the official front-door mount point); dsh-host-webserver carries the node:http static+API bridge (fixed: abort now keys on res 'close' + writableEnded — req 'close' fires on body end since Node 16 and was killing every SSE stream instantly, the reconnect-storm root cause); apps/dsc is now a thin assembly with web/-p subcommands. dsc -p runs the full isomorphic carrier chain in process (second real protocol consumer; probed end-to-end against the live model). Naming rule (user decree): packages under host/ and client/ carry the directory prefix in their npm name — dsh-host-apiproxy, dsh-client-web-runtime, dsh-client-web-ui renamed repo-wide in one frozen batch; explicit tsconfig paths entries added where the wildcard no longer matches. Acceptance: verify-session 31/31, verify-rpclog-panel 10/10, verify-session-real 7/7 (incl. new 12s connection-stability sentinels), tsc green, dsc web + dsc -p smoke both pass. refactor(gui): AbstractApiClient class hierarchy — OO client with inheritable seams AbstractApiClient (apiproxy) carries every protocol invariant: rpcId minting, four-quadrant envelope wrap/unwrap, zod parsing, SSE frame parsing, the payload-direct IApiClient surface (callers no longer mint rpcIds — the carrier does), and the instance-level envelope observation pump (batched via microtask; moved off module-level globals in rpc-log.ts, which is now a pure subscriber mapping envelopes into store entries — the debug panel observes the connection, it is not part of it). Platform subclasses own two abstract seams (doFetch, onEnvelope) plus three protocol-level virtuals for transportless overrides: InProcessApiClient (apiproxy; dsc -p uses new InProcessApiClient( host.handler)), WebApiClient (web-runtime), FixtureApiClient (fixture now subclasses instead of wrapping). Naming per decree: AbstractApiClient / IApiClient; ApiProxy stays the impl-side narrow-form contract. headless.ts call sites drop rpcRequest wrappers (payload-direct); split-design archive updated with the naming-rule ledger. tsc green; verify-session 31/31, verify-rpclog-panel 10/10, verify-session-real 7/7 (12s connection sentinel count=4); dsc -p smoke CALLER-OK. feat(gui): InputBar final form — bug batch, deepseekchat layout, single primary button, running locks input Squashes the whole InputBar iteration batch: IME/caret/auto-grow/focus/dedup bug fixes, layout aligned to the deepseekchat baseline, single primary button with hover flyout, finalized button semantics with the Codex-style icon circle, and running-state locking where stop is the only mid-turn action. The same batch carried the Chinese-to-English code comment sweep (density pruned), folded in here. docs(gui): purge work-log references from code comments 76 design-doc references cleared across the GUI packages: section pointers inlined as self-contained constraint statements, pure pointer comments dropped, milestone codenames and ruling tags out, and the 14 contract file headers switched to the formal RFC (the only sanctioned external reference). web-styling.md now cites the styling RFC instead of the disposable research archive. grep for work-log reference variants is clean across the GUI packages. docs(gui): file-header comments self-contained — drop RFC filename references RFC renames/reorgs must not require a source sweep (the 2026-07-20 two-way merge proved it). 11 headers lose only the '(RFC …)' tail and stay self-contained; api-proxy.ts keeps its minimal-first note. fix(gui): session streaming — freeze interrupted partials, sweep stale running calls, send force-scrolls Aborted turns never emit the finalizing assistant/message, so the accumulated partial and its running tool cards kept rendering below later messages — the "new message lands above the stopped reply" illusion. turn/end side effects now freeze content-bearing partials into interrupted terminal nodes (fractional seq keeps flow order; the live freeze and history replay converge through applyEventSideEffects, so a refresh reconstructs identical frozen nodes) and turn running tool cards into interrupted terminal cards; only content-free partials are swept outright. ConversationView gains the send-force-scroll rule (own words must be visible) alongside the pre-update atBottom follow flag. Regressions pinned as E2-4a–c (real host) and §E1-11h (fixture). feat(gui): webserver hardening verify script feat(gui): dark-mode toggle pinned to the sidebar bottom Interim home before the Settings page exists (the button re-homes with zero logic change — mechanics live in utils/theme.ts): html[data-theme] flip + dsc.theme localStorage, stored choice wins over the OS prefers-color-scheme default, applied in mount() before first paint so a dark reload never flashes light. Moon/sun inline SVG icon button at the sidebar's pinned bottom row. Pure front-end local concern: no RPC, no Session/store involvement. Dark sweep of list/conversation/input card/RPC panel found no unreadable pairs — no token changes needed. docs(gui): GUI RFCs and web styling handbook Layering+RPC protocol and web client architecture RFCs (post-reorg, developer-facing polish folded in) plus the styling engineering handbook. Mission work logs live in the commit above; PRs can be cut from this commit to include formal docs only. fix(gui): client object-layer hardening — audit timing/reference/resilience batches (S3-S5,C1-C3,C5-C8) fix(gui): carrier error channel + webserver backpressure (audit A1-A5,A7-A10,R2,R5) feat(gui): session persistence surface — cold list, project cwd, legacy no-cwd retirement refactor: rename dsc CLI to dsh — apps/cli, bin name, package scope Includes the root tsconfig project-references fix for host/* and client/web-runtime (originally a separate build fix commit). test(gui): three-tier suite — protocol/object/browser lanes, tier-a fill to per-file 100% test(gui): jsdom lane for web-ui + web-runtime coverage gate entry docs(gui): GUI testing system RFC (zh) feat(gui): tool-card views — contract slot, host-computed delivery, three-level card fallback fix(gui): lint clean across GUI packages — wrap long doc comments, drop dead type args, sync-return methods without awaits docs(gui): doc-sync mechanical fixes — JSDoc on apiproxy/host exports, RFC sketch fences ignore-check, md-wrap paragraphs, drop missions links, web-ui plain-ts entry chore(gui): module-graph regen + knip clean — drop dead re-exports, internalize createFixtureApi, scan web-ui tsx and verify mjs scripts build(gui): wire client/host packages into the lib build shape — tsc references + tsdown (web-ui css-external), lib manifests, cordis peer, apiproxy typed subpaths, vite src aliases test(gui): host-side per-file 100% coverage — apiproxy schema/carrier suites, webserver http-bridge suite, host-runtime composition suite; client/* coverage excluded pending the browser-side testing work item docs(gui): package READMEs for the five GUI packages — model-experience audit entries, limitations sections docs(gui): bilingual RFC pairs + client JSDoc completion — translate the three GUI RFCs to English with i18n records and manifest ratchet, Consequences sections both sides, full client/* export JSDoc, regen doc graphs and RFC index fix(scripts): doc-typecheck built-declarations mode maps /src/* subpath wildcards (apiproxy browser-safe channels) docs(gui): apply dsh rename across pr-gates docs — READMEs, layering RFC en, web-ui entry comment, i18n re-record fix(gui): post-rebase lint reconciliation — wrap main-tree long doc comments, read-through narrowing guards, abortError Error normalization, handleUnary generic justification fix(gui): post-rebase doc/test reconciliation — align host specs with evolved carrier contracts (sentinel rpcId, stream/error surfacing, url-path transport messages, defaults.cwd), Agent Note titles and relocated links, KV Cache effect sections, JSDoc on evolved exports fix(gui): second-rebase reconciliation to 509db0cb3 — restore api panel exports the baseline suites consume, knip workspace entries for jsdom lane and apps/web smokes, hoist result narrowing, align testing.md to the narrowed web-ui exclusion fix(test): vitest-scoped tsconfig maps bare imports for tsx specs — with GUI manifests now pointing at lib, an unmapped importer loaded a second copy of the web-runtime singletons fix(gui): typecheck + lint clean over the tool-card batch — brand callIds and object-form turn/end reason in the view spec, narrow fixture arg stringification, wrap long v8-ignore comments docs(gui): export JSDoc for tool-card surfaces + testing-note pairing header docs: rfc for web testing feat: add tools to host-runtime fix(gui): dispatch agent/error via agentEvents in host-runtime spec — mounted invariants plugin rejects raw ctx.emit without the scope carrier fix(gui): restore GUI knip workspaces + scripts/mjs entries and regenerate lockfile after master rebase fix(gui): post-rebase gate repairs — drop context-node envelope (master unwrapped injected content envelopes), regen event matrix, condense testing.md web-ui exclusion within budget fix(session): browser-safe deep-equal in surface — node:util import broke the vite bundle ci(gates): frontend vite build joins pre-push — node: imports in the client closure pass tsc but break the browser bundle test(tui): drop the checkout-dependent process.cwd() harness default — a long worktree path pushes the footer token counters past the 88-column fake terminal test(gui): jsdom behavior E2E — conversation main path over fixture runtime, reconnect banner lifecycle test(gui): jsdom RPC panel behavior — ledger rows, expand, pairing, pause/clear, follow-pause, payload truncation test(gui): jsdom tier-2 — InputBar guards, reasoning fold, JSON blocks, message variants, theme, create-then-select; act-harden banner case test(gui): jsdom tier-3 — ConversationView states/paging/force-bottom, ToolCallCard arms, PendingCard, list rows test(gui): jsdom tails — view-card variants, LogRow directions, registry hygiene, badge overflow, hook ops, mount glue test(gui): jsdom tails round 2 — call-ref blocks, resume follow, view precedence, failed create, empty-diff arm test(gui): jsdom final arms — anchor compensation, follow-off, interval ticks, view halves, node-over-running precedence test(gui): web-ui joins the per-file 100% coverage gate Annotation-only src changes plus the config swap. The web-ui exclusion is replaced by a single index.tsx entry (stale byte-identical duplicate of mount.tsx, nothing imports it; same entry-glue treatment as bin.ts) and the coverage include gains .tsx. v8-ignore sites (each with its reason inline): - ConversationView 3x ref-null guards; InputBar disabled-click guard - ToolCallCard both-null arms + windowless-custom argsRaw arm - LogRow css-module key fallbacks (start/stop block); RpcLogBody 3x ref-null guards - web-runtime drift from the tool-card batch: fixture presenter catch/str typo-guards, dense-array guards (fold-adapter reset, session rebuild, fixture backscan), live view-present arm (fixture replays are text-only; view vocabulary is covered by the history samples) test(gui): close the PR #443 host-side coverage gaps — apiproxy client abort arms, api-proxy cold/view paths, webserver drain - apiproxy fetch/client.ts: 3 new cases (pre-aborted signal short-circuits before transport + string reason mapping, non-Error/string reason falls to the default AbortError message, signal-less doFetch passthrough) - runtime/api-proxy.ts: one v8-ignore (summarizeCold cwd arm — list() filters cwd-less legacy metas) + api-proxy-cold.spec.ts (cold list merge: mtime source, locate-undefined and vanished-log fallbacks, lineage; no-persistence/no-factory resume → internal) + 2 view cases (history views with meta passthrough and orphan/bad-args/presenterless soft-falls, session/disposed open-call cleanup on the mux stream) - webserver/index.ts: /api/big fixture drives both drain-wait legs (full 8MiB readback after drain, mid-chunk disconnect wakes via 'close') feat: app shell fix: rebase conflicts fix: coverage fix(gui): lint clean after rebase — wrap long v8-ignore comments, unconditional v1 detail-block claim chore(gui): remove browser/probe verify scripts from scripts/ The six GUI acceptance/probe scripts (carrier-errors, rpclog-panel, session, session-real, webserver-backpressure, webserver-hardening) leave the repo's scripts/ tree; the three code comments that pointed at them now describe the coverage lane without naming a script path. fix(webserver): guard the request callback — one malformed request must not kill the process The async handle() had no top-level catch, so any throw inside it (a bad %-escape reaching decodeURIComponent, a client dropping mid-body, a response stream erroring) became an unhandled rejection and took the whole process down (audit R1 must-fix). The guard answers 400 when headers are not out yet, destroys the socket when they are, and reports the failure to onError (the package never prints). Spec covers all three legs: %-escape barrage → 400 + server stays alive, non-Error throw wrapped for onError, mid-stream explosion → socket teardown. feat: client AGENTS.md fix: client/AGENTS.md fix: rebase feat(gui): T0 cut 1 — 12 client package skeletons with contract stubs, dshClient declarations, tsdown client preset, theme token sheets feat(gui): T0 cut 2 — pure git mv migration per v3 §11 (connection six, runtime sessions/kernel, ui-conversation chat, ui-primitives markdown family, web shell + e2e) feat(gui): T0 cuts 3+4 — import rewiring to new package names, .legacy demotion of owner-rewrite files, legacy web-runtime/web-ui/apps-web retired to attic feat(gui): connection 对账刀——index.ts 精确导出清单替换 export *,intents.legacy 溶解删除 feat(client/ui-slots): SlotCore real implementation — kind semantics, sync version + microtask-batched notify, onMutate bridge feat(gui): web shell vite alias — retarget to new client packages, shell static surface only feat(gui): host 侧刀属地半——HostWebPluginRegistry(entries 扫描+internal/plugin 去抖重扫+dshClient 校验+exports./client 解析)、GET /plugins/<id>/client.js 分发端点、GET / 与 SPA fallback 注入 __DSH_BOOT__(webPlugins 可选注入,不传行为不变) feat(web-react): add use-sync-external-store dep + local shim typings feat(web-react): bindSnapshotSelector via uSES with-selector shim feat(gui): ui-layout concession-chain solver — pure computeColumns with contract geometry feat(gui): ui-layout LayoutService — four persisted stores, clamped actions, list-driven prune feat(gui): ui-layout AppFrame styles — grid columns, collapse-safe borders, edge drag handles test(gui): 存量 spec 平移——connection 三件+runtime 六件自 attic 捞回改包名路径全绿;api-helpers 按归属拆分(wire 半留 connection、classifier 半随 conversation.ts 入 runtime);boot-intents/preinit/rpc-log 随 intents/rpc-log 退役不迁(记 v3 §3.2 溶解项) feat(client/ui-primitives): StateDot/Button/Pill/Input/Menu atoms, ConnectionBanner de-legacied to pure props, JsonBlock CSS on --dsw tokens feat(web-react): createSnapshotStore engine (rafFlush batch, persist opt-in, dev freeze) + spec feat(gui): ui-layout AppFrame — grid tracks, pointer-capture drag handles with rAF throttle, frame ResizeObserver feat(web-react): useInvoke (external pending store, stable invoke, concurrency count) + spec test(web-react): bind spec — equality bail, custom eq, zero resubscribe, StrictMode, method sources feat(gui): ui-layout index rewiring — real exports, client apply provides ctx.layout and defines three slots feat(web-react): SessionProvider (renderBody deps) + RootBindingProvider + binding contexts + spec feat(gui): web shell AppRoot boot-page styles — self-contained with neutral token fallbacks feat(gui): web shell AppRoot — boot gate over loader status, fail-loud plugin failure list fix(gui): AppRoot gates on explicit settled signal — status-derived readiness races the incrementally filled table feat(client/ui-theme): ThemeService real implementation — registry with built-in light/dark, apply toggles body[data-ds-dark-theme], third-party token overrides as body inline vars feat(web-react): scopedSlots outlet (kind matrix, inject WeakMap caches, per-entry error boundary) + spec feat(gui): web shell module-table seed — pure-library entities for the loader require surface feat(client/i18n): I18nService real implementation — ns×locale registry, stable bind(ns) reference, zh fallback chain, zh/en skeleton dictionaries feat(gui): web shell assembly closure — layout exports via module table, SessionProvider + scopedSlots + RootBindingProvider feat: client/ui-conversation feat: code codedoc build(gui): root bundle green — web shell excluded from the lib workspace (vite app), ui-primitives lib externalizes css side-effect imports (web-ui precedent) gates(gui): verify-cordis-config follows aggregate tsconfig references (root is a shell over host/client programs); module graph regenerated for the twelve client packages chore(gui): retire legacy migration sources — every owner rewrite landed (t0-checklist §7 ledger honored); orphan css of retired components removed gates(gui): knip green groundwork — e2e/tsx entries for the new packages, loader-runtime deps ignored where loading is by specifier string, fake plugin ids un-bare-named, dead test export dropped chore(client): manifest shape batch A — ui-slots/web-react/ui-primitives invariant companions, files whitelist, cordis+invariants peer/dev, tsconfig refs chore(client): manifest shape batch B — connection/runtime/ui-conversation/ui-trajectory files whitelist, cordis peer+dev, explicit invariant lib entries (clientBundle signature) chore(client): manifest shape batch C — i18n/ui-layout/ui-sidebar/ui-theme invariant companions, files whitelist, invariants peer/dev, tsconfig refs chore(client): manifest shape batch D — web shell gains node-half lib entry + invariant companion + uniform files whitelist chore(client): drop verified-unused deps — dsh-tools from runtime/ui-conversation (types ride /presentation), ui-primitives+clsx from ui-layout gates(gui): doc-gate fixes — theme JSDoc prose, three client type-link exemptions, agent-note paths follow the migration, config catalog regenerated gates(gui): type-equiv manifest follows the types.ts extraction, approval JSDoc keeps its link form, persistence catalog regenerated docs(gui): per-constant JSDoc on the contract geometry exports (export-jsdoc gate) test(gates): loader-composition budget covers cold tsx resolution after the program split (was flaking at the default 5s) docs(gui): README substantiation batch 1 — ui-slots/ui-primitives/web-react/connection: Model Experience short form, real deferred-work ledgers, description accuracy pass fix(client): theme/i18n dual-entry split — service classes + cordis merges move to src/client (host catalog scanner no longer misclassifies client services), node halves keep types + empty apply; catalogs regenerated docs(gui): README substantiation batch 2 — runtime/ui-layout/ui-sidebar/ui-conversation: Model Experience short form, package-owned deferred-work ledgers (unload stub, watch approximation, /client value-import rule, global details state, two-state dots, stats duration gap, single-bundle caches) docs(gui): README substantiation batch 3 — ui-trajectory/ui-theme/i18n/web: Model Experience short form, deferred-work ledgers (placeholder charter, no theme toggle owner, empty locale dictionaries, one-shot rendering); both README gates green test(scripts): purity spec adopts clientBundle two-arg signature (explicit libEntry, no default) gates(gui): knip green — declaration-merge dep ignored, fake plugin id assembled at runtime, invariants dep de-duplicated to peer+dev, stale apps/web section dropped feat(gui): 门禁波次 host 三包 invariant 形状——apiproxy explained-empty 伴生(wire 契约层零事件面)、webserver 真关系伴生(manifest 行必解析出 clientPath,防 __DSH_BOOT__ 广告 404 bundle;apps/cli 发布 webPlugins 键供审计)、runtime 补 files 白名单;三包 exports/files/peer+dev/tsconfig refs 齐 fw-react 形状;constraints+invariants 双 gate 零违规 build(client): ui-layout/ui-sidebar tsdown configs adopt the explicit two-arg clientBundle signature (orphaned follow-up of the manifest shape batch) refactor(gui): shell boot becomes a library face — bootWebShell(el) exported for the apps/web entry; main.ts retired refactor(gui): exports 纪律刀1——ui-theme/i18n node index 收敛为只空 apply(Translate/LocaleDict/ThemeTokens 类型下沉 src/client/),ui-conversation 的 I18nService import 改 /client 子路径 build(typecheck): converge to root host aggregate + tsconfig.client.json — delete tsconfig.host.json, verify-cordis-config seeds both aggregates feat(gui): apps/web restored as the vite application — thin main over bootWebShell; dsh-client-web becomes a plain lib (index exports shell surface, vite files and e2e moved out) chore(gates): knip.json rewritten on the master base — same semantics, minimal diff (formatting churn dropped) docs(gui): 时效清扫②——testing.md 删 web-ui 覆盖豁免残句;web-styling.md 加 token 换代头注(--dsw-* 现行、工程约束条款仍有效并注明收编处) docs(gui): 时效清扫③——四对 GUI Agent Note 加路径更新头注(web-runtime/web-ui/dsh-frontend→现行 12 包结构;设计结论存续声明;双语对同步) docs(gui): 时效清扫③b——四对 note 头注的 i18n 配对哈希重录 build(typecheck): minimal-diff tsconfig shape — drop root files entry (purity spec + preset move to client program), compress comments, drop redundant util/home root ref feat(gui): apps/web restoration follow-through — dsh-frontend package name, cli dist resolve, root build:web filter, tsdown exemption dropped, vitest web lane + knip + client aggregate retargeted, e2e paths rebased refactor(gui): exports 纪律刀2——connection wire 六件 git mv 进 src/client/(wire 即该 dshClient 插件的 client 半),node index=只空 apply,/client 半边整面导出(v3 §3.2 清单原样),包内 tests 改 src/client 直取 refactor(gui): exports 纪律刀3——runtime 实现整体下沉 src/client/(sessions/slots/loader;契约类型与 cordis merge 随迁 client/index),node index=只空 apply;./loader exports 指 client/loader;全消费面(web 壳/ui-sidebar/ui-trajectory/tests)bare→/client 机械跟改;vitest.e2e 换 tsconfig.vitest paths(root tsconfig 排除 client 会把 /client import 掉到 exports 的浏览器 dist bundle) refactor(gui): exports 纪律刀3 补遗——ui-layout 三处 bare runtime import 改 /client(刀3 消费面机械跟改漏提交件;跨属地机械一行×3 报备 ui-shell) test(gui): drop the getSessionManager singleton case — the init/get pair is a dead legacy-boot surface with zero live consumers (SessionsService constructs and holds the manager under the plugin architecture); source removal tracked with rt-core refactor(gui): 删 manager.ts 尾部 initSessionManager/getSessionManager 单例对——旧 boot 直连遗物,插件化下 SessionsService 构造持有 manager,全仓零活消费者(convo-b 测试清扫对表,其测试用例已先行退役 7e2c51898);头注释同步去单例措辞 code refactor
2026-07-19 21:17:57 +08:00
| [`host-apiproxy`](../packages/host/apiproxy) | `host` | [`invariants`](../packages/support/invariants) |
| [`host-directory-picker`](../packages/host/directory-picker) | `host` | [`invariants`](../packages/support/invariants) |
feat(gui): step1 skeleton — dsc web serves built web UI over booted harness host Five new modules: apps/dsc (bin: parseArgs + node:http static server + signal shutdown), packages/host/apiproxy (programmatic harness core composition, agents:[]), packages/client/web-runtime (React-free browser runtime), packages/client/web-ui (React mount), apps/web (vite build entry producing dist consumed by apps/dsc via package exports). Root wiring: apps/* workspace glob, dsh-* paths for host/client groups, demo:web script, apps/web/dist gitignore. No protocol/API routes yet — contract lands in step2 (see missions/tasks/20260719-1902-apiproxy-api-design). Includes the design + implementation archives (spec v2.1, deepseekchat baseline and harness boot research, implementation run log). Acceptance: 12/12 passed incl. real-key llm.stream smoke (51 chunks). feat(gui): apiproxy — four-quadrant RPC contract + fetch carriers, live end to end Contract layer (src/api/, 14 files): four named wire message types (ClientRequest / ServerResponse / ServerRequest / ClientResponse) as a discriminated union over strict bidirectional rpcId (initiator mints, responder echoes; channel and message fully decoupled — HTTP is the client->server pipe, SSE the reverse); narrow RpcRequest<P>/ RpcResponse<T> signature forms; RpcMethodMap with RequestPayload<K>/ ResponseValue<K> derivation; typed RpcError details map; approval/ question responses modeled as ClientResponse via a single /api/respond endpoint (RpcReceipt carrier ack); zod schemas anchored per Wire<T> against exactOptionalPropertyTypes. impl/api-proxy.ts: describe/list/create, both SSE streams (frame queue pump, subscribed baseline, lifecycle frames, signal cleanup); history pages on message boundaries (tail-back scan, partial included in the tail page); prompt dispatches queue->agent.send / steer->agent.steer with rpcId carried through MessageSource; cancel for attached sessions; cold-session resume deduped via a per-id promise map; host-level provider/model defaults injected at create/resume. fetch/: mechanical UNARY_ROUTES table, two-level parse with path==method check, SSE frames completed to ServerRequest full form; client mints -> narrows -> envelopes outbound, verifies rpcId echo inbound, streams SSE frames, four-quadrant onEnvelope tap (debug panel choke point). Real-browser fixes: URL base resolves to location.origin (hardcoded internal base broke real pages), browser-safe export paths. Design archives: contract design.md v2.0 with decision log, core-coverage audit, comparative studies, step2 impl run log. Probed end to end over real HTTP: prompt -> live model stream -> history returns the finished reply. feat(gui): RpcLog debug panel — fixture-driven milestone, playwright-verified 10/10 web-runtime: rpcLog + ui slices (zustand), four-quadrant RpcLogEntry (client-request / server-response / server-request / client-response), onEnvelope tap -> microtask-batched pump with 500-entry ring buffer, ConnectionController (private state, backoff reconnect), fixture API with fake envelopes (?fixture switch), bootWebRuntime; contract types via temporary local copies (api-types.ts, swapped for real imports when W3 client lands). web-ui: components/panels/RpcLog five-piece set (badge with unread count, floating panel, direction glyphs per quadrant, same-rpcId pair highlighting in two families, JSON payload expand, follow/pause, clear), App shell, utils/formatRelative, light-theme CSS variables with dark placeholders. dsc bin: mime lookup fixed to use the actually-served file (naked '/?query' no longer falls through to octet-stream download); shutdown closes SSE keep-alive connections so SIGTERM actually exits. Acceptance: scripts/verify-rpclog-panel.mjs (chromium headless) ALL PASS 10/10 over design.md §D 1-6. pkg: add web scripts for building feat(gui): session milestone — list + conversation over Session OOP, styled RpcLog v2.1 web-runtime: Session/SessionManager object layer (resident instances, mux frame routing, lineage flattening), foldSurface adapter with padding sentinels for paged windows, chunk accumulator for streaming partials, batched change notification (useSyncExternalStore contract), connection sinks + reconnect fix (the 300ms self-abort reconnect storm that made the session list flap is gone), fixture rewritten as a scripted host (60-turn history, typewriter replay, resident pending approval, child session); temporary contract copies deleted in favor of real apiproxy imports. web-ui: sessions screen (list with lineage indent + selection as container-local state), conversation view (turn grouping, reasoning fold, tool cards, steering, pending interaction cards, upward paging with scroll anchoring), input bar with queue/steer/stop; RpcLog panel restyled per docs/web-styling.md (tokenized palette, quadrant badge glyphs now vertical ↑↓⇟⇞, pair highlighting, floating shadow). docs/web-styling.md: living style guide (tokens, visual baseline, coding rules, evolution log). Acceptance: verify-session.mjs 31/31, verify-session-real.mjs 5/5 (real model streaming), verify-rpclog-panel.mjs 10/10. feat(gui): hostruntime split + repo-wide package prefix rename Package split (design: 20260720-0101-hostruntime-split-design): dsh-host-runtime carries bootHost + createApiProxy + startHost() (RunningHost {api, handler, defaults, ctx, dispose} — the seam Electron and any future shell reuses; ctx is the official front-door mount point); dsh-host-webserver carries the node:http static+API bridge (fixed: abort now keys on res 'close' + writableEnded — req 'close' fires on body end since Node 16 and was killing every SSE stream instantly, the reconnect-storm root cause); apps/dsc is now a thin assembly with web/-p subcommands. dsc -p runs the full isomorphic carrier chain in process (second real protocol consumer; probed end-to-end against the live model). Naming rule (user decree): packages under host/ and client/ carry the directory prefix in their npm name — dsh-host-apiproxy, dsh-client-web-runtime, dsh-client-web-ui renamed repo-wide in one frozen batch; explicit tsconfig paths entries added where the wildcard no longer matches. Acceptance: verify-session 31/31, verify-rpclog-panel 10/10, verify-session-real 7/7 (incl. new 12s connection-stability sentinels), tsc green, dsc web + dsc -p smoke both pass. refactor(gui): AbstractApiClient class hierarchy — OO client with inheritable seams AbstractApiClient (apiproxy) carries every protocol invariant: rpcId minting, four-quadrant envelope wrap/unwrap, zod parsing, SSE frame parsing, the payload-direct IApiClient surface (callers no longer mint rpcIds — the carrier does), and the instance-level envelope observation pump (batched via microtask; moved off module-level globals in rpc-log.ts, which is now a pure subscriber mapping envelopes into store entries — the debug panel observes the connection, it is not part of it). Platform subclasses own two abstract seams (doFetch, onEnvelope) plus three protocol-level virtuals for transportless overrides: InProcessApiClient (apiproxy; dsc -p uses new InProcessApiClient( host.handler)), WebApiClient (web-runtime), FixtureApiClient (fixture now subclasses instead of wrapping). Naming per decree: AbstractApiClient / IApiClient; ApiProxy stays the impl-side narrow-form contract. headless.ts call sites drop rpcRequest wrappers (payload-direct); split-design archive updated with the naming-rule ledger. tsc green; verify-session 31/31, verify-rpclog-panel 10/10, verify-session-real 7/7 (12s connection sentinel count=4); dsc -p smoke CALLER-OK. feat(gui): InputBar final form — bug batch, deepseekchat layout, single primary button, running locks input Squashes the whole InputBar iteration batch: IME/caret/auto-grow/focus/dedup bug fixes, layout aligned to the deepseekchat baseline, single primary button with hover flyout, finalized button semantics with the Codex-style icon circle, and running-state locking where stop is the only mid-turn action. The same batch carried the Chinese-to-English code comment sweep (density pruned), folded in here. docs(gui): purge work-log references from code comments 76 design-doc references cleared across the GUI packages: section pointers inlined as self-contained constraint statements, pure pointer comments dropped, milestone codenames and ruling tags out, and the 14 contract file headers switched to the formal RFC (the only sanctioned external reference). web-styling.md now cites the styling RFC instead of the disposable research archive. grep for work-log reference variants is clean across the GUI packages. docs(gui): file-header comments self-contained — drop RFC filename references RFC renames/reorgs must not require a source sweep (the 2026-07-20 two-way merge proved it). 11 headers lose only the '(RFC …)' tail and stay self-contained; api-proxy.ts keeps its minimal-first note. fix(gui): session streaming — freeze interrupted partials, sweep stale running calls, send force-scrolls Aborted turns never emit the finalizing assistant/message, so the accumulated partial and its running tool cards kept rendering below later messages — the "new message lands above the stopped reply" illusion. turn/end side effects now freeze content-bearing partials into interrupted terminal nodes (fractional seq keeps flow order; the live freeze and history replay converge through applyEventSideEffects, so a refresh reconstructs identical frozen nodes) and turn running tool cards into interrupted terminal cards; only content-free partials are swept outright. ConversationView gains the send-force-scroll rule (own words must be visible) alongside the pre-update atBottom follow flag. Regressions pinned as E2-4a–c (real host) and §E1-11h (fixture). feat(gui): webserver hardening verify script feat(gui): dark-mode toggle pinned to the sidebar bottom Interim home before the Settings page exists (the button re-homes with zero logic change — mechanics live in utils/theme.ts): html[data-theme] flip + dsc.theme localStorage, stored choice wins over the OS prefers-color-scheme default, applied in mount() before first paint so a dark reload never flashes light. Moon/sun inline SVG icon button at the sidebar's pinned bottom row. Pure front-end local concern: no RPC, no Session/store involvement. Dark sweep of list/conversation/input card/RPC panel found no unreadable pairs — no token changes needed. docs(gui): GUI RFCs and web styling handbook Layering+RPC protocol and web client architecture RFCs (post-reorg, developer-facing polish folded in) plus the styling engineering handbook. Mission work logs live in the commit above; PRs can be cut from this commit to include formal docs only. fix(gui): client object-layer hardening — audit timing/reference/resilience batches (S3-S5,C1-C3,C5-C8) fix(gui): carrier error channel + webserver backpressure (audit A1-A5,A7-A10,R2,R5) feat(gui): session persistence surface — cold list, project cwd, legacy no-cwd retirement refactor: rename dsc CLI to dsh — apps/cli, bin name, package scope Includes the root tsconfig project-references fix for host/* and client/web-runtime (originally a separate build fix commit). test(gui): three-tier suite — protocol/object/browser lanes, tier-a fill to per-file 100% test(gui): jsdom lane for web-ui + web-runtime coverage gate entry docs(gui): GUI testing system RFC (zh) feat(gui): tool-card views — contract slot, host-computed delivery, three-level card fallback fix(gui): lint clean across GUI packages — wrap long doc comments, drop dead type args, sync-return methods without awaits docs(gui): doc-sync mechanical fixes — JSDoc on apiproxy/host exports, RFC sketch fences ignore-check, md-wrap paragraphs, drop missions links, web-ui plain-ts entry chore(gui): module-graph regen + knip clean — drop dead re-exports, internalize createFixtureApi, scan web-ui tsx and verify mjs scripts build(gui): wire client/host packages into the lib build shape — tsc references + tsdown (web-ui css-external), lib manifests, cordis peer, apiproxy typed subpaths, vite src aliases test(gui): host-side per-file 100% coverage — apiproxy schema/carrier suites, webserver http-bridge suite, host-runtime composition suite; client/* coverage excluded pending the browser-side testing work item docs(gui): package READMEs for the five GUI packages — model-experience audit entries, limitations sections docs(gui): bilingual RFC pairs + client JSDoc completion — translate the three GUI RFCs to English with i18n records and manifest ratchet, Consequences sections both sides, full client/* export JSDoc, regen doc graphs and RFC index fix(scripts): doc-typecheck built-declarations mode maps /src/* subpath wildcards (apiproxy browser-safe channels) docs(gui): apply dsh rename across pr-gates docs — READMEs, layering RFC en, web-ui entry comment, i18n re-record fix(gui): post-rebase lint reconciliation — wrap main-tree long doc comments, read-through narrowing guards, abortError Error normalization, handleUnary generic justification fix(gui): post-rebase doc/test reconciliation — align host specs with evolved carrier contracts (sentinel rpcId, stream/error surfacing, url-path transport messages, defaults.cwd), Agent Note titles and relocated links, KV Cache effect sections, JSDoc on evolved exports fix(gui): second-rebase reconciliation to 509db0cb3 — restore api panel exports the baseline suites consume, knip workspace entries for jsdom lane and apps/web smokes, hoist result narrowing, align testing.md to the narrowed web-ui exclusion fix(test): vitest-scoped tsconfig maps bare imports for tsx specs — with GUI manifests now pointing at lib, an unmapped importer loaded a second copy of the web-runtime singletons fix(gui): typecheck + lint clean over the tool-card batch — brand callIds and object-form turn/end reason in the view spec, narrow fixture arg stringification, wrap long v8-ignore comments docs(gui): export JSDoc for tool-card surfaces + testing-note pairing header docs: rfc for web testing feat: add tools to host-runtime fix(gui): dispatch agent/error via agentEvents in host-runtime spec — mounted invariants plugin rejects raw ctx.emit without the scope carrier fix(gui): restore GUI knip workspaces + scripts/mjs entries and regenerate lockfile after master rebase fix(gui): post-rebase gate repairs — drop context-node envelope (master unwrapped injected content envelopes), regen event matrix, condense testing.md web-ui exclusion within budget fix(session): browser-safe deep-equal in surface — node:util import broke the vite bundle ci(gates): frontend vite build joins pre-push — node: imports in the client closure pass tsc but break the browser bundle test(tui): drop the checkout-dependent process.cwd() harness default — a long worktree path pushes the footer token counters past the 88-column fake terminal test(gui): jsdom behavior E2E — conversation main path over fixture runtime, reconnect banner lifecycle test(gui): jsdom RPC panel behavior — ledger rows, expand, pairing, pause/clear, follow-pause, payload truncation test(gui): jsdom tier-2 — InputBar guards, reasoning fold, JSON blocks, message variants, theme, create-then-select; act-harden banner case test(gui): jsdom tier-3 — ConversationView states/paging/force-bottom, ToolCallCard arms, PendingCard, list rows test(gui): jsdom tails — view-card variants, LogRow directions, registry hygiene, badge overflow, hook ops, mount glue test(gui): jsdom tails round 2 — call-ref blocks, resume follow, view precedence, failed create, empty-diff arm test(gui): jsdom final arms — anchor compensation, follow-off, interval ticks, view halves, node-over-running precedence test(gui): web-ui joins the per-file 100% coverage gate Annotation-only src changes plus the config swap. The web-ui exclusion is replaced by a single index.tsx entry (stale byte-identical duplicate of mount.tsx, nothing imports it; same entry-glue treatment as bin.ts) and the coverage include gains .tsx. v8-ignore sites (each with its reason inline): - ConversationView 3x ref-null guards; InputBar disabled-click guard - ToolCallCard both-null arms + windowless-custom argsRaw arm - LogRow css-module key fallbacks (start/stop block); RpcLogBody 3x ref-null guards - web-runtime drift from the tool-card batch: fixture presenter catch/str typo-guards, dense-array guards (fold-adapter reset, session rebuild, fixture backscan), live view-present arm (fixture replays are text-only; view vocabulary is covered by the history samples) test(gui): close the PR #443 host-side coverage gaps — apiproxy client abort arms, api-proxy cold/view paths, webserver drain - apiproxy fetch/client.ts: 3 new cases (pre-aborted signal short-circuits before transport + string reason mapping, non-Error/string reason falls to the default AbortError message, signal-less doFetch passthrough) - runtime/api-proxy.ts: one v8-ignore (summarizeCold cwd arm — list() filters cwd-less legacy metas) + api-proxy-cold.spec.ts (cold list merge: mtime source, locate-undefined and vanished-log fallbacks, lineage; no-persistence/no-factory resume → internal) + 2 view cases (history views with meta passthrough and orphan/bad-args/presenterless soft-falls, session/disposed open-call cleanup on the mux stream) - webserver/index.ts: /api/big fixture drives both drain-wait legs (full 8MiB readback after drain, mid-chunk disconnect wakes via 'close') feat: app shell fix: rebase conflicts fix: coverage fix(gui): lint clean after rebase — wrap long v8-ignore comments, unconditional v1 detail-block claim chore(gui): remove browser/probe verify scripts from scripts/ The six GUI acceptance/probe scripts (carrier-errors, rpclog-panel, session, session-real, webserver-backpressure, webserver-hardening) leave the repo's scripts/ tree; the three code comments that pointed at them now describe the coverage lane without naming a script path. fix(webserver): guard the request callback — one malformed request must not kill the process The async handle() had no top-level catch, so any throw inside it (a bad %-escape reaching decodeURIComponent, a client dropping mid-body, a response stream erroring) became an unhandled rejection and took the whole process down (audit R1 must-fix). The guard answers 400 when headers are not out yet, destroys the socket when they are, and reports the failure to onError (the package never prints). Spec covers all three legs: %-escape barrage → 400 + server stays alive, non-Error throw wrapped for onError, mid-stream explosion → socket teardown. feat: client AGENTS.md fix: client/AGENTS.md fix: rebase feat(gui): T0 cut 1 — 12 client package skeletons with contract stubs, dshClient declarations, tsdown client preset, theme token sheets feat(gui): T0 cut 2 — pure git mv migration per v3 §11 (connection six, runtime sessions/kernel, ui-conversation chat, ui-primitives markdown family, web shell + e2e) feat(gui): T0 cuts 3+4 — import rewiring to new package names, .legacy demotion of owner-rewrite files, legacy web-runtime/web-ui/apps-web retired to attic feat(gui): connection 对账刀——index.ts 精确导出清单替换 export *,intents.legacy 溶解删除 feat(client/ui-slots): SlotCore real implementation — kind semantics, sync version + microtask-batched notify, onMutate bridge feat(gui): web shell vite alias — retarget to new client packages, shell static surface only feat(gui): host 侧刀属地半——HostWebPluginRegistry(entries 扫描+internal/plugin 去抖重扫+dshClient 校验+exports./client 解析)、GET /plugins/<id>/client.js 分发端点、GET / 与 SPA fallback 注入 __DSH_BOOT__(webPlugins 可选注入,不传行为不变) feat(web-react): add use-sync-external-store dep + local shim typings feat(web-react): bindSnapshotSelector via uSES with-selector shim feat(gui): ui-layout concession-chain solver — pure computeColumns with contract geometry feat(gui): ui-layout LayoutService — four persisted stores, clamped actions, list-driven prune feat(gui): ui-layout AppFrame styles — grid columns, collapse-safe borders, edge drag handles test(gui): 存量 spec 平移——connection 三件+runtime 六件自 attic 捞回改包名路径全绿;api-helpers 按归属拆分(wire 半留 connection、classifier 半随 conversation.ts 入 runtime);boot-intents/preinit/rpc-log 随 intents/rpc-log 退役不迁(记 v3 §3.2 溶解项) feat(client/ui-primitives): StateDot/Button/Pill/Input/Menu atoms, ConnectionBanner de-legacied to pure props, JsonBlock CSS on --dsw tokens feat(web-react): createSnapshotStore engine (rafFlush batch, persist opt-in, dev freeze) + spec feat(gui): ui-layout AppFrame — grid tracks, pointer-capture drag handles with rAF throttle, frame ResizeObserver feat(web-react): useInvoke (external pending store, stable invoke, concurrency count) + spec test(web-react): bind spec — equality bail, custom eq, zero resubscribe, StrictMode, method sources feat(gui): ui-layout index rewiring — real exports, client apply provides ctx.layout and defines three slots feat(web-react): SessionProvider (renderBody deps) + RootBindingProvider + binding contexts + spec feat(gui): web shell AppRoot boot-page styles — self-contained with neutral token fallbacks feat(gui): web shell AppRoot — boot gate over loader status, fail-loud plugin failure list fix(gui): AppRoot gates on explicit settled signal — status-derived readiness races the incrementally filled table feat(client/ui-theme): ThemeService real implementation — registry with built-in light/dark, apply toggles body[data-ds-dark-theme], third-party token overrides as body inline vars feat(web-react): scopedSlots outlet (kind matrix, inject WeakMap caches, per-entry error boundary) + spec feat(gui): web shell module-table seed — pure-library entities for the loader require surface feat(client/i18n): I18nService real implementation — ns×locale registry, stable bind(ns) reference, zh fallback chain, zh/en skeleton dictionaries feat(gui): web shell assembly closure — layout exports via module table, SessionProvider + scopedSlots + RootBindingProvider feat: client/ui-conversation feat: code codedoc build(gui): root bundle green — web shell excluded from the lib workspace (vite app), ui-primitives lib externalizes css side-effect imports (web-ui precedent) gates(gui): verify-cordis-config follows aggregate tsconfig references (root is a shell over host/client programs); module graph regenerated for the twelve client packages chore(gui): retire legacy migration sources — every owner rewrite landed (t0-checklist §7 ledger honored); orphan css of retired components removed gates(gui): knip green groundwork — e2e/tsx entries for the new packages, loader-runtime deps ignored where loading is by specifier string, fake plugin ids un-bare-named, dead test export dropped chore(client): manifest shape batch A — ui-slots/web-react/ui-primitives invariant companions, files whitelist, cordis+invariants peer/dev, tsconfig refs chore(client): manifest shape batch B — connection/runtime/ui-conversation/ui-trajectory files whitelist, cordis peer+dev, explicit invariant lib entries (clientBundle signature) chore(client): manifest shape batch C — i18n/ui-layout/ui-sidebar/ui-theme invariant companions, files whitelist, invariants peer/dev, tsconfig refs chore(client): manifest shape batch D — web shell gains node-half lib entry + invariant companion + uniform files whitelist chore(client): drop verified-unused deps — dsh-tools from runtime/ui-conversation (types ride /presentation), ui-primitives+clsx from ui-layout gates(gui): doc-gate fixes — theme JSDoc prose, three client type-link exemptions, agent-note paths follow the migration, config catalog regenerated gates(gui): type-equiv manifest follows the types.ts extraction, approval JSDoc keeps its link form, persistence catalog regenerated docs(gui): per-constant JSDoc on the contract geometry exports (export-jsdoc gate) test(gates): loader-composition budget covers cold tsx resolution after the program split (was flaking at the default 5s) docs(gui): README substantiation batch 1 — ui-slots/ui-primitives/web-react/connection: Model Experience short form, real deferred-work ledgers, description accuracy pass fix(client): theme/i18n dual-entry split — service classes + cordis merges move to src/client (host catalog scanner no longer misclassifies client services), node halves keep types + empty apply; catalogs regenerated docs(gui): README substantiation batch 2 — runtime/ui-layout/ui-sidebar/ui-conversation: Model Experience short form, package-owned deferred-work ledgers (unload stub, watch approximation, /client value-import rule, global details state, two-state dots, stats duration gap, single-bundle caches) docs(gui): README substantiation batch 3 — ui-trajectory/ui-theme/i18n/web: Model Experience short form, deferred-work ledgers (placeholder charter, no theme toggle owner, empty locale dictionaries, one-shot rendering); both README gates green test(scripts): purity spec adopts clientBundle two-arg signature (explicit libEntry, no default) gates(gui): knip green — declaration-merge dep ignored, fake plugin id assembled at runtime, invariants dep de-duplicated to peer+dev, stale apps/web section dropped feat(gui): 门禁波次 host 三包 invariant 形状——apiproxy explained-empty 伴生(wire 契约层零事件面)、webserver 真关系伴生(manifest 行必解析出 clientPath,防 __DSH_BOOT__ 广告 404 bundle;apps/cli 发布 webPlugins 键供审计)、runtime 补 files 白名单;三包 exports/files/peer+dev/tsconfig refs 齐 fw-react 形状;constraints+invariants 双 gate 零违规 build(client): ui-layout/ui-sidebar tsdown configs adopt the explicit two-arg clientBundle signature (orphaned follow-up of the manifest shape batch) refactor(gui): shell boot becomes a library face — bootWebShell(el) exported for the apps/web entry; main.ts retired refactor(gui): exports 纪律刀1——ui-theme/i18n node index 收敛为只空 apply(Translate/LocaleDict/ThemeTokens 类型下沉 src/client/),ui-conversation 的 I18nService import 改 /client 子路径 build(typecheck): converge to root host aggregate + tsconfig.client.json — delete tsconfig.host.json, verify-cordis-config seeds both aggregates feat(gui): apps/web restored as the vite application — thin main over bootWebShell; dsh-client-web becomes a plain lib (index exports shell surface, vite files and e2e moved out) chore(gates): knip.json rewritten on the master base — same semantics, minimal diff (formatting churn dropped) docs(gui): 时效清扫②——testing.md 删 web-ui 覆盖豁免残句;web-styling.md 加 token 换代头注(--dsw-* 现行、工程约束条款仍有效并注明收编处) docs(gui): 时效清扫③——四对 GUI Agent Note 加路径更新头注(web-runtime/web-ui/dsh-frontend→现行 12 包结构;设计结论存续声明;双语对同步) docs(gui): 时效清扫③b——四对 note 头注的 i18n 配对哈希重录 build(typecheck): minimal-diff tsconfig shape — drop root files entry (purity spec + preset move to client program), compress comments, drop redundant util/home root ref feat(gui): apps/web restoration follow-through — dsh-frontend package name, cli dist resolve, root build:web filter, tsdown exemption dropped, vitest web lane + knip + client aggregate retargeted, e2e paths rebased refactor(gui): exports 纪律刀2——connection wire 六件 git mv 进 src/client/(wire 即该 dshClient 插件的 client 半),node index=只空 apply,/client 半边整面导出(v3 §3.2 清单原样),包内 tests 改 src/client 直取 refactor(gui): exports 纪律刀3——runtime 实现整体下沉 src/client/(sessions/slots/loader;契约类型与 cordis merge 随迁 client/index),node index=只空 apply;./loader exports 指 client/loader;全消费面(web 壳/ui-sidebar/ui-trajectory/tests)bare→/client 机械跟改;vitest.e2e 换 tsconfig.vitest paths(root tsconfig 排除 client 会把 /client import 掉到 exports 的浏览器 dist bundle) refactor(gui): exports 纪律刀3 补遗——ui-layout 三处 bare runtime import 改 /client(刀3 消费面机械跟改漏提交件;跨属地机械一行×3 报备 ui-shell) test(gui): drop the getSessionManager singleton case — the init/get pair is a dead legacy-boot surface with zero live consumers (SessionsService constructs and holds the manager under the plugin architecture); source removal tracked with rt-core refactor(gui): 删 manager.ts 尾部 initSessionManager/getSessionManager 单例对——旧 boot 直连遗物,插件化下 SessionsService 构造持有 manager,全仓零活消费者(convo-b 测试清扫对表,其测试用例已先行退役 7e2c51898);头注释同步去单例措辞 code refactor
2026-07-19 21:17:57 +08:00
| [`host-webserver`](../packages/host/webserver) | `host` | [`invariants`](../packages/support/invariants) |
| [`storage`](../packages/storage/storage) | `storage` | [`invariants`](../packages/support/invariants) |
| [`subprocess`](../packages/subprocess/subprocess) | `subprocess` | [`invariants`](../packages/support/invariants) |
| [`typert-generator`](../packages/typert/generator) | `typert` | [`invariants`](../packages/support/invariants) |
| [`typert-registry`](../packages/typert/registry) | `typert` | [`invariants`](../packages/support/invariants) |
2026-07-25 10:38:42 +08:00
| [`llm`](../packages/llm/llm) | `llm` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`timeout`](../packages/util/timeout) |
| [`client-connection`](../packages/client/connection) | `client` | [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/support/invariants) |
| [`client-hmr`](../packages/client/hmr) | `client` | [`client-modules`](../packages/client/modules), [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/support/invariants) |
| [`client-locale`](../packages/client/locale) | `client` | [`client-runtime`](../packages/client/runtime), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants) |
| [`client-test-runtime`](../packages/client/test-runtime) | `client` | [`client-runtime`](../packages/client/runtime), [`client-ui-slots`](../packages/client/ui-slots), [`client-web-react`](../packages/client/web-react), [`host-apiproxy`](../packages/host/apiproxy), [`invariants`](../packages/support/invariants) |
| [`client-ui-settings`](../packages/client/ui-settings) | `client` | [`client-runtime`](../packages/client/runtime), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants) |
| [`client-ui-trajectory`](../packages/client/ui-trajectory) | `client` | [`client-runtime`](../packages/client/runtime), [`client-ui-primitives`](../packages/client/ui-primitives), [`invariants`](../packages/support/invariants) |
| [`credentials`](../packages/credentials/credentials) | `credentials` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants) |
feat(subprocess): migrate lsp-local, subagent-acp, and the env scrubs onto the seam Review direction (tianyicui, PR #660): in a stacked PR, change all other process-running places to use the new service. - lsp-local: LspConnection spawns through ctx.subprocess (piped protocol streams + a no-spill collected stderr tail); its private process-tree helpers (POSIX group signalling, Windows taskkill, liveness polling) are deleted in favor of the seam's handle verbs, and its buildChildEnv now rides scrubbedParentEnv (LSP children also stop inheriting stale DSH_*). The plugin injects 'subprocess'; compositions/tests mount dsh-subprocess-local. - subagent-acp: the ACP child spawns through the seam (piped ndjson streams, inherited stderr); spawn failure surfaces through done-rejection into the same startup race; disposal is handle.dispose with the plugin's configured graces. dsh-subagent-subprocess is DELETED — its dispose ladder and scrub are the seam's, and the isolated-config-dir helper had no consumer. - mcp-client, pty-local, sdk-helper: adopt scrubbedParentEnv as the one scrub definition (their spawns stay put by ownership: the MCP SDK and node-pty own those calls; the SDK wizard runs outside any composition). - Coverage: per-file 100% over every touched src file, with each v8 ignore carrying a platform or contract reason; new suites cover stdio dispositions, the dispose ladder tiers, injected-win32 tree semantics, waitForExit, settled-kill/terminate no-ops, and spawn-failure disposal. - Docs: consumer-migration Agent Note (en; zh follows in this PR), seam note updated in place, subprocess.md rewritten for the reshaped vocabulary (type-equiv re-registered), READMEs and SERVICE_ROLES updated, taskkill added to knip ignoreBinaries.
2026-07-26 15:27:59 +08:00
| [`helper`](../packages/sdk/helper) | `sdk` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`subprocess`](../packages/subprocess/subprocess) |
| [`telemetry`](../packages/sdk/telemetry) | `sdk` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`paths`](../packages/util/paths) |
| [`settings`](../packages/settings/settings) | `settings` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants) |
| [`storage-domain`](../packages/storage/storage-domain) | `storage` | [`invariants`](../packages/support/invariants), [`storage`](../packages/storage/storage) |
| [`storage-json`](../packages/storage/storage-json) | `storage` | [`invariants`](../packages/support/invariants), [`storage`](../packages/storage/storage) |
| [`storage-sqlite`](../packages/storage/storage-sqlite) | `storage` | [`invariants`](../packages/support/invariants), [`storage`](../packages/storage/storage) |
| [`subprocess-local`](../packages/subprocess/subprocess-local) | `subprocess` | [`invariants`](../packages/support/invariants), [`subprocess`](../packages/subprocess/subprocess) |
| [`typert-loader`](../packages/typert/loader) | `typert` | [`invariants`](../packages/support/invariants), [`typert-registry`](../packages/typert/registry) |
| [`llm-deepseek`](../packages/llm/llm-deepseek) | `llm` | [`credentials`](../packages/credentials/credentials), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`settings`](../packages/settings/settings), [`timeout`](../packages/util/timeout) |
| [`llm-pi-ai`](../packages/llm/llm-pi-ai) | `llm` | [`credentials`](../packages/credentials/credentials), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`settings`](../packages/settings/settings), [`timeout`](../packages/util/timeout) |
| [`session`](../packages/core/session) | `core` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope) |
| [`system-prompt`](../packages/core/system-prompt) | `core` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope) |
| [`web`](../packages/web/web) | `web` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm) |
| [`client-ui-models`](../packages/client/ui-models) | `client` | [`client-connection`](../packages/client/connection), [`client-runtime`](../packages/client/runtime), [`client-schema-form`](../packages/client/schema-form), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slots`](../packages/client/ui-slots), [`client-web-react`](../packages/client/web-react), [`invariants`](../packages/support/invariants) |
| [`client-ui-question`](../packages/client/ui-question) | `client` | [`client-locale`](../packages/client/locale), [`invariants`](../packages/support/invariants) |
| [`client-ui-settings-general`](../packages/client/ui-settings-general) | `client` | [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-settings`](../packages/client/ui-settings), [`client-ui-slots`](../packages/client/ui-slots), [`client-web-react`](../packages/client/web-react), [`invariants`](../packages/support/invariants) |
| [`client-ui-sidebar`](../packages/client/ui-sidebar) | `client` | [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants) |
2026-07-29 21:40:45 +08:00
| [`client-ui-slash`](../packages/client/ui-slash) | `client` | [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants) |
| [`client-ui-theme`](../packages/client/ui-theme) | `client` | [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants) |
2026-07-30 15:56:41 +08:00
| [`client-ui-workspace`](../packages/client/ui-workspace) | `client` | [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants) |
| [`credentials-local`](../packages/credentials/credentials-local) | `credentials` | [`atomic-write`](../packages/util/atomic-write), [`credentials`](../packages/credentials/credentials), [`invariants`](../packages/support/invariants), [`paths`](../packages/util/paths) |
| [`lsp`](../packages/lsp/lsp) | `lsp` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm) |
| [`sandbox`](../packages/sandbox/sandbox) | `sandbox` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm) |
| [`settings-local`](../packages/settings/settings-local) | `settings` | [`atomic-write`](../packages/util/atomic-write), [`invariants`](../packages/support/invariants), [`paths`](../packages/util/paths), [`settings`](../packages/settings/settings) |
| [`agent`](../packages/core/agent) | `core` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt) |
| [`bash`](../packages/bash/bash) | `bash` | [`invariants`](../packages/support/invariants), [`sandbox`](../packages/sandbox/sandbox), [`subprocess`](../packages/subprocess/subprocess) |
| [`fs`](../packages/fs/fs) | `fs` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox) |
| [`compact`](../packages/compact/compact) | `compact` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
| [`compact-tool-result-prune`](../packages/compact/compact-tool-result-prune) | `compact` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
| [`web-fetch-local`](../packages/web/web-fetch-local) | `web` | [`invariants`](../packages/support/invariants), [`timeout`](../packages/util/timeout), [`web`](../packages/web/web) |
| [`web-search-exa`](../packages/web/web-search-exa) | `web` | [`invariants`](../packages/support/invariants), [`web`](../packages/web/web) |
| [`web-search-perplexity`](../packages/web/web-search-perplexity) | `web` | [`invariants`](../packages/support/invariants), [`web`](../packages/web/web) |
| [`spill`](../packages/spill/spill) | `spill` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
Merge remote-tracking branch 'origin/master' into session-query-search # Conflicts: # .agents/notes/implemented/feature/2026-07-10-session-query-service.md # .agents/notes/implemented/feature/2026-07-10-sqlite-session-query-provider.md # .agents/notes/proposed/feature/2026-07-10-sqlite-session-query-provider.md # docs/architecture.md # docs/capability-seams.md # docs/config-catalog.md # docs/cordis-catalog/services.md # docs/core-data-structures/core.md # docs/core-data-structures/persistence.md # docs/core-data-structures/session-query.md # docs/module-graph.md # docs/rfc/INDEX.md # packages/README.md # packages/cordis/tool-cordis/src/api-catalog.ts # packages/hooks/hooks-claude/tests/coverage.spec.ts # packages/session-persistence/session-persistence-jsonl/src/index.ts # packages/session-persistence/session-persistence-jsonl/tests/jsonl.spec.ts # packages/session-persistence/session-persistence-sqlite/README.md # packages/session-persistence/session-persistence-sqlite/src/index.ts # packages/session-persistence/session-persistence-sqlite/src/schema.ts # packages/session-persistence/session-persistence-sqlite/tests/sqlite.spec.ts # packages/session-persistence/session-persistence/README.md # packages/session-persistence/session-persistence/package.json # packages/session-query/README.md # packages/session-query/session-query/README.md # packages/session-query/session-query/package.json # packages/session-query/session-query/src/config.ts # packages/session-query/session-query/src/index.ts # packages/session-query/session-query/src/types.ts # pnpm-lock.yaml # scripts/gen-doc-graphs.ts # scripts/type-equiv.manifest.json # tsconfig.host.json # tsconfig.json
2026-07-23 13:56:56 +08:00
| [`session-persistence`](../packages/session-persistence/session-persistence) | `session-persistence` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`session`](../packages/core/session) |
| [`llm-replay`](../packages/support/llm-replay) | `support` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
| [`app-boot`](../packages/ui/app-boot) | `ui` | [`invariants`](../packages/support/invariants), [`paths`](../packages/util/paths), [`system-prompt`](../packages/core/system-prompt) |
| [`client-ui-layout`](../packages/client/ui-layout) | `client` | [`client-runtime`](../packages/client/runtime), [`client-ui-slots`](../packages/client/ui-slots), [`client-ui-theme`](../packages/client/ui-theme), [`invariants`](../packages/support/invariants) |
2026-07-29 21:40:45 +08:00
| [`client-ui-skill`](../packages/client/ui-skill) | `client` | [`client-connection`](../packages/client/connection), [`client-runtime`](../packages/client/runtime), [`client-ui-slash`](../packages/client/ui-slash), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants) |
| [`client-ui-subagent`](../packages/client/ui-subagent) | `client` | [`client-runtime`](../packages/client/runtime), [`client-ui-slash`](../packages/client/ui-slash), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants) |
| [`code-runtime-worker`](../packages/code-runtime/code-runtime-worker) | `code-runtime` | [`code-runtime`](../packages/code-runtime/code-runtime), [`invariants`](../packages/support/invariants), [`session`](../packages/core/session), [`timeout`](../packages/util/timeout) |
2026-07-30 15:56:41 +08:00
| [`host-directory-picker-browse`](../packages/host/directory-picker-browse) | `host` | [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slots`](../packages/client/ui-slots), [`client-ui-workspace`](../packages/client/ui-workspace), [`invariants`](../packages/support/invariants) |
| [`host-directory-picker-native`](../packages/host/directory-picker-native) | `host` | [`client-runtime`](../packages/client/runtime), [`client-ui-slots`](../packages/client/ui-slots), [`client-ui-workspace`](../packages/client/ui-workspace), [`invariants`](../packages/support/invariants) |
feat(subprocess): migrate lsp-local, subagent-acp, and the env scrubs onto the seam Review direction (tianyicui, PR #660): in a stacked PR, change all other process-running places to use the new service. - lsp-local: LspConnection spawns through ctx.subprocess (piped protocol streams + a no-spill collected stderr tail); its private process-tree helpers (POSIX group signalling, Windows taskkill, liveness polling) are deleted in favor of the seam's handle verbs, and its buildChildEnv now rides scrubbedParentEnv (LSP children also stop inheriting stale DSH_*). The plugin injects 'subprocess'; compositions/tests mount dsh-subprocess-local. - subagent-acp: the ACP child spawns through the seam (piped ndjson streams, inherited stderr); spawn failure surfaces through done-rejection into the same startup race; disposal is handle.dispose with the plugin's configured graces. dsh-subagent-subprocess is DELETED — its dispose ladder and scrub are the seam's, and the isolated-config-dir helper had no consumer. - mcp-client, pty-local, sdk-helper: adopt scrubbedParentEnv as the one scrub definition (their spawns stay put by ownership: the MCP SDK and node-pty own those calls; the SDK wizard runs outside any composition). - Coverage: per-file 100% over every touched src file, with each v8 ignore carrying a platform or contract reason; new suites cover stdio dispositions, the dispose ladder tiers, injected-win32 tree semantics, waitForExit, settled-kill/terminate no-ops, and spawn-failure disposal. - Docs: consumer-migration Agent Note (en; zh follows in this PR), seam note updated in place, subprocess.md rewritten for the reshaped vocabulary (type-equiv re-registered), READMEs and SERVICE_ROLES updated, taskkill added to knip ignoreBinaries.
2026-07-26 15:27:59 +08:00
| [`lsp-local`](../packages/lsp/lsp-local) | `lsp` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`lsp`](../packages/lsp/lsp), [`subprocess`](../packages/subprocess/subprocess), [`timeout`](../packages/util/timeout) |
| [`sandbox-local`](../packages/sandbox/sandbox-local) | `sandbox` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox) |
| [`session-projection`](../packages/session-projection/session-projection) | `session-projection` | [`invariants`](../packages/support/invariants), [`session`](../packages/core/session) |
| [`llm-retry`](../packages/llm/llm-retry) | `llm` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`timeout`](../packages/util/timeout) |
feat(web): show durable token usage and context occupancy in the stats line The chat stats line took its token totals from the loaded conversation nodes, so paging changed them and compaction erased the billing behind replaced content. It also had no way to show context occupancy: the numerator and capacity never reached the browser. Both now come from token-meter session projections read through the standard useProjection seat. Window nodes keep supplying turn and step counts plus LLM and tool wall times, which are correctly window-scoped facts about what is on screen; accounting no longer comes from there. `tokenUsage` supplies billing and cache hit. `contextPressure` supplies occupancy, pairing the newest provider-reported prompt size with the newest capacity recorded by `request/context`. Deployments without token-meter drop the token groups; a route whose adapter advertises no capacity drops the occupancy group rather than rendering a placeholder. Occupancy is deliberately approximate: the numerator and capacity are independent last-wins fields, not one atomic request observation, so switching models pairs a fresh capacity with the prior route's pressure until the next request reports usage. It is a user-facing reference figure that nothing in the harness makes decisions from, and it matches how the TUI status line has always computed occupancy. The Agent Note and token-meter README state this as a decision, including why the atomic alternative was implemented and rejected, so it is not re-litigated as a defect. Snapshot delta is one added `Context N% of 128K` segment across eight web goldens; the preceding commit absorbed master's pre-existing golden drift.
2026-07-30 14:48:19 +08:00
| [`token-meter`](../packages/llm/token-meter) | `llm` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session-projection/session-projection) |
| [`goal`](../packages/goal/goal) | `goal` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-projection`](../packages/session-projection/session-projection) |
| [`bash-local`](../packages/bash/bash-local) | `bash` | [`bash`](../packages/bash/bash), [`invariants`](../packages/support/invariants), [`subprocess`](../packages/subprocess/subprocess), [`timeout`](../packages/util/timeout) |
| [`fs-local`](../packages/fs/fs-local) | `fs` | [`fs`](../packages/fs/fs), [`invariants`](../packages/support/invariants) |
| [`fs-policy`](../packages/fs/fs-policy) | `fs` | [`fs`](../packages/fs/fs), [`invariants`](../packages/support/invariants) |
| [`skill-local`](../packages/skill/skill-local) | `skill` | [`fs`](../packages/fs/fs), [`invariants`](../packages/support/invariants), [`paths`](../packages/util/paths), [`skill`](../packages/skill/skill) |
| [`web-search-deepseek`](../packages/web/web-search-deepseek) | `web` | [`agent`](../packages/core/agent), [`credentials`](../packages/credentials/credentials), [`invariants`](../packages/support/invariants), [`session`](../packages/core/session), [`web`](../packages/web/web) |
| [`spill-local`](../packages/spill/spill-local) | `spill` | [`invariants`](../packages/support/invariants), [`spill`](../packages/spill/spill) |
| [`hook-protocol`](../packages/hooks/hook-protocol) | `hooks` | [`bash`](../packages/bash/bash), [`invariants`](../packages/support/invariants), [`session`](../packages/core/session) |
| [`session-persistence-jsonl`](../packages/session-persistence/session-persistence-jsonl) | `session-persistence` | [`invariants`](../packages/support/invariants), [`session`](../packages/core/session), [`session-persistence`](../packages/session-persistence/session-persistence) |
| [`session-persistence-sqlite`](../packages/session-persistence/session-persistence-sqlite) | `session-persistence` | [`invariants`](../packages/support/invariants), [`session`](../packages/core/session), [`session-persistence`](../packages/session-persistence/session-persistence) |
| [`session-title`](../packages/session-title/session-title) | `session-title` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session-projection/session-projection) |
| [`commands`](../packages/ui/commands) | `ui` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`scope`](../packages/core/scope), [`session`](../packages/core/session) |
| [`user-approval`](../packages/ui/user-approval) | `ui` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt) |
| [`user-interaction`](../packages/ui/user-interaction) | `ui` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm) |
| [`time-context`](../packages/context/time-context) | `context` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`session`](../packages/core/session) |
| [`tmux-context`](../packages/context/tmux-context) | `context` | [`agent`](../packages/core/agent), [`bash`](../packages/bash/bash), [`invariants`](../packages/support/invariants), [`session`](../packages/core/session) |
2026-07-30 15:56:41 +08:00
| [`host-directory-picker-auto`](../packages/host/directory-picker-auto) | `host` | [`host-directory-picker-browse`](../packages/host/directory-picker-browse), [`host-directory-picker-native`](../packages/host/directory-picker-native), [`host-webserver`](../packages/host/webserver), [`invariants`](../packages/support/invariants) |
| [`pty`](../packages/pty/pty) | `pty` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants) |
| [`sandbox-policy`](../packages/sandbox/sandbox-policy) | `sandbox` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`sandbox`](../packages/sandbox/sandbox), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt) |
| [`scripts`](../packages/sdk/scripts) | `sdk` | [`app-boot`](../packages/ui/app-boot), [`invariants`](../packages/support/invariants) |
| [`session-projection-cache`](../packages/session-projection/session-projection-cache) | `session-projection` | [`invariants`](../packages/support/invariants), [`session`](../packages/core/session), [`session-persistence`](../packages/session-persistence/session-persistence), [`session-projection`](../packages/session-projection/session-projection), [`storage-domain`](../packages/storage/storage-domain) |
| [`tasks`](../packages/tasks/tasks) | `tasks` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`session`](../packages/core/session) |
| [`session-telemetry`](../packages/telemetry/session-telemetry) | `telemetry` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`session`](../packages/core/session) |
| [`workflow`](../packages/workflow/workflow) | `workflow` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
| [`workspace`](../packages/workspace/workspace) | `workspace` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`session`](../packages/core/session), [`session-persistence`](../packages/session-persistence/session-persistence), [`storage`](../packages/storage/storage), [`storage-domain`](../packages/storage/storage-domain) |
| [`tools`](../packages/core/tools) | `core` | [`agent`](../packages/core/agent), [`code-runtime`](../packages/code-runtime/code-runtime), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`user-approval`](../packages/ui/user-approval) |
| [`command-goal`](../packages/goal/command-goal) | `goal` | [`commands`](../packages/ui/commands), [`goal`](../packages/goal/goal), [`invariants`](../packages/support/invariants) |
| [`goal-session`](../packages/goal/goal-session) | `goal` | [`agent`](../packages/core/agent), [`goal`](../packages/goal/goal), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) |
| [`bash-sandbox`](../packages/bash/bash-sandbox) | `bash` | [`bash`](../packages/bash/bash), [`bash-local`](../packages/bash/bash-local), [`invariants`](../packages/support/invariants), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy) |
| [`fs-sandbox`](../packages/fs/fs-sandbox) | `fs` | [`fs`](../packages/fs/fs), [`fs-local`](../packages/fs/fs-local), [`invariants`](../packages/support/invariants), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy) |
| [`command-compact`](../packages/compact/command-compact) | `compact` | [`commands`](../packages/ui/commands), [`compact`](../packages/compact/compact), [`invariants`](../packages/support/invariants) |
feat(web): show durable token usage and context occupancy in the stats line The chat stats line took its token totals from the loaded conversation nodes, so paging changed them and compaction erased the billing behind replaced content. It also had no way to show context occupancy: the numerator and capacity never reached the browser. Both now come from token-meter session projections read through the standard useProjection seat. Window nodes keep supplying turn and step counts plus LLM and tool wall times, which are correctly window-scoped facts about what is on screen; accounting no longer comes from there. `tokenUsage` supplies billing and cache hit. `contextPressure` supplies occupancy, pairing the newest provider-reported prompt size with the newest capacity recorded by `request/context`. Deployments without token-meter drop the token groups; a route whose adapter advertises no capacity drops the occupancy group rather than rendering a placeholder. Occupancy is deliberately approximate: the numerator and capacity are independent last-wins fields, not one atomic request observation, so switching models pairs a fresh capacity with the prior route's pressure until the next request reports usage. It is a user-facing reference figure that nothing in the harness makes decisions from, and it matches how the TUI status line has always computed occupancy. The Agent Note and token-meter README state this as a decision, including why the atomic alternative was implemented and rejected, so it is not re-litigated as a defect. Snapshot delta is one added `Context N% of 128K` segment across eight web goldens; the preceding commit absorbed master's pre-existing golden drift.
2026-07-30 14:48:19 +08:00
| [`compact-basic`](../packages/compact/compact-basic) | `compact` | [`agent`](../packages/core/agent), [`compact`](../packages/compact/compact), [`compact-tool-result-prune`](../packages/compact/compact-tool-result-prune), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`token-meter`](../packages/llm/token-meter) |
| [`session-query`](../packages/session-query/session-query) | `session-query` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session-persistence/session-persistence), [`session-title`](../packages/session-title/session-title) |
| [`session-title-llm`](../packages/session-title/session-title-llm) | `session-title` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-title`](../packages/session-title/session-title), [`timeout`](../packages/util/timeout) |
| [`acp`](../packages/acp/acp) | `acp` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`session`](../packages/core/session), [`user-approval`](../packages/ui/user-approval) |
| [`permission`](../packages/ui/permission) | `ui` | [`bash`](../packages/bash/bash), [`commands`](../packages/ui/commands), [`invariants`](../packages/support/invariants), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session`](../packages/core/session), [`session-projection`](../packages/session-projection/session-projection), [`settings`](../packages/settings/settings), [`user-approval`](../packages/ui/user-approval) |
feat(web): show durable token usage and context occupancy in the stats line The chat stats line took its token totals from the loaded conversation nodes, so paging changed them and compaction erased the billing behind replaced content. It also had no way to show context occupancy: the numerator and capacity never reached the browser. Both now come from token-meter session projections read through the standard useProjection seat. Window nodes keep supplying turn and step counts plus LLM and tool wall times, which are correctly window-scoped facts about what is on screen; accounting no longer comes from there. `tokenUsage` supplies billing and cache hit. `contextPressure` supplies occupancy, pairing the newest provider-reported prompt size with the newest capacity recorded by `request/context`. Deployments without token-meter drop the token groups; a route whose adapter advertises no capacity drops the occupancy group rather than rendering a placeholder. Occupancy is deliberately approximate: the numerator and capacity are independent last-wins fields, not one atomic request observation, so switching models pairs a fresh capacity with the prior route's pressure until the next request reports usage. It is a user-facing reference figure that nothing in the harness makes decisions from, and it matches how the TUI status line has always computed occupancy. The Agent Note and token-meter README state this as a decision, including why the atomic alternative was implemented and rejected, so it is not re-litigated as a defect. Snapshot delta is one added `Context N% of 128K` segment across eight web goldens; the preceding commit absorbed master's pre-existing golden drift.
2026-07-30 14:48:19 +08:00
| [`client-ui-conversation`](../packages/client/ui-conversation) | `client` | [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slash`](../packages/client/ui-slash), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants), [`token-meter`](../packages/llm/token-meter) |
feat(subprocess): migrate lsp-local, subagent-acp, and the env scrubs onto the seam Review direction (tianyicui, PR #660): in a stacked PR, change all other process-running places to use the new service. - lsp-local: LspConnection spawns through ctx.subprocess (piped protocol streams + a no-spill collected stderr tail); its private process-tree helpers (POSIX group signalling, Windows taskkill, liveness polling) are deleted in favor of the seam's handle verbs, and its buildChildEnv now rides scrubbedParentEnv (LSP children also stop inheriting stale DSH_*). The plugin injects 'subprocess'; compositions/tests mount dsh-subprocess-local. - subagent-acp: the ACP child spawns through the seam (piped ndjson streams, inherited stderr); spawn failure surfaces through done-rejection into the same startup race; disposal is handle.dispose with the plugin's configured graces. dsh-subagent-subprocess is DELETED — its dispose ladder and scrub are the seam's, and the isolated-config-dir helper had no consumer. - mcp-client, pty-local, sdk-helper: adopt scrubbedParentEnv as the one scrub definition (their spawns stay put by ownership: the MCP SDK and node-pty own those calls; the SDK wizard runs outside any composition). - Coverage: per-file 100% over every touched src file, with each v8 ignore carrying a platform or contract reason; new suites cover stdio dispositions, the dispose ladder tiers, injected-win32 tree semantics, waitForExit, settled-kill/terminate no-ops, and spawn-failure disposal. - Docs: consumer-migration Agent Note (en; zh follows in this PR), seam note updated in place, subprocess.md rewritten for the reshaped vocabulary (type-equiv re-registered), READMEs and SERVICE_ROLES updated, taskkill added to knip ignoreBinaries.
2026-07-26 15:27:59 +08:00
| [`pty-local`](../packages/pty/pty-local) | `pty` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`pty`](../packages/pty/pty), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session`](../packages/core/session), [`subprocess`](../packages/subprocess/subprocess) |
| [`tasks-local`](../packages/tasks/tasks-local) | `tasks` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`tasks`](../packages/tasks/tasks), [`timeout`](../packages/util/timeout) |
2026-07-31 14:33:39 +08:00
| [`session-telemetry-otel`](../packages/telemetry/session-telemetry-otel) | `telemetry` | [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`paths`](../packages/util/paths), [`session`](../packages/core/session), [`session-telemetry`](../packages/telemetry/session-telemetry) |
| [`agent-loop`](../packages/core/agent-loop) | `core` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-persistence`](../packages/session-persistence/session-persistence), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
| [`tool-goal`](../packages/goal/tool-goal) | `goal` | [`agent`](../packages/core/agent), [`goal`](../packages/goal/goal), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
| [`tool-bash`](../packages/bash/tool-bash) | `bash` | [`agent`](../packages/core/agent), [`bash`](../packages/bash/bash), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`paths`](../packages/util/paths), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session-persistence`](../packages/session-persistence/session-persistence), [`system-prompt`](../packages/core/system-prompt), [`tasks`](../packages/tasks/tasks), [`tools`](../packages/core/tools), [`user-approval`](../packages/ui/user-approval) |
| [`tool-fs`](../packages/fs/tool-fs) | `fs` | [`fs`](../packages/fs/fs), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`user-approval`](../packages/ui/user-approval) |
| [`tool-fs-search`](../packages/fs/tool-fs-search) | `fs` | [`bash`](../packages/bash/bash), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`retention`](../packages/util/retention), [`session`](../packages/core/session), [`spill`](../packages/spill/spill), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
| [`tool-str-replace-editor`](../packages/fs/tool-str-replace-editor) | `fs` | [`fs`](../packages/fs/fs), [`invariants`](../packages/support/invariants), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`tools`](../packages/core/tools) |
| [`tool-skill`](../packages/skill/tool-skill) | `skill` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`skill`](../packages/skill/skill), [`tools`](../packages/core/tools) |
| [`subagent`](../packages/subagent/subagent) | `subagent` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`tools`](../packages/core/tools) |
| [`tool-web`](../packages/web/tool-web) | `web` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`web`](../packages/web/web) |
| [`spill-policy`](../packages/spill/spill-policy) | `spill` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`retention`](../packages/util/retention), [`session`](../packages/core/session), [`spill`](../packages/spill/spill), [`tools`](../packages/core/tools) |
| [`timeout-policy`](../packages/timeout/timeout-policy) | `timeout` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`timeout`](../packages/util/timeout), [`tools`](../packages/core/tools) |
| [`tool-todo`](../packages/todo/tool-todo) | `todo` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`session`](../packages/core/session), [`session-projection`](../packages/session-projection/session-projection), [`tools`](../packages/core/tools) |
| [`plan-mode`](../packages/plan/plan-mode) | `plan` | [`agent`](../packages/core/agent), [`commands`](../packages/ui/commands), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-projection`](../packages/session-projection/session-projection), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`user-interaction`](../packages/ui/user-interaction) |
| [`tool-cordis`](../packages/cordis/tool-cordis) | `cordis` | [`invariants`](../packages/support/invariants), [`scope`](../packages/core/scope), [`tools`](../packages/core/tools) |
| [`hooks-codex`](../packages/hooks/hooks-codex) | `hooks` | [`agent`](../packages/core/agent), [`hook-protocol`](../packages/hooks/hook-protocol), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session-persistence/session-persistence), [`tools`](../packages/core/tools) |
| [`session-checkpoint-policy`](../packages/session-persistence/session-checkpoint-policy) | `session-persistence` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session-persistence/session-persistence), [`tools`](../packages/core/tools) |
| [`session-query-sqlite`](../packages/session-query/session-query-sqlite) | `session-query` | [`invariants`](../packages/support/invariants), [`session`](../packages/core/session), [`session-persistence`](../packages/session-persistence/session-persistence), [`session-query`](../packages/session-query/session-query) |
| [`tool-session-query`](../packages/session-query/tool-session-query) | `session-query` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-query`](../packages/session-query/session-query), [`system-prompt`](../packages/core/system-prompt), [`timeout`](../packages/util/timeout), [`tools`](../packages/core/tools) |
| [`session-title-all-messages-llm`](../packages/session-title/session-title-all-messages-llm) | `session-title` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-title`](../packages/session-title/session-title), [`session-title-llm`](../packages/session-title/session-title-llm) |
| [`session-title-first-message-llm`](../packages/session-title/session-title-first-message-llm) | `session-title` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-title`](../packages/session-title/session-title), [`session-title-llm`](../packages/session-title/session-title-llm) |
| [`agent-loop-testkit`](../packages/support/agent-loop-testkit) | `support` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools) |
| [`tool-ask-user`](../packages/ui/tool-ask-user) | `ui` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`tools`](../packages/core/tools), [`user-interaction`](../packages/ui/user-interaction) |
| [`client-ui-command`](../packages/client/ui-command) | `client` | [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slash`](../packages/client/ui-slash), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants) |
| [`client-ui-goal`](../packages/client/ui-goal) | `client` | [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slots`](../packages/client/ui-slots), [`goal`](../packages/goal/goal), [`invariants`](../packages/support/invariants) |
| [`session-reference`](../packages/context/session-reference) | `context` | [`agent`](../packages/core/agent), [`compact`](../packages/compact/compact), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`retention`](../packages/util/retention), [`session`](../packages/core/session), [`session-query`](../packages/session-query/session-query) |
| [`workspace-context`](../packages/context/workspace-context) | `context` | [`agent`](../packages/core/agent), [`fs`](../packages/fs/fs), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`paths`](../packages/util/paths), [`session`](../packages/core/session), [`tools`](../packages/core/tools) |
| [`repeat-tool-guard`](../packages/guard/repeat-tool-guard) | `guard` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`tools`](../packages/core/tools) |
| [`tool-lsp`](../packages/lsp/tool-lsp) | `lsp` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`lsp`](../packages/lsp/lsp), [`system-prompt`](../packages/core/system-prompt), [`timeout`](../packages/util/timeout), [`tools`](../packages/core/tools) |
feat(subprocess): migrate lsp-local, subagent-acp, and the env scrubs onto the seam Review direction (tianyicui, PR #660): in a stacked PR, change all other process-running places to use the new service. - lsp-local: LspConnection spawns through ctx.subprocess (piped protocol streams + a no-spill collected stderr tail); its private process-tree helpers (POSIX group signalling, Windows taskkill, liveness polling) are deleted in favor of the seam's handle verbs, and its buildChildEnv now rides scrubbedParentEnv (LSP children also stop inheriting stale DSH_*). The plugin injects 'subprocess'; compositions/tests mount dsh-subprocess-local. - subagent-acp: the ACP child spawns through the seam (piped ndjson streams, inherited stderr); spawn failure surfaces through done-rejection into the same startup race; disposal is handle.dispose with the plugin's configured graces. dsh-subagent-subprocess is DELETED — its dispose ladder and scrub are the seam's, and the isolated-config-dir helper had no consumer. - mcp-client, pty-local, sdk-helper: adopt scrubbedParentEnv as the one scrub definition (their spawns stay put by ownership: the MCP SDK and node-pty own those calls; the SDK wizard runs outside any composition). - Coverage: per-file 100% over every touched src file, with each v8 ignore carrying a platform or contract reason; new suites cover stdio dispositions, the dispose ladder tiers, injected-win32 tree semantics, waitForExit, settled-kill/terminate no-ops, and spawn-failure disposal. - Docs: consumer-migration Agent Note (en; zh follows in this PR), seam note updated in place, subprocess.md rewritten for the reshaped vocabulary (type-equiv re-registered), READMEs and SERVICE_ROLES updated, taskkill added to knip ignoreBinaries.
2026-07-26 15:27:59 +08:00
| [`mcp-client`](../packages/mcp/mcp-client) | `mcp` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`subprocess`](../packages/subprocess/subprocess), [`tools`](../packages/core/tools) |
| [`tool-bash-persistent`](../packages/pty/tool-bash-persistent) | `pty` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`pty`](../packages/pty/pty), [`timeout`](../packages/util/timeout), [`tools`](../packages/core/tools) |
2026-07-22 22:37:20 +08:00
| [`tool-pty`](../packages/pty/tool-pty) | `pty` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`pty`](../packages/pty/pty), [`retention`](../packages/util/retention), [`system-prompt`](../packages/core/system-prompt), [`tasks`](../packages/tasks/tasks), [`tools`](../packages/core/tools) |
| [`tool-tasks`](../packages/tasks/tool-tasks) | `tasks` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`retention`](../packages/util/retention), [`system-prompt`](../packages/core/system-prompt), [`tasks`](../packages/tasks/tasks), [`tools`](../packages/core/tools) |
| [`tool-workflow`](../packages/workflow/tool-workflow) | `workflow` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`workflow`](../packages/workflow/workflow) |
feat(subprocess): migrate lsp-local, subagent-acp, and the env scrubs onto the seam Review direction (tianyicui, PR #660): in a stacked PR, change all other process-running places to use the new service. - lsp-local: LspConnection spawns through ctx.subprocess (piped protocol streams + a no-spill collected stderr tail); its private process-tree helpers (POSIX group signalling, Windows taskkill, liveness polling) are deleted in favor of the seam's handle verbs, and its buildChildEnv now rides scrubbedParentEnv (LSP children also stop inheriting stale DSH_*). The plugin injects 'subprocess'; compositions/tests mount dsh-subprocess-local. - subagent-acp: the ACP child spawns through the seam (piped ndjson streams, inherited stderr); spawn failure surfaces through done-rejection into the same startup race; disposal is handle.dispose with the plugin's configured graces. dsh-subagent-subprocess is DELETED — its dispose ladder and scrub are the seam's, and the isolated-config-dir helper had no consumer. - mcp-client, pty-local, sdk-helper: adopt scrubbedParentEnv as the one scrub definition (their spawns stay put by ownership: the MCP SDK and node-pty own those calls; the SDK wizard runs outside any composition). - Coverage: per-file 100% over every touched src file, with each v8 ignore carrying a platform or contract reason; new suites cover stdio dispositions, the dispose ladder tiers, injected-win32 tree semantics, waitForExit, settled-kill/terminate no-ops, and spawn-failure disposal. - Docs: consumer-migration Agent Note (en; zh follows in this PR), seam note updated in place, subprocess.md rewritten for the reshaped vocabulary (type-equiv re-registered), READMEs and SERVICE_ROLES updated, taskkill added to knip ignoreBinaries.
2026-07-26 15:27:59 +08:00
| [`subagent-acp`](../packages/subagent/subagent-acp) | `subagent` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subprocess`](../packages/subprocess/subprocess) |
| [`subagent-inprocess`](../packages/subagent/subagent-inprocess) | `subagent` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`user-approval`](../packages/ui/user-approval) |
| [`tool-subagent`](../packages/subagent/tool-subagent) | `subagent` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`subagent`](../packages/subagent/subagent), [`tasks`](../packages/tasks/tasks), [`tools`](../packages/core/tools) |
| [`repository-plugin`](../packages/cordis/repository-plugin) | `cordis` | [`invariants`](../packages/support/invariants), [`mcp-client`](../packages/mcp/mcp-client), [`paths`](../packages/util/paths), [`skill-local`](../packages/skill/skill-local) |
| [`hooks-claude`](../packages/hooks/hooks-claude) | `hooks` | [`agent`](../packages/core/agent), [`hook-protocol`](../packages/hooks/hook-protocol), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-persistence`](../packages/session-persistence/session-persistence), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools) |
| [`tui`](../packages/ui/tui) | `ui` | [`agent`](../packages/core/agent), [`agent-loop`](../packages/core/agent-loop), [`commands`](../packages/ui/commands), [`compact`](../packages/compact/compact), [`goal`](../packages/goal/goal), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`llm-retry`](../packages/llm/llm-retry), [`session`](../packages/core/session), [`session-persistence`](../packages/session-persistence/session-persistence), [`session-query`](../packages/session-query/session-query), [`session-reference`](../packages/context/session-reference), [`session-title`](../packages/session-title/session-title), [`skill`](../packages/skill/skill), [`subprocess`](../packages/subprocess/subprocess), [`system-prompt`](../packages/core/system-prompt), [`token-meter`](../packages/llm/token-meter), [`tools`](../packages/core/tools), [`user-interaction`](../packages/ui/user-interaction) |
feat(web): show durable token usage and context occupancy in the stats line The chat stats line took its token totals from the loaded conversation nodes, so paging changed them and compaction erased the billing behind replaced content. It also had no way to show context occupancy: the numerator and capacity never reached the browser. Both now come from token-meter session projections read through the standard useProjection seat. Window nodes keep supplying turn and step counts plus LLM and tool wall times, which are correctly window-scoped facts about what is on screen; accounting no longer comes from there. `tokenUsage` supplies billing and cache hit. `contextPressure` supplies occupancy, pairing the newest provider-reported prompt size with the newest capacity recorded by `request/context`. Deployments without token-meter drop the token groups; a route whose adapter advertises no capacity drops the occupancy group rather than rendering a placeholder. Occupancy is deliberately approximate: the numerator and capacity are independent last-wins fields, not one atomic request observation, so switching models pairs a fresh capacity with the prior route's pressure until the next request reports usage. It is a user-facing reference figure that nothing in the harness makes decisions from, and it matches how the TUI status line has always computed occupancy. The Agent Note and token-meter README state this as a decision, including why the atomic alternative was implemented and rejected, so it is not re-litigated as a defect. Snapshot delta is one added `Context N% of 128K` segment across eight web goldens; the preceding commit absorbed master's pre-existing golden drift.
2026-07-30 14:48:19 +08:00
| [`client-ui-model`](../packages/client/ui-model) | `client` | [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-command`](../packages/client/ui-command), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slash`](../packages/client/ui-slash), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants) |
| [`client-ui-permission`](../packages/client/ui-permission) | `client` | [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-schema-form`](../packages/client/schema-form), [`client-ui-command`](../packages/client/ui-command), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slash`](../packages/client/ui-slash), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants), [`permission`](../packages/ui/permission) |
| [`client-ui-plan`](../packages/client/ui-plan) | `client` | [`client-connection`](../packages/client/connection), [`client-locale`](../packages/client/locale), [`client-runtime`](../packages/client/runtime), [`client-ui-conversation`](../packages/client/ui-conversation), [`client-ui-primitives`](../packages/client/ui-primitives), [`client-ui-slots`](../packages/client/ui-slots), [`invariants`](../packages/support/invariants), [`plan-mode`](../packages/plan/plan-mode) |
| [`agent-spine-demo`](../packages/examples/agent-spine-demo) | `examples` | [`agent`](../packages/core/agent), [`agent-loop`](../packages/core/agent-loop), [`goal`](../packages/goal/goal), [`goal-session`](../packages/goal/goal-session), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`llm-retry`](../packages/llm/llm-retry), [`paths`](../packages/util/paths), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`session-title`](../packages/session-title/session-title), [`skill`](../packages/skill/skill), [`skill-local`](../packages/skill/skill-local), [`system-prompt`](../packages/core/system-prompt), [`tasks-local`](../packages/tasks/tasks-local), [`tool-bash`](../packages/bash/tool-bash), [`tool-goal`](../packages/goal/tool-goal), [`tool-skill`](../packages/skill/tool-skill), [`tool-tasks`](../packages/tasks/tool-tasks), [`tools`](../packages/core/tools), [`workspace-context`](../packages/context/workspace-context) |
| [`sdk-protocol`](../packages/sdk/sdk-protocol) | `sdk` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent) |
| [`tool-ralph`](../packages/workflow/tool-ralph) | `workflow` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`subagent`](../packages/subagent/subagent), [`system-prompt`](../packages/core/system-prompt), [`tools`](../packages/core/tools), [`workflow`](../packages/workflow/workflow) |
| [`workflow-workerthread`](../packages/workflow/workflow-workerthread) | `workflow` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`tools`](../packages/core/tools), [`workflow`](../packages/workflow/workflow) |
| [`subagent-fork`](../packages/subagent/subagent-fork) | `subagent` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subagent-inprocess`](../packages/subagent/subagent-inprocess) |
| [`subagent-spawn`](../packages/subagent/subagent-spawn) | `subagent` | [`invariants`](../packages/support/invariants), [`subagent`](../packages/subagent/subagent), [`subagent-inprocess`](../packages/subagent/subagent-inprocess) |
| [`jsonrpc`](../packages/ui/jsonrpc) | `ui` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`llm-deepseek`](../packages/llm/llm-deepseek), [`scope`](../packages/core/scope), [`sdk-protocol`](../packages/sdk/sdk-protocol), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent) |
| [`acp-demo`](../packages/examples/acp-demo) | `examples` | [`acp`](../packages/acp/acp), [`agent-spine-demo`](../packages/examples/agent-spine-demo), [`app-boot`](../packages/ui/app-boot), [`invariants`](../packages/support/invariants), [`session-checkpoint-policy`](../packages/session-persistence/session-checkpoint-policy), [`session-persistence-jsonl`](../packages/session-persistence/session-persistence-jsonl), [`session-query`](../packages/session-query/session-query), [`session-query-sqlite`](../packages/session-query/session-query-sqlite), [`tools`](../packages/core/tools), [`workspace-context`](../packages/context/workspace-context) |
| [`cli-demo`](../packages/examples/cli-demo) | `examples` | [`agent`](../packages/core/agent), [`agent-spine-demo`](../packages/examples/agent-spine-demo), [`app-boot`](../packages/ui/app-boot), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session), [`session-checkpoint-policy`](../packages/session-persistence/session-checkpoint-policy), [`session-persistence-jsonl`](../packages/session-persistence/session-persistence-jsonl), [`tools`](../packages/core/tools), [`workspace-context`](../packages/context/workspace-context) |
| [`sdk-client`](../packages/sdk/sdk-client) | `sdk` | [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`sdk-protocol`](../packages/sdk/sdk-protocol), [`session`](../packages/core/session) |
| [`subagent-dsh-sdk`](../packages/subagent/subagent-dsh-sdk) | `subagent` | [`agent`](../packages/core/agent), [`invariants`](../packages/support/invariants), [`llm`](../packages/llm/llm), [`sdk-client`](../packages/sdk/sdk-client), [`session`](../packages/core/session), [`subagent`](../packages/subagent/subagent), [`subprocess`](../packages/subprocess/subprocess) |