Commit Graph
100 Commits
Author SHA1 Message Date
Yichen Jiang 25d959aa08 Merge remote-tracking branch 'origin/master' into worktree/web-multimodal-image-input
# Conflicts:
#	.agents/notes/implemented/architecture/2026-07-05-reconstructable-requests.i18n.yaml
#	.agents/notes/implemented/architecture/2026-07-05-reconstructable-requests.md
#	.agents/notes/implemented/architecture/2026-07-05-reconstructable-requests.zh.md
#	.agents/notes/implemented/architecture/2026-07-25-web-input-machine-and-slash-pipeline.i18n.yaml
#	.agents/notes/implemented/architecture/2026-07-25-web-input-machine-and-slash-pipeline.md
#	.agents/notes/implemented/architecture/2026-07-25-web-input-machine-and-slash-pipeline.zh.md
#	THIRD_PARTY_NOTICES.md
#	apps/cli/composition.md
#	apps/cli/config/base.cordis.yml
#	apps/cli/package.json
#	apps/cli/src/app-cli-entry.ts
#	apps/cli/src/bin.ts
#	apps/cli/tests/args.spec.ts
#	apps/web/tests/built-boot.snapshot.ts
#	apps/web/tests/navigation-panes.e2e.ts
#	docs/architecture.i18n.yaml
#	docs/architecture.md
#	docs/architecture.zh.md
#	docs/config-catalog.md
#	docs/cordis-catalog/services.md
#	docs/core-data-structures/core.i18n.yaml
#	docs/core-data-structures/llm-streaming.i18n.yaml
#	docs/event-producer-consumer.md
#	docs/module-graph.md
#	examples/acp-agent/tests/snapshots/cordis-inspect-jsdoc/session.jsonl
#	packages/README.i18n.yaml
#	packages/bundle/README.i18n.yaml
#	packages/client/connection/README.i18n.yaml
#	packages/client/connection/README.md
#	packages/client/connection/README.zh.md
#	packages/client/connection/src/client/fixture.ts
#	packages/client/connection/src/http-bridge.ts
#	packages/client/connection/src/index.ts
#	packages/client/connection/tests/fixture.spec.ts
#	packages/client/connection/tests/node-half.spec.ts
#	packages/client/runtime/README.i18n.yaml
#	packages/client/runtime/README.md
#	packages/client/runtime/README.zh.md
#	packages/client/runtime/src/client/contract/session.ts
#	packages/client/runtime/src/client/sessions/session.ts
#	packages/client/ui-conversation/README.i18n.yaml
#	packages/client/ui-conversation/README.md
#	packages/client/ui-conversation/README.zh.md
#	packages/client/ui-conversation/src/client/apply.ts
#	packages/client/ui-conversation/src/client/chat/AssistantMarkdown.tsx
#	packages/client/ui-conversation/src/client/chat/ChatView.tsx
#	packages/client/ui-conversation/src/client/chat/MessageItem.module.css
#	packages/client/ui-conversation/src/client/chat/MessageItem.tsx
#	packages/client/ui-conversation/src/client/contract/slots.ts
#	packages/client/ui-conversation/src/client/index.ts
#	packages/client/ui-conversation/src/client/input/contract.ts
#	packages/client/ui-conversation/src/client/input/facade.ts
#	packages/client/ui-conversation/src/client/input/hub.ts
#	packages/client/ui-conversation/src/client/locales.ts
#	packages/client/ui-conversation/src/client/service.ts
#	packages/client/ui-conversation/src/client/skeleton/ConversationSession.tsx
#	packages/client/ui-conversation/src/client/skeleton/InputBar.tsx
#	packages/client/ui-conversation/tests/apply-inject.spec.tsx
#	packages/client/ui-conversation/tests/input-bar.spec.tsx
#	packages/client/ui-conversation/tests/input-matrix.spec.tsx
#	packages/client/ui-conversation/tests/input-scenarios.spec.tsx
#	packages/client/ui-conversation/tests/service-orchestration.spec.ts
#	packages/client/ui-conversation/tests/skeleton.spec.tsx
#	packages/client/ui-trajectory/tests/views.spec.tsx
#	packages/compact/compact-basic/README.i18n.yaml
#	packages/cordis/tool-cordis/src/api-catalog.ts
#	packages/host/apiproxy/README.i18n.yaml
#	packages/host/apiproxy/README.md
#	packages/host/apiproxy/README.zh.md
#	packages/host/apiproxy/src/api-proxy.ts
#	packages/host/apiproxy/src/api/rpc.ts
#	packages/host/apiproxy/src/api/sessions.ts
#	packages/host/apiproxy/src/index.ts
#	packages/host/apiproxy/tests/api-proxy-models.spec.ts
#	packages/host/apiproxy/tests/rpc-schemas.spec.ts
#	packages/llm/llm-pi-ai/README.i18n.yaml
#	packages/llm/llm-pi-ai/README.md
#	packages/llm/llm-pi-ai/README.zh.md
#	packages/llm/llm-pi-ai/src/adapter.ts
#	packages/llm/llm/README.i18n.yaml
#	packages/ui/tui/README.md
#	packages/ui/tui/README.zh.md
#	packages/ui/tui/src/components/content.ts
#	packages/ui/tui/src/components/transcript.ts
#	packages/ui/tui/tests/tui.spec.ts
#	pnpm-lock.yaml
2026-08-08 01:56:29 +08:00
Yichen Jiang b1074e60ab test(web): re-record the skill-tool-row golden for the resolved seat label
Master added this scenario while this branch was open, so its golden froze
the composer seat's "Select model" fallback. The scaffold's route-only
adapter (added here for fixture-less scenarios) makes the seat resolve the
model those scenarios actually route to, which is what the other eight
goldens on this branch already show. Only the two seat lines move.
2026-08-07 18:54:50 +08:00
Yichen Jiang 209fb4d355 Merge remote-tracking branch 'origin/master' into worktree/default-model-persistence
# Conflicts:
#	packages/client/ui-conversation/README.i18n.yaml
#	packages/host/apiproxy/README.i18n.yaml
2026-08-07 18:18:40 +08:00
Yichen Jiang f3049e5663 fix(llm-pi-ai): describing a model must not fail on a bad profile level
`resolveModel` validated the profile's reasoning level against the exact
model and threw when it did not fit. That call builds the model catalog,
and the catalog build catches per PROVIDER — so one mis-set field took the
whole provider out of every picker behind a single error row, hiding even
the models that do support the level. Measured: `anthropic` set to `max`
threw for six of its eight models.

Describing what a model can do now reports an unusable profile level as no
default rather than throwing; the request path still refuses it, which is
where a bad configuration belongs. The existing spec asserted the old
throw and now asserts both halves of that split.

Known gap, left deliberately: a model that cannot take the route's level
still fails its first request while the picker shows 「Default」 for it,
because the request path keeps using the profile level as the fallback.
Reaching that needs a hand-written `settings.yaml` — the Models page no
longer writes the field — and the error names the model and the level, so
selecting a supported level is a way out. Closing it properly means giving
`AgentOptions` a `reasoningEffort` so compositions without a model picker
keep an entry point, then dropping the provider-scoped field altogether;
that is its own change.
2026-08-07 18:07:15 +08:00
Yichen Jiang 2dc1406dfd feat(ui-models): drop the provider-scoped reasoning effort, and red-flag a bad route id
**Reasoning effort leaves the provider cards entirely.** It is a per-MODEL
capability and the models under one provider disagree about which levels
they accept: setting `anthropic` to `max` made six of its eight models
throw UNSUPPORTED_REASONING_EFFORT, and because the catalog build catches
per provider, the whole provider vanished from the picker behind one error
row. A provider-scoped control can only ever be set to a value some of its
models reject.

The composer's model picker already offers each model its own levels, and
a switch there now records provider, model, and effort together as the
next session's default — so the setting has a better home at the right
granularity. The profile field stays in `settings.yaml` for a deployment
that knows its route; only the control is gone, from both cards and both
adapter families. Two `components.spec` cases used the control as the
vehicle for their op assertions and now use `baseURL`, which is what they
were actually testing.

**A rejected Provider ID now reads as a fault.** It shared the neutral
hint paragraph with the field's guidance, so the copy telling the user
what they got wrong looked like advice. Reuses the existing `.error`
style, matching the split the key field already makes.
2026-08-07 17:36:08 +08:00
Yichen Jiang 135064c831 fix(ui-models): stop the shared hint contradicting a filled-in field
The line under the create form names the one blocked gate worth naming,
and its fallback arm reads "no models yet". An unmet Provider ID gate fell
through to that arm, so a card with two models listed right above it was
told it needed one. The key gate was already excluded for this reason; the
route gate was assumed excluded because its field explains itself, and was
not.

Tightening the route rule in the previous commit is what made this easy to
hit — a digit-leading id now fails the gate — but the fallthrough predates
it and fires for an empty or taken id just the same.
2026-08-07 17:02:05 +08:00
Yichen Jiang 5a90eb41fb fix(ui-models): three faults the running app surfaced
**A hand-declared route must not offer a reasoning effort.** The earlier
commit read the create card's missing control as drift and added one. It
is the other way round: such a model has no reasoning capability — pi-ai's
installed catalog is what supplies one, and it ships nothing under the
route — so `resolveModel` throws UNSUPPORTED_REASONING_EFFORT for every
model on it and the whole provider drops out of the picker. Verified
against the adapter, not inferred. The create card no longer offers it and
the editor withholds it on the directory's `declared` bit, which is the
real bug: that control has always been wrong for these routes.

**A blocked composer locked the way out of the block.** Reusing the
no-workspace inert posture disabled the model seat along with everything
else, so the bar asked for a model while preventing the one control that
picks one. A block now rides its own `blocked` owner prop: the textarea,
send, commands, plan seat, and access chip all lock, and the model seat
alone stays live.

**A Provider ID could derive an illegal credential reference.** The card
accepted a digit-leading id, whose derived `123_API_KEY` then failed at
the credential seam with a raw regular expression the user cannot act on.
The id must now start with a letter, and a test pins the relation between
the two rules rather than the regex.
2026-08-07 16:45:50 +08:00
Yichen Jiang 210b6fec5c Merge pull request #1879 from deepseek-harness/worktree/web-skill-icon-14
fix(web): render Skill icon at 14px
2026-08-07 16:25:34 +08:00
Yichen Jiang 8e57dd1dac fix(web): render Skill icon at 14px 2026-08-07 16:15:17 +08:00
Yichen Jiang 53bd0a90a6 Merge pull request #1754 from deepseek-harness/worktree/web-skill-tool-row
feat(web): add dedicated skill tool row
2026-08-07 15:45:24 +08:00
Yichen Jiang d03d3ab70b Merge remote-tracking branch 'origin/master' into worktree/default-model-persistence
Carries two edits beyond conflict resolution, both forced by what master
brought in:

