@mjasnikovs/pi-task 0.38.28 → 0.38.30
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/dist/config/config.d.ts +70 -70
- package/dist/config/config.js +26 -35
- package/dist/config/extension-list.d.ts +6 -5
- package/dist/config/extension-list.js +3 -2
- package/dist/config/reasoning-args.d.ts +9 -7
- package/dist/config/reasoning-args.js +12 -10
- package/dist/config/reasoning.d.ts +44 -105
- package/dist/config/reasoning.js +27 -704
- package/dist/config/register.d.ts +34 -48
- package/dist/config/register.js +41 -51
- package/dist/config/tool-list.d.ts +16 -16
- package/dist/config/tool-list.js +1 -1
- package/dist/remote/bridge.d.ts +19 -10
- package/dist/remote/bridge.js +3 -2
- package/dist/remote/broadcast.js +3 -1
- package/dist/remote/events.js +12 -11
- package/dist/remote/history.d.ts +1 -1
- package/dist/remote/protocol.d.ts +6 -3
- package/dist/remote/protocol.js +2 -1
- package/dist/remote/push.d.ts +16 -16
- package/dist/remote/push.js +27 -27
- package/dist/remote/register.d.ts +3 -3
- package/dist/remote/register.js +17 -19
- package/dist/remote/server.d.ts +9 -8
- package/dist/remote/server.js +15 -14
- package/dist/remote/session-state.d.ts +5 -4
- package/dist/remote/session-state.js +8 -5
- package/dist/remote/sw.d.ts +7 -6
- package/dist/remote/sw.js +7 -6
- package/dist/remote/tailscale.d.ts +4 -2
- package/dist/remote/tailscale.js +4 -2
- package/dist/remote/ui-highlight.js +6 -5
- package/dist/remote/ui-render.js +4 -4
- package/dist/remote/ui-script.js +24 -24
- package/dist/remote/ui-styles.d.ts +1 -1
- package/dist/remote/ui-styles.js +10 -13
- package/dist/remote/ui-tools.js +9 -6
- package/dist/shared/child-extensions.d.ts +29 -17
- package/dist/shared/child-extensions.js +29 -17
- package/dist/shared/child-output.d.ts +30 -24
- package/dist/shared/child-output.js +25 -17
- package/dist/shared/child-process.d.ts +47 -40
- package/dist/shared/child-process.js +50 -59
- package/dist/shared/command-watchdog.d.ts +22 -16
- package/dist/shared/command-watchdog.js +28 -21
- package/dist/shared/fs-text.d.ts +16 -10
- package/dist/shared/fs-text.js +16 -10
- package/dist/shared/git-runner.d.ts +25 -25
- package/dist/shared/git-runner.js +25 -25
- package/dist/shared/leaked-tool-call.d.ts +17 -11
- package/dist/shared/leaked-tool-call.js +23 -15
- package/dist/shared/model-endpoint.d.ts +29 -16
- package/dist/shared/model-endpoint.js +33 -21
- package/dist/shared/pi-invocation.d.ts +7 -4
- package/dist/shared/pi-invocation.js +12 -7
- package/dist/shared/pkg-version.d.ts +13 -5
- package/dist/shared/pkg-version.js +13 -5
- package/dist/shared/reasoning-capability.d.ts +35 -24
- package/dist/shared/reasoning-capability.js +35 -24
- package/dist/shared/stream-watchdog.d.ts +60 -44
- package/dist/shared/stream-watchdog.js +62 -45
- package/dist/task/accept-debt.d.ts +41 -43
- package/dist/task/accept-debt.js +73 -65
- package/dist/task/api-synthesis.d.ts +24 -21
- package/dist/task/api-synthesis.js +32 -26
- package/dist/task/apis-contract.d.ts +32 -64
- package/dist/task/apis-contract.js +32 -64
- package/dist/task/artifact-closure.d.ts +27 -13
- package/dist/task/artifact-closure.js +95 -67
- package/dist/task/auto-commit.d.ts +46 -35
- package/dist/task/auto-commit.js +51 -38
- package/dist/task/auto-io.d.ts +45 -25
- package/dist/task/auto-io.js +57 -29
- package/dist/task/auto-orchestrator.d.ts +26 -24
- package/dist/task/auto-orchestrator.js +178 -162
- package/dist/task/auto-prompts.d.ts +36 -24
- package/dist/task/auto-prompts.js +40 -26
- package/dist/task/autofix-ledger.d.ts +27 -25
- package/dist/task/autofix-ledger.js +29 -26
- package/dist/task/batch-test-task.d.ts +20 -12
- package/dist/task/batch-test-task.js +67 -60
- package/dist/task/boot-probe.d.ts +60 -44
- package/dist/task/boot-probe.js +91 -72
- package/dist/task/cancel-input.d.ts +30 -16
- package/dist/task/cancel-input.js +20 -11
- package/dist/task/cancel-points.d.ts +27 -20
- package/dist/task/cancel-points.js +30 -22
- package/dist/task/child-runner.d.ts +46 -51
- package/dist/task/child-runner.js +48 -49
- package/dist/task/child-status.d.ts +23 -16
- package/dist/task/child-status.js +23 -16
- package/dist/task/clamp-output.js +12 -5
- package/dist/task/command-run.d.ts +31 -28
- package/dist/task/command-run.js +44 -35
- package/dist/task/command-shrink.d.ts +25 -18
- package/dist/task/command-shrink.js +37 -31
- package/dist/task/command-watchdog.d.ts +9 -6
- package/dist/task/command-watchdog.js +21 -15
- package/dist/task/context-attribution.d.ts +34 -26
- package/dist/task/context-attribution.js +34 -26
- package/dist/task/context-silence.d.ts +39 -29
- package/dist/task/context-silence.js +35 -25
- package/dist/task/context-usage.d.ts +25 -7
- package/dist/task/context-usage.js +21 -6
- package/dist/task/contracts.d.ts +8 -4
- package/dist/task/contracts.js +25 -17
- package/dist/task/coverage-loop.d.ts +22 -18
- package/dist/task/coverage-loop.js +35 -30
- package/dist/task/critique-probes.d.ts +13 -14
- package/dist/task/critique-probes.js +50 -39
- package/dist/task/debug-log.d.ts +13 -5
- package/dist/task/debug-log.js +32 -20
- package/dist/task/decompose-fidelity.d.ts +11 -9
- package/dist/task/decompose-fidelity.js +38 -33
- package/dist/task/decompose-granularity.d.ts +41 -38
- package/dist/task/decompose-granularity.js +41 -38
- package/dist/task/deep-render-check.d.ts +22 -14
- package/dist/task/deep-render-check.js +40 -31
- package/dist/task/dropped-input.d.ts +12 -7
- package/dist/task/dropped-input.js +5 -2
- package/dist/task/enforce-attribution.d.ts +38 -47
- package/dist/task/enforce-attribution.js +46 -52
- package/dist/task/enforce-guidelines.d.ts +31 -20
- package/dist/task/enforce-guidelines.js +32 -21
- package/dist/task/enrichment.d.ts +7 -2
- package/dist/task/enrichment.js +26 -14
- package/dist/task/env-notes.d.ts +16 -7
- package/dist/task/env-notes.js +48 -31
- package/dist/task/env-template-closure.d.ts +4 -4
- package/dist/task/env-template-closure.js +42 -34
- package/dist/task/external-context.d.ts +28 -21
- package/dist/task/external-context.js +17 -12
- package/dist/task/failure-classifier.d.ts +4 -5
- package/dist/task/failure-classifier.js +6 -7
- package/dist/task/file-inventory.d.ts +15 -11
- package/dist/task/file-inventory.js +25 -22
- package/dist/task/final-gate-fix.d.ts +74 -86
- package/dist/task/final-gate-fix.js +97 -116
- package/dist/task/final-gate-progress.d.ts +29 -46
- package/dist/task/final-gate-progress.js +40 -51
- package/dist/task/final-gate.d.ts +64 -97
- package/dist/task/final-gate.js +192 -199
- package/dist/task/fix-child.d.ts +21 -27
- package/dist/task/fix-child.js +21 -27
- package/dist/task/foreign-path.d.ts +6 -5
- package/dist/task/foreign-path.js +0 -0
- package/dist/task/frozen-conflict.d.ts +9 -10
- package/dist/task/frozen-conflict.js +61 -64
- package/dist/task/frozen-path-guard.d.ts +35 -14
- package/dist/task/frozen-path-guard.js +56 -39
- package/dist/task/gate-child.d.ts +27 -28
- package/dist/task/gate-child.js +37 -35
- package/dist/task/gate-deps.d.ts +34 -27
- package/dist/task/gate-deps.js +169 -159
- package/dist/task/gate-tally.d.ts +77 -80
- package/dist/task/gate-tally.js +65 -68
- package/dist/task/git-state-guard.d.ts +15 -11
- package/dist/task/git-state-guard.js +76 -66
- package/dist/task/impl-widget.d.ts +25 -16
- package/dist/task/impl-widget.js +27 -17
- package/dist/task/implementation-thinking.d.ts +33 -31
- package/dist/task/implementation-thinking.js +5 -6
- package/dist/task/implementation-turn.d.ts +34 -31
- package/dist/task/implementation-turn.js +29 -27
- package/dist/task/inline-markdown.d.ts +20 -7
- package/dist/task/inline-markdown.js +15 -6
- package/dist/task/launch-config-gap.js +25 -39
- package/dist/task/launch-contract.d.ts +18 -21
- package/dist/task/launch-contract.js +28 -30
- package/dist/task/launch-manifest.d.ts +6 -2
- package/dist/task/launch-manifest.js +35 -34
- package/dist/task/ledger.js +16 -14
- package/dist/task/lint-fix.d.ts +6 -8
- package/dist/task/lint-fix.js +67 -69
- package/dist/task/loop-detector.d.ts +9 -8
- package/dist/task/loop-detector.js +16 -12
- package/dist/task/mid-run-input.d.ts +17 -15
- package/dist/task/mid-run-input.js +17 -15
- package/dist/task/orchestrator.d.ts +24 -28
- package/dist/task/orchestrator.js +62 -64
- package/dist/task/orientation.d.ts +18 -23
- package/dist/task/orientation.js +24 -31
- package/dist/task/owned-freeze-conflict.d.ts +21 -20
- package/dist/task/owned-freeze-conflict.js +52 -85
- package/dist/task/owned-freeze-reassign.d.ts +40 -60
- package/dist/task/owned-freeze-reassign.js +41 -61
- package/dist/task/parsers.d.ts +4 -2
- package/dist/task/parsers.js +4 -4
- package/dist/task/phases.d.ts +41 -48
- package/dist/task/phases.js +180 -248
- package/dist/task/plan-io.d.ts +6 -7
- package/dist/task/plan-io.js +6 -7
- package/dist/task/plan-orchestrator.d.ts +10 -8
- package/dist/task/plan-orchestrator.js +14 -10
- package/dist/task/plan-prompts.d.ts +6 -5
- package/dist/task/plan-prompts.js +6 -5
- package/dist/task/plan-readonly.d.ts +4 -5
- package/dist/task/plan-readonly.js +4 -5
- package/dist/task/plan-rounds.d.ts +17 -29
- package/dist/task/plan-rounds.js +21 -34
- package/dist/task/plan-session.d.ts +58 -72
- package/dist/task/plan-session.js +61 -83
- package/dist/task/probe-gaming.d.ts +28 -27
- package/dist/task/probe-gaming.js +0 -0
- package/dist/task/prohibition-probe.d.ts +14 -16
- package/dist/task/prompts.d.ts +3 -4
- package/dist/task/prompts.js +17 -26
- package/dist/task/qa-transcript.d.ts +15 -22
- package/dist/task/qa-transcript.js +15 -21
- package/dist/task/question-box.d.ts +17 -13
- package/dist/task/question-box.js +19 -15
- package/dist/task/question-dedup.d.ts +6 -7
- package/dist/task/question-dedup.js +13 -14
- package/dist/task/question-dialog.d.ts +22 -32
- package/dist/task/question-dialog.js +22 -32
- package/dist/task/question-source.d.ts +18 -44
- package/dist/task/question-source.js +22 -51
- package/dist/task/refuted-constraint.d.ts +11 -31
- package/dist/task/refuted-constraint.js +27 -51
- package/dist/task/regenerable-artifacts.d.ts +12 -31
- package/dist/task/regenerable-artifacts.js +12 -31
- package/dist/task/render-check.d.ts +11 -22
- package/dist/task/render-check.js +33 -46
- package/dist/task/repo-health-check.d.ts +10 -14
- package/dist/task/repo-health-check.js +17 -23
- package/dist/task/requirements.d.ts +38 -71
- package/dist/task/requirements.js +78 -126
- package/dist/task/research-fanout-budget.d.ts +51 -88
- package/dist/task/research-fanout-budget.js +51 -88
- package/dist/task/research-worker.d.ts +33 -36
- package/dist/task/research-worker.js +39 -61
- package/dist/task/resume-gap.d.ts +14 -15
- package/dist/task/root-cause-repair.d.ts +9 -9
- package/dist/task/root-cause-repair.js +28 -40
- package/dist/task/run-bracket.d.ts +10 -13
- package/dist/task/run-end.d.ts +12 -22
- package/dist/task/run-end.js +8 -16
- package/dist/task/run-final-gate.d.ts +19 -21
- package/dist/task/run-final-gate.js +62 -80
- package/dist/task/runner-globs.d.ts +12 -13
- package/dist/task/runner-globs.js +12 -13
- package/dist/task/runner-resolve.d.ts +9 -9
- package/dist/task/runner-resolve.js +22 -23
- package/dist/task/script-escape.d.ts +10 -12
- package/dist/task/script-escape.js +13 -14
- package/dist/task/serve-entry.d.ts +1 -1
- package/dist/task/serve-entry.js +22 -25
- package/dist/task/service-blocks.js +4 -2
- package/dist/task/shipped-source.d.ts +11 -29
- package/dist/task/shipped-source.js +11 -29
- package/dist/task/skip-escape.js +10 -14
- package/dist/task/spec-urls.d.ts +26 -65
- package/dist/task/spec-urls.js +26 -65
- package/dist/task/spec-validation.d.ts +17 -20
- package/dist/task/spec-validation.js +17 -20
- package/dist/task/stall-detector.d.ts +23 -30
- package/dist/task/stall-detector.js +23 -30
- package/dist/task/stream-watchdog.d.ts +14 -12
- package/dist/task/stream-watchdog.js +14 -12
- package/dist/task/substitution-probe.d.ts +17 -20
- package/dist/task/substitution-probe.js +17 -20
- package/dist/task/task-gates.d.ts +36 -41
- package/dist/task/task-gates.js +95 -106
- package/dist/task/task-io.d.ts +4 -4
- package/dist/task/task-io.js +4 -4
- package/dist/task/task-parsers.js +4 -3
- package/dist/task/task-provenance.d.ts +2 -2
- package/dist/task/task-provenance.js +11 -13
- package/dist/task/task-types.d.ts +4 -3
- package/dist/task/terminal-outcome.d.ts +14 -16
- package/dist/task/terminal-outcome.js +12 -14
- package/dist/task/test-assembly.d.ts +13 -20
- package/dist/task/test-assembly.js +13 -20
- package/dist/task/timings.d.ts +5 -3
- package/dist/task/timings.js +5 -3
- package/dist/task/title-label.d.ts +9 -4
- package/dist/task/title-label.js +9 -4
- package/dist/task/type-only-answer.d.ts +44 -52
- package/dist/task/type-only-answer.js +44 -52
- package/dist/task/unfailable-command.d.ts +18 -24
- package/dist/task/unfailable-command.js +21 -27
- package/dist/task/unknown-routing.d.ts +10 -4
- package/dist/task/unknown-routing.js +10 -4
- package/dist/task/user-directives.d.ts +5 -8
- package/dist/task/user-directives.js +5 -8
- package/dist/task/verify-quality.d.ts +18 -22
- package/dist/task/verify-quality.js +45 -46
- package/dist/task/verify-reconcile.d.ts +15 -10
- package/dist/task/verify-reconcile.js +45 -43
- package/dist/task/verify-resolution.d.ts +24 -20
- package/dist/task/verify-resolution.js +51 -50
- package/dist/task/verify-work.d.ts +59 -66
- package/dist/task/verify-work.js +101 -138
- package/dist/task/widget.d.ts +15 -14
- package/dist/task/widget.js +22 -17
- package/dist/task/wiring-claims.d.ts +25 -32
- package/dist/task/wiring-claims.js +30 -35
- package/dist/task/write-guard.d.ts +39 -39
- package/dist/task/write-guard.js +48 -51
- package/dist/task/yolo.d.ts +34 -30
- package/dist/task/yolo.js +42 -37
- package/dist/workers/abstention.d.ts +21 -41
- package/dist/workers/abstention.js +27 -48
- package/dist/workers/brave-search.d.ts +4 -3
- package/dist/workers/brave-search.js +5 -2
- package/dist/workers/brave-warning.d.ts +7 -4
- package/dist/workers/brave-warning.js +19 -7
- package/dist/workers/ddg-search.d.ts +6 -6
- package/dist/workers/ddg-search.js +18 -12
- package/dist/workers/docs-cache.js +5 -2
- package/dist/workers/docs-chunk.d.ts +30 -37
- package/dist/workers/docs-chunk.js +37 -41
- package/dist/workers/docs-core.d.ts +28 -44
- package/dist/workers/docs-core.js +25 -44
- package/dist/workers/docs-index.js +4 -3
- package/dist/workers/docs-lookup.d.ts +15 -22
- package/dist/workers/docs-lookup.js +12 -21
- package/dist/workers/docs-project.d.ts +15 -9
- package/dist/workers/docs-project.js +17 -10
- package/dist/workers/docs-resolve.d.ts +19 -20
- package/dist/workers/docs-resolve.js +35 -32
- package/dist/workers/docs-retrieve.d.ts +5 -6
- package/dist/workers/docs-retrieve.js +18 -15
- package/dist/workers/exa-search.d.ts +9 -6
- package/dist/workers/exa-search.js +23 -12
- package/dist/workers/fetch-core.d.ts +13 -16
- package/dist/workers/fetch-core.js +23 -23
- package/dist/workers/focused-extractor.d.ts +12 -12
- package/dist/workers/focused-extractor.js +16 -19
- package/dist/workers/html-clean.js +24 -14
- package/dist/workers/http-request.d.ts +28 -20
- package/dist/workers/http-request.js +22 -17
- package/dist/workers/npm-version.d.ts +28 -11
- package/dist/workers/npm-version.js +24 -15
- package/dist/workers/phantom-imports.d.ts +15 -12
- package/dist/workers/phantom-imports.js +30 -24
- package/dist/workers/pi-worker-core.d.ts +86 -54
- package/dist/workers/pi-worker-core.js +112 -112
- package/dist/workers/pi-worker-docs.d.ts +24 -19
- package/dist/workers/pi-worker-docs.js +67 -76
- package/dist/workers/pi-worker-fetch.d.ts +7 -3
- package/dist/workers/pi-worker-fetch.js +27 -19
- package/dist/workers/pi-worker-search.js +12 -8
- package/dist/workers/pi-worker.d.ts +9 -4
- package/dist/workers/pi-worker.js +23 -10
- package/dist/workers/reasoning-warning.d.ts +18 -17
- package/dist/workers/reasoning-warning.js +22 -20
- package/dist/workers/research-cache.js +50 -78
- package/dist/workers/search-core.js +7 -5
- package/dist/workers/search-types.d.ts +10 -9
- package/dist/workers/search-types.js +9 -8
- package/dist/workers/session-hint.d.ts +13 -14
- package/dist/workers/session-hint.js +8 -9
- package/dist/workers/shared.d.ts +21 -25
- package/dist/workers/shared.js +0 -0
- package/dist/workers/single-read-extension.d.ts +14 -7
- package/dist/workers/single-read-extension.js +14 -7
- package/dist/workers/single-read-guard.d.ts +25 -28
- package/dist/workers/single-read-guard.js +32 -32
- package/dist/workers/typeonly-log.d.ts +12 -9
- package/dist/workers/typeonly-log.js +29 -33
- package/dist/workers/worker-channels.d.ts +15 -23
- package/dist/workers/worker-channels.js +15 -23
- package/dist/workers/worker-failure.d.ts +38 -46
- package/dist/workers/worker-failure.js +31 -39
- package/dist/workers/worker-kill.d.ts +25 -26
- package/dist/workers/worker-kill.js +16 -19
- package/dist/workers/worker-profiles.d.ts +43 -53
- package/dist/workers/worker-profiles.js +30 -38
- package/package.json +10 -8
|
@@ -2,26 +2,23 @@
|
|
|
2
2
|
* The **Focused extractor** — the one place a no-tools child pi is run to answer ONE question
|
|
3
3
|
* over content already in hand, cite a verbatim `<excerpt>`, and have that citation checked.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* and no retry. What genuinely differed was DATA, and the copies hid it:
|
|
5
|
+
* Its two callers are `fetchFocused` (fetch-core) and `docsLookup` (docs-lookup), and
|
|
6
|
+
* everything they do around the child is the same: the `--no-tools` argv,
|
|
7
|
+
* `getPiInvocation`, `runChild`, `parseChildOutput`, and no retry. What genuinely differs is
|
|
8
|
+
* DATA, and each difference has a name here:
|
|
10
9
|
*
|
|
11
|
-
* 1. the prompt body —
|
|
10
|
+
* 1. the prompt body — {@link FocusedRequest.prompt} is a string the caller assembles;
|
|
12
11
|
* 2. WHAT the excerpt is verified against — see {@link FocusedRequest.verifyAgainst};
|
|
13
12
|
* 3. what the answer MEANS (fetch's coverage-miss sentinel) — left to the caller, because
|
|
14
13
|
* classifying an answer is the caller's domain, not the child runner's.
|
|
15
14
|
*
|
|
16
|
-
* Two
|
|
17
|
-
* - **Failure.**
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* names — an `excerptVerified === false` that cannot afterwards be attributed to
|
|
24
|
-
* fabrication vs a normaliser gap. Every caller now gets the struct.
|
|
15
|
+
* Two invariants the result type enforces rather than documents:
|
|
16
|
+
* - **Failure.** A child that aborted or exited non-zero has no `answer` field at all. It
|
|
17
|
+
* cannot be read as one, which matters because `parseChildOutput` returns the whole
|
|
18
|
+
* trimmed stdout when there is no `<answer>` tag — a crashed child's error dump.
|
|
19
|
+
* - **Evidence.** Every caller gets the full {@link ExcerptVerification}, not a bare
|
|
20
|
+
* boolean, so a false verdict can afterwards be attributed to fabrication rather than a
|
|
21
|
+
* normaliser gap.
|
|
25
22
|
*/
|
|
26
23
|
import { spawn as defaultSpawn } from 'node:child_process';
|
|
27
24
|
import { getPiInvocation } from '../shared/pi-invocation.js';
|
|
@@ -30,9 +27,9 @@ import { childBaseArgs } from '../shared/child-extensions.js';
|
|
|
30
27
|
import { parseChildOutput, verifyExcerpt } from '../shared/child-output.js';
|
|
31
28
|
import { formatChildFailure } from './shared.js';
|
|
32
29
|
/**
|
|
33
|
-
* The argv every focused extraction child runs with: the shared child base
|
|
34
|
-
* extensions
|
|
35
|
-
*
|
|
30
|
+
* The argv every focused extraction child runs with: the shared child base — any whitelisted
|
|
31
|
+
* extensions, then `--print --no-skills --no-extensions --no-prompt-templates
|
|
32
|
+
* --no-context-files --no-session` — then the caller's thinking fragment, then `--no-tools`.
|
|
36
33
|
*
|
|
37
34
|
* `--no-tools` is the contract, not a default: the child is given all the content it may use
|
|
38
35
|
* inside its prompt, so a tool call could only reach for something unsourced.
|
|
@@ -47,7 +44,7 @@ export const focusedChildArgs = (thinking = []) => [
|
|
|
47
44
|
* `<answer>`/`<excerpt>` and verify the excerpt against `verifyAgainst`.
|
|
48
45
|
*
|
|
49
46
|
* Never retries — a re-ask of a deterministic extraction over unchanged content is a second
|
|
50
|
-
* bill for the same answer
|
|
47
|
+
* bill for the same answer. Exactly one child is spawned per call.
|
|
51
48
|
*/
|
|
52
49
|
export async function runFocusedExtraction(req) {
|
|
53
50
|
const spawn = req.spawn ?? defaultSpawn;
|
|
@@ -20,7 +20,8 @@ export function cleanHtml(html, baseUrl) {
|
|
|
20
20
|
finalUrl: baseUrl
|
|
21
21
|
};
|
|
22
22
|
}
|
|
23
|
-
//
|
|
23
|
+
// Readability found no article — turndown the raw body instead. An empty body
|
|
24
|
+
// yields empty markdown, and the title falls back to the URL's hostname.
|
|
24
25
|
const body = doc.body;
|
|
25
26
|
const bodyHtml = body ? body.innerHTML : '';
|
|
26
27
|
const markdown = turndown.turndown(bodyHtml).trim();
|
|
@@ -32,14 +33,17 @@ export function cleanHtml(html, baseUrl) {
|
|
|
32
33
|
}
|
|
33
34
|
const DEFAULT_TIMEOUT_MS = 15_000;
|
|
34
35
|
const DEFAULT_MAX_BYTES = 2 * 1024 * 1024; // 2 MB
|
|
35
|
-
// Read at runtime
|
|
36
|
+
// Read at runtime — `readPkgVersion` re-reads package.json on every call — so the
|
|
37
|
+
// User-Agent never drifts out of sync with releases and no build step bakes it in.
|
|
36
38
|
const PKG_VERSION = readPkgVersion();
|
|
37
39
|
const USER_AGENT = `pi-worker/${PKG_VERSION} (+https://npmjs.com/package/@mjasnikovs/pi-worker)`;
|
|
38
|
-
// Decide how to handle a response based on its content-type
|
|
39
|
-
//
|
|
40
|
-
//
|
|
41
|
-
//
|
|
42
|
-
//
|
|
40
|
+
// Decide how to handle a response based on its content-type, case-insensitively
|
|
41
|
+
// and ignoring any `; charset=…` tail. HTML and XHTML run through the
|
|
42
|
+
// readability/turndown pipeline; text-ish formats (any `text/*`, JSON and `+json`,
|
|
43
|
+
// XML and `+xml`, javascript) are already clean and pass through verbatim; anything
|
|
44
|
+
// else — PDF, images, octet-stream — is rejected as `not-html`. A missing
|
|
45
|
+
// content-type is treated as text: many plain-text endpoints (llms.txt,
|
|
46
|
+
// robots.txt) omit the header.
|
|
43
47
|
function classifyContentType(contentType) {
|
|
44
48
|
const mime = contentType.split(';')[0].trim().toLowerCase();
|
|
45
49
|
if (mime === '')
|
|
@@ -58,14 +62,17 @@ function classifyContentType(contentType) {
|
|
|
58
62
|
}
|
|
59
63
|
// Extract the charset from a content-type header, if present and supported by
|
|
60
64
|
// TextDecoder; otherwise fall back to UTF-8 so non-UTF-8 pages aren't mangled.
|
|
65
|
+
// Quotes around the label are stripped, and an unsupported label decodes exactly
|
|
66
|
+
// as UTF-8 would rather than throwing.
|
|
61
67
|
function decoderFor(contentType) {
|
|
62
68
|
const match = /charset=([^;]+)/i.exec(contentType);
|
|
63
69
|
const charset = match?.[1]?.trim().replace(/^["']|["']$/g, '');
|
|
64
70
|
if (charset) {
|
|
65
71
|
try {
|
|
66
|
-
// The runtime accepts any charset label string
|
|
67
|
-
//
|
|
68
|
-
//
|
|
72
|
+
// The runtime accepts any charset label string, but the type here is the
|
|
73
|
+
// narrow `Encoding` union — passing a plain `string` is TS2345,
|
|
74
|
+
// "not assignable to parameter of type 'Encoding | undefined'". Cast to
|
|
75
|
+
// the actual param type rather than widening the guard.
|
|
69
76
|
return new TextDecoder(charset, {
|
|
70
77
|
fatal: false
|
|
71
78
|
});
|
|
@@ -113,16 +120,19 @@ export async function fetchAndClean(url, opts = {}) {
|
|
|
113
120
|
let bytesRead = 0;
|
|
114
121
|
try {
|
|
115
122
|
while (true) {
|
|
116
|
-
//
|
|
117
|
-
//
|
|
123
|
+
// `reader.read()` resolves to `any` under this tsconfig — assigning
|
|
124
|
+
// its `value` to a `number` raises no error — so the destructure is
|
|
125
|
+
// pinned to the Uint8Array the reader actually yields.
|
|
118
126
|
const { value, done } = (await reader.read());
|
|
119
127
|
if (done)
|
|
120
128
|
break;
|
|
121
129
|
if (value) {
|
|
122
130
|
bytesRead += value.byteLength;
|
|
123
131
|
if (bytesRead > maxBytes) {
|
|
124
|
-
// OUR abort
|
|
125
|
-
//
|
|
132
|
+
// OUR abort. All three cancels fire the same signal, so the
|
|
133
|
+
// seam is what tells them apart: `ctl.abort()` here,
|
|
134
|
+
// `ctl.userAborted()` for the caller's, `ctl.timedOut()` for
|
|
135
|
+
// the clock. Only the local `sizeExceeded` flag says it was us.
|
|
126
136
|
sizeExceeded = true;
|
|
127
137
|
ctl.abort();
|
|
128
138
|
break;
|
|
@@ -1,32 +1,36 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* http-request — the one bounded HTTP request in this codebase.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* the
|
|
8
|
-
*
|
|
9
|
-
*
|
|
4
|
+
* Its five callers — brave-search, ddg-search, exa-search, html-clean and
|
|
5
|
+
* npm-version — would otherwise each hand-roll the same dozen lines: an internal
|
|
6
|
+
* `AbortController`, a `setTimeout` that aborts it, a `userAborted` flag set from
|
|
7
|
+
* the caller's signal, and a `finally` that clears the timer and removes the
|
|
8
|
+
* listener. Five copies of a rule is five chances to drift, and the flag is the
|
|
9
|
+
* piece that goes missing: without it a user cancel is indistinguishable from a
|
|
10
|
+
* host that is down.
|
|
10
11
|
*
|
|
11
12
|
* What is shared is the BOUNDING, not the interpretation. Each caller still owns
|
|
12
13
|
* its own status-code policy (DDG treats 429/403 as rate-limiting, Brave splits
|
|
13
|
-
* auth from rate-limit, npm treats every non-OK as "no answer") and its
|
|
14
|
-
* type, because those genuinely differ.
|
|
15
|
-
*
|
|
16
|
-
*
|
|
14
|
+
* auth from rate-limit, npm-version treats every non-OK as "no answer") and its
|
|
15
|
+
* own error type, because those genuinely differ. A status code never reaches
|
|
16
|
+
* {@link HttpRequestError} at all — the handler is simply called with the
|
|
17
|
+
* response, whatever its status.
|
|
17
18
|
*
|
|
18
19
|
* The handler runs INSIDE the timeout. `fetch` resolves as soon as the headers
|
|
19
20
|
* arrive, so a seam that returned the `Response` and cleared its own timer would
|
|
20
|
-
* leave the body read unbounded — a hung stream would hang forever.
|
|
21
|
-
*
|
|
22
|
-
*
|
|
21
|
+
* leave the body read unbounded — a hung stream would hang forever. Here the
|
|
22
|
+
* clock aborts a handler that is still reading, and `ctl.timedOut()` is true
|
|
23
|
+
* inside it when that happens.
|
|
23
24
|
*/
|
|
24
25
|
/** Injectable fetch — the narrow signature keeps test fakes free of Bun's extras. */
|
|
25
26
|
export type FetchLike = (url: string, init?: RequestInit) => Promise<Response>;
|
|
26
27
|
/**
|
|
27
|
-
* Why the request never produced a response.
|
|
28
|
-
*
|
|
29
|
-
*
|
|
28
|
+
* Why the request never produced a response. Only two kinds, because a status code
|
|
29
|
+
* is not a failure of the REQUEST and what a status means is the caller's policy.
|
|
30
|
+
*
|
|
31
|
+
* `aborted` means the CALLER cancelled — nothing else mints it. A timeout during
|
|
32
|
+
* `fetch` arrives here as `network`, since only `userAborted` is consulted on that
|
|
33
|
+
* path; a handler that needs to tell the clock apart reads `ctl.timedOut()`.
|
|
30
34
|
*/
|
|
31
35
|
export declare class HttpRequestError extends Error {
|
|
32
36
|
readonly kind: 'aborted' | 'network';
|
|
@@ -44,7 +48,9 @@ export interface HttpRequestOpts {
|
|
|
44
48
|
redirect?: 'follow' | 'error' | 'manual';
|
|
45
49
|
/** Wall clock over the request AND the handler. Required — no silent default. */
|
|
46
50
|
timeoutMs: number;
|
|
47
|
-
/** The caller's cancel. Its firing is what makes `userAborted()` true
|
|
51
|
+
/** The caller's cancel. Its firing is what makes `userAborted()` true — and a
|
|
52
|
+
* signal that is ALREADY aborted sets the flag before `fetch` is called, so
|
|
53
|
+
* the immediate rejection is still classified as `aborted`. */
|
|
48
54
|
signal?: AbortSignal;
|
|
49
55
|
fetchImpl?: FetchLike;
|
|
50
56
|
}
|
|
@@ -54,8 +60,9 @@ export interface HttpRequestControl {
|
|
|
54
60
|
readonly signal: AbortSignal;
|
|
55
61
|
/**
|
|
56
62
|
* Abort the in-flight request from inside the handler — a size cap hit, an
|
|
57
|
-
* early stop.
|
|
58
|
-
*
|
|
63
|
+
* early stop. It sets NEITHER flag, so after calling it `userAborted()` and
|
|
64
|
+
* `timedOut()` are both false while `signal.aborted` is true. A handler that
|
|
65
|
+
* calls this can therefore tell its own abort from the other two.
|
|
59
66
|
*/
|
|
60
67
|
abort(): void;
|
|
61
68
|
/** The CALLER cancelled. Not the timeout, not `abort()`. */
|
|
@@ -70,5 +77,6 @@ export interface HttpRequestControl {
|
|
|
70
77
|
* `handle` throws propagates untouched — that is the caller's own policy talking.
|
|
71
78
|
*/
|
|
72
79
|
export declare function httpRequest<T>(url: string, opts: HttpRequestOpts, handle: (response: Response, ctl: HttpRequestControl) => Promise<T>): Promise<T>;
|
|
73
|
-
/** A readable one-liner for an unknown thrown value
|
|
80
|
+
/** A readable one-liner for an unknown thrown value: an Error's `message`,
|
|
81
|
+
* otherwise `String(err)` — so `42` and `null` render as text, not as a crash. */
|
|
74
82
|
export declare function describeError(err: unknown): string;
|
|
@@ -1,30 +1,34 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* http-request — the one bounded HTTP request in this codebase.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* the
|
|
8
|
-
*
|
|
9
|
-
*
|
|
4
|
+
* Its five callers — brave-search, ddg-search, exa-search, html-clean and
|
|
5
|
+
* npm-version — would otherwise each hand-roll the same dozen lines: an internal
|
|
6
|
+
* `AbortController`, a `setTimeout` that aborts it, a `userAborted` flag set from
|
|
7
|
+
* the caller's signal, and a `finally` that clears the timer and removes the
|
|
8
|
+
* listener. Five copies of a rule is five chances to drift, and the flag is the
|
|
9
|
+
* piece that goes missing: without it a user cancel is indistinguishable from a
|
|
10
|
+
* host that is down.
|
|
10
11
|
*
|
|
11
12
|
* What is shared is the BOUNDING, not the interpretation. Each caller still owns
|
|
12
13
|
* its own status-code policy (DDG treats 429/403 as rate-limiting, Brave splits
|
|
13
|
-
* auth from rate-limit, npm treats every non-OK as "no answer") and its
|
|
14
|
-
* type, because those genuinely differ.
|
|
15
|
-
*
|
|
16
|
-
*
|
|
14
|
+
* auth from rate-limit, npm-version treats every non-OK as "no answer") and its
|
|
15
|
+
* own error type, because those genuinely differ. A status code never reaches
|
|
16
|
+
* {@link HttpRequestError} at all — the handler is simply called with the
|
|
17
|
+
* response, whatever its status.
|
|
17
18
|
*
|
|
18
19
|
* The handler runs INSIDE the timeout. `fetch` resolves as soon as the headers
|
|
19
20
|
* arrive, so a seam that returned the `Response` and cleared its own timer would
|
|
20
|
-
* leave the body read unbounded — a hung stream would hang forever.
|
|
21
|
-
*
|
|
22
|
-
*
|
|
21
|
+
* leave the body read unbounded — a hung stream would hang forever. Here the
|
|
22
|
+
* clock aborts a handler that is still reading, and `ctl.timedOut()` is true
|
|
23
|
+
* inside it when that happens.
|
|
23
24
|
*/
|
|
24
25
|
/**
|
|
25
|
-
* Why the request never produced a response.
|
|
26
|
-
*
|
|
27
|
-
*
|
|
26
|
+
* Why the request never produced a response. Only two kinds, because a status code
|
|
27
|
+
* is not a failure of the REQUEST and what a status means is the caller's policy.
|
|
28
|
+
*
|
|
29
|
+
* `aborted` means the CALLER cancelled — nothing else mints it. A timeout during
|
|
30
|
+
* `fetch` arrives here as `network`, since only `userAborted` is consulted on that
|
|
31
|
+
* path; a handler that needs to tell the clock apart reads `ctl.timedOut()`.
|
|
28
32
|
*/
|
|
29
33
|
export class HttpRequestError extends Error {
|
|
30
34
|
kind;
|
|
@@ -95,7 +99,8 @@ export async function httpRequest(url, opts, handle) {
|
|
|
95
99
|
opts.signal.removeEventListener('abort', onUserAbort);
|
|
96
100
|
}
|
|
97
101
|
}
|
|
98
|
-
/** A readable one-liner for an unknown thrown value
|
|
102
|
+
/** A readable one-liner for an unknown thrown value: an Error's `message`,
|
|
103
|
+
* otherwise `String(err)` — so `42` and `null` render as text, not as a crash. */
|
|
99
104
|
export function describeError(err) {
|
|
100
105
|
if (err instanceof Error)
|
|
101
106
|
return err.message;
|
|
@@ -1,18 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Live npm registry version lookup.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* Without it the auto-answer and research workers have no live source for "what
|
|
5
|
+
* is the latest published version of X", so they answer from training data, which
|
|
6
|
+
* is stale by construction — it can only ever name a version that existed when the
|
|
7
|
+
* model was trained.
|
|
8
8
|
*
|
|
9
|
-
* This
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* This fetches the registry's metadata endpoint and returns just enough to anchor
|
|
10
|
+
* a worker in the present: the dist-tag `latest`, a short list of versions, and
|
|
11
|
+
* the publish date of `latest`.
|
|
12
12
|
*/
|
|
13
13
|
export interface NpmVersionInfo {
|
|
14
14
|
pkg: string;
|
|
15
|
+
/** The `latest` dist-tag — the stable release npm would install. */
|
|
15
16
|
latest: string;
|
|
17
|
+
/**
|
|
18
|
+
* The LAST `RECENT_VERSIONS_LIMIT` keys of the registry's `versions` map,
|
|
19
|
+
* reversed. That is publication order, not semver order, and it is not
|
|
20
|
+
* filtered: for a package that publishes prereleases these are mostly canary
|
|
21
|
+
* and experimental builds, not the recent stable releases.
|
|
22
|
+
*/
|
|
16
23
|
recent: string[];
|
|
17
24
|
publishedAt?: string;
|
|
18
25
|
}
|
|
@@ -23,10 +30,20 @@ export interface NpmVersionOpts {
|
|
|
23
30
|
}
|
|
24
31
|
/**
|
|
25
32
|
* Fetch the latest version + recent version list for an npm package.
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
33
|
+
*
|
|
34
|
+
* Returns `null` on every ordinary failure — an invalid package name, a registry
|
|
35
|
+
* that is down, a 404, a malformed body, a missing `dist-tags.latest`. The caller
|
|
36
|
+
* treats null as "no fresh data available" and continues without it.
|
|
37
|
+
*
|
|
38
|
+
* It is NOT null-on-everything: a user cancel is re-thrown (see below), and so is
|
|
39
|
+
* anything that is not an {@link HttpRequestError}.
|
|
40
|
+
*
|
|
41
|
+
* An invalid name is rejected before any request is made. `UPPER` is valid — the
|
|
42
|
+
* name regex is case-insensitive — while an empty name, a leading `.` or `_`, a
|
|
43
|
+
* space, more than 214 characters, or an unscoped path like `a/b/c` are not.
|
|
29
44
|
*/
|
|
30
45
|
export declare function npmVersionLookup(pkg: string, opts?: NpmVersionOpts): Promise<NpmVersionInfo | null>;
|
|
31
|
-
/** Format an NpmVersionInfo as a short Markdown block for EXTERNAL CONTEXT
|
|
46
|
+
/** Format an NpmVersionInfo as a short Markdown block for EXTERNAL CONTEXT: a
|
|
47
|
+
* `### npm: <pkg>` heading, a `latest:` line that gains ` (published YYYY-MM-DD)`
|
|
48
|
+
* when the date is known, and a `recent:` line only when the list is non-empty. */
|
|
32
49
|
export declare function formatNpmVersionSection(info: NpmVersionInfo): string;
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Live npm registry version lookup.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* Without it the auto-answer and research workers have no live source for "what
|
|
5
|
+
* is the latest published version of X", so they answer from training data, which
|
|
6
|
+
* is stale by construction — it can only ever name a version that existed when the
|
|
7
|
+
* model was trained.
|
|
8
8
|
*
|
|
9
|
-
* This
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* This fetches the registry's metadata endpoint and returns just enough to anchor
|
|
10
|
+
* a worker in the present: the dist-tag `latest`, a short list of versions, and
|
|
11
|
+
* the publish date of `latest`.
|
|
12
12
|
*/
|
|
13
13
|
import { httpRequest, HttpRequestError } from './http-request.js';
|
|
14
14
|
const REGISTRY_BASE = 'https://registry.npmjs.org';
|
|
@@ -16,9 +16,17 @@ const DEFAULT_TIMEOUT_MS = 3000;
|
|
|
16
16
|
const RECENT_VERSIONS_LIMIT = 10;
|
|
17
17
|
/**
|
|
18
18
|
* Fetch the latest version + recent version list for an npm package.
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
19
|
+
*
|
|
20
|
+
* Returns `null` on every ordinary failure — an invalid package name, a registry
|
|
21
|
+
* that is down, a 404, a malformed body, a missing `dist-tags.latest`. The caller
|
|
22
|
+
* treats null as "no fresh data available" and continues without it.
|
|
23
|
+
*
|
|
24
|
+
* It is NOT null-on-everything: a user cancel is re-thrown (see below), and so is
|
|
25
|
+
* anything that is not an {@link HttpRequestError}.
|
|
26
|
+
*
|
|
27
|
+
* An invalid name is rejected before any request is made. `UPPER` is valid — the
|
|
28
|
+
* name regex is case-insensitive — while an empty name, a leading `.` or `_`, a
|
|
29
|
+
* space, more than 214 characters, or an unscoped path like `a/b/c` are not.
|
|
22
30
|
*/
|
|
23
31
|
export async function npmVersionLookup(pkg, opts = {}) {
|
|
24
32
|
if (!isValidPackageName(pkg))
|
|
@@ -54,10 +62,9 @@ export async function npmVersionLookup(pkg, opts = {}) {
|
|
|
54
62
|
});
|
|
55
63
|
}
|
|
56
64
|
catch (err) {
|
|
57
|
-
// A user cancel is
|
|
58
|
-
//
|
|
59
|
-
//
|
|
60
|
-
// caller went on assembling a block for a run the user had already stopped.
|
|
65
|
+
// A user cancel is RE-THROWN, not swallowed. Returning null here would make a
|
|
66
|
+
// cancelled lookup indistinguishable from a registry that is down, and the
|
|
67
|
+
// caller would go on assembling a version block for a run the user stopped.
|
|
61
68
|
if (err instanceof HttpRequestError && err.kind === 'aborted')
|
|
62
69
|
throw err;
|
|
63
70
|
if (err instanceof HttpRequestError)
|
|
@@ -65,7 +72,9 @@ export async function npmVersionLookup(pkg, opts = {}) {
|
|
|
65
72
|
throw err;
|
|
66
73
|
}
|
|
67
74
|
}
|
|
68
|
-
/** Format an NpmVersionInfo as a short Markdown block for EXTERNAL CONTEXT
|
|
75
|
+
/** Format an NpmVersionInfo as a short Markdown block for EXTERNAL CONTEXT: a
|
|
76
|
+
* `### npm: <pkg>` heading, a `latest:` line that gains ` (published YYYY-MM-DD)`
|
|
77
|
+
* when the date is known, and a `recent:` line only when the list is non-empty. */
|
|
69
78
|
export function formatNpmVersionSection(info) {
|
|
70
79
|
const lines = [`### npm: ${info.pkg}`, `latest: ${info.latest}`];
|
|
71
80
|
if (info.publishedAt) {
|
|
@@ -37,7 +37,9 @@ export declare function findPhantomImports(text: string, cwd: string, loadText?:
|
|
|
37
37
|
/** Render flagged phantoms as an authoritative research section, or '' if none. */
|
|
38
38
|
export declare function formatApiCorrections(phantoms: PhantomImport[]): string;
|
|
39
39
|
/**
|
|
40
|
-
* A top-of-handoff override banner (Layer B
|
|
40
|
+
* A top-of-handoff override banner (Layer B — orchestrator.ts names the two layers:
|
|
41
|
+
* A strips phantoms from the refined spec, B overrides at delivery). The check has
|
|
42
|
+
* proven —
|
|
41
43
|
* against the installed type definitions — that the spec, or a design doc it
|
|
42
44
|
* references, names an API that does NOT exist. The implementer is told the spec/doc
|
|
43
45
|
* is authoritative, so a residual affirmative that survived into the composed spec, or
|
|
@@ -57,17 +59,18 @@ export declare function formatApiOverrideBanner(phantoms: PhantomImport[]): stri
|
|
|
57
59
|
export declare function findDeliveryPhantoms(spec: string, cwd: string): Promise<PhantomImport[]>;
|
|
58
60
|
/**
|
|
59
61
|
* Subtractively rewrite every flagged phantom specifier in `text` to the canonical
|
|
60
|
-
* import the installed types prove, so NO affirmative occurrence of the
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
* it at the source and compose has nothing to contradict.
|
|
62
|
+
* import the installed types prove, so NO affirmative occurrence of the specifier
|
|
63
|
+
* survives downstream. This is the half an APPENDED correction cannot do: later
|
|
64
|
+
* phases copy identifiers verbatim, so a `bun:sql` left in the text keeps being
|
|
65
|
+
* re-stated no matter what a correction says about it.
|
|
65
66
|
*
|
|
66
|
-
* Deterministic, no LLM.
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
67
|
+
* Deterministic, no LLM. Handles the four forms a phantom takes: an import/require
|
|
68
|
+
* `from "<spec>"`, a fabricated `declare module "<spec>"`, a backticked prose
|
|
69
|
+
* mention `` `<spec>` ``, and a bare word. A path-like `./bun:sql/x` is left alone
|
|
70
|
+
* by the bare-word rule's lookbehind.
|
|
71
|
+
*
|
|
72
|
+
* Idempotent: once a specifier is replaced the literal is gone. The `declare
|
|
73
|
+
* module` replacement deliberately does not echo the spec, so a second pass over
|
|
74
|
+
* the output returns it byte for byte.
|
|
72
75
|
*/
|
|
73
76
|
export declare function rewritePhantomSpecifiers(text: string, phantoms: PhantomImport[]): string;
|
|
@@ -3,13 +3,16 @@
|
|
|
3
3
|
* task/spec but NOT actually declared by the runtime's installed types.
|
|
4
4
|
*
|
|
5
5
|
* A real `<runtime>:<sub>` builtin appears as `declare module "<runtime>:<sub>"`
|
|
6
|
-
* in the runtime's type files
|
|
7
|
-
* @types/node declares node:
|
|
8
|
-
*
|
|
9
|
-
* is the
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
6
|
+
* in the runtime's type files: bun-types declares exactly bun:bundle, bun:ffi,
|
|
7
|
+
* bun:jsc, bun:sqlite and bun:test, and @types/node declares node:assert,
|
|
8
|
+
* node:buffer, node:child_process and the rest. A specifier with no such
|
|
9
|
+
* declaration is a hallucination — the symbol, if it exists at all, lives on the
|
|
10
|
+
* base module. `bun:sql` is the canonical case: bun-types declares no such module,
|
|
11
|
+
* but `declare module "bun"` does declare `const sql: SQL`, so the correct import
|
|
12
|
+
* is from `"bun"` and a `declare module "bun:sql"` shim only hides the mistake.
|
|
13
|
+
*
|
|
14
|
+
* The check is deterministic and needs no LLM call. It can only be as good as the
|
|
15
|
+
* installed types: a runtime whose types are not on disk is skipped entirely.
|
|
13
16
|
*/
|
|
14
17
|
import * as fs from 'node:fs';
|
|
15
18
|
import * as path from 'node:path';
|
|
@@ -22,9 +25,9 @@ const DECLARE_MODULE_RE = /declare module "([^"]+)"/g;
|
|
|
22
25
|
export function extractRuntimeSpecifiers(text) {
|
|
23
26
|
const seen = new Set();
|
|
24
27
|
for (const m of text.matchAll(SPEC_RE)) {
|
|
25
|
-
//
|
|
26
|
-
//
|
|
27
|
-
// case-sensitive
|
|
28
|
+
// Kept exactly as written, including case: `BUN:SQL` and `bun:sql` are two
|
|
29
|
+
// entries here. `splitRuntimeNamespace` lowercases the runtime downstream,
|
|
30
|
+
// and the subpath is case-sensitive, so nothing is folded at this layer.
|
|
28
31
|
if (!seen.has(m[0]))
|
|
29
32
|
seen.add(m[0]);
|
|
30
33
|
}
|
|
@@ -44,8 +47,8 @@ export function classifyRuntimeImport(spec, runtime, sub, typeText) {
|
|
|
44
47
|
if (!real) {
|
|
45
48
|
const leaf = sub.split('/').pop() ?? sub;
|
|
46
49
|
// Does the base runtime module declare a symbol named like the submodule
|
|
47
|
-
// leaf
|
|
48
|
-
//
|
|
50
|
+
// leaf? Case-insensitive, which is what makes `bun:sql` find the real
|
|
51
|
+
// `const sql: SQL` and report `SQL` as the symbol to import.
|
|
49
52
|
const re = new RegExp(`\\b(?:const|class|function|let|var|namespace|interface|type)\\s+(${escapeRe(leaf)})\\b`, 'i');
|
|
50
53
|
const m = re.exec(typeText);
|
|
51
54
|
baseSymbol = m ? m[1] : null;
|
|
@@ -174,7 +177,9 @@ export function formatApiCorrections(phantoms) {
|
|
|
174
177
|
return `API CORRECTIONS\n${lines.join('\n')}`;
|
|
175
178
|
}
|
|
176
179
|
/**
|
|
177
|
-
* A top-of-handoff override banner (Layer B
|
|
180
|
+
* A top-of-handoff override banner (Layer B — orchestrator.ts names the two layers:
|
|
181
|
+
* A strips phantoms from the refined spec, B overrides at delivery). The check has
|
|
182
|
+
* proven —
|
|
178
183
|
* against the installed type definitions — that the spec, or a design doc it
|
|
179
184
|
* references, names an API that does NOT exist. The implementer is told the spec/doc
|
|
180
185
|
* is authoritative, so a residual affirmative that survived into the composed spec, or
|
|
@@ -225,18 +230,19 @@ export async function findDeliveryPhantoms(spec, cwd) {
|
|
|
225
230
|
}
|
|
226
231
|
/**
|
|
227
232
|
* Subtractively rewrite every flagged phantom specifier in `text` to the canonical
|
|
228
|
-
* import the installed types prove, so NO affirmative occurrence of the
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
*
|
|
232
|
-
*
|
|
233
|
+
* import the installed types prove, so NO affirmative occurrence of the specifier
|
|
234
|
+
* survives downstream. This is the half an APPENDED correction cannot do: later
|
|
235
|
+
* phases copy identifiers verbatim, so a `bun:sql` left in the text keeps being
|
|
236
|
+
* re-stated no matter what a correction says about it.
|
|
237
|
+
*
|
|
238
|
+
* Deterministic, no LLM. Handles the four forms a phantom takes: an import/require
|
|
239
|
+
* `from "<spec>"`, a fabricated `declare module "<spec>"`, a backticked prose
|
|
240
|
+
* mention `` `<spec>` ``, and a bare word. A path-like `./bun:sql/x` is left alone
|
|
241
|
+
* by the bare-word rule's lookbehind.
|
|
233
242
|
*
|
|
234
|
-
*
|
|
235
|
-
*
|
|
236
|
-
*
|
|
237
|
-
* skips via its lookbehind). Handles the four forms a phantom takes: an import/require
|
|
238
|
-
* `from "<spec>"`, a fabricated `declare module "<spec>"`, a backticked prose mention
|
|
239
|
-
* `` `<spec>` ``, and a bare word `<spec>`.
|
|
243
|
+
* Idempotent: once a specifier is replaced the literal is gone. The `declare
|
|
244
|
+
* module` replacement deliberately does not echo the spec, so a second pass over
|
|
245
|
+
* the output returns it byte for byte.
|
|
240
246
|
*/
|
|
241
247
|
export function rewritePhantomSpecifiers(text, phantoms) {
|
|
242
248
|
let out = text;
|