Skip to content

proposal(ai-sdk): goal-driven step loop with a model-agnostic decide step #2657

Description

@thymikee

Purpose

PR #2654 showed a real gain and the wrong home for it. The gain: a loop that asks a model one narrow question per step, "which on-screen element advances this goal", over the interactive snapshot, turns N agent turns into one call and cuts decide time per action from seconds to well under one second. The wrong home: a vendor HTTP client, a vendor API key, a pricing constant, and two always-listed CLI commands and MCP tools in core that refuse without the key.

agent-device core is the device side of an agent. Model handling belongs in agent-device/ai-sdk, where the host already brings its own model. That decision is independent of which vendor is behind the head.

The gain is not vendor-specific. On the PR's three sign-in screens plus a real iOS artifact, a small general model (gpt-5.4-nano, reasoning off) with a schema-forced choice among candidate refs chose correctly 16/16 at 620-730 ms median and about 200 input tokens per decision. A decision-only vendor can plug into the same seam from outside this repo.

Proposed shape

  • Lives under agent-device/ai-sdk next to createAgentDeviceTools, using the optional ai peer. No new CLI command, MCP tool, registry entry, flag or env contract in core.
  • runGoal({ client, goal, model | decide, inputs, maxSteps, minConfidence, onStep }):
    • candidates come from the structured interactive snapshot: kind, name, value, enabled, ref (needs feat(snapshot): carry the presenter's platform-neutral role on structured snapshot nodes #2656);
    • decide defaults to AI SDK generateObject against the host's model with a schema of { target: enum(refs | none), done, blocked }; a host may pass its own decide(state) => decision;
    • acts through client.interactions.press / fill with settle: true, every mutation pinned to the snapshot's refsGeneration (ADR 0014);
    • "did anything change" is read from the settle diff and tail on SettleObservation, not from a second snapshot;
    • text is supplied, never generated; sensitive values go through the ADR 0017 channel (recordAs, AD_VAR_*), not a new --input or env family;
    • returns the step table (screen, decision, outcome, snapshot/decide/action ms) and a typed status: done, blocked, escalated, max-steps.
  • Out of scope: scroll, back, alert and gesture planning; multi-screen plans; text generation.

Completion conditions

  • examples/sdk/goal-loop.ts or the ai-sdk export drives a sign-in flow on an iOS simulator and on an Android emulator with a host-configured model, with the step table and the --debug request log showing ~sN pinned refs attached.
  • Unit coverage over a fake device port and a fake decide, with fixtures that carry the errors production emits.
  • Docs: one section in website/docs/docs/ai-sdk.md.

Open decisions

  • In-tree ai-sdk export, or examples/sdk/goal-loop.ts first.
  • Confidence floor and unproductive-step limit defaults; whether blocked is ever terminal on its own.
  • Whether kind-based candidate filtering is enough or the loop needs a hittable / interactionBlocked gate.

Dependencies

Blocked by: #2656. Related: #2634 (fill on fields that normalize their input), ADR 0014, ADR 0017.

Activity

  1. thymikee commented on Oct 9, 2026

    @thymikee
    MemberAuthor

    Decision: example first; defer a public API. src/ai-sdk exposes tools, not a goal executor. Add examples/sdk/goal-loop.ts:

    • the host chooses the model (decide is a plain callback),
    • bounded steps and wall time, with cancellation,
    • mutations bound to the latest observation (no acting on stale refs),
    • "the model said done" kept distinct from "completion observed".

    Revisit a runGoal API only after the example has real users.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions