diff --git a/server/src/agent/classifier.ts b/server/src/agent/classifier.ts index 5af629a..5dac0b5 100644 --- a/server/src/agent/classifier.ts +++ b/server/src/agent/classifier.ts @@ -62,12 +62,16 @@ export async function classifyGoal( switch (parsed.type) { case "intent": { + const rawExtras = parsed.extras; + const validExtras = (rawExtras && typeof rawExtras === "object" && !Array.isArray(rawExtras)) + ? rawExtras as Record + : undefined; const intent: IntentCommand = { - intentAction: (parsed.intentAction as string) ?? "", - uri: parsed.uri as string | undefined, - intentType: parsed.intentType as string | undefined, - extras: parsed.extras as Record | undefined, - packageName: parsed.packageName as string | undefined, + intentAction: typeof parsed.intentAction === "string" ? parsed.intentAction : "", + uri: typeof parsed.uri === "string" ? parsed.uri : undefined, + intentType: typeof parsed.intentType === "string" ? parsed.intentType : undefined, + extras: validExtras, + packageName: typeof parsed.packageName === "string" ? parsed.packageName : undefined, }; if (!intent.intentAction) { console.warn("[Classifier] Intent missing intentAction, falling through"); diff --git a/server/src/agent/llm.ts b/server/src/agent/llm.ts index ef5df47..de93888 100644 --- a/server/src/agent/llm.ts +++ b/server/src/agent/llm.ts @@ -293,7 +293,8 @@ Scrolling: App Control: {"action": "launch", "package": "com.app.name", "reason": "Open app"} - {"action": "switch_app", "package": "com.app.name", "reason": "Switch app"}`; + {"action": "switch_app", "package": "com.app.name", "reason": "Switch app"} + {"action": "intent", "intentAction": "android.intent.action.VIEW", "uri": "tel:123", "reason": "Fire Android intent directly"}`; // Multi-step actions (always useful) actions += ` diff --git a/server/src/agent/parser.ts b/server/src/agent/parser.ts index e7f625d..99e8bc2 100644 --- a/server/src/agent/parser.ts +++ b/server/src/agent/parser.ts @@ -339,6 +339,7 @@ export function parseGoal( caps: DeviceCapabilities ): PipelineResult { const trimmed = goal.trim(); + if (!trimmed) return { stage: "parser", type: "done", reason: "Empty goal" }; for (const matcher of PATTERNS) { const result = matcher(trimmed, caps); if (result) return result; diff --git a/server/src/agent/pipeline.ts b/server/src/agent/pipeline.ts index a74712f..a0db2d7 100644 --- a/server/src/agent/pipeline.ts +++ b/server/src/agent/pipeline.ts @@ -8,6 +8,7 @@ import type { InstalledApp, PipelineResult, + ActionDecision, } from "@droidclaw/shared"; import { sessions } from "../ws/sessions.js"; import { db } from "../db.js"; @@ -185,7 +186,7 @@ export async function runPipeline( onStep?.({ stepNumber: 1, - action: { action: parseResult.type, reason: `Parser: ${parseResult.type}` } as any, + action: { action: parseResult.type, reason: `Parser: ${parseResult.type}` } as unknown as ActionDecision, reasoning: `Parser: direct ${parseResult.type} action`, screenHash: "", }); @@ -228,7 +229,7 @@ export async function runPipeline( onStep?.({ stepNumber: 1, - action: { action: "intent", reason: "Classifier: intent" } as any, + action: { action: "intent", intentAction: classResult.intent.intentAction, uri: classResult.intent.uri, reason: "Classifier: intent" } as unknown as ActionDecision, reasoning: `Classifier: ${classResult.intent.intentAction}`, screenHash: "", });