@mjasnikovs/pi-task 0.17.6 → 0.17.7
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.
|
@@ -3,7 +3,14 @@
|
|
|
3
3
|
* orchestrator can fan out docs/URL lookups before the research phase.
|
|
4
4
|
*/
|
|
5
5
|
export declare function extractEnrichTargets(text: string): {
|
|
6
|
+
/** Packages that get a (heavy) docs fetch — capped at ENRICH_CAP. */
|
|
6
7
|
packages: string[];
|
|
8
|
+
/**
|
|
9
|
+
* Every named package, up to ENRICH_VERSION_CAP, for a cheap live npm version
|
|
10
|
+
* lookup. A superset of `packages`; the extras get a version block only (no
|
|
11
|
+
* docs body). Order-preserving and deduped, same as `packages`.
|
|
12
|
+
*/
|
|
13
|
+
versionPackages: string[];
|
|
7
14
|
urls: string[];
|
|
8
15
|
services: Array<{
|
|
9
16
|
name: string;
|
package/dist/task/enrichment.js
CHANGED
|
@@ -21,6 +21,13 @@ const ENRICH_DENYLIST = new Set([
|
|
|
21
21
|
'rm'
|
|
22
22
|
]);
|
|
23
23
|
const ENRICH_CAP = 3;
|
|
24
|
+
// Heavy docs/url fetches stay capped at ENRICH_CAP (each is a worker spawn +
|
|
25
|
+
// page fetch). A live npm VERSION lookup is just one cheap registry GET, so it
|
|
26
|
+
// can cover far more packages — every dependency a task explicitly names should
|
|
27
|
+
// get a grounded latest-version block, not just the first ENRICH_CAP of them. A
|
|
28
|
+
// task that named 6 runtime deps used to leave the 4th–6th (e.g. tailwindcss)
|
|
29
|
+
// with NO live version, so a version question fell back to stale training data.
|
|
30
|
+
const ENRICH_VERSION_CAP = 12;
|
|
24
31
|
const ENRICH_SERVICE_HEADER = 'EXTERNAL-DEPENDENCIES';
|
|
25
32
|
const ENRICH_HEADER_LINE_RE = /^[A-Z][A-Z0-9 -]+$/;
|
|
26
33
|
const ENRICH_SERVICE_BULLET_RE = /^\s*-\s+(.+?)\s{2,}(.+?)\s*$/;
|
|
@@ -61,13 +68,15 @@ function parseServices(text) {
|
|
|
61
68
|
return out;
|
|
62
69
|
}
|
|
63
70
|
export function extractEnrichTargets(text) {
|
|
64
|
-
const pkgs =
|
|
71
|
+
const pkgs = [];
|
|
72
|
+
const seen = new Set();
|
|
65
73
|
for (const m of text.matchAll(ENRICH_PKG_RE)) {
|
|
66
74
|
const t = m[1];
|
|
67
|
-
if (ENRICH_DENYLIST.has(t))
|
|
75
|
+
if (ENRICH_DENYLIST.has(t) || seen.has(t))
|
|
68
76
|
continue;
|
|
69
|
-
|
|
70
|
-
|
|
77
|
+
seen.add(t);
|
|
78
|
+
pkgs.push(t);
|
|
79
|
+
if (pkgs.length >= ENRICH_VERSION_CAP)
|
|
71
80
|
break;
|
|
72
81
|
}
|
|
73
82
|
const urls = new Set();
|
|
@@ -78,5 +87,5 @@ export function extractEnrichTargets(text) {
|
|
|
78
87
|
break;
|
|
79
88
|
}
|
|
80
89
|
const services = parseServices(text);
|
|
81
|
-
return { packages:
|
|
90
|
+
return { packages: pkgs.slice(0, ENRICH_CAP), versionPackages: pkgs, urls: [...urls], services };
|
|
82
91
|
}
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { docsRaw } from '../workers/docs-core.js';
|
|
12
12
|
import { fetchRaw } from '../workers/fetch-core.js';
|
|
13
|
+
import { npmVersionLookup } from '../workers/npm-version.js';
|
|
13
14
|
import type { SearchCoreInput, SearchCoreResult } from '../workers/search-core.js';
|
|
14
15
|
import type { PhaseDeps } from './child-runner.js';
|
|
15
16
|
/** Injectable workers so enrichment is testable without spawning real lookups. */
|
|
@@ -17,6 +18,7 @@ export interface ExternalContextDeps {
|
|
|
17
18
|
docsRaw?: typeof docsRaw;
|
|
18
19
|
fetchRaw?: typeof fetchRaw;
|
|
19
20
|
searchFn?: (input: SearchCoreInput) => Promise<SearchCoreResult>;
|
|
21
|
+
npmVersionLookup?: typeof npmVersionLookup;
|
|
20
22
|
}
|
|
21
23
|
type GatherDeps = Pick<PhaseDeps, 'cwd' | 'signal' | 'recordSubStep'>;
|
|
22
24
|
/**
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { docsRaw } from '../workers/docs-core.js';
|
|
12
12
|
import { fetchRaw } from '../workers/fetch-core.js';
|
|
13
|
-
import { formatNpmVersionSection } from '../workers/npm-version.js';
|
|
13
|
+
import { formatNpmVersionSection, npmVersionLookup } from '../workers/npm-version.js';
|
|
14
14
|
import { search as defaultSearch } from '../workers/search-core.js';
|
|
15
15
|
import { extractEnrichTargets } from './enrichment.js';
|
|
16
16
|
import { formatServiceBlock, formatFreshnessSkippedBlock } from './service-blocks.js';
|
|
@@ -22,7 +22,15 @@ export async function gatherExternalContext(refined, deps, researchDeps = {}) {
|
|
|
22
22
|
const docsRawFn = researchDeps.docsRaw ?? docsRaw;
|
|
23
23
|
const fetchRawFn = researchDeps.fetchRaw ?? fetchRaw;
|
|
24
24
|
const searchFn = researchDeps.searchFn ?? defaultSearch;
|
|
25
|
+
const npmVersionFn = researchDeps.npmVersionLookup ?? npmVersionLookup;
|
|
25
26
|
const enrichTargets = extractEnrichTargets(refined);
|
|
27
|
+
// Every named dep past the heavy-docs cap still gets a cheap live version
|
|
28
|
+
// lookup, so a version block exists for ALL of them (the docs-fetched ones
|
|
29
|
+
// emit their version below from the bundled lookup). Without this, deps 4..N
|
|
30
|
+
// had no live version and a "which version?" question fell back to the model's
|
|
31
|
+
// stale training data — how tailwindcss got pinned to an old major.
|
|
32
|
+
const docsPkgs = new Set(enrichTargets.packages);
|
|
33
|
+
const extraVersionPkgs = enrichTargets.versionPackages.filter(p => !docsPkgs.has(p));
|
|
26
34
|
if (enrichTargets.packages.length === 0
|
|
27
35
|
&& enrichTargets.urls.length === 0
|
|
28
36
|
&& enrichTargets.services.length === 0) {
|
|
@@ -30,7 +38,7 @@ export async function gatherExternalContext(refined, deps, researchDeps = {}) {
|
|
|
30
38
|
}
|
|
31
39
|
const enrichSections = [];
|
|
32
40
|
const tEnrichStart = Date.now();
|
|
33
|
-
const [docsResults, fetchResults, serviceResults] = await Promise.all([
|
|
41
|
+
const [docsResults, fetchResults, serviceResults, extraVersionResults] = await Promise.all([
|
|
34
42
|
Promise.all(enrichTargets.packages.map(pkg => docsRawFn({
|
|
35
43
|
pkg,
|
|
36
44
|
query: refined.split('\n').find(l => l.trim()) ?? refined,
|
|
@@ -42,16 +50,22 @@ export async function gatherExternalContext(refined, deps, researchDeps = {}) {
|
|
|
42
50
|
query: `${s.name} ${s.query}`,
|
|
43
51
|
count: 3,
|
|
44
52
|
signal: deps.signal
|
|
45
|
-
}).catch(() => null)))
|
|
53
|
+
}).catch(() => null))),
|
|
54
|
+
Promise.all(extraVersionPkgs.map(pkg => npmVersionFn(pkg, { signal: deps.signal }).catch(() => null)))
|
|
46
55
|
]);
|
|
47
|
-
// npm version blocks
|
|
48
|
-
//
|
|
49
|
-
// the
|
|
56
|
+
// npm version blocks lead the section so the model anchors on live version
|
|
57
|
+
// data before reading any docs body. The docs-fetched packages carry their
|
|
58
|
+
// version in docsRaw's bundled lookup; the remaining named deps get theirs
|
|
59
|
+
// from the cheap standalone lookup above. Together they cover EVERY named dep.
|
|
50
60
|
for (let i = 0; i < enrichTargets.packages.length; i++) {
|
|
51
61
|
const v = docsResults[i]?.npmVersion;
|
|
52
62
|
if (v)
|
|
53
63
|
enrichSections.push(formatNpmVersionSection(v));
|
|
54
64
|
}
|
|
65
|
+
for (const v of extraVersionResults) {
|
|
66
|
+
if (v)
|
|
67
|
+
enrichSections.push(formatNpmVersionSection(v));
|
|
68
|
+
}
|
|
55
69
|
for (let i = 0; i < enrichTargets.packages.length; i++) {
|
|
56
70
|
const r = docsResults[i];
|
|
57
71
|
if (r?.kind === 'ok' && r.chunks.length > 0) {
|
package/dist/task/prompts.js
CHANGED
|
@@ -221,6 +221,7 @@ LIVE-DATA RULE:
|
|
|
221
221
|
- "### service: <name>" blocks are LIVE web data; authoritative over training data for that service.
|
|
222
222
|
- "### freshness-check skipped" → tag UNKNOWN and say current state needs verification.
|
|
223
223
|
- No npm block + question is about latest/current version → tag UNKNOWN (training data goes stale).
|
|
224
|
+
- VERSION-PIN questions ("pin to X.y vs latest", "which major version") are costly-to-reverse build-shaping choices: unless the spec or an "### npm:" block already settles it (then ANSWER that value), tag UNKNOWN and surface it. NEVER auto-answer a downgrade to an OLDER major "to avoid breaking changes" from memory — that reasoning is exactly the stale-training-data trap. If an "### npm:" block shows a newer major than your instinct, that block is the live latest; do not silently pin an older major the live data and spec never asked for.
|
|
224
225
|
|
|
225
226
|
TRIAGE — run these checks IN ORDER first. The REVERSIBILITY TEST below applies ONLY to a question that survives all checks as a genuine preference.
|
|
226
227
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mjasnikovs/pi-task",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.7",
|
|
4
4
|
"description": "Deterministic spec-orchestration for local models, with a bundled real-time remote web view and web/docs/fetch/worker subagent tools.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|