Commit Graph

378 Commits

Author SHA1 Message Date
Yichen Jiang
596334254c docs: align the config-page docs and terminology with the single-key editor rounds 2026-07-30 12:45:22 +08:00
Yichen Jiang
16f1cfe04e feat(ui-models): pin the deepseek endpoint placeholder, add pi-ai base URL, drop the fold hint 2026-07-30 12:39:56 +08:00
Yichen Jiang
d1bfdbff84 feat(ui-models)!: single-key hand-written provider editors with derived credential references
The Models page drops the generic schema renderer and the visible
environment-variable field: each editor is a curated per-family card whose
primary input is one write-only API key stored under a derived
<ROUTE>_API_KEY reference (recorded as apiKeyEnv in the pi-ai profile), an
unkeyed whole-section provider opens as its setup card, and the collapsed
customized-settings fold carries baseURL/reasoningEffort (deepseek) or
reasoning (pi-ai). dsh-client-schema-form reduces to the schema/draft model
layer (no React).
2026-07-30 12:17:56 +08:00
Yichen Jiang
51415debe5 docs: bilingual config-plane documentation, regenerated catalogs, and the web-config-plane Agent Note 2026-07-30 10:53:39 +08:00
Yichen Jiang
ebff7db11e fix(schema-form): extract the clone-spine walk and drop the unused ui-primitives dependency 2026-07-30 10:40:43 +08:00
Yichen Jiang
0d96676f35 feat(web): mount the config plane in dsh web and pin the Models page keyless
apps/cli/cordis.yml gains settings-local, credentials-local, and the bare
dormant llm-pi-ai row (manifest deps added for the resolver contract);
llm-deepseek drops its !!js apiKey inline for per-request credential
resolution. Both adapters tag apiKeyEnv role('credential-ref') so the
form mounts the credential control. The web e2e scaffold isolates a
harness home per run — an in-process boot must never touch the
developer's real ~/.dsh — and the new models-settings scenario pins the
whole loop through the shipped app: dormant directory as add vocabulary,
schema-driven editor apply landing in settings.yaml, the route
registering live (topology frame), and a write-only key landing in the
temp .env with the configured badge converging. A hermetic test-owned
reference name keeps a developer's real provider keys from flipping the
badge. schema-form joins the platform module table (seed + externals)
so client bundles share one instance.
2026-07-30 09:29:40 +08:00
Yichen Jiang
686e40ebf6 feat(ui-models): schema-driven provider configuration page
The Models settings section joins llm.providers (the configurable
directory with live state), settings.describe (schemas, layered redacted
values, secret slots), and credentials.describe (value-free badges) into
provider rows with one editor card at a time. The editor renders the
provider's profile subtree through dsh-client-schema-form; the
credential-ref role mounts a control that shows configured/source state
and stores keys write-only through credentials.set. Apply without
removals merges a minimal patch (stored secrets outside it survive);
apply after a reset — and row deletion — replace the user section so
removals land. The client runtime bridges the three new host frames to
typed ctx events (settings/credentials/models changed), the page
refetches on any of them once loaded, and ui-model's per-session picker
directories reload on models/changed so a settings-born route appears in
open pickers without a reopen.
2026-07-30 00:46:45 +08:00
Yichen Jiang
9592c8f271 feat(schema-form): schema-driven React form renderer package
@deepseek-ai/dsh-client-schema-form rehydrates the wire's serialized
schemastery envelope (new Schema(json)) and edits a draft user section
against it: presence-in-draft marks a field overridden with a per-field
reset, inherited values render as placeholders, role('secret') slots are
write-only with configured-state placeholders from the wire's secrets
list, dict adds take a union-typed sKey as their vocabulary, and any
node the renderer cannot faithfully edit falls back to a read-only view
instead of silently disappearing. renderField(context) is the role hook
the Models page will use for the credential-ref control; validateDraft
runs the same rehydrated validator the host uses, so the browser and
host judge one schema.
2026-07-30 00:24:19 +08:00
Yichen Jiang
191067559e feat(apiproxy): settings/credentials/llm wire domains, frames, and write guard
Eight compiler-locked methods: settings.describe/update/replace serve
redacted layered namespace views (secrets structurally absent from every
layer, write-only in the update direction) and fold seam refusals into
settings-rejected; credentials.describe/set/unset expose value-free views
with credential-rejected on shadowed writes; llm.providers merges the
configurable directory with live routes and llm.models claims the
host-scoped catalog reservation through the buildModelCatalog extraction
session.models now shares. Three HostFrame invalidations bridge the seam
events (host/settings-changed, host/credentials-changed,
host/models-changed), and the connection route generalizes the native-
dialog check into a privileged-method set covering all four writes. The
fixture and both fake clients grow the same face.
2026-07-30 00:13:12 +08:00
Yichen Jiang
fee12f1af0 refactor(llm-deepseek)!: rename the provider route to deepseek-official
The native adapter's route was named deepseek, colliding with pi-ai's
catalog provider of the same name, so the two DeepSeek paths could never
be mounted side by side. The web settings page needs both configurable at
once. Compositions, fixtures, goldens, scaffolding defaults, and docs all
move together (pre-release, no shim); TUI/session-query-spill/
missing-credential goldens re-recorded through their keyless refresh
modes because provider-name length shifts box padding and spill
truncation points.
2026-07-29 16:36:07 +08:00
imccyu
a447e26272 fix: test 2026-07-29 03:07:06 +08:00
imccyu
64742d3d30 fix: remove machine busy loading test 2026-07-29 03:01:34 +08:00
imccyu
9f7919d894 fix: remove machine busy loading 2026-07-29 02:40:29 +08:00
imccyu
d4ba2ecd63 Merge origin/master: goal domain lands beside plan
Union resolutions throughout — the fixture serves both the goal and plan
projection units (catalog gains /goal beside /plan; the retired
goal-fixture sample command yields to the real goal mirror), the mux
baseline spec expects all four unit frames, and the tsconfig paths /
Model Experience allowlist carry both domains' outlets.
2026-07-29 02:27:16 +08:00
imccyu
d34629d1aa fix: lint 2026-07-29 01:56:32 +08:00
imccyu
a40e0978da style: fix fixture indentation from the master merge 2026-07-29 01:41:44 +08:00
imccyu
0477f33079 feat(fixture): mirror the goal domain — /goal command, six verbs, projection unit
The keyless fixture now carries the goal chain end to end, mirroring the
host: /goal enters the command catalog and its execute path brackets a
goal/change create with the command lifecycle pair; the six mutation verbs
CAS-check the projected current goal and append whole-value changes (the
shared append path broadcasts the session event and the goal projection
frame); the tail-page projections block and the mux-open baseline always
carry the goal key (null before create / after clear). Connection specs
follow: the lifecycle round-trip replaces the not-implemented stubs, and
the mux baseline expects the third unit frame.

