fix(client-web): reject unknown web kind, lock fixture to contract, observe web card in boot smoke

- webCardModel returns null for an unknown web `kind` (wire from a newer host)
  instead of drawing it as a malformed fetch, matching the unknown-`card` and
  terminal-model wire-boundary default.
- Fixture WEB_SEARCH_RESULT/WEB_FETCH_RESULT and the source type derive from the
  contract's ToolResultView via Extract, so a new contract field fails at the
  type level rather than drifting silently.
- built-boot smoke asserts the web_search/web_fetch turns render their keyed
  WebRow cards, giving the registration and wire projection an assembled check.
- DetailsPanel comment no longer claims the card omits content for search.
- ui-primitives README inline-Chinese limitation now lists WebBlock's controls.
This commit is contained in:
Chinesezjc
2026-07-30 20:42:29 +08:00
parent 917e114b68
commit 224a9d5f09
8 changed files with 48 additions and 28 deletions

View File

@@ -136,27 +136,16 @@ const TERMINAL_EXIT_STATUS: Record<string, { exitCode: number } | { signal: stri
[TERMINAL_OUTPUT_FIXTURE]: { exitCode: 1 },
}
/**
* One inline-authored source for the `web_search` fixture. Structurally the
* contract's `WebSource`; authored locally because the fixture cannot import the
* web tool, and kept minimal so a missing optional field renders its fallback.
*/
interface WebSourceFixture {
url: string
title?: string
snippet?: string
publishedAt?: string
}
/**
* The structured `web_search` result view for fixture turn 66, authored inline
* because this client-side fixture cannot import the web tool that projects it.
* The sources exercise the citation list's features: a titled source with a
* snippet and a date, a source with no title (its hostname labels the link) and
* a snippet but no date, and a source with a title and a date but no snippet.
* `truncated` marks the capped indicator.
* `truncated` marks the capped indicator. The shape is the contract's own
* search view minus its wire discriminants and fallback content.
*/
const WEB_SEARCH_RESULT: { answer: string; sources: WebSourceFixture[]; truncated: boolean } = {
const WEB_SEARCH_RESULT: Omit<Extract<ToolResultView, { card: 'web'; kind: 'search' }>, 'card' | 'kind' | 'content'> = {
answer: 'DeepSeek Harness is a plugin-based agent harness on vendored Cordis where **every capability is a plugin**.',
sources: [
{
@@ -179,7 +168,7 @@ const WEB_SEARCH_RESULT: { answer: string; sources: WebSourceFixture[]; truncate
}
/** The `web_fetch` result view for fixture turn 67, authored inline for the same reason. */
const WEB_FETCH_RESULT: { url: string; statusCode: number; truncated: boolean } = {
const WEB_FETCH_RESULT: Omit<Extract<ToolResultView, { card: 'web'; kind: 'fetch' }>, 'card' | 'kind' | 'content'> = {
url: 'https://www.deepseek.com/blog/harness-architecture',
statusCode: 200,
truncated: false,