Skip to content

feat(runtime): exec_actions + get_fragments tools; semantic fragment targets - #41

Open
joelgwebber wants to merge 2 commits into
feat/l1-executorfrom
feat/exec-actions-tools
Open

feat(runtime): exec_actions + get_fragments tools; semantic fragment targets#41
joelgwebber wants to merge 2 commits into
feat/l1-executorfrom
feat/exec-actions-tools

Conversation

@joelgwebber

Copy link
Copy Markdown
Collaborator

Stacked on #36 (L1 executor). This is A + B-lite of the exec-actions decomposition (sites-10b3): expose the resumable executor as a real WebMCP fallback, composed over corpus-vocabulary fragments.

A — exec_actions + get_fragments (always-on fallback surface)

Two always-on WebMCP tools register alongside the view-scoped tools:

  • get_fragments — lists the action fragments available on the current view, each a pluckable {id, tool, op, label, uses}. Returns the current view's base set (ensure_view filtered); the 1-hop guidance horizon + distance tiering is the follow-up (sites-90dc).
  • exec_actions — runs an agent-composed list of fragment references (+ arg overrides) resumably. It expands each ref to its compiled step, runs the L1 executor, and returns the status with the untried tail re-expressed as fragment refs. So raw compiled steps / selectors never cross the tool boundary, and a resume is just a re-call with the returned remaining. On an interruption (unexpected modal, missing field) it stops instead of hanging and reports the reason + observed path + remaining refs + a readable remainingView.

Registration dedups against the shared native registry (getTools()), so the tools appear exactly once even though an Angular/Zone SPA boots the injected bundle in several execution contexts (a per-instance flag can't dedup across them).

B-lite — semantic fragment targets (generator)

The generator now carries each step's corpus-vocabulary target into the IR (Step.target): the source compquery for a query step (FormField[label*="First" i] FieldInput), the destination view name for navigate/goto/waitFor-view (a goto deep link resolves to its view via ViewForURL), the key for keypress. Fragment labels and the executor's tail/interrupt projection render this instead of reconstructing raw locators, so an agent composes and reads in component/view names. It is label/provenance only — the runtime still selects via the compiled query, so the IR firewall holds. Golden IRs and the frozen example plans are re-stamped.

Verification (live on JetBlue, --webmcp)

  • 6 tools, 1× each (dedup holds across JB's execution contexts).
  • get_fragments returns component-vocabulary labels — click FlightCard[flight_no*="{{flight}}"] FareOption[label*="{{cabin}}"], SortControl, and gotoSelectFlights — no raw selectors/URLs.
  • exec_actions runs a fragment-ref list (select_fare.0/.1/.2) to completion (done:true, 3/3); on a bad arg it interrupts cleanly with {interrupt:{at,action,reason,observed.path}, remaining:[refs], remainingView:[…]}.
  • 65 runtime unit tests pass (new meta-tools.test.ts covers registration, view-filtered get_fragments, and the interrupt→resume ref tail); generator golden tests pass.

Part of sites-10b3. Follow-up: contextual fragment filtering / 1-hop horizon (sites-90dc).

…targets (sites-b573, sites-6a1a)

A + B-lite of the exec-actions decomposition (sites-10b3), stacked on the L1
executor (#36).

B-lite (generator): carry each step's corpus-vocabulary target into the IR as
Step.target -- the source compquery for a query step, the destination view name
for navigate/goto/waitFor-view (a goto deep link resolves via ViewForURL), the
key for keypress. Fragment labels + the execActions tail/interrupt projection
render this instead of reconstructing raw locators, so agents compose/read in
component/view names. Label/provenance only; the runtime still selects via the
compiled query (firewall holds). Golden IRs + frozen example plans re-stamped.

A (runtime): register exec_actions + get_fragments as always-on WebMCP tools.
exec_actions consumes fragment REFERENCES (+ arg overrides), expands them to
compiled steps, runs the resumable executor, and returns the status with the
untried tail re-expressed as fragment refs -- so raw steps never cross the tool
boundary and a resume is just a re-call with 'remaining'. get_fragments returns
the current view's base set (ensure_view filtered; the 1-hop guidance horizon is
sites-90dc). Registration dedups against the shared native registry via
getTools() so the tools appear exactly once across the multiple execution
contexts an Angular/Zone SPA boots the bundle in.

Verified live on JB: 6 tools 1x each; get_fragments returns component-vocabulary
labels (FlightCard/FareOption/SelectFareButton/SortControl, goto -> SelectFlights);
exec_actions runs a fragment-ref list to completion (done 3/3) and, on a bad arg,
interrupts cleanly with reason + observed path + remaining refs + readable view.
65 runtime tests pass (meta-tools.test.ts added); generator golden tests pass.

Signed-off-by: Joel Webber <joel@fullstory.com>
…rred boot

When boot is deferred to post-settle on an Angular/Zone SPA (sites-67f3), the
always-on meta tools (exec_actions/get_fragments) failed to register:

- Angular's Zone re-wraps document.modelContext between boot and the async
  registerMetaTools pass, so the ctx captured at boot goes stale and its
  registerTool never lands. Re-read the live document.modelContext at call time.
- Two same-tick native registerTool calls race and JetBlue drops the second
  (exec_actions present, get_fragments missing). Await the two registrations
  sequentially.

Found running the full JB checkout with #40's deferred boot + these tools
together; verified 6 tools 1x each afterward. (sites-06d2)

Signed-off-by: Joel Webber <joel@fullstory.com>
joelgwebber added a commit that referenced this pull request Sep 12, 2026
…orizon (sites-90dc)

get_fragments returned only the current view's tools. It now also returns the
1-hop horizon: tools one navigation away, reached via the current view's tools'
guidance graph (Suggestion[]), so an agent can pre-compose the next step instead
of hitting a hard cut at the view boundary. Each fragment carries view (owning
tool's view) and distance (0 = here, 1 = one hop). Reachability rides per-tool
guidance only; one hop deliberately (deeper is journey territory).

New scopedFragments(ir, path) in the executor (pure, testable); boot's
currentFragments delegates to it. projectFragments now also tags each fragment
with its owning tool's view.

67 runtime tests (scopedFragments base/horizon/distance + a get_fragments horizon
test added). Stacks on #41.

Signed-off-by: Joel Webber <joel@fullstory.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant