@mjasnikovs/pi-task 0.14.10 → 0.14.11

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.
@@ -57,6 +57,16 @@ export declare class TaskRunner {
57
57
  * finally block's call. */
58
58
  private _disposeWidget;
59
59
  private _deliverSpec;
60
+ /**
61
+ * The spec as the implementer should receive it (Layer B). Layer A strips phantom
62
+ * specifiers from the upstream pipeline text, but a residual affirmative can survive
63
+ * into the composed spec, or arrive when pi expands an `@design.md` the spec
64
+ * references — and the implementer is told that doc is authoritative. Prepend a
65
+ * VERIFIED API OVERRIDES banner that outranks the spec for any specifier the
66
+ * deterministic check proves does not exist. No-op (returns the spec unchanged) when
67
+ * nothing is flagged or the runtime's types aren't installed.
68
+ */
69
+ private _specForDelivery;
60
70
  }
61
71
  export interface RunSingleTaskOptions {
62
72
  /** Await the session going idle after the spec is delivered, so the caller
@@ -26,6 +26,7 @@ import { startWidget } from './widget.js';
26
26
  import { publishViewer, publishNotify, registerBridgeCommand, getBridge } from '../remote/bridge.js';
27
27
  import { pushNotify } from '../remote/push.js';
28
28
  import { parseVerifyBlock } from './spec-validation.js';
29
+ import { findDeliveryPhantoms, formatApiOverrideBanner } from '../workers/phantom-imports.js';
29
30
  import { titleForDisplay } from './parsers.js';
30
31
  import { formatTimings } from './timings.js';
31
32
  import { getParentContextWindow, resolveContextUsage } from './context-usage.js';
@@ -261,20 +262,38 @@ export class TaskRunner {
261
262
  this._stopWidget = null;
262
263
  }
263
264
  async _deliverSpec(ctx) {
265
+ const spec = this._specForDelivery();
264
266
  if (this._sendSpec) {
265
- await this._sendSpec(this._pc.spec);
267
+ await this._sendSpec(spec);
266
268
  return;
267
269
  }
268
270
  if (!piApi) {
269
271
  throw new Error('extension not initialised (no ExtensionAPI captured)');
270
272
  }
271
273
  if (ctx.isIdle()) {
272
- piApi.sendUserMessage(this._pc.spec);
274
+ piApi.sendUserMessage(spec);
273
275
  }
274
276
  else {
275
- piApi.sendUserMessage(this._pc.spec, { deliverAs: 'followUp' });
277
+ piApi.sendUserMessage(spec, { deliverAs: 'followUp' });
276
278
  }
277
279
  }
280
+ /**
281
+ * The spec as the implementer should receive it (Layer B). Layer A strips phantom
282
+ * specifiers from the upstream pipeline text, but a residual affirmative can survive
283
+ * into the composed spec, or arrive when pi expands an `@design.md` the spec
284
+ * references — and the implementer is told that doc is authoritative. Prepend a
285
+ * VERIFIED API OVERRIDES banner that outranks the spec for any specifier the
286
+ * deterministic check proves does not exist. No-op (returns the spec unchanged) when
287
+ * nothing is flagged or the runtime's types aren't installed.
288
+ */
289
+ _specForDelivery() {
290
+ const phantoms = findDeliveryPhantoms(this._pc.spec, this._cwd);
291
+ const banner = formatApiOverrideBanner(phantoms);
292
+ if (!banner)
293
+ return this._pc.spec;
294
+ this._deps.logDebug?.(`impl-handoff API override banner prepended for: ${phantoms.map(p => p.spec).join(', ')}`);
295
+ return `${banner}\n\n${this._pc.spec}`;
296
+ }
278
297
  }
279
298
  /** Dialog copy for the post-interrupt steering prompt. */
280
299
  const STEER_TITLE = 'Paused — steer the model';
@@ -36,6 +36,25 @@ export declare function loadRuntimeTypeText(runtime: string, cwd: string): strin
36
36
  export declare function findPhantomImports(text: string, cwd: string, loadText?: (runtime: string, cwd: string) => string | null): PhantomImport[];
37
37
  /** Render flagged phantoms as an authoritative research section, or '' if none. */
38
38
  export declare function formatApiCorrections(phantoms: PhantomImport[]): string;
