@mjasnikovs/pi-task 0.40.33 → 0.40.34
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.
- package/README.md +1 -1
- package/dist/shared/child-output.d.ts +1 -1
- package/dist/shared/child-output.js +1 -1
- package/dist/task/deep-render-check.d.ts +8 -6
- package/dist/task/deep-render-check.js +47 -41
- package/dist/workers/docs-ecosystems.d.ts +1 -1
- package/dist/workers/docs-index.js +3 -2
- package/dist/workers/eco-cargo.d.ts +1 -1
- package/dist/workers/eco-cargo.js +2 -2
- package/dist/workers/eco-hackage.js +3 -2
- package/dist/workers/export-gap.d.ts +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -268,7 +268,7 @@ bun run lint # prettier + eslint + tsc --noEmit
|
|
|
268
268
|
bun run build # tsc → dist/
|
|
269
269
|
```
|
|
270
270
|
|
|
271
|
-
Built with [Bun](https://bun.sh), TypeScript (strict), and [TypeBox](https://github.com/sinclairzx81/typebox) for tool schemas.
|
|
271
|
+
Built with [Bun](https://bun.sh), TypeScript (strict), and [TypeBox](https://github.com/sinclairzx81/typebox) for tool schemas.
|
|
272
272
|
|
|
273
273
|
## License
|
|
274
274
|
|
|
@@ -65,7 +65,7 @@ export declare function verifyExcerpt(excerpt: string, content: string): Excerpt
|
|
|
65
65
|
* fabrication. An excerpt assembled from several real spans is a stitched quote,
|
|
66
66
|
* which is what the extraction prompt produces — and calling that a possible
|
|
67
67
|
* hallucination was wrong on 21 of 21 measured cases, on a fifth of every run's
|
|
68
|
-
* answers.
|
|
68
|
+
* answers.
|
|
69
69
|
*/
|
|
70
70
|
export declare function formatResultText(header: string, parsed: {
|
|
71
71
|
answer: string;
|
|
@@ -112,7 +112,7 @@ export function verifyExcerpt(excerpt, content) {
|
|
|
112
112
|
* fabrication. An excerpt assembled from several real spans is a stitched quote,
|
|
113
113
|
* which is what the extraction prompt produces — and calling that a possible
|
|
114
114
|
* hallucination was wrong on 21 of 21 measured cases, on a fifth of every run's
|
|
115
|
-
* answers.
|
|
115
|
+
* answers.
|
|
116
116
|
*/
|
|
117
117
|
export function formatResultText(header, parsed, check) {
|
|
118
118
|
if (!parsed.excerpt) {
|
|
@@ -70,7 +70,8 @@ export interface SessionRequest {
|
|
|
70
70
|
failed: boolean;
|
|
71
71
|
/** CDP resource type, collapsed: what ISSUED this request. */
|
|
72
72
|
initiator: 'xhr' | 'document' | 'other';
|
|
73
|
-
/**
|
|
73
|
+
/** Before the submit, the sign-in request itself, or issued at or after the
|
|
74
|
+
* submit. */
|
|
74
75
|
phase: 'pre' | 'auth' | 'post';
|
|
75
76
|
}
|
|
76
77
|
export interface DeepSessionFacts {
|
|
@@ -93,8 +94,8 @@ export interface DeepSessionFacts {
|
|
|
93
94
|
status: number | null;
|
|
94
95
|
failed: boolean;
|
|
95
96
|
} | null;
|
|
96
|
-
/** Same-origin XHR/fetch requests issued
|
|
97
|
-
*
|
|
97
|
+
/** Same-origin XHR/fetch requests issued at or after the submit, excluding the
|
|
98
|
+
* sign-in request itself. Derived: `sessionRequests` in phase 'post'. */
|
|
98
99
|
postAuthDataAttempted: number;
|
|
99
100
|
postAuthData2xx: number;
|
|
100
101
|
/** Origins the client called that are not the app's own, whose requests failed
|
|
@@ -112,8 +113,9 @@ export interface DeepSessionFacts {
|
|
|
112
113
|
* The three request-shaped facts, computed from the log and from nothing else. The
|
|
113
114
|
* driver records `sessionRequests` and calls this; the values are exactly what the
|
|
114
115
|
* pre-log driver computed by filtering the same map (the sign-in request is the
|
|
115
|
-
* first same-origin non-GET after submit; the data requests are the
|
|
116
|
-
*
|
|
116
|
+
* first same-origin non-GET issued at or after the submit; the data requests are the
|
|
117
|
+
* same-origin
|
|
118
|
+
* XHR/fetch issued at or after the submit, the sign-in request itself excluded).
|
|
117
119
|
*/
|
|
118
120
|
export declare function deriveLegacyFacts(log: SessionRequest[]): Pick<DeepSessionFacts, 'authRequest' | 'postAuthDataAttempted' | 'postAuthData2xx'>;
|
|
119
121
|
/**
|
|
@@ -225,7 +227,7 @@ export interface DriveSessionOptions {
|
|
|
225
227
|
/**
|
|
226
228
|
* The session over an already-connected browser: navigate, inspect, sign in if the
|
|
227
229
|
* landing is a wall and credentials exist, settle, phase the same-origin request
|
|
228
|
-
* log against the
|
|
230
|
+
* log against the submit, re-enter once the sign-in was accepted, and
|
|
229
231
|
* hand the facts to `judge`. Pure protocol logic — no process, no filesystem, no
|
|
230
232
|
* socket — so every branch is testable against a fake `CdpLike`.
|
|
231
233
|
*
|
|
@@ -202,8 +202,9 @@ export function pinnedLocalPort(vars) {
|
|
|
202
202
|
* The three request-shaped facts, computed from the log and from nothing else. The
|
|
203
203
|
* driver records `sessionRequests` and calls this; the values are exactly what the
|
|
204
204
|
* pre-log driver computed by filtering the same map (the sign-in request is the
|
|
205
|
-
* first same-origin non-GET after submit; the data requests are the
|
|
206
|
-
*
|
|
205
|
+
* first same-origin non-GET issued at or after the submit; the data requests are the
|
|
206
|
+
* same-origin
|
|
207
|
+
* XHR/fetch issued at or after the submit, the sign-in request itself excluded).
|
|
207
208
|
*/
|
|
208
209
|
export function deriveLegacyFacts(log) {
|
|
209
210
|
const auth = log.find(r => r.phase === 'auth') ?? null;
|
|
@@ -636,7 +637,7 @@ export async function launchBrowser(bin, userDataDir, { signal } = {}) {
|
|
|
636
637
|
/**
|
|
637
638
|
* The session over an already-connected browser: navigate, inspect, sign in if the
|
|
638
639
|
* landing is a wall and credentials exist, settle, phase the same-origin request
|
|
639
|
-
* log against the
|
|
640
|
+
* log against the submit, re-enter once the sign-in was accepted, and
|
|
640
641
|
* hand the facts to `judge`. Pure protocol logic — no process, no filesystem, no
|
|
641
642
|
* socket — so every branch is testable against a fake `CdpLike`.
|
|
642
643
|
*
|
|
@@ -645,18 +646,27 @@ export async function launchBrowser(bin, userDataDir, { signal } = {}) {
|
|
|
645
646
|
export async function driveSession(cdp, { url, credentials, judge, quietMs }) {
|
|
646
647
|
const origin = new URL(url).origin;
|
|
647
648
|
const requests = new Map();
|
|
649
|
+
let nextSeq = 0;
|
|
648
650
|
let lastActivity = Date.now();
|
|
649
651
|
cdp.on('Network.requestWillBeSent', p => {
|
|
650
|
-
const
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
652
|
+
const id = String(p.requestId);
|
|
653
|
+
// A redirect hop reuses the requestId, carrying the NEXT hop's method and
|
|
654
|
+
// url. The request the client made is the first hop and the status that
|
|
655
|
+
// judges it is the chain's last, so the first hop stays and the eventual
|
|
656
|
+
// response lands on it. Overwriting reads a POST that 302s as a GET, and
|
|
657
|
+
// then no sign-in request is ever found.
|
|
658
|
+
if (p.redirectResponse === undefined || !requests.has(id)) {
|
|
659
|
+
const req = p.request;
|
|
660
|
+
requests.set(id, {
|
|
661
|
+
url: String(req?.url ?? ''),
|
|
662
|
+
method: String(req?.method ?? 'GET'),
|
|
663
|
+
type: String(p.type ?? ''),
|
|
664
|
+
status: null,
|
|
665
|
+
mimeType: null,
|
|
666
|
+
failed: false,
|
|
667
|
+
seq: nextSeq++
|
|
668
|
+
});
|
|
669
|
+
}
|
|
660
670
|
lastActivity = Date.now();
|
|
661
671
|
});
|
|
662
672
|
cdp.on('Network.responseReceived', p => {
|
|
@@ -711,27 +721,23 @@ export async function driveSession(cdp, { url, credentials, judge, quietMs }) {
|
|
|
711
721
|
const initiatorOf = (r) => isData(r) ? 'xhr'
|
|
712
722
|
: r.type === 'Document' ? 'document'
|
|
713
723
|
: 'other';
|
|
714
|
-
/** The same-origin request log, phased against the
|
|
715
|
-
*
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
});
|
|
732
|
-
}
|
|
733
|
-
return out;
|
|
734
|
-
};
|
|
724
|
+
/** The same-origin request log, phased against the submit. `postSeq` is the
|
|
725
|
+
* first `seq` the submit could issue — Infinity while no submit has happened —
|
|
726
|
+
* so before a submit every request so far is 'pre'; the sign-in request sits at
|
|
727
|
+
* or after the boundary but is 'auth', not 'post'. */
|
|
728
|
+
const sessionLog = (authId, postSeq) => [...requests]
|
|
729
|
+
.filter(([, r]) => sameOrigin(r))
|
|
730
|
+
.map(([id, r]) => ({
|
|
731
|
+
method: r.method,
|
|
732
|
+
path: pathOf(r.url),
|
|
733
|
+
status: r.status,
|
|
734
|
+
mimeType: r.mimeType,
|
|
735
|
+
failed: r.failed,
|
|
736
|
+
initiator: initiatorOf(r),
|
|
737
|
+
phase: id === authId ? 'auth'
|
|
738
|
+
: r.seq >= postSeq ? 'post'
|
|
739
|
+
: 'pre'
|
|
740
|
+
}));
|
|
735
741
|
const facts = (log, over) => ({
|
|
736
742
|
sessionRequests: log,
|
|
737
743
|
...deriveLegacyFacts(log),
|
|
@@ -749,13 +755,15 @@ export async function driveSession(cdp, { url, credentials, judge, quietMs }) {
|
|
|
749
755
|
const unsubmitted = (over) => facts(sessionLog(null, Number.POSITIVE_INFINITY), over);
|
|
750
756
|
if (!before.hasPassword || credentials === null)
|
|
751
757
|
return judge(unsubmitted({}));
|
|
752
|
-
const submitMark = Date.now();
|
|
753
758
|
const filled = await evaluate(fillExpr(credentials.identifier, credentials.password));
|
|
754
759
|
if (!filled?.ok)
|
|
755
760
|
return judge(unsubmitted({ submitted: false }));
|
|
756
761
|
// Separate turn: the fill's input events schedule framework state updates that
|
|
757
762
|
// the submit handler must already see.
|
|
758
763
|
await sleep(300);
|
|
764
|
+
// Captured after the fill: background traffic before the submit must not be
|
|
765
|
+
// eligible as the sign-in request.
|
|
766
|
+
const submitSeq = nextSeq;
|
|
759
767
|
const submitted = await evaluate(SUBMIT_EXPR);
|
|
760
768
|
if (!submitted?.ok)
|
|
761
769
|
return judge(unsubmitted({ submitted: false }));
|
|
@@ -765,16 +773,14 @@ export async function driveSession(cdp, { url, credentials, judge, quietMs }) {
|
|
|
765
773
|
// own 2xx is the precondition for judging anything, and it is EXCLUDED from the
|
|
766
774
|
// data evidence — a broken build satisfies "at least one same-origin 2xx" with
|
|
767
775
|
// exactly this request and nothing else.
|
|
768
|
-
const after = new Map([...requests].filter(([, r]) => r.at >= submitMark));
|
|
769
776
|
let authId = null;
|
|
770
|
-
for (const [id, r] of
|
|
771
|
-
if (sameOrigin(r) && r.method !== 'GET') {
|
|
777
|
+
for (const [id, r] of requests) {
|
|
778
|
+
if (r.seq >= submitSeq && sameOrigin(r) && r.method !== 'GET') {
|
|
772
779
|
authId = id;
|
|
773
780
|
break;
|
|
774
781
|
}
|
|
775
782
|
}
|
|
776
|
-
const authReq = authId
|
|
777
|
-
const authAt = authReq?.at ?? submitMark;
|
|
783
|
+
const authReq = authId === null ? null : requests.get(authId);
|
|
778
784
|
const now = await evaluate(INSPECT_EXPR);
|
|
779
785
|
const domJudgment = judgeRenderedDom(now?.html ?? '');
|
|
780
786
|
const leftAuthWall = !(now?.hasPassword ?? false) || (now?.pathname ?? '') !== before.pathname;
|
|
@@ -795,7 +801,7 @@ export async function driveSession(cdp, { url, credentials, judge, quietMs }) {
|
|
|
795
801
|
lastActivity = Date.now();
|
|
796
802
|
await settle(() => lastActivity, RE_NAV_CAP_MS, quietMs);
|
|
797
803
|
}
|
|
798
|
-
return judge(facts(sessionLog(authId,
|
|
804
|
+
return judge(facts(sessionLog(authId, submitSeq), {
|
|
799
805
|
submitted: true,
|
|
800
806
|
foreignOriginFailures: foreignOriginFailures(),
|
|
801
807
|
leftAuthWall,
|
|
@@ -87,7 +87,7 @@ export interface EcosystemProfile {
|
|
|
87
87
|
/**
|
|
88
88
|
* Packages whose declarations belong in THIS package's index, because this
|
|
89
89
|
* package exports names it does not declare — `hspec`/`hspec-core`,
|
|
90
|
-
* `axum`/`axum-core
|
|
90
|
+
* `axum`/`axum-core`.
|
|
91
91
|
*/
|
|
92
92
|
supplements?: (pkg: ResolvedPackage, cwd: string, io: EcosystemIo) => Promise<ResolvedPackage[]>;
|
|
93
93
|
/**
|
|
@@ -312,8 +312,9 @@ function ingestBody(cache, pkg, profile, contentHash, supplements = []) {
|
|
|
312
312
|
}
|
|
313
313
|
}
|
|
314
314
|
// A facade package indexes to a table of contents: `hspec` is 14 chunks of
|
|
315
|
-
// export lists and every signature is in `hspec-core`. Fill only the holes
|
|
316
|
-
//
|
|
315
|
+
// export lists and every signature is in `hspec-core`. Fill only the holes,
|
|
316
|
+
// and only one hop out: after one hop hspec has zero unresolved names left,
|
|
317
|
+
// and no package measured had anything for a second hop to fetch.
|
|
317
318
|
const found = supplements.length > 0 ? (profile.exportGap?.(pkg.root) ?? null) : null;
|
|
318
319
|
const gap = found !== null && !found.empty ? found : null;
|
|
319
320
|
for (const sup of gap === null ? [] : supplements) {
|
|
@@ -122,7 +122,7 @@ export declare function manifestCrates(cwd: string): Set<string> | undefined;
|
|
|
122
122
|
* The trigger is the hole, with no threshold — measured, and for the same reason
|
|
123
123
|
* as hackage: across twenty-two crates the unresolved fraction reads 100% on a
|
|
124
124
|
* crate with one re-export and 0% on a crate with none, so a ratio separates
|
|
125
|
-
* nothing.
|
|
125
|
+
* nothing.
|
|
126
126
|
*/
|
|
127
127
|
export declare function cargoExportGap(root: string): ExportGap;
|
|
128
128
|
/**
|
|
@@ -869,7 +869,7 @@ export function manifestCrates(cwd) {
|
|
|
869
869
|
}
|
|
870
870
|
return out;
|
|
871
871
|
}
|
|
872
|
-
// ── the facade gap
|
|
872
|
+
// ── the facade gap, cargo half ──────────────────────────────────────────────
|
|
873
873
|
/** A `pub use …;` statement, attributes and line breaks included. */
|
|
874
874
|
const PUB_USE_RE = /\bpub\s+use\s+([^;]+);/g;
|
|
875
875
|
/** Every item head that introduces a name, visibility ignored — a facade may
|
|
@@ -995,7 +995,7 @@ function moduleOfPath(relPath) {
|
|
|
995
995
|
* The trigger is the hole, with no threshold — measured, and for the same reason
|
|
996
996
|
* as hackage: across twenty-two crates the unresolved fraction reads 100% on a
|
|
997
997
|
* crate with one re-export and 0% on a crate with none, so a ratio separates
|
|
998
|
-
* nothing.
|
|
998
|
+
* nothing.
|
|
999
999
|
*/
|
|
1000
1000
|
export function cargoExportGap(root) {
|
|
1001
1001
|
const deps = runtimeDeps(root);
|
|
@@ -600,8 +600,9 @@ export function manifestPackages(cwd) {
|
|
|
600
600
|
* re-export puts the name in the export list; a `module X` re-export puts
|
|
601
601
|
* nothing there at all, which is why `shouldBe` is invisible to the first.
|
|
602
602
|
*
|
|
603
|
-
*
|
|
604
|
-
*
|
|
603
|
+
* The trigger is the hole, with no threshold. Swept over the 299 modules with
|
|
604
|
+
* 5+ exports, the trigger count reads 33 at 50% and 18 at 95%, so the fraction
|
|
605
|
+
* is not carrying the decision and any value picked would just sound right.
|
|
605
606
|
*/
|
|
606
607
|
const EXPORT_NAME_RE = /^[A-Za-z_][\w']*$/;
|
|
607
608
|
/** `module X` inside an export list is a re-export; `Prelude` is base, and base is not fetched. */
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* `hspec` indexes to a table of contents and every signature is in `hspec-core`;
|
|
5
5
|
* `axum` re-exports `IntoResponse` and the trait lives in `axum-core`. Both are
|
|
6
6
|
* the same failure — a query retrieves the package's own chunks and not one of
|
|
7
|
-
* them defines the thing asked about —
|
|
8
|
-
*
|
|
7
|
+
* them defines the thing asked about — so the re-export is followed exactly one
|
|
8
|
+
* hop, to a dependency the package's own name prefixes.
|
|
9
9
|
*
|
|
10
10
|
* The boundary is shared; the parsing is not. Haskell states the gap in an export
|
|
11
11
|
* list, Rust in `pub use`, so each ecosystem answers the same three questions in
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mjasnikovs/pi-task",
|
|
3
|
-
"version": "0.40.
|
|
3
|
+
"version": "0.40.34",
|
|
4
4
|
"description": "Deterministic task planning and spec-orchestration for local models — crash-safe /task pipelines with verify/enforce gates, a real-time remote web view, and web/docs/fetch/worker subagent tools.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|