feat(web): define ChatEvent discriminated union, eliminate all any sites (R9)
Created web/src/lib/types.ts with discriminated unions for SSE event payloads: ChatEvent (7 variants: session, tool_use, tool_result, text_delta, text, done, error), ToolCallResult, MessageContent, and typed data shapes for live events (PlanProposedData, PlanStepEventData, QuestionRaisedData, QuestionAnsweredData, EntityTouchedData, HealthChangedData) plus WailsGlobal for the desktop bridge. Replaced all ~15 `any` sites across 7 files: - api.ts: Message.content any -> MessageContent | string; removed local ChatEvent interface (now imported from types.ts as a discriminated union); JSON.parse cast to ChatEvent. - stores/chat.ts: removed local ToolCallResult interface (imported from types.ts, re-exported for backward compat); extractApprovals accesses args with typeof guards instead of implicit any access; toChatMessages handles string|object Message.content cleanly. - stores/activity.ts: update_plan_step seq/status extracted via typeof guards instead of `as any` casts; toolActivityLabel uses a str() helper for safe string extraction from unknown args. - stores/workspace.ts: applyPlanStepEvent takes PlanStepEventData; applyEvent casts data to Record<string, unknown>; switch cases cast to typed interfaces (PlanProposedData, QuestionRaisedData, etc.) instead of `as any`; applyHealthChanged uses HealthChangedData. - Config.svelte: (window as any).wails -> typed WailsGlobal cast; catch (e: any) -> catch (e: unknown) with instanceof Error check. - utils.ts: WithoutChild/WithoutChildren `any` -> `unknown`. - vite.config.ts: authProxy proxy/proxyReq `any` -> ProxyOptions type. Result: eslint no-explicit-any warnings dropped 12 -> 0. Tests (6/6) and build pass. VERSION 0.7.10 -> 0.7.11. Plan R9 marked done.
This commit is contained in:
@@ -70,8 +70,8 @@ export const activityLog = derived([messages, planSteps, currentTask], ([$msgs,
|
||||
for (const t of $msgs[mi].tools) {
|
||||
// Track current step from update_plan_step calls
|
||||
if (t.type === 'tool_use' && t.name === 'update_plan_step') {
|
||||
const s = (t.args as any)?.seq as number | undefined
|
||||
const status = (t.args as any)?.status as string | undefined
|
||||
const s = typeof t.args?.seq === 'number' ? t.args.seq : undefined
|
||||
const status = typeof t.args?.status === 'string' ? t.args.status : undefined
|
||||
if (s && status === 'running') currentStepSeq = s
|
||||
} else if (t.name === 'set_goal' || t.name === 'propose_plan' || t.name === 'complete_task') {
|
||||
currentStepSeq = 0
|
||||
@@ -132,7 +132,7 @@ export const activityLog = derived([messages, planSteps, currentTask], ([$msgs,
|
||||
for (let mi = 0; mi < $msgs.length; mi++) {
|
||||
for (const t of $msgs[mi].tools) {
|
||||
if (t.type === 'tool_result' && t.name === 'upsert_knowledge' && !t.error) {
|
||||
const title = t.args?.title ?? ''
|
||||
const title = typeof t.args?.title === 'string' ? t.args.title : ''
|
||||
entries.push({
|
||||
id: `knowledge_${mi}`,
|
||||
type: 'knowledge',
|
||||
@@ -172,11 +172,12 @@ export const activityLog = derived([messages, planSteps, currentTask], ([$msgs,
|
||||
|
||||
function toolActivityLabel(t: ToolCallResult): string {
|
||||
const args = t.args ?? {}
|
||||
const str = (v: unknown): string => typeof v === 'string' ? v : ''
|
||||
switch (t.name) {
|
||||
case 'set_goal': return 'Set goal'
|
||||
case 'propose_plan': return 'Proposed plan'
|
||||
case 'search_knowledge': return `Research: ${args.query || ''}`
|
||||
case 'get_entity': return `Lookup: ${args.slug_or_id || ''}`
|
||||
case 'search_knowledge': return `Research: ${str(args.query)}`
|
||||
case 'get_entity': return `Lookup: ${str(args.slug_or_id)}`
|
||||
case 'get_entity_knowledge': return 'Check prior knowledge'
|
||||
case 'get_relations': return 'Check relationships'
|
||||
case 'list_lxcs': return 'List containers'
|
||||
@@ -184,8 +185,8 @@ function toolActivityLabel(t: ToolCallResult): string {
|
||||
case 'get_health_summary': return 'Fleet health'
|
||||
case 'get_state_snapshot': return 'State snapshot'
|
||||
case 'run': {
|
||||
const purpose = args.purpose as string || ''
|
||||
const target = (args.target as string) || ''
|
||||
const purpose = str(args.purpose)
|
||||
const target = str(args.target)
|
||||
if (purpose) return purpose
|
||||
if (target) return `Run on ${target}`
|
||||
return 'Run command'
|
||||
|
||||
Reference in New Issue
Block a user