- `CustomProviderCard`: master added front-end key validation and a
  component-level `keyValue` (already trimmed) while still writing
  `apiKeyEnv` unconditionally. Kept this branch's blank-key rule and its
  committed-profile retry gate, and adopted master's single `keyValue` so
  the component has one spelling of the key rather than two.
- `docs/user/guide/providers`: master merged #1810, whose default-model
  section still taught overriding the `api-gateway` row in
  `$DSH_HOME/config.yaml` — the behavior this branch replaced. Rewritten
  for the settings section the picker now writes, plus the review fix from
  #1810 replacing the colloquial 挂着 in the opener.
2026-08-07 15:44:57 +08:00
Yichen Jiang bb43ff4f37 feat(ui): make a session that cannot send refuse to accept one
A default naming a route the Models page has since removed left the
composer saying 选择模型 while the input still accepted a message, which
then failed inside the adapter mid-turn.

`session.prompt` now refuses with `model-unavailable` before opening a
turn. That is the enforcement boundary: the method stays callable no
matter what a client disables. `session.models` reports the same fact as
`routable`, and ui-model pushes a block through the new
`ctx.conversation.blocks` registry so the bar renders the disabled
textarea it already renders without a workspace, carrying the blocker's
own reason. The push direction is forced — ui-model already depends on
ui-conversation, so ui-conversation cannot read it back.

The gate is `routable`, not "matches no advertised group": catalog
membership is advisory, so a route serving a model it stopped advertising
is missing from the groups yet perfectly usable, and `null` before the
first load never blocks so a slow Host cannot lock a working composer.

The scaffold gains a route-only adapter for fixture-less keyless
scenarios. Registering zero providers is a test artifact — every product
composition mounts one — and the goldens that froze the seat's fallback
label now show the model those scenarios actually route to.
2026-08-07 15:26:42 +08:00
Yichen Jiang 7ce9c244ae Merge pull request #1810 from deepseek-harness/claude/docs-model-providers
docs(user): 新增「配置模型」入门文档,并让文档站点自带图片
2026-08-07 15:15:27 +08:00
Yichen Jiang a45beb1292 Merge branch 'master' into worktree/web-skill-tool-row 2026-08-07 15:06:42 +08:00
Yichen Jiang 23a4c0a51e Merge pull request #1827 from deepseek-harness/worktree/charming-swartz-83bf33
fix(llm,web): validate API key format before it reaches an HTTP header
2026-08-07 14:36:04 +08:00
Yichen Jiang d5c5e7f2e9 Merge remote-tracking branch 'origin/master' into worktree/charming-swartz-83bf33
# Conflicts:
#	apps/web/tests/models-settings.e2e.ts
#	docs/config-catalog.md
#	packages/client/ui-models/README.i18n.yaml
#	packages/client/ui-models/README.md
#	packages/client/ui-models/README.zh.md
#	packages/client/ui-models/src/client/ProviderEditor.tsx
#	packages/client/ui-models/tests/provider-form.spec.tsx
#	packages/llm/llm-deepseek/README.i18n.yaml
#	packages/llm/llm-deepseek/README.md
#	packages/llm/llm-deepseek/README.zh.md
#	packages/llm/llm-deepseek/src/index.ts
2026-08-07 14:12:17 +08:00
Yichen Jiang 63724ed5f9 Merge pull request #1856 from deepseek-harness/worktree/fix-wsl-open-config
fix(apiproxy): open config files through Windows on WSL
2026-08-07 14:11:30 +08:00
Yichen Jiang 0823a3484a fix(web): report a wrapped paste as the same API key format failure 2026-08-07 14:03:11 +08:00
Yichen Jiang 72618f29b5 fix(ui-models): stop the create card pinning a reference on a blank key
Master's credential-lifecycle work taught the editor card that a pi-ai
profile names `apiKeyEnv` only when a key is actually stored, so a route
left blank keeps its provider-native auth path. The create card kept
writing the derived reference unconditionally, so a route declared for a
credential chain or ADC was born pointing at a reference nothing sets —
and now rendered a red missing-key dot for it.

Both cards apply one rule. The obsolete assertion moves with the behavior
(the with-key case is covered by the neighbouring test), and the merged
Models e2e golden shows the declared route unmarked rather than flagged.
2026-08-07 13:57:52 +08:00
Yichen Jiang 768e2e866f fix(web): keep skill row pairing client-local 2026-08-07 13:56:42 +08:00
Yichen Jiang d63c70bcf1 Merge remote-tracking branch 'origin/master' into worktree/default-model-persistence
# Conflicts:
#	apps/web/tests/models-settings.e2e.ts
#	packages/client/ui-models/README.i18n.yaml
#	packages/client/ui-models/README.md
#	packages/client/ui-models/README.zh.md
#	packages/client/ui-models/src/client/ModelsSection.module.css
#	packages/client/ui-models/src/client/ModelsSection.tsx
2026-08-07 13:54:15 +08:00
Yichen Jiang 9679597204 feat(ui-models): tag the provider rows this deployment declared
A row's stored profile could not tell a hand-declared gateway from a
shipped provider whose models someone narrowed — both look identical from
outside the adapter — so the Models page had no way to mark the routes a
deployment added itself.

