Merge remote-tracking branch 'origin/master' into worktree/web-theme-settings-integration-fde706

This commit is contained in:
Yichen Jiang
2026-08-10 17:05:18 +08:00
488 changed files with 1747 additions and 1433 deletions

View File

@@ -77,9 +77,9 @@ export interface SearchCardModel {
/**
* Whether every file group in a matches view is structurally valid: the wire
* frame carries `shape` and `card` as strings the host schema checks, but not the
* grouped shape, so a version mismatch or loose producer could deliver
* grouped `files` fields, so a version mismatch or loose producer could deliver
* `shape: 'matches'` with a missing or malformed `files`. Rendering that would
* crash {@link SearchBlock} at `.reduce`/`.map`; an invalid shape falls to the
* crash {@link SearchBlock} at `.reduce`/`.map`; invalid fields select the
* generic path instead.
* @param files - the candidate `files` field off the untrusted result view.
* @returns whether `files` is a valid {@link SearchFileGroup} array.
@@ -136,12 +136,13 @@ export function searchCardModel(block: ToolCallBlock): SearchCardModel | null {
// The recovery footer only matters when the tool capped the result: an
// uncapped card holds every match/path, so the raw text adds nothing the card
// does not already show. When capped, the raw result's `Full … stored at …`
// locator is the only path to the dropped rows, so surface it.
// locator is the only way to retrieve the omitted rows, so include it.
const recovery = result.truncated ? flattenContent(block.content) : undefined
if (result.shape === 'matches') {
// `files` rides the untrusted wire frame: the host schema checks `card`/`shape`
// strings but not the grouped shape, so validate it before SearchBlock, which
// would crash on a missing/malformed `files`. An invalid shape falls to generic.
// strings but not the grouped `files` fields, so validate them before
// SearchBlock, which would crash on a missing or malformed `files`.
// Invalid fields select the generic view.
if (!isValidFiles(result.files)) return null
return { title: result.title, recovery, card: { kind: 'matches', files: result.files, ...common } }
}

View File

@@ -21,8 +21,8 @@ function isAnswer(value: unknown): value is AnswerEntry {
return typeof value === 'object' && value !== null
}
/** Answered-count summary off the result JSON (a skipped question has
* empty `selected` and no `custom`); null on unexpected shape (generic fallback). */
/** Answered-count summary from the result JSON (a skipped question has
* empty `selected` and no `custom`); null when answer fields are invalid. */
function answeredSummary(text: string, t: AskQuestionRowProps['t']): string | null {
let parsed: unknown
try {

View File

@@ -41,7 +41,7 @@ function summarize(argsRaw: string, t: TodoRowProps['t']): RowSummary | null {
// Mid-stream truncation or malformed model JSON: fall back to the generic summary.
return null
}
// Valid JSON with an invalid shape (null root, non-array todos, null items —
// Valid JSON with invalid todo fields (null root, non-array todos, null items —
// a rejected tool/call retains such args verbatim): same generic fallback.
if (typeof parsed !== 'object' || parsed === null) return null
const todos = (parsed as { todos?: unknown }).todos