3.2 KiB
3.2 KiB
id, title, status, wave, depends_on, plan, spec
| id | title | status | wave | depends_on | plan | spec | |
|---|---|---|---|---|---|---|---|
| 03-render-recovery-link | Render recovery links | done | 3 |
|
plan.md | ../../specs/agent-stall-recovery/spec.md |
Task 03: Render Recovery Links
Acceptance
- Kanban action messages and persistent last-agent-error notices render a
localized external link when
remediation_urlis present. - Office failed-session entries render the same link from initial API data and
live
session.state_changedmetadata. - Browser-side defense-in-depth validation rejects invalid URLs before creating
an
href; valid links usetarget="_blank"andrel="noopener noreferrer". - The existing sanitized message, collapsed technical details, recovery buttons, keyboard navigation, and mobile chat scroll owner remain intact.
Verification
cd apps && pnpm --filter @kandev/web test -- --run components/task/chat/messages/action-message.test.tsx lib/session-last-agent-error.test.ts components/task/simple/chat-entries.test.ts lib/remediation-url.test.ts
cd apps/web && pnpm run typecheck && pnpm run i18n:check && pnpm run i18n:ratchet
Add focused tests for generic/quota cards, dismissed notices, Office mapping, invalid metadata, translated labels, and the minimum 44px phone affordance.
Files likely touched
apps/web/lib/session-last-agent-error.tsapps/web/components/task/chat/messages/action-message.tsxapps/web/components/task/chat/messages/action-message.test.tsxapps/web/components/task/chat/message-list-shared.tsxapps/web/app/office/tasks/[id]/page.tsxapps/web/app/office/tasks/[id]/types.tsapps/web/components/task/simple/chat-entries.tsapps/web/components/task/simple/components/run-error-entry.tsx- focused Office/chat tests
apps/web/src/locales/en/task.jsonapps/web/src/locales/en/chat.json- matching pseudo-locale files
Dependencies and risks
Task 02. The external URL is a user-visible security boundary; keep validation at the backend and browser edges. Do not render the raw diagnostic message as HTML or use translated text for control flow.
Results
Implemented 2026-08-07.
lib/remediation-url.tsmirrors the backend allowlist at the browser edge (host, scheme, path shape, bounded identifier, no port/query/fragment/ userinfo);components/task/remediation-link.tsxrenders the localizedchat:openProviderPagelink withtarget="_blank",rel="noopener noreferrer"and a 44px phone touch target, and returns null for invalid input.- Wired into
ActionMessageDetails(generic + quota recovery cards), the persistentLastAgentErrorNotice(readLastAgentErrorreadsremediation_url/remediationUrl), and OfficeRunErrorEntryviabuildRunErrorsFromSessions; the office page mapsmetadatafrom the API and merges livesession.state_changedmetadata. - Unit tests: validator table, action-message link cases (quota/generic/
invalid), notice link cases, session-last-agent-error parsing, and
buildRunErrorsFromSessionspreservation.
Verification: targeted vitest run (106 tests) and the full web unit suite
(1236 files / 9669 tests) passed; typecheck, i18n:check, i18n:ratchet
clean (en/pt-pt/zh-cn catalogs + regenerated pseudo).