deepseek-harness/packages/api/session-controller/tests/fake-api.client.ts

569 lines
23 KiB
TypeScript
Raw Normal View History

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
// Test-local programmable IApiClient fake (NOT the fixture: fixture is a demo
// data source on a real clock; behavior tests need per-case responses and
// deferred-controlled timing). Session streams are hand pumps: pushFollow/pushControl.
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
import type {
IApiClient, MessageId,
RpcError, RpcResponse, SessionId, SessionSearchItem, SkillEntry,
SubagentCatalog, SubagentInterruptReceipt, SubagentPromptReceipt,
WorkspaceId, WorkspaceView,
} from '@deepseek-ai/dsh-api-remotes/client'
import type {
SessionAddress,
SessionControlBaseline,
SessionControlFrame,
SessionFollowFrame,
SessionFollowRequest,
SessionPage,
SessionPageRequest,
SessionProjectionBaseline,
SessionSelectModelRequest,
SessionSelectModelValue,
} from '@deepseek-ai/dsh-api-session-controller/types'
import type { WorkspaceRemote } from '@deepseek-ai/dsh-api-workspace-controller/client'
import type { WorkspaceFollowFrame } from '@deepseek-ai/dsh-api-workspace-controller/types'
import type { RemoteFailure, RemoteResult } from '@deepseek-ai/dsh-typert-protocol'
import {
RemoteStream,
RemoteStreamError,
type RemoteStreamOptions,
} from '@deepseek-ai/dsh-api-gateway/client'
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
import { RpcId } from '@deepseek-ai/dsh-client-connection/client'
import type { SessionRemotes } from '../src/client/sessions/remotes.ts'
import { historyRecordLastSeq } from '../src/client/sessions/history-records.ts'
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
const AVAILABLE_STREAM_CONNECTION = {
hostDescription: {
getSnapshot: () => ({
version: 'fixture', cwd: '/f', attachedSessions: 0, home: '/h', canOpenPath: true,
}),
subscribe: () => () => {},
},
}
/** Programmable-default workspace row (branded id, ISO-ish times). */
function fakeWorkspace(id: string, over: Partial<WorkspaceView> = {}): WorkspaceView {
return {
workspaceId: id as WorkspaceId,
path: '/f/ws',
title: 'ws',
sessionIds: [],
createdAt: '2026-01-01T00:00:00.000Z',
updatedAt: '2026-01-01T00:00:00.000Z',
...over,
}
}
function addressSessionId(address: SessionAddress): SessionId {
return address.kind === 'session' ? address.sessionId : address.childSessionId
}
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
export interface Deferred<T> {
promise: Promise<T>
resolve(value: T): void
reject(error: unknown): void
}
/** Test-held settlement: the case decides when an RPC lands (history-pending injections etc.). */
export function deferred<T>(): Deferred<T> {
let resolve!: (value: T) => void
let reject!: (error: unknown) => void
const promise = new Promise<T>((res, rej) => {
resolve = res
reject = rej
})
return { promise, resolve, reject }
}
let nextRpc = 0
export function ok<T>(value: T): RpcResponse<T> {
return { rpcId: RpcId(`fake-${nextRpc++}`), result: { ok: true, value } }
}
export function err<T>(error: RpcError): RpcResponse<T> {
return { rpcId: RpcId(`fake-${nextRpc++}`), result: { ok: false, error } }
}
/** Successful generated Remote result for programmable domain fakes. */
export function remoteOk<T>(value: T): RemoteResult<T> {
return { ok: true, value }
}
/**
* Failed generated Remote result carrying an owner's own failure vocabulary,
* which the carrier's closed RPC code set does not contain.
* @param error - the owner-declared failure.
* @returns the failure branch of a Remote result.
*/
export function remoteErr<T>(error: RemoteFailure): RemoteResult<T> {
return { ok: false, error }
}
type ValueStreamItem<F> =
| { kind: 'frame'; value: F; delivered?: () => void }
| { kind: 'end' }
| { kind: 'fail'; error: unknown }
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
interface ValueStreamConn<F> {
feed(item: ValueStreamItem<F>): void
}
interface OpenValueStream<F> {
readonly values: AsyncGenerator<F>
dispose(): void
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
}
/**
* Commands Remote double: the generated face delivers the carrier's outcome, so
* a test that programs nothing sees an empty catalog and an unmatched line.
* @returns the Remote namespaces the session cluster calls.
*/
export type RuntimeRemotes = SessionRemotes & { readonly workspace: WorkspaceRemote }
export function fakeRemote(api = new FakeApiClient()): RuntimeRemotes {
return api.sessionRemotes()
}
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
export class FakeApiClient implements IApiClient {
/** Chronological call record: [method, payload]. */
readonly calls: { method: string; payload: unknown }[] = []
/** Session ids in physical follow-generation opening order. */
readonly followStarts: SessionId[] = []
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
// Programmable slots (defaults answer OK-empty); reassign per case.
onList: (payload: unknown) => Promise<RpcResponse<{ items: never[] }>> = () => Promise.resolve(ok({ items: [] }))
onSearch: (payload: unknown) => Promise<RpcResponse<{ items: SessionSearchItem[]; hasMore: boolean }>> =
() => Promise.resolve(ok({ items: [], hasMore: false }))
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
onCreate: (payload: unknown) => Promise<RpcResponse<{ sessionId: SessionId }>> = () => Promise.resolve(ok({ sessionId: 'fk-new' as SessionId }))
onSelectModel: (payload: SessionSelectModelRequest) => Promise<RpcResponse<SessionSelectModelValue>> =
payload => Promise.resolve(ok({
selected: {
provider: payload.provider,
model: payload.model,
...(payload.reasoningEffort === undefined
? {}
: { reasoningEffort: payload.reasoningEffort }),
},
}))
onRename: (payload: unknown) => Promise<RpcResponse<{ title: string; seq: number }>> = () => Promise.resolve(ok({ title: 'fk-renamed', seq: 0 }))
2026-07-29 03:03:43 +08:00
onFork: (payload: unknown) => Promise<RpcResponse<{ sessionId: SessionId }>> = () => Promise.resolve(ok({ sessionId: 'fk-fork' as SessionId }))
onHistory: (payload: { sessionId: SessionId; throughSeq?: number; beforeSeq?: number; maxMessages?: number })
=> Promise<RpcResponse<SessionPage & { readonly projections?: SessionProjectionBaseline }>> =
() => Promise.resolve(ok({ records: [], hasMore: false }))
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
onPrompt: (payload: unknown) => Promise<RpcResponse<{ accepted: true }>> = () => Promise.resolve(ok({ accepted: true as const }))
2026-07-23 15:20:47 +08:00
onAttachment: (payload: unknown) => Promise<RpcResponse<{ attachment: { attachmentId: never; mediaType: 'image/png'; bytes: number; width: number; height: number }; data: string }>> =
() => Promise.resolve(ok({ attachment: { attachmentId: 'a' as never, mediaType: 'image/png', bytes: 1, width: 1, height: 1 }, data: 'AA==' }))
onUpdateQueue: (payload: unknown) => Promise<RpcResponse<{ accepted: true }>> = () => Promise.resolve(ok({ accepted: true as const }))
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
onCancel: (payload: unknown) => Promise<RpcResponse<{ accepted: true }>> = () => Promise.resolve(ok({ accepted: true as const }))
feat(web): permission presets and approval answering for the web UI The web host now composes the sandboxed product path (sandbox-local + sandbox-policy behind bash-sandbox/fs-sandbox, with user-approval and permission on top); BootHostOptions.sandbox carries the deployment defaults (workspace-write + ask). createApiProxy owns the approval pending registry: a ctx.approval ask becomes an answerable approval/requested mux frame with a stable rpcId, replayed verbatim on every mux open until settled; respond routes by the echoed rpcId, validates the ApprovalResponsePayload audit correlation, and broadcasts approval/resolved; the ask's abort signal withdraws the question as cancelled. session.permissions / session.setPermission project ctx.permission into a protocol-owned PermissionOption select; idle switches are held last-write-wins and flushed into the next prompted turn (the ACP bridge's anchoring pattern). The shared hasOpenTurn fold moved to dsh-session, deduplicating the private copies in user-approval, the ACP bridge, and the proxy. Client, per the designer draft: a pending approval takes over the composer (ApprovalPanel replaces the InputBar — amber strip, justification headline, paired command, one-shot refuse/allow, keyed by rpcId so a queued second approval remounts live; the resolved frame restores the composer); the sidebar session row shows an amber waiting-approval dot that outranks the running ring (manager-tracked approvalId set, idempotent under mux-open replays, cleared per connection generation, lit for uninstantiated sessions too); the permission selector is a composer bottom-row chip over an invisible native select, with a presentation-only title-case transform (workspace-write renders as Workspace Write; wire names untouched). Question placeholders stay in the message flow. The connection fixture mirrors the host behavior for keyless browser acceptance.
2026-07-24 13:39:00 +08:00
onDescribe: (payload: unknown) => Promise<RpcResponse<{
version: string
cwd: string
attachedSessions: number
home: string
canOpenPath: boolean
}>> =
() => Promise.resolve(ok({
version: '0-fake', cwd: '/f', attachedSessions: 0, home: '/h', canOpenPath: true,
}))
onPickDirectory: (payload: unknown) => Promise<RpcResponse<{ path: string | null }>> =
() => Promise.resolve(ok({ path: null }))
onOpenPath: (payload: unknown) => Promise<RpcResponse<{ opened: true }>> =
() => Promise.resolve(ok({ opened: true as const }))
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
onListDirectory: (payload: unknown) => Promise<RpcResponse<{
path: string
home: string
crumbs: { name: string; path: string; hidden: boolean }[]
entries: { name: string; path: string; hidden: boolean }[]
truncated: boolean
}>> =
() => Promise.resolve(ok({ path: '/home/fake', home: '/home/fake', crumbs: [{ name: '/', path: '/', hidden: false }], entries: [], truncated: false }))
onCreateDirectory: (payload: unknown) => Promise<RpcResponse<{ path: string }>> =
() => Promise.resolve(ok({ path: '/home/fake/new' }))
private readonly followConns = new Map<SessionId, ValueStreamConn<SessionFollowFrame>[]>()
private readonly controlConns: ValueStreamConn<SessionControlFrame>[] = []
private readonly workspaceConns: ValueStreamConn<WorkspaceFollowFrame>[] = []
/** Optional Host opening cursor override for stale-page and reconnect tests. */
followCursor: number | undefined
controlBaseline: SessionControlBaseline = {
queues: {},
jobs: {},
projections: {},
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
}
workspaceBaseline: Extract<WorkspaceFollowFrame, { type: 'baseline' }>['value'] = {
items: [],
archivedSessionIds: [],
}
lastSearchSignal: AbortSignal | undefined
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
onSubagentList: (payload: unknown) => Promise<RemoteResult<SubagentCatalog>>
= () => Promise.resolve(remoteOk({ entries: [], parentAvailable: true }))
onSubagentPrompt: (payload: unknown) => Promise<RemoteResult<SubagentPromptReceipt>>
= () => Promise.resolve(remoteOk({ messageId: 'fake-message' as MessageId }))
onSubagentInterrupt: (payload: unknown) => Promise<RemoteResult<SubagentInterruptReceipt>>
= () => Promise.resolve(remoteOk({ accepted: true as const }))
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
readonly host: IApiClient['host'] = {
2026-07-22 17:23:22 +08:00
describe: (payload: unknown) => this.record('host.describe', payload, this.onDescribe(payload)),
pickDirectory: (payload: unknown) => this.record('host.pickDirectory', payload, this.onPickDirectory(payload)),
listDirectory: (payload: unknown) => this.record('host.listDirectory', payload, this.onListDirectory(payload)),
createDirectory: (payload: unknown) => this.record('host.createDirectory', payload, this.onCreateDirectory(payload)),
openPath: (payload: unknown) => this.record('host.openPath', payload, this.onOpenPath(payload)),
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
}
onWorkspaceCreate: (payload: unknown) => Promise<RemoteResult<{ workspace: WorkspaceView; created: boolean }>> =
() => Promise.resolve(remoteOk({ workspace: fakeWorkspace('fk-ws'), created: true }))
onWorkspaceRename: (payload: unknown) => Promise<RemoteResult<{ workspace: WorkspaceView }>> =
() => Promise.resolve(remoteOk({ workspace: fakeWorkspace('fk-ws') }))
onWorkspaceDelete: (payload: unknown) => Promise<RemoteResult<{ deleted: true }>> =
() => Promise.resolve(remoteOk({ deleted: true }))
onWorkspaceInsertBefore: (payload: unknown) => Promise<RemoteResult<{ workspaceIds: WorkspaceId[] }>> =
() => Promise.resolve(remoteOk({ workspaceIds: [] }))
onWorkspaceInsertSessionBefore: (payload: unknown) => Promise<RemoteResult<{ workspace: WorkspaceView }>> =
() => Promise.resolve(remoteOk({ workspace: fakeWorkspace('fk-ws') }))
onWorkspaceArchiveSession: (payload: unknown) => Promise<RemoteResult<{ archivedSessionIds: SessionId[] }>> =
payload => Promise.resolve(remoteOk({ archivedSessionIds: [(payload as { sessionId: SessionId }).sessionId] }))
// Payloads stay `unknown` (lint-lane note above); response rows are the real
// wire shapes so cases can program requires-bearing catalogs and dual-address
// skill lists without casts.
onSkillList: (payload: unknown) => Promise<RpcResponse<{ skills: SkillEntry[] }>>
= () => Promise.resolve(ok({ skills: [] }))
readonly agentPresets: IApiClient['agentPresets'] = {
refactor(agent-presets,web): copy-only preset authoring with a path to the files The web YAML editor is gone. agentPreset.write (arbitrary composition text) became agentPreset.copy { from, agentPreset, name? }: a host-side whole-directory copy of ids the host resolves itself — symlinks dereferenced, modes re-tightened to owner-only with owner-execute kept, metadata rewritten to keep the source's description but never its name or roster order. No composition text or path crosses the wire in either authoring direction, and the entryListSchema/!!js concern dissolves with assertComposition itself. The settings section becomes: a read-only viewer over shipped compositions, a copy dialog (id + optional display name) as the only create entry, delete for custom rows, and a location action leading into the preset's own files — agentPreset.openDocument { agentPreset } resolves the directory host-side and opens it natively, or answers { opened: false, path } for the row to show as text where the deployment has no desktop. agentPreset.list reports hasDocument beside authorable; the gateway's nativeOpen config pins the capability where canOpenNativePath platform detection would mislead. The privileged set is now read/copy/openDocument/remove. With files as the only composition editor, standing mounts grew stamp-keyed generations: ensureStanding compares the composition file's mtime+size and starts the next generation for later sessions, while every joined session keeps the generation it runs on. New keyless web lane (agent-preset-authoring, overlay pins nativeOpen: false so goldens render one branch on every platform) drives view/copy/reveal/delete end to end; the real-composition CLI e2e switches to copy semantics.
2026-08-08 22:35:26 +08:00
openDocument: (payload: { agentPreset: string }) =>
this.record('agentPreset.openDocument', payload, Promise.resolve(ok({ opened: true as const }))),
}
readonly skills: IApiClient['skills'] = {
list: (payload: unknown) => this.record('skill.list', payload, this.onSkillList(payload)),
}
readonly settings: IApiClient['settings'] = {
describe: payload => this.record('settings.describe', payload, Promise.resolve(ok({ writable: true, hasDocument: false, namespaces: [] }))),
openDocument: payload => this.record('settings.openDocument', payload, Promise.resolve(ok({ opened: true as const }))),
feat(settings): detect stale writers with a revision, and announce raw changes The remaining P1 from the #939 review, plus the P2 it shares a mechanism with. Nothing carried a version, so two tabs editing one namespace silently overwrote each other — reproduced as tab B's `reasoning` lost to tab A's older draft. The seam's per-namespace write queue orders writes; it cannot tell a fresh writer from one replaying a snapshot a predecessor superseded. Each namespace now carries a monotonic `revision` over its RAW section. A write may send `expectedRevision`, checked at the FRONT of the queue (not at call time, which would race the very predecessor it guards against); a mismatch rejects with `SettingsConflictError` → `settings-conflict` on the wire, carrying both revisions. The editor captures the revision it opened at and, on conflict, asks the user to reopen rather than replaying its snapshot. The same counter fixes the missing broadcast. `settings/updated` is gated on the resolved value — correct for consumers, wrong for configuration surfaces: storing an override equal to the composition base leaves the resolved value alone while changing what the document says (the field is now overridden, not inherited) and moving every open editor's revision. `settings/document-updated (ns, revision)` fires on any raw-section change, in-process or external, and `host/settings-changed` now rides it. That event also closes the stale model picker: editing a provider's `models` changes no route, so `llm/adapters-updated` never fired and an open picker kept serving the old catalog. A change to an exposed provider namespace now emits `host/models-changed` too — that namespace holds the catalog. Docs: both sides of the five touched README pairs, a type-equiv block for `SettingsPathOp`, and an Agent Note recording what the plane exposes and who may overwrite what. The deferred wire-redaction gaps (secrets behind union/intersection/transform, `.default(...)` in the served envelope, schema text in rejection messages, `new Function` rehydration, pi-ai's `headers`) are recorded as TODO(settings-wire-redaction) and in Known Limitations rather than half-fixed.
2026-07-30 19:24:21 +08:00
update: payload => this.record('settings.update', payload, Promise.resolve(ok({ ns: 'fake', schema: {}, value: {}, applies: 'live' as const, secrets: [], revision: 0 }))),
replace: payload => this.record('settings.replace', payload, Promise.resolve(ok({ ns: 'fake', schema: {}, value: {}, applies: 'live' as const, secrets: [], revision: 0 }))),
mutate: payload => this.record('settings.mutate', payload, Promise.resolve(ok({ ns: 'fake', schema: {}, value: {}, applies: 'live' as const, secrets: [], revision: 0 }))),
}
readonly credentials: IApiClient['credentials'] = {
describe: payload => this.record('credentials.describe', payload, Promise.resolve(ok({ credentials: {} }))),
set: payload => this.record('credentials.set', payload, Promise.resolve(ok({}))),
unset: payload => this.record('credentials.unset', payload, Promise.resolve(ok({}))),
}
readonly llm: IApiClient['llm'] = {
providers: payload => this.record('llm.providers', payload, Promise.resolve(ok({ providers: [] }))),
models: payload => this.record('llm.models', payload, Promise.resolve(ok({
default: { provider: 'fixture', model: 'fixture' },
routableProviders: [],
groups: [],
failures: [],
}))),
feat(llm): interrogate a draft provider endpoint for its models Once a pi-ai route became a declaration rather than a catalog lookup, adding an OpenAI-compatible gateway meant knowing its model ids up front. Most such endpoints publish that list at `GET /models`, but no seam operation could ask: every one is keyed by a registered provider route, and the provider being added has no route, no stored profile, and no stored credential — the endpoint and key are values in a form. Interrogation is therefore keyed by settings namespace, which a configuration surface already holds from the configurable-provider directory. `registerModelDiscovery` offers it per namespace, `discoverModels` asks, and the request carries the draft itself. The reply is candidates, not a catalog: every field but the id is optional because most listings disclose nothing else, and adopting one is a settings write like any other. Nothing here reads or writes settings or credentials, so `settings.yaml` still decides what a route serves. `llm.discoverModels` carries the same draft over the wire. Its apiKey is the third and last payload a secret may ride, and it is never stored, logged, or echoed; every refusal folds into `model-discovery-failed`, naming the endpoint asked but never the credential offered. The pi-ai side is a plain GET for OpenAI-compatible protocols only — their listing shape is the one gateways, self-hosted servers, and the official endpoints agree on. Others say so, sending the user to hand-entry rather than reporting a guessed shape as an empty provider. The reply is read under a four-megabyte ceiling held on the bytes actually received, because the endpoint is a URL the user typed.
2026-08-04 10:14:46 +08:00
discoverModels: payload => this.record('llm.discoverModels', payload, Promise.resolve(ok({ models: [] }))),
}
/** Remote namespaces bound to this fake's programmable unary slots and stream pumps. */
sessionRemotes(): RuntimeRemotes {
return {
$stream: <Item>(options: RemoteStreamOptions<Item>) => (
new RemoteStream(AVAILABLE_STREAM_CONNECTION, options)
),
commands: {
execute: () => Promise.resolve({ ok: true, value: undefined }),
},
session: {
list: payload => this.remoteResult('session.list', payload, this.onList(payload)),
search: (payload, signal) => {
this.lastSearchSignal = signal
return this.remoteResult('session.search', payload, this.onSearch(payload))
},
create: payload => this.remoteResult('session.create', payload, this.onCreate(payload)),
selectModel: payload => this.remoteResult(
'session.selectModel',
payload,
this.onSelectModel(payload),
),
rename: payload => this.remoteResult('session.rename', payload, this.onRename(payload)),
fork: payload => this.remoteResult('session.fork', payload, this.onFork(payload)),
prompt: payload => this.remoteResult('session.prompt', payload, this.onPrompt(payload)),
attachment: payload => this.remoteResult('session.attachment', payload, this.onAttachment(payload)),
updateQueue: payload => this.remoteResult('session.updateQueue', payload, this.onUpdateQueue(payload)),
cancel: payload => this.remoteResult('session.cancel', payload, this.onCancel(payload)),
page: request => this.page(request),
follow: (request, signal) => this.openFollow(request, signal),
control: signal => this.openControl(signal),
},
subagents: {
list: parentSessionId => this.record(
'subagents.list',
parentSessionId,
this.onSubagentList(parentSessionId),
),
prompt: request => this.record('subagents.prompt', request, this.onSubagentPrompt(request)),
interruptByParent: (childSessionId, parentSessionId, mode) => this.record(
'subagents.interruptByParent',
{ childSessionId, parentSessionId, mode },
this.onSubagentInterrupt({ childSessionId, parentSessionId, mode }),
),
},
workspace: {
create: payload => this.record('workspace.create', payload, this.onWorkspaceCreate(payload)),
rename: payload => this.record('workspace.rename', payload, this.onWorkspaceRename(payload)),
delete: payload => this.record('workspace.delete', payload, this.onWorkspaceDelete(payload)),
insertBefore: payload => this.record(
'workspace.insertBefore',
payload,
this.onWorkspaceInsertBefore(payload),
),
insertSessionBefore: payload => this.record(
'workspace.insertSessionBefore',
payload,
this.onWorkspaceInsertSessionBefore(payload),
),
archiveSession: payload => this.record(
'workspace.archiveSession',
payload,
this.onWorkspaceArchiveSession(payload),
),
follow: signal => this.openWorkspace(signal),
},
}
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
}
/** Push one live Session event to every follower of that Session. */
async pushFollow(
sessionId: SessionId,
frame: Extract<SessionFollowFrame, { type: 'event' }>,
): Promise<void> {
await Promise.all([...(this.followConns.get(sessionId) ?? [])].map(conn => new Promise<void>((resolve) => {
conn.feed({ kind: 'frame', value: frame, delivered: resolve })
})))
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
}
/** Push one Host-wide control update. */
pushControl(frame: Exclude<SessionControlFrame, { type: 'baseline' }>): void {
for (const conn of [...this.controlConns]) conn.feed({ kind: 'frame', value: frame })
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
}
/** Push one Workspace projection increment. */
pushWorkspace(frame: Exclude<WorkspaceFollowFrame, { type: 'baseline' }>): void {
for (const conn of [...this.workspaceConns]) conn.feed({ kind: 'frame', value: frame })
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
}
/** End (clean close) or fail (throw) every open stream — reconnect-path material. */
endStreams(): void {
for (const conns of this.followConns.values()) {
for (const conn of [...conns]) conn.feed({ kind: 'end' })
}
for (const conn of [...this.controlConns]) conn.feed({ kind: 'end' })
for (const conn of [...this.workspaceConns]) conn.feed({ kind: '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
}
failStreams(error: unknown): void {
for (const conns of this.followConns.values()) {
for (const conn of [...conns]) conn.feed({ kind: 'fail', error })
}
for (const conn of [...this.controlConns]) conn.feed({ kind: 'fail', error })
for (const conn of [...this.workspaceConns]) conn.feed({ kind: 'fail', error })
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
}
callsOf(method: string): unknown[] {
return this.calls.filter(c => c.method === method).map(c => c.payload)
}
/** Number of currently attached journal generations for one Session. */
activeFollows(sessionId: SessionId): number {
return this.followConns.get(sessionId)?.length ?? 0
}
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
private record<T>(method: string, payload: unknown, response: Promise<T>): Promise<T> {
this.calls.push({ method, payload })
return response
}
private async remoteResult<T>(
method: string,
payload: unknown,
response: Promise<RpcResponse<T>>,
): Promise<RemoteResult<T>> {
return (await this.record(method, payload, response)).result
}
private page(request: SessionPageRequest): Promise<RemoteResult<SessionPage>> {
return this.fetchPage(request)
}
private async fetchPage(
request: SessionPageRequest,
response?: Promise<RpcResponse<SessionPage>>,
): Promise<RemoteResult<SessionPage>> {
const sessionId = addressSessionId(request.address)
const payload = request.address.kind === 'session'
? {
sessionId,
throughSeq: request.throughSeq,
...request.beforeSeq === undefined ? {} : { beforeSeq: request.beforeSeq },
...request.maxMessages === undefined ? {} : { maxMessages: request.maxMessages },
}
: {
parentSessionId: request.address.parentSessionId,
childSessionId: request.address.childSessionId,
mode: request.address.mode,
throughSeq: request.throughSeq,
...request.beforeSeq === undefined ? {} : { beforeSeq: request.beforeSeq },
...request.maxMessages === undefined ? {} : { maxMessages: request.maxMessages },
}
const method = request.address.kind === 'session' ? 'session.history' : 'subagent.history'
const result = await this.remoteResult(method, payload, response ?? this.onHistory({
sessionId,
throughSeq: request.throughSeq,
...request.beforeSeq === undefined ? {} : { beforeSeq: request.beforeSeq },
...request.maxMessages === undefined ? {} : { maxMessages: request.maxMessages },
}))
if (!result.ok) return result
return {
ok: true,
value: {
...result.value,
records: result.value.records
.filter(record => historyRecordLastSeq(record) <= request.throughSeq),
},
}
}
private async *openFollow(
request: SessionFollowRequest,
signal: AbortSignal = new AbortController().signal,
): AsyncGenerator<SessionFollowFrame> {
const sessionId = addressSessionId(request.address)
this.followStarts.push(sessionId)
this.calls.push({ method: 'session.follow', payload: request })
const conns = this.followConns.get(sessionId) ?? []
if (!this.followConns.has(sessionId)) this.followConns.set(sessionId, conns)
const stream = this.openValueStream(conns, signal)
try {
const response = await this.onHistory({
sessionId,
maxMessages: request.maxMessages ?? 50,
})
if (!response.result.ok) {
throw new RemoteStreamError(
response.result.error.code,
response.result.error.message,
response.result.error.details,
)
}
const page = response.result.value
const tail = page.records.at(-1)
const cursor = this.followCursor ?? (tail === undefined ? -1 : historyRecordLastSeq(tail))
yield {
type: 'snapshot',
header: {
version: 0,
id: sessionId,
createdAt: 0,
...(request.address.kind === 'subagent'
? { origin: 'subagent' as const, parentSession: request.address.parentSessionId }
: {}),
},
cursor,
records: page.records.filter(record => historyRecordLastSeq(record) <= cursor),
hasMore: page.hasMore,
projections: page.projections ?? { asOfSeq: cursor, values: {} },
}
yield* stream.values
} finally {
stream.dispose()
}
}
private async *openControl(
signal: AbortSignal = new AbortController().signal,
): AsyncGenerator<SessionControlFrame> {
const stream = this.openValueStream(this.controlConns, signal)
try {
yield { type: 'baseline', value: this.controlBaseline }
yield* stream.values
} finally {
stream.dispose()
}
}
private async *openWorkspace(
signal: AbortSignal = new AbortController().signal,
): AsyncGenerator<WorkspaceFollowFrame> {
const stream = this.openValueStream(this.workspaceConns, signal)
try {
yield { type: 'baseline', value: this.workspaceBaseline }
yield* stream.values
} finally {
stream.dispose()
}
}
private openValueStream<F>(
registry: ValueStreamConn<F>[],
signal: AbortSignal,
): OpenValueStream<F> {
const inbox: ValueStreamItem<F>[] = []
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
let wake: (() => void) | null = null
let inFlightDelivered: (() => void) | undefined
let disposed = false
const conn: ValueStreamConn<F> = {
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
feed: (item) => {
inbox.push(item)
wake?.()
},
}
registry.push(conn)
const dispose = (): void => {
if (disposed) return
disposed = true
inFlightDelivered?.()
for (const item of inbox) {
if (item.kind === 'frame') item.delivered?.()
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
}
const index = registry.indexOf(conn)
if (index >= 0) registry.splice(index, 1)
wake?.()
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
}
const values = (async function* (): AsyncGenerator<F> {
try {
while (!signal.aborted && !disposed) {
while (inbox.length > 0) {
const item = inbox.shift() as ValueStreamItem<F>
if (item.kind === 'end') return
if (item.kind === 'fail') throw item.error
inFlightDelivered = item.delivered
yield item.value
inFlightDelivered?.()
inFlightDelivered = undefined
}
await new Promise<void>((resolve) => {
wake = resolve
signal.addEventListener('abort', () => { resolve() }, { once: true })
})
wake = null
}
} finally {
dispose()
}
})()
return { values, dispose }
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
}
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
}