feat(verifier): add SetupAction to walk setup without the seeded root

This commit is contained in:
pj committed 2026-07-13 07:16:57 +05:30
1 parent bf983266ed
commit 2dea0e57d4
2 files changed
+42

No files matched your search

+32
View File
@@ -28,6 +28,11 @@ type Verifier struct {
// the shared picker (pick.ts) over the shared Pcg.
nextActionFn goja.Callable
// setupActionFn is the bundle-installed __sanderlingSetupAction__, which
// walks ONLY the setup generator. The LLM action generator runs it for setup
// precedence (e.g. login) without triggering the seeded action root.
setupActionFn goja.Callable
// sampleInputFn is the bundle-installed __sanderlingSampleInput__, which
// draws one value from the shared INPUT_CORPUS. The LLM action backend uses
// it to fill InputText values, reusing the exact corpus draw rather than
@@ -158,6 +163,11 @@ func (v *Verifier) Load(source string) error {
v.nextActionFn = callable
}
}
if fn := v.runtime.GlobalObject().Get("__sanderlingSetupAction__"); fn != nil {
if callable, ok := goja.AssertFunction(fn); ok {
v.setupActionFn = callable
}
}
// __sanderlingSampleInput__ draws an InputText value from the shared corpus.
// The LLM action backend uses it; a raw-JS fixture without the runtime entry
@@ -596,6 +606,28 @@ func (v *Verifier) NextAction() (Action, error) {
return DecodeAction(raw)
}
// SetupAction walks ONLY the setup generator (globalThis.setup), returning its
// action or ErrNoAction. The LLM action generator runs this for setup
// precedence (e.g. login) without triggering the seeded action root, which it
// replaces entirely. Mirrors NextAction's decode.
func (v *Verifier) SetupAction() (Action, error) {
if v.setupActionFn == nil {
return Action{}, ErrNoAction
}
value, err := v.setupActionFn(goja.Undefined())
if err != nil {
return Action{}, fmt.Errorf("setup action: %w", err)
}
if value == nil || goja.IsNull(value) || goja.IsUndefined(value) {
return Action{}, ErrNoAction
}
raw, err := json.Marshal(value.Export())
if err != nil {
return Action{}, fmt.Errorf("marshal action: %w", err)
}
return DecodeAction(raw)
}
var ErrNoAction = errors.New("verifier: no action available")
func (v *Verifier) formulaThunk(index int) func() (bool, error) {
+10
View File
@@ -139,6 +139,16 @@ export function installRuntime(
() => INPUT_CORPUS[rng.intN(INPUT_CORPUS.length)] ?? "",
);
defineLockedGlobal("__sanderlingExtractors__", () => evaluateExtractors());
// __sanderlingSetupAction__ walks ONLY the setup generator once, for the LLM
// action generator (Go), which drives selection itself and must not run the
// seeded action root — but still wants setup's precondition steps (e.g. login)
// to run first. Returns null when setup is unset or yields nothing.
defineLockedGlobal("__sanderlingSetupAction__", () => {
resolveRoot();
const setup = resolveSetup();
if (!setup) return null;
return serializeAction(walk(setup, rng, host));
});
defineLockedGlobal("__sanderlingNextAction__", () => {
// resolveRoot runs first: on web it also resets the per-tick candidate
// cache, which setup's walk below must see fresh.