39
+ /**
40
+ * A top-of-handoff override banner (Layer B). The deterministic check has proven —
41
+ * against the installed type definitions — that the spec, or a design doc it
42
+ * references, names an API that does NOT exist. The implementer is told the spec/doc
43
+ * is authoritative, so a residual affirmative that survived into the composed spec, or
44
+ * one re-injected when pi expands an `@design.md` the spec references, can still push it
45
+ * to write `bun:sql` / a `declare module` shim. This banner sits ABOVE the spec and is
46
+ * declared to outrank it for these specifiers only. Empty when nothing is flagged.
47
+ */
48
+ export declare function formatApiOverrideBanner(phantoms: PhantomImport[]): string;
49
+ /**
50
+ * The phantoms the IMPLEMENTER would actually encounter: those named in the delivered
51
+ * spec text itself, PLUS those in any @-file the spec references (pi expands that
52
+ * mention at send time, re-injecting the doc's affirmative). Concatenates the spec with
53
+ * each readable referenced doc and runs the standard deterministic check over the whole.
54
+ * Unreadable mentions are skipped; silent when types are absent or nothing is flagged.
55
+ * Drives the impl-handoff override banner (Layer B).
56
+ */
57
+ export declare function findDeliveryPhantoms(spec: string, cwd: string): PhantomImport[];
39
58
  /**
40
59
  * Subtractively rewrite every flagged phantom specifier in `text` to the canonical
41
60
  * import the installed types prove, so NO affirmative occurrence of the non-existent
@@ -186,6 +186,56 @@ export function formatApiCorrections(phantoms) {
186
186
  const lines = phantoms.map(p => ` - ${p.suggestion}`);
187
187
  return `API CORRECTIONS\n${lines.join('\n')}`;
188
188
  }
189
+ /**
190
+ * A top-of-handoff override banner (Layer B). The deterministic check has proven —
191
+ * against the installed type definitions — that the spec, or a design doc it
192
+ * references, names an API that does NOT exist. The implementer is told the spec/doc
193
+ * is authoritative, so a residual affirmative that survived into the composed spec, or
194
+ * one re-injected when pi expands an `@design.md` the spec references, can still push it
195
+ * to write `bun:sql` / a `declare module` shim. This banner sits ABOVE the spec and is
196
+ * declared to outrank it for these specifiers only. Empty when nothing is flagged.
197
+ */
198
+ export function formatApiOverrideBanner(phantoms) {
199
+ if (phantoms.length === 0)
200
+ return '';
201
+ const lines = phantoms.map(p => ` - ${p.suggestion}`);
202
+ return ('VERIFIED API OVERRIDES — checked against the installed type definitions. These '
203
+ + 'SUPERSEDE the spec below and any design/spec document it references: the doc is '
204
+ + 'WRONG about these specifiers, so do NOT follow it here.\n'
205
+ + lines.join('\n')
206
+ + '\nUse the corrected import shown for each. Never write the phantom specifier, '
207
+ + 'and never add a `declare module` shim to make it resolve.');
208
+ }
209
+ // An @-file mention in a spec ("@DESIGN/foo.md", path until whitespace) minus the
210
+ // trailing prose punctuation a sentence adds. Mirrors auto-orchestrator's mention
211
+ // rules so we read the same docs pi re-expands when delivering the spec.
212
+ const MENTION_RE = /(?:^|\s)@([^\s]+)/g;
213
+ const MENTION_TRAILING_PUNCT = /[.,;:!?)\]}>"']+$/;
214
+ /**
215
+ * The phantoms the IMPLEMENTER would actually encounter: those named in the delivered
216
+ * spec text itself, PLUS those in any @-file the spec references (pi expands that
217
+ * mention at send time, re-injecting the doc's affirmative). Concatenates the spec with
218
+ * each readable referenced doc and runs the standard deterministic check over the whole.
219
+ * Unreadable mentions are skipped; silent when types are absent or nothing is flagged.
220
+ * Drives the impl-handoff override banner (Layer B).
221
+ */
222
+ export function findDeliveryPhantoms(spec, cwd) {
223
+ let text = spec;
224
+ const seen = new Set();
225
+ for (const m of spec.matchAll(MENTION_RE)) {
226
+ const rel = m[1].replace(MENTION_TRAILING_PUNCT, '');
227
+ if (rel === '' || seen.has(rel))
228
+ continue;
229
+ seen.add(rel);
230
+ try {
231
+ text += '\n' + fs.readFileSync(path.resolve(cwd, rel), 'utf8');
232
+ }
233
+ catch {
234
+ // not a readable file — leave the @token, skip it
235
+ }
236
+ }
237
+ return findPhantomImports(text, cwd);
238
+ }
189
239
  /**
190
240
  * Subtractively rewrite every flagged phantom specifier in `text` to the canonical
191
241
  * import the installed types prove, so NO affirmative occurrence of the non-existent
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mjasnikovs/pi-task",
3
- "version": "0.14.10",
3
+ "version": "0.14.11",
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",