The directory entry now carries `declared`, answered by the owning adapter
against its own installed catalog, and the page renders a Custom tag from
it. Absence stays "this adapter draws no such distinction" rather than
"shipped", so a route no adapter claims is labelled neither way.

Also records the default-route work's Agent Note and the e2e evidence for
all three changes: the composer switch writing the section, and the Models
page declaring a route with its own reasoning effort.
2026-08-07 13:49:47 +08:00
Yichen Jiang 0cb922199d fix(apiproxy): open config files through Windows on WSL 2026-08-07 13:30:05 +08:00
Yichen Jiang ddacc8de45 Merge pull request #1735 from deepseek-harness/worktree/provider-credential-lifecycle
fix(web): recover provider credential lifecycle
2026-08-07 13:27:18 +08:00
Yichen Jiang e0f9f7a6e6 fix(ui-models): let a hand-declared route set its reasoning effort
The create card omitted the provider-level effort the editor card offers
for the same namespace, so a route declared through 添加自定义提供方 gained
a setting the moment it was reopened for editing — one the creating user
was never shown.

Both cards now render one shared control. The field, its vocabulary, and
the inherit-means-absent rule live with the control rather than in the
editor, which is what stops the two from drifting apart again.
2026-08-07 13:25:46 +08:00
Yichen Jiang f2d1a29636 feat(apiproxy): make the default model a user setting the picker writes
The route a new session starts from was frozen into the gateway's
composition entry, so switching models in a conversation reached only that
conversation and every later session went back to the shipped default.

The gateway now owns an `api-gateway` settings section: the entry is the
base layer and the user document layers over it, so `session.selectModel`
records an accepted switch as the default for the next session. The write
is wholesale rather than a merge — switching to a model with no reasoning
effort has to clear a stored one — and a storage failure is reported
without undoing the switch, which already applies to its own session.

`targetFor` now resolves its tiers on every read instead of seeding once:
an explicit selection, else the session's own logged request header, else
the live default. That is what keeps a session that has run a turn deriving
its route from its log forever after, while a session still blank — New
Session reuses one rather than minting another — starts from a default
saved after it was created.
2026-08-07 13:23:19 +08:00
Yichen Jiang 09d1b0d27f test(web): align skill snapshot with turn actions 2026-08-07 12:50:23 +08:00
Yichen Jiang 9264f383e6 Merge remote-tracking branch 'origin/master' into worktree/web-skill-tool-row
# Conflicts:
#	apps/web/tsconfig.json
#	tsconfig.host.json
2026-08-07 12:48:08 +08:00
Yichen Jiang 545080105f Merge branch 'master' into worktree/charming-swartz-83bf33 2026-08-07 11:37:57 +08:00
Yichen Jiang abd2e2973b Merge branch 'master' into worktree/provider-credential-lifecycle 2026-08-07 11:37:41 +08:00
Yichen Jiang 3aa3212fd7 Merge branch 'master' into worktree/web-skill-tool-row 2026-08-07 11:37:34 +08:00
Yichen Jiang 5a422337f8 fix(web): silence the stale gate hint, clear whitespace fields, narrow the paste heuristic 2026-08-07 11:36:01 +08:00
Yichen Jiang df5790919c Merge branch 'master' into worktree/web-skill-tool-row 2026-08-07 11:31:49 +08:00
Yichen Jiang 6edd98e2b2 Merge branch 'master' into worktree/provider-credential-lifecycle 2026-08-07 11:28:22 +08:00
Yichen Jiang 91e9d5afa2 Merge branch 'master' into worktree/charming-swartz-83bf33 2026-08-07 11:28:05 +08:00
Yichen Jiang a7d3744268 test(web): cover the create card's blank-key copy substitution 2026-08-07 11:16:20 +08:00
Yichen Jiang ec1111f18e docs: keep the API key Agent Note current with the review fixes 2026-08-07 11:04:02 +08:00
Yichen Jiang 1019f149c4 fix(web,llm): address review — document the card contract, pin the host diagnosis, gate the probe 2026-08-07 11:02:38 +08:00
Yichen Jiang 6b75bb0425 fix(web): say the API key format is wrong rather than naming the characters 2026-08-06 23:15:08 +08:00
Yichen Jiang a89c26b611 test(web): pin the API key field refusal end to end 2026-08-06 22:42:56 +08:00
Yichen Jiang cf9eade39d feat(web): refuse an unusable API key on the field that holds it 2026-08-06 22:30:43 +08:00
Yichen Jiang 665b5697bb docs(llm-pi-ai): document the invalid-credential refusal 2026-08-06 22:13:02 +08:00
Yichen Jiang 45d78c9272 fix(llm-pi-ai): refuse an unusable API key before the header is built 2026-08-06 22:12:50 +08:00
Yichen Jiang fe8a88c96a Merge pull request #1803 from deepseek-harness/worktree/align-core-web-rl-prompt
fix(web): align core profile with RL prompt
2026-08-06 21:57:28 +08:00
Yichen Jiang b1660ab8a4 docs(llm-deepseek): document the invalid-credential refusal 2026-08-06 21:40:06 +08:00
Yichen Jiang 5514dd2bd6 fix(llm-deepseek): refuse an API key no header can carry 2026-08-06 21:39:57 +08:00
Yichen Jiang a48b84c001 fix(scripts): only publish images the repository owns, and keep their suffix
Review found four real gaps in the image placement this PR introduced.

Link rewriting only needs a target to exist, but publication copies its bytes
onto the site: a reference reaching out of the tree through `../..` or a
symlink would put a build-machine file on a published page. Only a regular
file whose real path stays inside the repository is copied now, and anything
else fails the projection naming the page and the target.

A placed reference kept none of its `?query` or `#fragment`, which the GitHub
branch has always carried and which decides what an SVG view fragment or a
Vite query means. The suffix rides along again, and the file name is
percent-encoded because the destination is a Markdown inline target.

Page outputs and placed images now claim projected paths from one map, so the
"fail loud rather than overwrite" invariant covers a page and an image landing
on one path, not only two images. `docsSourceFiles()` reports placed images, so
replacing a screenshot re-projects under `docs:dev` instead of serving the
previous copy until something touches the page.

The guide said to set `agent-loop`'s `agents` to change the default model,
which does nothing for `dsh web`: that default is `api-gateway`'s, and the
shipped composition leaves `agents` empty. It also promised that a catalog
provider needs only an API key, which is false for Bedrock, Vertex, Azure, and
Codex. Both are corrected.

The projection note and the doc-site skill carried the superseded "a
repository image becomes a raw GitHub URL" rule; both now describe what ships.
2026-08-06 21:14:39 +08:00
Yichen Jiang b6b57ceda3 docs(llm): document the invalid-credential code 2026-08-06 20:57:43 +08:00
Yichen Jiang 88f5de5755 docs: regenerate cordis catalog and event graph for shifted index.ts lines 2026-08-06 20:57:30 +08:00
Yichen Jiang 53d47a8f86 docs(cli): clarify core prompt environment semantics 2026-08-06 20:45:57 +08:00
Yichen Jiang 5b842895a8 feat(llm): define the legal API key shape in the seam 2026-08-06 20:43:50 +08:00
Yichen Jiang c826966181 Merge remote-tracking branch 'origin/master' into claude/docs-model-providers
# Conflicts:
#	docs/user/guide/quickstart.i18n.yaml
2026-08-06 20:41:55 +08:00
Yichen Jiang 266629f1c3 test(web): cover core profile request header 2026-08-06 20:41:19 +08:00
Yichen Jiang c16cad7c03 Merge remote-tracking branch 'origin/master' into worktree/web-skill-tool-row
# Conflicts:
#	packages/host/apiproxy/README.i18n.yaml
2026-08-06 20:41:13 +08:00
Yichen Jiang 9ff7eb84f0 docs: propose API key format validation 2026-08-06 20:35:02 +08:00
Yichen Jiang b5600e9376 Merge remote-tracking branch 'origin/master' into worktree/align-core-web-rl-prompt
# Conflicts:
#	apps/cli/reference/README.i18n.yaml
#	apps/cli/reference/README.md
#	apps/cli/reference/README.zh.md
#	apps/cli/src/app-cli-entry.ts
#	apps/cli/src/dump-config.ts
#	apps/cli/src/web.ts
#	apps/cli/tests/built-bin.e2e.ts
#	apps/cli/tests/web-prompt-context.spec.ts
#	apps/web/tests/scaffold.ts
2026-08-06 20:34:57 +08:00
Yichen Jiang 2426be893a fix(scripts): let the documentation site carry its own images
An image the manifest does not publish was rewritten to a raw.githubusercontent
URL, which serves a private repository to nobody: raw answers 404 unauthenticated,
and a github.com session does not authenticate it. Every image on the site was
broken for every reader, and no gate could say so — link checking asks whether
the file exists in the repository, not whether a site reader can fetch it.

The projector now copies a referenced image into the generated tree beside the
page and points the reference at it, so Vite bundles it like any other site
asset and repository visibility cannot reach the published page. The copy lands
beside the page so one relative URL is right from both locale trees and a page's
assets leave with the page; two sources projecting onto one path throw rather
than letting the last copy win.

Canonical Markdown keeps writing ordinary repository-relative paths, so the same
file still renders when read in the repository.

The guide this was found through is renamed to its subject: 配置模型.
2026-08-06 20:21:17 +08:00
Yichen Jiang 50c46be948 fix(cli): isolate Web runtime context from headless 2026-08-06 20:11:44 +08:00
Yichen Jiang a231b56eba docs(user): show the Models page in the provider guide
The page told a reader to open Settings → Models and named the two buttons,
which is thin help for someone who has never seen the page. Two screenshots
per language carry it instead: the Models page with its provider card and both
add actions, and the custom-provider form with the fields it asks for.

