fix(agent): address code review issues
- Add empty goal guard in parser (returns done instead of passthrough) - Replace `as any` casts in pipeline.ts with proper ActionDecision types - Add runtime type guards for untrusted LLM output in classifier - Add intent action to dynamic prompt so UI agent can fire intents Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -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<string, string>
|
||||
: 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<string, string> | 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");
|
||||
|
||||
@@ -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 += `
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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: "",
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user