@mjasnikovs/pi-task 0.39.4 → 0.40.0
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 +20 -5
- package/dist/task/auto-orchestrator.d.ts +36 -0
- package/dist/task/auto-orchestrator.js +43 -6
- package/dist/task/cancel-points.d.ts +34 -6
- package/dist/task/cancel-points.js +62 -10
- package/dist/task/child-status.js +13 -1
- package/dist/task/context-attribution.js +18 -6
- package/dist/task/external-context.d.ts +8 -1
- package/dist/task/external-context.js +52 -4
- package/dist/task/orchestrator.js +47 -5
- package/dist/task/phases.js +2 -1
- package/dist/task/plan-orchestrator.js +10 -1
- package/dist/task/prompts.d.ts +7 -1
- package/dist/task/prompts.js +14 -4
- package/dist/task/research-worker.js +11 -0
- package/dist/task/run-bracket.js +13 -1
- package/dist/task/task-gates.js +34 -0
- package/dist/workers/docs-cache.js +50 -3
- package/dist/workers/docs-chunk.d.ts +6 -3
- package/dist/workers/docs-chunk.js +8 -5
- package/dist/workers/docs-core.d.ts +27 -3
- package/dist/workers/docs-core.js +104 -41
- package/dist/workers/docs-ecosystems.d.ts +173 -0
- package/dist/workers/docs-ecosystems.js +449 -0
- package/dist/workers/docs-index.d.ts +2 -1
- package/dist/workers/docs-index.js +55 -27
- package/dist/workers/docs-project.d.ts +10 -0
- package/dist/workers/docs-project.js +86 -24
- package/dist/workers/docs-resolve.d.ts +6 -1
- package/dist/workers/docs-resolve.js +4 -3
- package/dist/workers/docs-retrieve.d.ts +2 -0
- package/dist/workers/docs-retrieve.js +11 -11
- package/dist/workers/eco-cargo.d.ts +115 -0
- package/dist/workers/eco-cargo.js +793 -0
- package/dist/workers/eco-hackage.d.ts +93 -0
- package/dist/workers/eco-hackage.js +508 -0
- package/dist/workers/npm-version.d.ts +5 -3
- package/dist/workers/npm-version.js +6 -4
- package/dist/workers/pi-worker-docs.d.ts +18 -4
- package/dist/workers/pi-worker-docs.js +57 -19
- package/dist/workers/research-cache.d.ts +2 -13
- package/dist/workers/research-cache.js +22 -46
- package/dist/workers/shared.d.ts +16 -5
- package/dist/workers/shared.js +0 -0
- package/package.json +1 -1
|
@@ -8,6 +8,7 @@ import { retrieveChunks as defaultRetrieveChunks } from './docs-retrieve.js';
|
|
|
8
8
|
import { docsLookup } from './docs-lookup.js';
|
|
9
9
|
import { projectCorpus } from './docs-project.js';
|
|
10
10
|
import { docsRaw, packageCorpus, buildVersionBanner } from './docs-core.js';
|
|
11
|
+
import { ECOSYSTEMS } from './docs-ecosystems.js';
|
|
11
12
|
import { npmVersionLookup as defaultNpmVersionLookup, formatNpmVersionSection } from './npm-version.js';
|
|
12
13
|
import { childFailureReason, makeWorkerTool, workerAnswer, workerUnavailable } from './shared.js';
|
|
13
14
|
import { isTypeOnlyAnswer } from '../task/type-only-answer.js';
|
|
@@ -20,11 +21,14 @@ import { groupChildArgs } from '../config/group-args.js';
|
|
|
20
21
|
const RENDER_QUERY_MAX = 100;
|
|
21
22
|
const Params = Type.Object({
|
|
22
23
|
module: Type.String({
|
|
23
|
-
description: 'Bare
|
|
24
|
+
description: 'Bare package name (e.g. "zod", "@scope/name", "react/jsx-runtime"), OR "." to look up the current project\'s own source code.'
|
|
24
25
|
}),
|
|
25
26
|
query: Type.String({
|
|
26
27
|
description: 'What to extract from the docs. The child pi reads ranked chunks and returns ONLY content answering this.'
|
|
27
|
-
})
|
|
28
|
+
}),
|
|
29
|
+
ecosystem: Type.Optional(Type.Union([Type.Literal('npm'), Type.Literal('cargo'), Type.Literal('hackage')], {
|
|
30
|
+
description: 'Which registry to read. Only needed in a repo holding more than one package manifest; otherwise the manifest decides.'
|
|
31
|
+
}))
|
|
28
32
|
});
|
|
29
33
|
/**
|
|
30
34
|
* Pull `@see {@link https://…}` pointers out of retrieved .d.ts/README text.
|
|
@@ -90,18 +94,24 @@ export function registerPiWorkerDocs(pi, internals = {}) {
|
|
|
90
94
|
makeWorkerTool(pi, {
|
|
91
95
|
name: 'pi-worker-docs',
|
|
92
96
|
label: 'Pi Worker Docs',
|
|
93
|
-
description: 'Look up an INSTALLED
|
|
94
|
-
+ 'answer from its
|
|
95
|
-
+ 'from a live
|
|
97
|
+
description: 'Look up an INSTALLED package and return a focused, version-pinned '
|
|
98
|
+
+ 'answer from its type declarations and README, PLUS the latest published '
|
|
99
|
+
+ 'version from a live registry call. USE THIS BEFORE ANSWERING any question '
|
|
96
100
|
+ 'about how to use a library, what it exports, its types/overloads/config, '
|
|
97
|
-
+ 'or the latest published version of
|
|
101
|
+
+ 'or the latest published version of a package. Do NOT answer package '
|
|
98
102
|
+ 'APIs from memory, do NOT run `npm view`/bash to get a package version, and '
|
|
99
103
|
+ 'do NOT web-search for an installed package — this tool is the source of '
|
|
100
104
|
+ 'truth and is version-pinned to what is actually installed (training-data '
|
|
101
105
|
+ 'versions and APIs are typically months stale).\n'
|
|
106
|
+
+ 'SUPPORTED ECOSYSTEMS: npm (package.json), cargo (Cargo.toml), hackage '
|
|
107
|
+
+ '(*.cabal). The MANIFEST in the working '
|
|
108
|
+
+ 'directory decides which registry a name is looked up in — you do not. If '
|
|
109
|
+
+ 'the directory holds none of those manifests, this tool REFUSES and '
|
|
110
|
+
+ 'installs nothing; use `pi-worker-search` or `pi-worker-fetch` for that '
|
|
111
|
+
+ 'package instead.\n'
|
|
102
112
|
+ 'For a non-package framework/runtime version (e.g. Node.js, Ubuntu), use '
|
|
103
113
|
+ '`pi-worker-search` instead. If the package is not installed it is '
|
|
104
|
-
+
|
|
114
|
+
+ "auto-installed from the project's own registry. The cache lives at "
|
|
105
115
|
+ '~/.cache/pi-worker/docs.sqlite, keyed by exact installed version; the '
|
|
106
116
|
+ 'registry lookup is best-effort and silently absent when offline.\n'
|
|
107
117
|
+ '\n'
|
|
@@ -168,7 +178,8 @@ export function registerPiWorkerDocs(pi, internals = {}) {
|
|
|
168
178
|
}
|
|
169
179
|
if (projectResult.kind === 'no_chunks') {
|
|
170
180
|
// The project IS indexed and has nothing — a real answer.
|
|
171
|
-
return workerAnswer(`Project "${projectResult.projectName}" has no
|
|
181
|
+
return workerAnswer(`Project "${projectResult.projectName}" has no `
|
|
182
|
+
+ `${projectResult.sourceLabel} files indexed.`, {
|
|
172
183
|
hitCache: projectResult.hitCache,
|
|
173
184
|
indexedFiles: projectResult.filesIngested
|
|
174
185
|
});
|
|
@@ -214,6 +225,7 @@ export function registerPiWorkerDocs(pi, internals = {}) {
|
|
|
214
225
|
pkg: params.module,
|
|
215
226
|
query: params.query,
|
|
216
227
|
cwd: ctx.cwd,
|
|
228
|
+
...(params.ecosystem ? { ecosystem: params.ecosystem } : {}),
|
|
217
229
|
resolvePackage: internals.resolvePackage,
|
|
218
230
|
ensureIndexed: internals.ensureIndexed,
|
|
219
231
|
retrieveChunks: internals.retrieveChunks,
|
|
@@ -222,7 +234,9 @@ export function registerPiWorkerDocs(pi, internals = {}) {
|
|
|
222
234
|
npmVersionLookup: internals.npmVersionLookup,
|
|
223
235
|
signal
|
|
224
236
|
});
|
|
225
|
-
const npmHeader = rawResult.npmVersion ?
|
|
237
|
+
const npmHeader = rawResult.npmVersion ?
|
|
238
|
+
`${formatNpmVersionSection(rawResult.npmVersion, rawResult.registryLabel)}\n\n`
|
|
239
|
+
: '';
|
|
226
240
|
const npmDetails = rawResult.npmVersion ?
|
|
227
241
|
{
|
|
228
242
|
npmLatest: rawResult.npmVersion.latest,
|
|
@@ -248,13 +262,16 @@ export function registerPiWorkerDocs(pi, internals = {}) {
|
|
|
248
262
|
return workerUnavailable(npmHeader + rawResult.message, details, 'docs-error');
|
|
249
263
|
}
|
|
250
264
|
if (rawResult.kind === 'no_chunks') {
|
|
251
|
-
const banner = buildVersionBanner(rawResult.autoInstallPin, rawResult.pkg.name, rawResult.pkg.version, ctx.cwd);
|
|
265
|
+
const banner = buildVersionBanner(rawResult.autoInstallPin, rawResult.pkg.name, rawResult.pkg.version, ctx.cwd, ECOSYSTEMS[rawResult.pkg.ecosystem]);
|
|
252
266
|
// The package resolved and genuinely ships nothing to read — an
|
|
253
267
|
// answer, and a stable one for this run.
|
|
254
268
|
return workerAnswer(banner
|
|
255
269
|
+ npmHeader
|
|
256
|
-
+ `Package ${rawResult.pkg.name}@${rawResult.pkg.version} has no
|
|
270
|
+
+ `Package ${rawResult.pkg.name}@${rawResult.pkg.version} has no `
|
|
271
|
+
+ `${ECOSYSTEMS[rawResult.pkg.ecosystem].surfaceLabel}. Use pi-worker to `
|
|
272
|
+
+ 'read source directly.', {
|
|
257
273
|
version: rawResult.pkg.version,
|
|
274
|
+
ecosystem: rawResult.pkg.ecosystem,
|
|
258
275
|
hitCache: rawResult.hitCache,
|
|
259
276
|
indexedFiles: rawResult.indexedFiles ?? 0,
|
|
260
277
|
cacheError: rawResult.cacheError,
|
|
@@ -264,9 +281,10 @@ export function registerPiWorkerDocs(pi, internals = {}) {
|
|
|
264
281
|
});
|
|
265
282
|
}
|
|
266
283
|
const { pkg, chunks, hitCache, indexingMs, cacheError, autoInstalled } = rawResult;
|
|
267
|
-
const versionBanner = buildVersionBanner(rawResult.autoInstallPin, pkg.name, pkg.version, ctx.cwd);
|
|
284
|
+
const versionBanner = buildVersionBanner(rawResult.autoInstallPin, pkg.name, pkg.version, ctx.cwd, ECOSYSTEMS[pkg.ecosystem]);
|
|
268
285
|
const baseDetails = {
|
|
269
286
|
version: pkg.version,
|
|
287
|
+
ecosystem: pkg.ecosystem,
|
|
270
288
|
hitCache,
|
|
271
289
|
chunksRetrieved: chunks.length,
|
|
272
290
|
indexingMs,
|
|
@@ -387,14 +405,34 @@ export function docsCacheable(d, text) {
|
|
|
387
405
|
/** The docs cache key: a package's answer is per (module, question), with the question
|
|
388
406
|
* lowercased and its whitespace collapsed so phrasing variants share one entry. Returns
|
|
389
407
|
* null for the project-source `.` lookup, which is never cached — the working tree
|
|
390
|
-
* mutates as tasks implement.
|
|
408
|
+
* mutates as tasks implement.
|
|
409
|
+
*
|
|
410
|
+
* The ecosystem joins the key only when the caller named one, so the keys of every
|
|
411
|
+
* call that lets the manifest decide are the ones they always were. */
|
|
391
412
|
export function docsCacheKey(params) {
|
|
392
|
-
|
|
393
|
-
null
|
|
394
|
-
|
|
413
|
+
if (params.module === '.')
|
|
414
|
+
return null;
|
|
415
|
+
const scope = params.ecosystem ? `${params.ecosystem}::` : '';
|
|
416
|
+
return `${scope}${normalizeQuery(params.module)}::${normalizeQuery(params.query)}`;
|
|
395
417
|
}
|
|
396
418
|
/** Package provenance for per-entry resume invalidation: the package ROOT of the
|
|
397
|
-
* specifier (`hono/client` → `hono`), and undefined for the project-source `.`.
|
|
398
|
-
|
|
399
|
-
|
|
419
|
+
* specifier (`hono/client` → `hono`), and undefined for the project-source `.`.
|
|
420
|
+
*
|
|
421
|
+
* The ecosystem comes from the RESOLVED lookup, not from the optional argument.
|
|
422
|
+
* Reading the argument would record npm for every single-manifest cargo or cabal
|
|
423
|
+
* project — the ordinary case, since the argument only exists to disambiguate a
|
|
424
|
+
* polyglot repo — and the entry's version would then be looked for in a
|
|
425
|
+
* `package.json` that is not there, leaving it un-prunable forever. */
|
|
426
|
+
export function docsCachePkg(params, details) {
|
|
427
|
+
if (params.module === '.')
|
|
428
|
+
return undefined;
|
|
429
|
+
const ecosystem = details.ecosystem ?? 'npm';
|
|
430
|
+
// The ROW knows how its own specifiers nest: `hono/client` → `hono`, but
|
|
431
|
+
// `serde_json::Value` → `serde_json`. Splitting on `/` alone recorded the
|
|
432
|
+
// whole path, and no manifest names that — so the entry's version came back
|
|
433
|
+
// undefined and it was never invalidated again.
|
|
434
|
+
return {
|
|
435
|
+
pkg: ECOSYSTEMS[ecosystem].parentPackage(params.module),
|
|
436
|
+
...(ecosystem !== 'npm' ? { ecosystem } : {})
|
|
437
|
+
};
|
|
400
438
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type EcosystemId } from './docs-ecosystems.js';
|
|
1
2
|
/** The env var the orchestrator stamps with the per-run id children inherit. */
|
|
2
3
|
export declare const RESEARCH_RUN_ID_ENV = "PI_TASK_RUN_ID";
|
|
3
4
|
/** Is this a transient filesystem error worth retrying inside the caller's deadline? */
|
|
@@ -24,18 +25,6 @@ export declare function configureResearchRun(enabled: boolean): string | undefin
|
|
|
24
25
|
* of asking the same thing resolve to the same (correct) result.
|
|
25
26
|
*/
|
|
26
27
|
export declare function normalizeQuery(s: string): string;
|
|
27
|
-
/**
|
|
28
|
-
* The project's declared dependency surface as a name→range map, flattened across every
|
|
29
|
-
* dependency block (a package listed in two blocks resolves to the first range seen, in
|
|
30
|
-
* block order — a real manifest does not disagree with itself, and a disagreement can
|
|
31
|
-
* only make the comparison stricter, i.e. re-fetch).
|
|
32
|
-
*
|
|
33
|
-
* Returns undefined when the manifest is missing or unparseable: the caller then cannot
|
|
34
|
-
* prove any package's version and must treat every package-scoped entry as unprovable.
|
|
35
|
-
* Deliberately NOT the lockfile: it is rewritten by installs that change no resolved
|
|
36
|
-
* version, which would drop digests for no correctness gain.
|
|
37
|
-
*/
|
|
38
|
-
export declare function depsMap(cwd: string): Promise<Record<string, string> | undefined>;
|
|
39
28
|
/**
|
|
40
29
|
* Resume hook: keep the interrupted run's cache id and PRUNE the entries the manifest
|
|
41
30
|
* has invalidated, rather than discarding the whole cache because anything moved. Returns
|
|
@@ -73,4 +62,4 @@ export declare function lookupResearch(cwd: string, runId: string, key: string):
|
|
|
73
62
|
*
|
|
74
63
|
* Best-effort: any failure is swallowed, leaving the caller's live result untouched.
|
|
75
64
|
*/
|
|
76
|
-
export declare function storeResearch(cwd: string, runId: string, key: string, text: string, details: unknown, pkg?: string): Promise<void>;
|
|
65
|
+
export declare function storeResearch(cwd: string, runId: string, key: string, text: string, details: unknown, pkg?: string, ecosystem?: EcosystemId): Promise<void>;
|
|
@@ -79,6 +79,7 @@
|
|
|
79
79
|
import * as fsp from 'node:fs/promises';
|
|
80
80
|
import * as path from 'node:path';
|
|
81
81
|
import { tasksDir } from '../task/task-io.js';
|
|
82
|
+
import { ECOSYSTEMS } from './docs-ecosystems.js';
|
|
82
83
|
const RESEARCH_CACHE_FILE = 'research-cache.json';
|
|
83
84
|
/** The env var the orchestrator stamps with the per-run id children inherit. */
|
|
84
85
|
export const RESEARCH_RUN_ID_ENV = 'PI_TASK_RUN_ID';
|
|
@@ -174,47 +175,13 @@ export function configureResearchRun(enabled) {
|
|
|
174
175
|
export function normalizeQuery(s) {
|
|
175
176
|
return s.replace(/\s+/g, ' ').trim().toLowerCase();
|
|
176
177
|
}
|
|
177
|
-
/**
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
* block order — a real manifest does not disagree with itself, and a disagreement can
|
|
181
|
-
* only make the comparison stricter, i.e. re-fetch).
|
|
182
|
-
*
|
|
183
|
-
* Returns undefined when the manifest is missing or unparseable: the caller then cannot
|
|
184
|
-
* prove any package's version and must treat every package-scoped entry as unprovable.
|
|
185
|
-
* Deliberately NOT the lockfile: it is rewritten by installs that change no resolved
|
|
186
|
-
* version, which would drop digests for no correctness gain.
|
|
187
|
-
*/
|
|
188
|
-
export async function depsMap(cwd) {
|
|
189
|
-
try {
|
|
190
|
-
const raw = await fsp.readFile(path.join(cwd, 'package.json'), 'utf8');
|
|
191
|
-
const pkg = JSON.parse(raw);
|
|
192
|
-
const blocks = [
|
|
193
|
-
'dependencies',
|
|
194
|
-
'devDependencies',
|
|
195
|
-
'peerDependencies',
|
|
196
|
-
'optionalDependencies'
|
|
197
|
-
];
|
|
198
|
-
const out = {};
|
|
199
|
-
for (const block of blocks) {
|
|
200
|
-
const deps = pkg[block];
|
|
201
|
-
if (!deps || typeof deps !== 'object')
|
|
202
|
-
continue;
|
|
203
|
-
for (const [name, range] of Object.entries(deps)) {
|
|
204
|
-
if (typeof range === 'string' && !(name in out))
|
|
205
|
-
out[name] = range;
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
// No dependency block at all is a real, stable state (a dependency-free repo).
|
|
209
|
-
return out;
|
|
210
|
-
}
|
|
211
|
-
catch {
|
|
212
|
-
return undefined;
|
|
213
|
-
}
|
|
178
|
+
/** The same question for any ecosystem: what does its manifest pin, name to version. */
|
|
179
|
+
function depsMapFor(cwd, ecosystem) {
|
|
180
|
+
return ECOSYSTEMS[ecosystem].declaredDeps(cwd);
|
|
214
181
|
}
|
|
215
182
|
/** The version declared for `pkg`, or undefined when it is not in the manifest. */
|
|
216
|
-
|
|
217
|
-
return (
|
|
183
|
+
function declaredVersion(cwd, pkg, ecosystem) {
|
|
184
|
+
return depsMapFor(cwd, ecosystem)?.[pkg];
|
|
218
185
|
}
|
|
219
186
|
/**
|
|
220
187
|
* Is this entry still current against the manifest as it stands now?
|
|
@@ -227,10 +194,10 @@ async function declaredVersion(cwd, pkg) {
|
|
|
227
194
|
* - `pkg` whose version moved, which left the manifest, or an unreadable manifest
|
|
228
195
|
* (no evidence either way): DROPPED.
|
|
229
196
|
*/
|
|
230
|
-
function entryStillFresh(entry,
|
|
197
|
+
function entryStillFresh(entry, depsFor) {
|
|
231
198
|
if (entry.pkg === undefined || entry.pkgVersion === undefined)
|
|
232
199
|
return true;
|
|
233
|
-
return
|
|
200
|
+
return depsFor(entry.ecosystem ?? 'npm')?.[entry.pkg] === entry.pkgVersion;
|
|
234
201
|
}
|
|
235
202
|
/**
|
|
236
203
|
* Resume hook: keep the interrupted run's cache id and PRUNE the entries the manifest
|
|
@@ -267,11 +234,17 @@ async function pruneCache(cwd) {
|
|
|
267
234
|
const file = await readCacheFile(cwd);
|
|
268
235
|
if (!file || file.pkgv !== PKG_PROVENANCE_VERSION)
|
|
269
236
|
return null;
|
|
270
|
-
|
|
237
|
+
// Read each manifest at most once: a file can hold entries from several.
|
|
238
|
+
const byEcosystem = new Map();
|
|
239
|
+
const depsFor = (id) => {
|
|
240
|
+
if (!byEcosystem.has(id))
|
|
241
|
+
byEcosystem.set(id, depsMapFor(cwd, id));
|
|
242
|
+
return byEcosystem.get(id);
|
|
243
|
+
};
|
|
271
244
|
const kept = {};
|
|
272
245
|
let dropped = 0;
|
|
273
246
|
for (const [key, entry] of Object.entries(file.entries)) {
|
|
274
|
-
if (entryStillFresh(entry,
|
|
247
|
+
if (entryStillFresh(entry, depsFor))
|
|
275
248
|
kept[key] = entry;
|
|
276
249
|
else
|
|
277
250
|
dropped++;
|
|
@@ -449,12 +422,12 @@ async function withCacheLock(cwd, fn) {
|
|
|
449
422
|
*
|
|
450
423
|
* Best-effort: any failure is swallowed, leaving the caller's live result untouched.
|
|
451
424
|
*/
|
|
452
|
-
export async function storeResearch(cwd, runId, key, text, details, pkg) {
|
|
425
|
+
export async function storeResearch(cwd, runId, key, text, details, pkg, ecosystem = 'npm') {
|
|
453
426
|
try {
|
|
454
|
-
// Resolved OUTSIDE the critical section: it reads
|
|
427
|
+
// Resolved OUTSIDE the critical section: it reads the manifest, which no other
|
|
455
428
|
// writer can be mutating, and keeping it out holds the lock for the file
|
|
456
429
|
// read/write alone. The version is still the one current at store time.
|
|
457
|
-
const pkgVersion = pkg === undefined ? undefined :
|
|
430
|
+
const pkgVersion = pkg === undefined ? undefined : declaredVersion(cwd, pkg, ecosystem);
|
|
458
431
|
await withCacheLock(cwd, async () => {
|
|
459
432
|
const existing = await readCacheFile(cwd);
|
|
460
433
|
const entries = existing && existing.runId === runId ? existing.entries : {};
|
|
@@ -463,6 +436,9 @@ export async function storeResearch(cwd, runId, key, text, details, pkg) {
|
|
|
463
436
|
details,
|
|
464
437
|
at: Date.now(),
|
|
465
438
|
...(pkg === undefined ? {} : { pkg }),
|
|
439
|
+
// npm is the absent default, so an npm-only file is byte-identical
|
|
440
|
+
// to what earlier versions wrote.
|
|
441
|
+
...(pkg !== undefined && ecosystem !== 'npm' ? { ecosystem } : {}),
|
|
466
442
|
...(pkgVersion === undefined ? {} : { pkgVersion })
|
|
467
443
|
};
|
|
468
444
|
// Evict oldest by write time if over the cap.
|
package/dist/workers/shared.d.ts
CHANGED
|
@@ -2,7 +2,13 @@ import type { Static, TSchema } from '@sinclair/typebox';
|
|
|
2
2
|
import type { AgentToolResult } from '@earendil-works/pi-agent-core';
|
|
3
3
|
import type { ExtensionAPI, ExtensionContext, Theme } from '@earendil-works/pi-coding-agent';
|
|
4
4
|
import type { Text } from '@earendil-works/pi-tui';
|
|
5
|
+
import type { EcosystemId } from './docs-ecosystems.js';
|
|
5
6
|
import { type WorkerFailureInput } from './worker-failure.js';
|
|
7
|
+
/** Which package, in which registry, a cached answer is about. */
|
|
8
|
+
export interface CachePackage {
|
|
9
|
+
pkg: string;
|
|
10
|
+
ecosystem?: EcosystemId;
|
|
11
|
+
}
|
|
6
12
|
/** Build a plain-text AgentToolResult. */
|
|
7
13
|
export declare function textResult<T>(text: string, details: T): AgentToolResult<T>;
|
|
8
14
|
/**
|
|
@@ -98,13 +104,18 @@ export interface WorkerToolSpec<TParams extends TSchema, TDetails> {
|
|
|
98
104
|
*/
|
|
99
105
|
cacheKey?(params: Static<TParams>): string | null;
|
|
100
106
|
/**
|
|
101
|
-
* The
|
|
102
|
-
* Recorded as structured provenance on the cache entry
|
|
103
|
-
* entries whose package moved version, instead of
|
|
104
|
-
* greenfield run adds packages every few tasks, so a
|
|
107
|
+
* The package this call's answer is ABOUT, and the registry it came from, for a
|
|
108
|
+
* package-scoped tool (docs). Recorded as structured provenance on the cache entry
|
|
109
|
+
* so a resume can drop just the entries whose package moved version, instead of
|
|
110
|
+
* the whole run's cache. A greenfield run adds packages every few tasks, so a
|
|
111
|
+
* whole-file gate never holds. The registry is part of it because `text` on npm
|
|
112
|
+
* and `text` on another registry are different packages that move separately.
|
|
105
113
|
* Omit — or return undefined — and the entry survives every resume of its run.
|
|
114
|
+
*
|
|
115
|
+
* `details` is the finished call's own record, so the registry recorded here is
|
|
116
|
+
* the one the lookup RESOLVED to — not one the caller happened to name.
|
|
106
117
|
*/
|
|
107
|
-
cachePkg?(params: Static<TParams
|
|
118
|
+
cachePkg?(params: Static<TParams>, details: TDetails): CachePackage | undefined;
|
|
108
119
|
/**
|
|
109
120
|
* Whether an ANSWER is safe to cache — a question about the answer's QUALITY
|
|
110
121
|
* (type-only, an abstention, an unverified excerpt), never about process
|
package/dist/workers/shared.js
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mjasnikovs/pi-task",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.40.0",
|
|
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",
|