They are the first images under docs/. The projector rewrites a
repository-relative image to a raw.githubusercontent URL pinned at the built
commit, so nothing is copied into the site bundle, and the pairing gate takes
no signature from image nodes — which is what lets each language carry its own
localized capture.
2026-08-06 19:58:31 +08:00
Yichen Jiang 0cf1ba7f87 fix(web): align core profile with RL prompt 2026-08-06 19:52:16 +08:00
Yichen Jiang 318142ebe9 docs(user): add a model-provider configuration guide
The guide tier said how to compose plugins with `cordis.yml` but never how to
reach a provider other than DeepSeek, so the two things a person actually does
— give a catalog provider its key from the Models page, and declare a gateway
the installed catalog does not ship — had no home outside package READMEs.

The new page covers both entry points and the relationship between them: the
Models page and `$DSH_HOME/settings.yaml` write one document, over a
`llm-pi-ai` adapter that mounts dormant until that document names routes. It
carries the settings shape, catalog replacement and its capacity fallbacks,
credential references, and the four failures a misconfigured route produces,
and links the generated config catalog for exhaustive fields.

It sits between Quick start and Configuration in the guide sidebar, which is
where a reader hits the question.
2026-08-06 19:21:04 +08:00
Yichen Jiang e43e4f187e fix(web): satisfy provider model gates 2026-08-06 17:31:25 +08:00
Yichen Jiang a667d2cd64 fix(web): address skill row review feedback 2026-08-06 17:21:13 +08:00
Yichen Jiang 66b136e6e3 Merge remote-tracking branch 'origin/master' into worktree/provider-credential-lifecycle
# Conflicts:
#	packages/client/ui-models/README.i18n.yaml
#	packages/client/ui-models/README.md
#	packages/client/ui-models/README.zh.md
#	packages/client/ui-models/src/client/ModelsSection.tsx
#	packages/client/ui-models/src/client/ProviderEditor.tsx
2026-08-06 16:54:33 +08:00
Yichen Jiang f2050bfd1e fix(web): surface provider credential status 2026-08-06 16:44:04 +08:00
Yichen Jiang 690f7ae035 Merge remote-tracking branch 'origin/master' into worktree/web-skill-tool-row 2026-08-06 16:41:57 +08:00
Yichen Jiang b13b0e2983 Merge pull request #1368 from deepseek-harness/claude/web-pi-ai-provider-form
feat(web): declare a provider and its models from the Models page
2026-08-06 16:37:14 +08:00
Yichen Jiang 8eebf7dd40 test(web): pin the remaining markdown fixture's event times
Master stabilized two of the three hand-built markdown fixtures by spacing
their event times, so the stats line's LLM segment stops depending on how fast
the replay ran. `markdown-images` was left on the old shape and kept failing
for exactly that reason — a different test each run, always the same one-line
difference.

Pin it the same way and record the segment its golden now always shows. This
supersedes the normalizer that dropped those segments outright, reverted here:
pinning the fixture keeps a real part of the page in the goldens instead of
hiding it from every one of them, and master's two goldens already record it.
2026-08-06 16:24:20 +08:00
Yichen Jiang 5e5fd977c5 Merge branch 'claude/pi-ai-model-discovery' into claude/web-pi-ai-provider-form 2026-08-06 16:18:57 +08:00
Yichen Jiang 2a790e6120 Merge branch 'claude/web-llm-pi-ai-config-385e24' into claude/pi-ai-model-discovery 2026-08-06 16:18:15 +08:00
Yichen Jiang be4c996d97 Merge remote-tracking branch 'origin/master' into claude/web-llm-pi-ai-config-385e24 2026-08-06 16:17:33 +08:00
Yichen Jiang 3e1c63b2ea test(web): stop the stats line's wall-clock segments from deciding a golden
`StatsLine` renders its LLM, tool-call, and throughput segments only while the
matching measurement exceeds zero, and all three are wall clock taken during
the replay. A machine that finishes a step inside one millisecond drops the
segment a slower one keeps, so a golden recorded what the recording machine's
speed was rather than what the page shows. Goldens across this suite already
disagreed about the LLM segment for that reason, and CI failed on whichever
test landed on a slow enough runner — a different test each run, always the
same one-line difference.

Tokenizing the values was never enough, because presence is what moves. The
normalizer now drops those segments outright, each taking one adjacent
separator so nothing is left holding a dangling separator or a doubled space,
and the recorded goldens are normalized the same way. `TTFT avg` stays: it
gates on a step count the fixture determines.
2026-08-06 15:47:42 +08:00
Yichen Jiang dd36db5434 test(ui-models): cover reading a stored capacity back out of a row
Every capacity assertion typed into the field first, so the path that reads a
stored count — the one an already-configured route takes every time it is
opened — was never exercised, and the coverage gate said so. A row now opens
on stored counts and is expected to spell them `1M` and `256K`, the same
vocabulary the field accepts.

`capacityText` takes the row it is rendering rather than looking it up again
by index, which retires the impossible empty-row branch that lookup needed.
The list editor's pre-flight model check is unreachable from the card that
disables submit on the same failure, and says so where it stands.
2026-08-06 15:18:18 +08:00
Yichen Jiang 3a3abc2bc4 fix(ui-models): restore the add-provider row and hint an empty capacity
The two ways to gain a provider had picked up the shared button base's pill
shape and shrunk to their labels, so they read as two stray buttons of
different lengths under the list instead of its last slot. They split the row
evenly again, on the row cards' own corner and the dashed outline this page
already uses for "nothing here yet"; the rule that overrides the base now
says so in one place rather than layering a second `.addButton` block.