Also: GoalBar inline errors use ASCII parens around the code (review
feedback on #842).
2026-07-29 01:40:23 +08:00
imccyu
532b2b9107 Merge origin/master (regenerate the event producer-consumer graph) 2026-07-29 01:21:29 +08:00
imccyu
f42d9653c1 Merge origin/master (session ISession face) and adapt the goal ref read
The runtime's new outward session contract exposes projections as a
ProjectionsFace (faceOf only); the ui-goal verb closure reads the current
value through faceOf('goal').getSnapshot() instead of the store-level get.
Catalogs regenerated after the merge.
2026-07-29 00:56:50 +08:00
imccyu
20dd8e8d05 Merge remote-tracking branch 'origin/master' into goal-ui-merge-master 2026-07-29 00:52:03 +08:00
imccyu
2eae1955d3 refactor(client): hoist IconSparkle16 into ui-primitives
The sparkle glyph gained a second consumer (GoalBar beside the Others
tool-row variant), which the duplication gate correctly flags as a clone —
the package-local-until-shared rule its comment recorded has run its
course. Both copies collapse into the shared ic_ds_* family; the icon-set
count test grows to 57.
2026-07-29 00:41:36 +08:00
imccyu
2f6b9843b4 Merge origin/master (session-projection cache column) into goal-ui-merge-master 2026-07-29 00:17:39 +08:00
imccyu
9d9cdd640e style(test-runtime): drop a trailing space 2026-07-29 00:09:40 +08:00
imccyu
3e6a7fea43 test(ui-sidebar): pin the shell chrome with local DOM snapshots
First consumer of the runtime's single-slot mounting: the real apply mounts
on its own fiber, renderSlot('sidebar', ...) captures exactly the sidebar
slot's output, and update() re-renders the collapsed rail in place. The
.snap files carry semantic class names and svg fingerprints only.
2026-07-28 23:54:14 +08:00
imccyu
a3f9a4d3ee test(client): migrate the ui-conversation and web benches onto SlotTestRuntime
The five ui-conversation machinery specs (apply-inject, chat-apply,
chat-toolview-slot, service-orchestration, selection-survival — now .tsx)
and the web app/app-shell specs assemble through the runtime instead of
hand-built Context + SlotsService + fake-session scaffolding per suite.
Session behavior mocks are typed against ISession, so incomplete fakes
fail at compile time.
2026-07-28 23:54:14 +08:00
imccyu
3f7a8c6f8e feat(client): add the dsh-client-test-runtime package
A jsdom slot test runtime for feature specs: a real Cordis Context, the
production SlotsService and web-react renderer, and typed session/workspace
doubles (TestSessions implements ISessions with FixtureSession sessions;
TestWorkspaces implements IWorkspaces), so the compiler flags fixture drift
when a production face changes. Fixtures feed plain data: list rows,
conversation snapshots, and ISession-typed behavior stubs; provide-bundle
materialization runs the shared SessionProvideChannel.

DOM snapshot support: declare()/renderSlot() mount a single slot inside a
data-slot wrapper for local .snap capture, and a snapshot serializer folds
CSS-module class hashes to their semantic locals and collapses svg internals
to a content fingerprint. The typed provide() constrains declared-service
fakes to Partial of the service's outward face.
2026-07-28 23:54:13 +08:00
imccyu
cb8968c80e test(plan): cover the final-state tails and refresh the TUI golden
The idle /plan and /plan off command paths pin the immediate-commit copy;
the chip's dead leaving/locked guard is deleted (both states disable the
button, so no click reaches the handler); the multi-turn TUI golden is
re-recorded keyless over the idle-commit wording and the standalone
plan/mode narration ordering.
2026-07-28 23:49:38 +08:00
imccyu
1c43ff3061 test(goal): close the per-file coverage gaps for the goal surface
- projection.spec covers applyGoalProjection's non-message early return and
  the foreign-change-kind arm directly (the pure-function posture the unit
  requires — a throwing apply would tear down the registry drive).
- New ui-goal browser-plugin spec exercises the plugin body over a real
  cordis Context with fake faces: dock registration (id/order), verbs
  reading the CAS ref from the current projected value, the
  no-current-goal short circuit, RPC-error mapping, HMR disposal, the
  GoalDock adapter's three projection states, and the inert node half.
2026-07-28 23:48:24 +08:00
imccyu
0efc7f045e refactor(client): carve outward interfaces for the session, workspace, layout, slash, and conversation services
Feature packages now reach these domains through interface types only:
ISession/SessionFace (identity + prompt/cancel/loadOlder + the useSession
snapshot source), ISessions, IWorkspaces, ILayout, IConversation, and the
existing SlashServiceContract now actually mounted on Context.slash. The
concrete services implement their face; wire-pump and assembly entry
points stay on the classes. The provide-channel materialization and
current-projection logic moves into SessionProvideChannel so the
production service and the client test runtime share one implementation.
The workspaces service consumes sessions through the narrow SessionsPort.
2026-07-28 23:43:32 +08:00
imccyu
a39bd9035e feat(web): replace the plan select with a status chip and placeholder swap
Plan mode is entered through /plan only — the select control is retired.
The conversation.input.plan seat (now right of the access-mode control)
renders a read-only Plan chip while the projection's effective target is
plan mode; its hover x executes /plan off, and the chip follows the folded
target (appears on /plan immediately, disappears on /plan off) with frames
correcting either way. While plan mode is targeted the composer textarea's
placeholder switches to the plan-task wording — InputBar reads the same
projection through the standard-kit useProjection (the TodoDock posture:
a type-only key merge, no domain service edge), and owner placeholders
still win.
2026-07-28 23:39:50 +08:00
imccyu
da4e54b515 docs: regenerate catalogs and settle the static gates for the goal surface
- Regenerate the cordis/config catalogs, doc graphs, and module graph for
  the four newly mounted plugins and the goal projection key.
- ui-goal README pair (Model Experience indirect + Known Limitations) with
  its i18n record; sentence-allowlist entry for the indirect form.
- knip workspace entry for ui-goal (tsx test pattern).
- type-equiv manifest follows the seven host-coupled goal symbols to
  domain.ts.
- The web-slash-command-dispatch note documents the dropped prompt
  interception and leaves with it; the goal-bar note's code paths follow the
  component into ui-goal (pairing re-recorded).
2026-07-28 23:34:19 +08:00
imccyu
d804c9c94d fix(web): blank means the conversation has not started
The summary blank bit switches from log emptiness to the absence of any
turn/start: standalone plugin events — command lifecycle records,
plan/mode, session titles, goal metadata — no longer surface a fresh
session in lists or steal the New Session view. Running /plan (or /goal)
on a blank session keeps it blank and reusable; the first accepted
prompt's turn clears it. Both carriers share one predicate (summarize +
the host/session-added frame); the cold path keeps its constant false
with the index-read rationale; the client mirror already flips only on
prompt acceptance and needed no change.
2026-07-28 23:17:46 +08:00
imccyu
970b432227 fix(plan): commit an idle selection immediately
set() on an idle agent appends plan/mode at once — no request boundary
would arrive until the next prompt, so a queued intent used to hang as
pending forever (the composer showed a dead pending target). A running
agent keeps the boundary-flush path unchanged. set() now reports which
branch ran (committed/queued/cancelled/noop); the /plan handler's copy
follows the branch (idle: "Plan mode on/off", mid-turn: the next-step
wording), and both commit paths share the header-delta narration. The
invariant drops turn enclosure: plan/mode is a standalone whole-value
event (the synthetic log-only turns removal already established the
between-turns append shape). The fixture mirrors the idle commit.
2026-07-28 23:09:15 +08:00
imccyu
afaa9ad828 style: satisfy the eslint lane on the plan surfaces
Drop the assertions eslint proved unnecessary (getByRole gains the element
type parameter instead, keeping tsc satisfied), declare the injected
setPlanMode as a function property (the seat face is this-free), and narrow
the fixture's command args without String()'s object stringification arm.
2026-07-28 22:36:35 +08:00
imccyu
c46419cf5c fix(apiproxy,client): the list projection column becomes a seedable watermarked block
Review finding (PR #791): the column carried bare values (no seq), so the
client could not seed its value store without risking a stale list block
outranking newer push frames — and nothing consumed the column at all,
leaving cold titles absent after a restart. SessionSummary.projections is
now the same SessionProjectionsBlock as the history tail (values +
asOfSeq; attached rows cut the live registry, cold rows serve the cache's
identity-checked cachedSnapshot whose asOfSeq is the lowest served-row
watermark). SessionManager.refreshList seeds each row's block into the
per-session projection store via per-key apply — partial-baseline
semantics: an absent key never clears, and higher-seq-wins keeps stale
list blocks beneath push frames and tail baselines — so cold titles
surface in the sidebar without opening a session.
2026-07-28 22:26:11 +08:00
imccyu
76796df39b test: close the two coverage tails on the plan surfaces
The plan unit's same-reference gate for a repeated identical /plan selection
(the one uncovered branch in plan-mode/index.ts) gains an explicit case, and
ui-plan's empty node-half apply is exercised the way sibling surface plugins
do (the ui-conversation no-op precedent). Both files read 100% across all
four axes under the CI coverage lane's per-file thresholds.
2026-07-28 21:49:53 +08:00
imccyu
9be6419cec docs: regenerate catalogs and re-pair bilingual READMEs
Regenerate the cordis/config/persistence catalogs and the module graph for
the ui-plan package and the plan projection unit; add the ui-plan Chinese
README and the Model Experience indirect sentence (allowlisted: the control
dispatches /plan lines, dsh-plan-mode owns the model surface); carry the
QuestionComposer Markdown/scroll improvements into the Chinese ui-question
README; re-record the three touched translation pairs.
2026-07-28 21:46:48 +08:00
imccyu
6e950940ee feat(web): ui-goal package — GoalBar over the goal session projection
New client plugin package. The GoalBar component migrates verbatim from the
original PR (phase labels, inline edit form, resume-when-paused, pending
lockout, inline errors); its data path is rebuilt for the projection model:

- GoalDock adapter reads useProjection('goal') from the standard kit — no
  controller, no store, no refresh chain, no event listener (the original
  PR's Session goal fields, write-revision fence, and coalesced refetch all
  dissolve into the value store's seq rule).
- The inject face carries only the three mutation verbs; each reads the CAS
  ref from the session's current projected value at call time and maps the
  settled RPC result inline (callbacks from inject, live state from
  useProjection).
- Seats at conversation.input.dock order 1 (the slot contract names GoalBar
  as a resident of this strip), below TodoPanel/QueueDock, tucked against
  the composer; hero phase renders nothing for free.
- IconSparkle16 is package-local (sole consumer) — ui-primitives keeps only
  the shared action icons.
- goalbar.spec migrates with GoalSnapshot props (activation is process-local
  and absent from the projection; the strip renders durable phase only).
2026-07-28 21:40:49 +08:00
imccyu
1c2923c84f feat(web): re-land the goals wire domain as mutation-only ref acknowledgements
Six mutation RPCs (create/edit/pause/resume/complete/clear) move into
dsh-host-apiproxy (the PR's host/runtime carrier is gone): goalService()
resolves ctx.get('goals') with a loud absence error, mutateGoal() resolves
the session's agent (agentFor, the command.* implicit-resume precedent) and
acknowledges with the new CAS ref only. GoalError codes ride err.details.

goal.get and the wire GoalView/goalViewSchema are gone: the read side is the
'goal' session projection (whole values on the history tail page and
session/projection frames), so responses never feed client state — the rule
whose absence forced the original PR's write-revision fences.
2026-07-28 21:05:43 +08:00
imccyu
da68c6b401 feat(web): teach the fixture the plan projection and /plan channel
The fixture mirrors the host's plan unit: a double-event fold over
command/run (name plan) and plan/mode serves the {active, pending} value in
the projections baseline and advances it with session/projection frames;
/plan joins the command catalog with the host handler's wording; the prompt
path commits an outstanding selection as plan/mode inside the opened turn
(the agent/step boundary parallel). Baseline expectations in the fixture
specs gain the always-present plan key.
2026-07-28 21:04:27 +08:00
imccyu
8dab7d2f92 feat(web): seat the plan control on conversation.input.plan over the projection
Rewrite ui-plan as a pure browser surface plugin. The control occupies the
composer's named plan seat (declared empty by ui-conversation); reads render
the host-computed plan projection through the standard-kit useProjection
(absent key = capability absence, hides the control), writes execute /plan
or /plan off through command.execute. The node half becomes the empty
roster apply: plan behavior (command, policy, projection unit) is owned by
dsh-plan-mode, already composed on the web roster with its policy in
cordis.yml. The superseded RPC-backed setPlanMode face, the WEB_PLAN_SECTION
duplicate, and the node-plugin spec are removed; the roster row moves from
the retired CLIENT_PACKAGES table to the cordis.yml dshClient roster.
2026-07-28 20:57:46 +08:00
imccyu
cbab62bdea Merge origin/master into goal-ui: adopt the rewritten client core and apiproxy carrier
Conflict rulings follow the projection-reattach plan:
- host/runtime package (deleted on master): take master; the PR's boot
  composition moves to the cordis.yml roster and its goals handlers will be
  re-landed in dsh-host-apiproxy; the session.prompt slash interception and
  its spec are dropped entirely (superseded by command.execute + command/run
  logging).
- client core (rewritten on master): take master; the PR's Session goal
  fields/methods, ConversationSnapshot.goal, goalActions injection, and the
  hard-mounted GoalBar are all superseded by the 'goal' session projection
  (useProjection) and will return as the ui-goal plugin.
- wire contract: union of master's workspace/command/skill domains and the
  PR's goal domain, minus goal.get (the read side is the projection block +
  session/projection frames; six mutation RPCs stay).
- GoalBar component and spec leave ui-conversation (they re-land in the new
  ui-goal package); IconSparkle16 stays in ui-conversation chat.
- The web-slash-command-dispatch note documents the dropped interception and
  is removed; the goal-bar note will be rewritten for the projection model.
- pnpm-lock.yaml taken from master (reinstall recomputes).
2026-07-28 20:55:36 +08:00
imccyu
f0f1966c97 feat(plan): register the plan session-projection unit
plan-mode contributes the RFC's complete double-event example: command/run
records named plan set the wanted target (off -> false, else true),
plan/mode commits and clears it, and view derives {active, pending}.
Pending is thereby a pure replay quantity recovered from the log alone.
The plan key merges into SessionProjectionMap from src/types.ts, served
through the new ./types and ./client outlets (session-title template);
compositions without the registry are unaffected.
2026-07-28 20:46:46 +08:00
imccyu
2ecc89a186 chore(web): drop plan RPC residue and stale carriers after merge
Sweep the silently auto-merged leftovers of the superseded #587 wire layer:
the PlanModeState interface and planMode/setPlanMode entries in the sessions
API and rpc map, the fixture plan RPC spec, the composer.controls README
paragraph and spec assertions, the old-format plan-mode snapshot, and the
two Agent Notes written against the RPC design (to be rewritten for the
projection reattachment). ChatView/PendingCard keep the approval-only
pending rendering; QuestionComposer improvements ride along unchanged.
2026-07-28 20:28:43 +08:00
imccyu
ae55771511 Merge origin/master: session projection base, command channel, composer seats
Resolution follows the reattachment plan: the #587 wire layer (planMode/
setPlanMode RPC, prompt.planMode, client Session projection fences) is
dropped in favor of the session-projection base now on master; master
structure wins in all shared files. Kept from the PR side: the approval-only
pending filter in ChatView (questions render solely through the composer
takeover) and the auto-merged QuestionComposer improvements. The deleted
host/runtime package and retired test carriers are removed with master.
2026-07-28 20:24:06 +08:00
07akioni
a39890bba5 Merge branch 'master' into feat/close-todo 2026-07-28 19:45:36 +08:00
07akioni
2c65ebf6be test(connection): expect fixture todos projection null on empty log 2026-07-28 19:44:36 +08:00
07akioni
7bf36c6aa3 Merge origin/master into feat/close-todo
Keep master's session-projection carrier for todos, and fold turn/start
clearance into the tool-todo projection unit (plus TUI/fixture mirrors).
2026-07-28 19:43:56 +08:00
07akioni
6978dbfb13 fix: cr 2026-07-28 19:16:39 +08:00
Tianyi Cui
291676f704 Merge branch 'master' into xtr/identified-immutable-messages 2026-07-28 18:55:50 +08:00