@proagentstore/cli 0.4.2 → 0.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -680,6 +680,45 @@ export class LocalRunner {
|
|
|
680
680
|
const head = (cut >= 0 ? text.slice(0, cut) : text).replace(/```[a-z]*\n?|```/g, " ").replace(/\s+/g, " ").trim();
|
|
681
681
|
return head.slice(0, 400);
|
|
682
682
|
}
|
|
683
|
+
/**
|
|
684
|
+
* After a WRITE action, read the field's REAL value back via the standard
|
|
685
|
+
* browser_evaluate tool (evaluates on the element by ref — no reinvented DOM
|
|
686
|
+
* traversal). Masked/intl fields transform input (e.g. "0404453580" → "+61404453580"),
|
|
687
|
+
* so without this signal the brain can't tell the value "took" and oscillates
|
|
688
|
+
* between formats. Returns the prompt's expected "…now reads…" / "REJECTED…" line.
|
|
689
|
+
* Never throws — best-effort feedback.
|
|
690
|
+
*/
|
|
691
|
+
async readBackField(mcp, action) {
|
|
692
|
+
if (!action.ref)
|
|
693
|
+
return "";
|
|
694
|
+
const fn = "el => ({ v: el.value !== undefined ? String(el.value) : (el.getAttribute('aria-checked') ?? el.textContent ?? '').trim(), " +
|
|
695
|
+
"bad: el.getAttribute('aria-invalid') === 'true' || !!(el.validity && !el.validity.valid), " +
|
|
696
|
+
"msg: el.validationMessage || '' })";
|
|
697
|
+
const res = await mcp.callTool("browser_evaluate", { element: action.name || "field", target: action.ref, function: fn }).catch(() => null);
|
|
698
|
+
if (!res || res.isError)
|
|
699
|
+
return "";
|
|
700
|
+
const txt = mcp.textOf(res);
|
|
701
|
+
const i = txt.indexOf("### Result");
|
|
702
|
+
if (i < 0)
|
|
703
|
+
return "";
|
|
704
|
+
const after = txt.slice(i + "### Result".length).trim();
|
|
705
|
+
const end = after.indexOf("\n###");
|
|
706
|
+
const block = (end >= 0 ? after.slice(0, end) : after).trim();
|
|
707
|
+
let parsed;
|
|
708
|
+
try {
|
|
709
|
+
parsed = JSON.parse(block);
|
|
710
|
+
}
|
|
711
|
+
catch {
|
|
712
|
+
return "";
|
|
713
|
+
}
|
|
714
|
+
if (typeof parsed?.v !== "string")
|
|
715
|
+
return "";
|
|
716
|
+
const name = action.name || "field";
|
|
717
|
+
const shown = parsed.v.length > 80 ? `${parsed.v.slice(0, 80)}…` : parsed.v;
|
|
718
|
+
if (parsed.bad)
|
|
719
|
+
return `⚠ "${name}" REJECTED: now reads "${shown}"${parsed.msg ? ` — ${parsed.msg.slice(0, 80)}` : ""}`;
|
|
720
|
+
return `"${name}" now reads "${shown}"`;
|
|
721
|
+
}
|
|
683
722
|
/** The snapshot ref the brain must target the element by (standard-tool `target`). */
|
|
684
723
|
refOf(action) {
|
|
685
724
|
const ref = (action.ref || "").trim();
|
|
@@ -735,7 +774,14 @@ export class LocalRunner {
|
|
|
735
774
|
const mcp = await this.getMcp();
|
|
736
775
|
const res = await mcp.callTool("browser_snapshot");
|
|
737
776
|
const full = this.extractSnapshot(mcp.textOf(res));
|
|
738
|
-
|
|
777
|
+
// The brain must see the WHOLE form or it can't finish. Real ATS forms are big
|
|
778
|
+
// (a single Coles step is ~31k chars); the old 16k cap cut the page in half, so
|
|
779
|
+
// the brain never saw the Submit button or the bottom required/EEO fields and
|
|
780
|
+
// scroll-looped hunting for controls that were truncated out of its view. Claude
|
|
781
|
+
// Sonnet's context easily holds a full page per turn (the accumulating action log
|
|
782
|
+
// is what's bounded, not the one-shot snapshot). Keep a generous ceiling only to
|
|
783
|
+
// guard against a pathological megapage.
|
|
784
|
+
const MAX = 60_000;
|
|
739
785
|
const snapshot = full.length > MAX ? `${full.slice(0, MAX)}\n… [snapshot truncated]` : full;
|
|
740
786
|
return {
|
|
741
787
|
url: page.url(),
|
|
@@ -817,12 +863,21 @@ export class LocalRunner {
|
|
|
817
863
|
if (action.action === "click" || action.action === "navigate" || action.action === "key") {
|
|
818
864
|
await active.waitForLoadState("networkidle", { timeout: 3_500 }).catch(() => undefined);
|
|
819
865
|
}
|
|
866
|
+
// For a WRITE, read the field's real value back (masked/intl fields transform
|
|
867
|
+
// input, e.g. "0404453580" → "+61404453580") so the brain knows it took and
|
|
868
|
+
// stops oscillating between formats — the signal inspectField used to give.
|
|
869
|
+
let feedback = this.conciseFeedback(text);
|
|
870
|
+
if (action.action === "type" || action.action === "select" || action.action === "check") {
|
|
871
|
+
const rb = await this.readBackField(mcp, action);
|
|
872
|
+
if (rb)
|
|
873
|
+
feedback = rb;
|
|
874
|
+
}
|
|
820
875
|
return {
|
|
821
876
|
ok: true,
|
|
822
877
|
url: active.url(),
|
|
823
878
|
title: await active.title().catch(() => ""),
|
|
824
879
|
challenge: await detectHumanChallenge(active),
|
|
825
|
-
feedback:
|
|
880
|
+
feedback: feedback || undefined,
|
|
826
881
|
};
|
|
827
882
|
}
|
|
828
883
|
// ── Agent-driven application lifecycle (called by the remote Workflow brain) ──
|