An empty capacity shows the adapter's route-level fallback as its placeholder,
so a blank field reads as "sized by the route" rather than as a model with no
capacity. It is a hint, not a mirror: the field counts K as 1000 while the
fallback is 262144, and a deployment may override it.

The picker's description says what the list is without promising an edit the
rows themselves already offer.
2026-08-06 15:18:18 +08:00
Yichen Jiang dc7510a902 fix(ui-models): close the media block that swallowed the fetch dialog's styles
The `@media (prefers-reduced-motion: reduce)` block never closed, so every
rule after it — the whole fetch dialog, its candidate list, and the rows
inside — applied only to viewers whose system asks for reduced motion. The
sheet still parsed and the classes still attached, so the list painted with
the browser's own bullets, indentation, and inline label, and the reported
model id ran straight into the capacity beside it.

Report the id alone: it is the string adoption writes, and the capacities the
endpoint disclosed are adopted with it and editable in the row that appears.
The candidate row's remaining font name is the one the theme declares.

The styles gate now checks that the sheet's braces balance, and reads every
theme sheet rather than the platform tokens alone, so a name declared in a
sibling is not called undeclared and a `--dsh-` typo cannot pass as a token.
2026-08-06 15:18:17 +08:00
Yichen Jiang 44484ec5f6 feat(web): declare a provider and its models from the Models page
The Models page could name a provider's key and little else. Adding an
OpenAI-compatible gateway meant opening $DSH_HOME/settings.yaml and
knowing the profile shape; correcting a stale context window meant the
same. This layer puts both on the page: a card that declares a route
pi-ai does not ship — id, endpoint, protocol, key, models — and a model
list on the pi-ai editor that can ask the provider what it serves and
adopt the answer.

It follows the DeepSeek catalog editor that landed in #1050 rather than
inventing a second look for the same job. Both editors now share the
section shell and heading, the danger-tinted delete, the add-model
button, the empty state, the per-row validator that names a bad row by
its position, and one K/M capacity vocabulary — 256K and 1M are read and
spelled back, while settings.yaml still stores plain token counts. The
row type is structurally open like that editor's, so a profile field
this card does not edit survives an edit here.

Three of that editor's decisions replaced weaker ones this branch had
made. Inheritance now reads the composition base rather than the
effective value, which would echo an override back the moment a reset
dropped it. Validation names the offending row instead of stating a
blanket problem. And emptying the list is no longer conflated with
handing the catalog back to the adapter — those are separate acts, with
separate affordances.

The create write carries the revision the card opened at, so a route
another tab declared meanwhile is a conflict rather than a silent
overwrite of its profile.
2026-08-06 15:18:17 +08:00
Yichen Jiang 3fe5b52c6d Merge remote-tracking branch 'origin/master' into worktree/web-skill-tool-row 2026-08-06 15:07:36 +08:00
Yichen Jiang d97e150845 Merge branch 'claude/web-llm-pi-ai-config-385e24' into claude/pi-ai-model-discovery 2026-08-06 15:07:02 +08:00
Yichen Jiang 2abd06f21f Merge remote-tracking branch 'origin/master' into claude/web-llm-pi-ai-config-385e24 2026-08-06 15:01:19 +08:00
Yichen Jiang a7c035285b revert: leave markdown snapshots unchanged 2026-08-06 14:50:21 +08:00
Yichen Jiang e0e84d265a refactor(web): deduplicate skill disclosure leading 2026-08-06 14:40:08 +08:00
Yichen Jiang ee0e33e10f test(web): refresh markdown snapshots 2026-08-06 14:31:19 +08:00
Yichen Jiang 2d71c928e6 Merge branch 'claude/web-llm-pi-ai-config-385e24' into claude/pi-ai-model-discovery 2026-08-06 14:26:48 +08:00
Yichen Jiang c7dbe4c6d9 Merge remote-tracking branch 'origin/master' into claude/web-llm-pi-ai-config-385e24 2026-08-06 14:24:58 +08:00
Yichen Jiang 1ed5dc81d2 docs: refresh module graph 2026-08-06 14:22:54 +08:00
Yichen Jiang bb920b32e0 feat(web): add dedicated skill tool row 2026-08-06 14:07:15 +08:00
Yichen Jiang 16ad5f3f86 Merge pull request #1732 from deepseek-harness/worktree/fix-1680-cjk-markdown
fix(web): render CJK emphasis and link code URLs
2026-08-06 13:15:50 +08:00
Yichen Jiang 099b903ac6 fix(web): preserve provider credential retry checkpoint 2026-08-06 13:15:09 +08:00
Yichen Jiang 42b9ee7a53 Merge branch 'claude/web-llm-pi-ai-config-385e24' into claude/pi-ai-model-discovery
# Conflicts:
#	docs/core-data-structures/core.i18n.yaml
#	docs/event-producer-consumer.md
2026-08-06 13:05:15 +08:00
Yichen Jiang 98b894f8eb Merge remote-tracking branch 'origin/master' into claude/web-llm-pi-ai-config-385e24
# Conflicts:
#	docs/event-producer-consumer.md
2026-08-06 13:03:40 +08:00
Yichen Jiang f330b6ae79 test(web): refresh targeted provider action golden 2026-08-06 12:51:18 +08:00
Yichen Jiang af652c949f fix(web): recover provider credential lifecycle 2026-08-06 12:09:14 +08:00
Yichen Jiang 178eaf3973 fix(web): link inline-code URLs 2026-08-06 11:41:46 +08:00
Yichen Jiang 1b5f06ed0b fix(web): render CJK-adjacent strong emphasis 2026-08-06 11:41:46 +08:00
Yichen Jiang 4bd66d8e7c Merge branch 'claude/web-llm-pi-ai-config-385e24' into claude/pi-ai-model-discovery
# Conflicts:
#	docs/cordis-catalog/events.md
#	docs/core-data-structures/core.i18n.yaml
#	docs/event-producer-consumer.md
#	packages/host/apiproxy/README.i18n.yaml
#	packages/llm/llm/README.i18n.yaml
2026-08-06 10:50:20 +08:00
Yichen Jiang 17c86bbb25 Merge remote-tracking branch 'origin/master' into claude/web-llm-pi-ai-config-385e24
# Conflicts:
#	packages/llm/llm-pi-ai/tests/adapter.spec.ts
#	packages/llm/llm/README.i18n.yaml
2026-08-06 10:47:36 +08:00
Yichen Jiang 66c2cb81d3 fix(llm): let an interrogation use the credential its route already stored
A configuration surface never holds a stored secret — it edits a redacted
descriptor — so once a key is saved, the draft it sends carries the route and
the endpoint and no credential at all. The interrogation went out
unauthenticated and the endpoint's 401 came back as "check the API key",
pointing at the one thing that was fine.

A named route now supplies its own credential, resolved exactly as a request
to it would be. A key typed into the form still wins: it is the one under
test, and may be the replacement for the stored one that is failing.

Resolution is a callback the probe invokes past the catalog short-circuit and
the protocol check, so a route answered from the installed registry costs no
credential lookup — and cannot fail over a credential the question never
needed.
2026-08-05 20:55:39 +08:00
Yichen Jiang b2d0e8972f docs(host): re-record the pairings master's wording moved
Master polished two Chinese sentences this branch also edits — an
expectation is now 陈旧 rather than 过期, and the package-root sentence
spells out 包(package). Taking master's wording alongside this branch's
own additions leaves the recorded pair fingerprints stale, so they are
re-recorded against the merged text.
2026-08-05 19:53:10 +08:00
Yichen Jiang 2dd4b8e78d fix(host): pin model discovery to loopback and drop its unread wire field
llm.discoverModels was reachable from any declared trusted host. The
method takes a caller-supplied baseURL and makes the host issue a GET to
it, then reports the status or the parsed body — so on a LAN deployment
an anonymous caller had a probe for whatever the host can reach and the
browser cannot, plus a path that carries a draft credential. The
PRIVILEGED_METHODS doc already states the rule this broke: trustedHosts
is a DNS-rebinding fence, not authentication, so the configuration plane
stays loopback-same-origin. It is in that set now, asserted both against
the hand-built fence and over real HTTP beside the catalog reads that
deliberately stay reachable.

supportsDiscovery and listModelDiscoveryNamespaces are gone. The field
was required on the wire and read by nobody: its own contract said a
surface should offer the action "instead of naming an adapter family it
would have to hardcode", while the surface hardcodes llm-pi-ai in two
places and gates the button on whether there is anything to probe. Its
shape did not fit the second caller either — the create card has no row
to read a per-row field from. Keeping a required field alive for a
consumer that may never arrive costs every producer and fixture a value
nobody consults, which is exactly how the fixtures drifted. The registry
that fed it had no other production consumer, so registration and
disposal are now observed through the offer itself.

The Agent Note claimed the key is never logged, which the wire schema
beside it already contradicts, and predated both the provider field and
the catalog-answer path. The two new public types pointed at core.md
without a type-equiv block or manifest entry, so the generated service
catalog named documentation that did not exist.
2026-08-05 19:51:11 +08:00
Yichen Jiang ffd2f188f2 fix(llm): answer a catalog route's models from pi-ai's own registry
Clicking "fetch available models" on a built-in provider went to the
network. That is the wrong source: pi-ai's registry is the authoritative
list for its own providers, and it carries the context windows and output
caps a `GET /models` listing does not disclose. Asking api.deepseek.com
what DeepSeek serves is both slower and worse, and against an endpoint
that answers a different shape it failed outright.

Interrogation is still keyed by settings namespace — the provider being
added has no route — but the request may now name the route it is
editing. An adapter that already describes that route answers from what
it knows, needs no endpoint at all, and never touches the network; only a
route the catalog does not describe reaches the wire, and one naming no
endpoint is told to set one or enter its models by hand.

`ConfigurableProviderView` gained `supportsDiscovery` so a surface offers
the action where a namespace can answer instead of hardcoding an adapter
family.

Three narrower corrections ride along. Discovery no longer claims Azure
or Codex: Azure authenticates with an `api-key` header and an
`api-version` query despite its OpenAI lineage, and Codex uses OAuth, so
both reported an authentication failure as a provider with no models.
Cancellation during the body read escaped as the raw abort reason rather
than a coded ABORTED. And the schema comment claiming the probe key is
never logged overstated it: the host neither stores nor returns it, but
it rides the client's outgoing envelope like every other secret-bearing
payload, and redacting that tap is a configuration-plane-wide change.
2026-08-05 19:51:02 +08:00