@mjasnikovs/pi-task 0.38.29 → 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 +16 -9
- package/dist/task/context-usage.js +16 -9
- 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 +36 -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 +179 -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 +29 -39
- package/dist/task/research-worker.js +37 -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 +69 -71
- package/dist/workers/pi-worker-core.js +100 -109
- 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 +21 -14
- 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
package/dist/task/yolo.js
CHANGED
|
@@ -8,13 +8,14 @@
|
|
|
8
8
|
*
|
|
9
9
|
* WHY PER-SITE, NOT ONE HOOK (the trap this module exists to avoid): the single
|
|
10
10
|
* interactive choke point is SessionUI.ask() (remote/bridge.ts) — auto-picking
|
|
11
|
-
* "index 0" inside it would be one tiny patch, and it would be wrong.
|
|
12
|
-
* verify-FAIL picker is reached
|
|
13
|
-
* attempts have
|
|
14
|
-
* central hook would pick AUTOFIX forever and
|
|
15
|
-
* break a non-converging loop. So every site
|
|
16
|
-
* called — which also means the prompt
|
|
17
|
-
*
|
|
11
|
+
* "index 0" inside it would be one tiny patch, and it would be wrong. One way the
|
|
12
|
+
* verify-FAIL picker is reached is after MAX_AUTO_AUTOFIX unattended autofix
|
|
13
|
+
* attempts have all failed, and it STILL tints AUTOFIX as recommended, because the
|
|
14
|
+
* research still recommends it. A central hook would pick AUTOFIX forever and
|
|
15
|
+
* defeat the exact cap that exists to break a non-converging loop. So every site
|
|
16
|
+
* decides for itself, BEFORE ask() is called — which also means the prompt
|
|
17
|
+
* notification (bridge.ts holds exactly one pushNotify, inside ask()) is
|
|
18
|
+
* suppressed structurally, with zero suppression code.
|
|
18
19
|
*
|
|
19
20
|
* Guard direction (repo constraint): an auto-pick may cost time, never work.
|
|
20
21
|
* Anything this module cannot stand behind — no recommendation to take, an answer
|
|
@@ -27,10 +28,11 @@
|
|
|
27
28
|
*/
|
|
28
29
|
import { getConfig } from '../config/config.js';
|
|
29
30
|
/**
|
|
30
|
-
* Visible provenance marker on
|
|
31
|
-
* task
|
|
32
|
-
* artifacts must never mistake an auto-pick for a human decision
|
|
33
|
-
*
|
|
31
|
+
* Visible provenance marker on the artifacts an auto-pick writes: gate trail lines
|
|
32
|
+
* and the task file's Q&A record (qa-transcript.ts `QA_PROVENANCE`). A later audit
|
|
33
|
+
* reading only the artifacts must never mistake an auto-pick for a human decision.
|
|
34
|
+
* The debt ledger does not use this stamp — it carries the `'yolo-accepted'` ORIGIN
|
|
35
|
+
* instead, which is stronger: a typed field rather than text.
|
|
34
36
|
*/
|
|
35
37
|
export const YOLO_STAMP = '(YOLO)';
|
|
36
38
|
/** Is unattended auto-pick on for this run? The ONLY config read in this module. */
|
|
@@ -38,16 +40,17 @@ export function isYoloMode() {
|
|
|
38
40
|
return getConfig().yoloMode;
|
|
39
41
|
}
|
|
40
42
|
/**
|
|
41
|
-
* The clarify/grill policy: take the RECOMMENDED option —
|
|
42
|
-
*
|
|
43
|
-
*
|
|
43
|
+
* The clarify/grill policy: take the RECOMMENDED option — `suggested`, falling
|
|
44
|
+
* back to the B-side `alt` when a fork offers only that. Recommended is positional
|
|
45
|
+
* for a human too: bridge.ts marks card index 0, and question-box.ts tints the
|
|
46
|
+
* marked card green.
|
|
44
47
|
*
|
|
45
48
|
* `unsafe` is the step-aside channel: a caller that KNOWS the recommendation must
|
|
46
|
-
* not be auto-accepted passes why, and the question is skipped instead.
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* invention the demotion
|
|
50
|
-
*
|
|
49
|
+
* not be auto-accepted passes why, and the question is skipped instead. The only
|
|
50
|
+
* thing that supplies it is `yoloPickAutoAnswer` below, on the anti-synthesis
|
|
51
|
+
* demotion — an answer proven to name an unverified API identifier. Auto-accepting
|
|
52
|
+
* that would re-promote exactly the invention the demotion exists to stop, so a
|
|
53
|
+
* machine may never take it; a human still can.
|
|
51
54
|
*/
|
|
52
55
|
export function yoloPickAnswer(enabled, opts) {
|
|
53
56
|
if (!enabled)
|
|
@@ -62,12 +65,12 @@ export function yoloPickAnswer(enabled, opts) {
|
|
|
62
65
|
return { kind: 'answer', answer: pick };
|
|
63
66
|
}
|
|
64
67
|
/**
|
|
65
|
-
* The same policy expressed over an {@link AutoAnswer}, for the grill site.
|
|
66
|
-
*
|
|
67
|
-
* unknown
|
|
68
|
-
* best-effort recommendation, which is precisely what a human would be
|
|
69
|
-
* the green card. The variants are told apart by
|
|
70
|
-
*
|
|
68
|
+
* The same policy expressed over an {@link AutoAnswer}, for the grill site. Of the
|
|
69
|
+
* four `reason` tags an unknown can carry — `api-synthesis`, `integration`,
|
|
70
|
+
* `threw`, `model-unknown` — only ANTI-SYNTHESIS is unsafe. The other three carry
|
|
71
|
+
* an ordinary best-effort recommendation, which is precisely what a human would be
|
|
72
|
+
* shown as the green card. The variants are told apart by that tag, never by
|
|
73
|
+
* pattern-matching the answer text.
|
|
71
74
|
*/
|
|
72
75
|
export function yoloPickAutoAnswer(enabled, auto) {
|
|
73
76
|
if (!enabled)
|
|
@@ -85,25 +88,27 @@ export function yoloPickAutoAnswer(enabled, auto) {
|
|
|
85
88
|
/**
|
|
86
89
|
* The verify-FAIL picker policy: ACCEPT (and write a debt), never AUTOFIX.
|
|
87
90
|
*
|
|
88
|
-
* Not a preference — a bound.
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
91
|
+
* Not a preference — a bound. task-gates.ts consults this only after its own
|
|
92
|
+
* unattended paths are exhausted: `autoFixNow` has run AUTOFIX up to
|
|
93
|
+
* MAX_AUTO_AUTOFIX times while the research recommended it, and the YOLO rescue has
|
|
94
|
+
* spent its one attempt on an ACCEPT recommendation with an untouched budget.
|
|
95
|
+
* Answering AUTOFIX here would restart that budget from the very site that proves
|
|
96
|
+
* it ran out. So YOLO takes the terminal option and records the defect under the
|
|
97
|
+
* `'yolo-accepted'` debt origin, which a human decision never produces.
|
|
94
98
|
*/
|
|
95
99
|
export function yoloVerifyResolution(enabled) {
|
|
96
100
|
return enabled ? { action: 'accept' } : null;
|
|
97
101
|
}
|
|
98
102
|
/**
|
|
99
|
-
* The final-integration-gate policy: keep autofixing WHILE the picker still
|
|
100
|
-
*
|
|
101
|
-
* then leave the
|
|
103
|
+
* The final-integration-gate policy: keep autofixing WHILE the picker still offers
|
|
104
|
+
* that card — run-final-gate.ts withdraws it from `options` after
|
|
105
|
+
* MAX_FINAL_GATE_AUTOFIX attempts, and passes that as `canAutofix` — then leave the
|
|
106
|
+
* run FAILED.
|
|
102
107
|
*
|
|
103
108
|
* 'leave', not 'accept': an unattended run that cannot fix the whole-repo gate has
|
|
104
|
-
* not produced a working project
|
|
105
|
-
* resume can re-enter
|
|
106
|
-
*
|
|
109
|
+
* not produced a working project. The honest terminal state is a failed run a
|
|
110
|
+
* resume can re-enter. Accepting instead would make the failure read as a success
|
|
111
|
+
* in every artifact that survives the run.
|
|
107
112
|
*/
|
|
108
113
|
export function yoloFinalGateChoice(enabled, canAutofix) {
|
|
109
114
|
if (!enabled)
|
|
@@ -3,31 +3,19 @@
|
|
|
3
3
|
* content it was given cannot answer the question, and the one place both halves
|
|
4
4
|
* of that contract live.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
6
|
+
* WHY ONE MODULE. The sentinel has two halves that MUST agree: the sentence a
|
|
7
7
|
* prompt instructs the child to emit, and the predicate that later recognises it.
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* that one regex; the other three never learned.
|
|
20
|
-
*
|
|
21
|
-
* And it was still live on the fetch channel. pi-worker-docs documents F-2(e) at
|
|
22
|
-
* length — an "unclear" non-answer exits 0, so it was memoised into the research
|
|
23
|
-
* cache and re-served to every sibling task (52 of run 15's cached entries were
|
|
24
|
-
* "unclear" with hitCache true), one dead end paid for many times, with
|
|
25
|
-
* escalation unable to re-fire because the miss never recurred. `pi-worker-fetch`
|
|
26
|
-
* cached on `childExitCode === 0` alone, so "unclear from this page" reproduced
|
|
27
|
-
* exactly that failure on pages instead of packages.
|
|
28
|
-
*
|
|
29
|
-
* With emitter and matcher reading one table, a fourth corpus is one row and
|
|
30
|
-
* cannot be half-wired.
|
|
8
|
+
* Held apart, each corpus grows its own phrasing and its own regex, and a regex
|
|
9
|
+
* that names only some of the corpora scores the rest as real answers — silently,
|
|
10
|
+
* because a non-answer is well-formed output. Here `buildExtractionPrompt` and
|
|
11
|
+
* `ABSTENTION_RE` are both built from `NOUNS`, so a new corpus is one row in that
|
|
12
|
+
* table and cannot be half-wired.
|
|
13
|
+
*
|
|
14
|
+
* The consumers split two ways. It must not be SCORED as an answer
|
|
15
|
+
* (typeonly-log.ts, task/type-only-answer.ts) and it must not be CACHED as one
|
|
16
|
+
* (`docsCacheable` in pi-worker-docs.ts, `fetchCacheable` in pi-worker-fetch.ts) —
|
|
17
|
+
* a non-answer exits 0 like any other, so a cache keyed on exit code alone
|
|
18
|
+
* memoises the dead end and re-serves it to every later sibling.
|
|
31
19
|
*/
|
|
32
20
|
/** The content a focused extractor was pointed at. One row per corpus. */
|
|
33
21
|
export type AbstentionKind = 'package' | 'project' | 'page';
|
|
@@ -37,28 +25,20 @@ export type AbstentionKind = 'package' | 'project' | 'page';
|
|
|
37
25
|
* describing what the prompts actually ask for.
|
|
38
26
|
*/
|
|
39
27
|
export declare function abstentionSentence(kind: AbstentionKind): string;
|
|
40
|
-
/**
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
* Callers use this for two different decisions and both matter: it must not be
|
|
44
|
-
* SCORED as an answer (typeonly-log, type-only-answer), and it must not be
|
|
45
|
-
* CACHED as one (pi-worker-docs, pi-worker-fetch) — a memoised non-answer is
|
|
46
|
-
* re-served to every later sibling and permanently suppresses escalation.
|
|
47
|
-
*/
|
|
28
|
+
/** True when the child declined to answer rather than answering. See the header
|
|
29
|
+
* for the two decisions this drives. */
|
|
48
30
|
export declare function isAbstention(text: string): boolean;
|
|
49
31
|
/**
|
|
50
|
-
* The extraction prompt every corpus shares.
|
|
32
|
+
* The extraction prompt every corpus shares — docs-core.ts and docs-project.ts
|
|
33
|
+
* both build theirs here, differing only in the nouns this signature takes.
|
|
51
34
|
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* had to guess at. Building the prompt from the same table that recognises its
|
|
56
|
-
* output is the whole point: rule 4 tells the child to write EXACTLY the sentence
|
|
57
|
-
* `isAbstention` looks for, by construction.
|
|
35
|
+
* Rule 4 interpolates `abstentionSentence`, so the sentence the child is told to
|
|
36
|
+
* write is by construction the sentence `isAbstention` looks for. Typing the words
|
|
37
|
+
* instead is what lets the two drift.
|
|
58
38
|
*
|
|
59
39
|
* `subject` is the prose noun ("npm package", "local project's source code");
|
|
60
|
-
* `tag` names both the identity element and the content element,
|
|
61
|
-
* because rules 1, 2 and 4 all refer to `<{tag}-content>`.
|
|
40
|
+
* `tag` names both the identity element and the content element, and the two must
|
|
41
|
+
* match because rules 1, 2 and 4 all refer to `<{tag}-content>`.
|
|
62
42
|
*/
|
|
63
43
|
export declare function buildExtractionPrompt(opts: {
|
|
64
44
|
kind: AbstentionKind;
|
|
@@ -3,31 +3,19 @@
|
|
|
3
3
|
* content it was given cannot answer the question, and the one place both halves
|
|
4
4
|
* of that contract live.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
6
|
+
* WHY ONE MODULE. The sentinel has two halves that MUST agree: the sentence a
|
|
7
7
|
* prompt instructs the child to emit, and the predicate that later recognises it.
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* Held apart, each corpus grows its own phrasing and its own regex, and a regex
|
|
9
|
+
* that names only some of the corpora scores the rest as real answers — silently,
|
|
10
|
+
* because a non-answer is well-formed output. Here `buildExtractionPrompt` and
|
|
11
|
+
* `ABSTENTION_RE` are both built from `NOUNS`, so a new corpus is one row in that
|
|
12
|
+
* table and cannot be half-wired.
|
|
11
13
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* That already cost one bug, recorded in typeonly-log: matching only the first
|
|
18
|
-
* silently scored every PROJECT abstention as a valid answer. The fix went into
|
|
19
|
-
* that one regex; the other three never learned.
|
|
20
|
-
*
|
|
21
|
-
* And it was still live on the fetch channel. pi-worker-docs documents F-2(e) at
|
|
22
|
-
* length — an "unclear" non-answer exits 0, so it was memoised into the research
|
|
23
|
-
* cache and re-served to every sibling task (52 of run 15's cached entries were
|
|
24
|
-
* "unclear" with hitCache true), one dead end paid for many times, with
|
|
25
|
-
* escalation unable to re-fire because the miss never recurred. `pi-worker-fetch`
|
|
26
|
-
* cached on `childExitCode === 0` alone, so "unclear from this page" reproduced
|
|
27
|
-
* exactly that failure on pages instead of packages.
|
|
28
|
-
*
|
|
29
|
-
* With emitter and matcher reading one table, a fourth corpus is one row and
|
|
30
|
-
* cannot be half-wired.
|
|
14
|
+
* The consumers split two ways. It must not be SCORED as an answer
|
|
15
|
+
* (typeonly-log.ts, task/type-only-answer.ts) and it must not be CACHED as one
|
|
16
|
+
* (`docsCacheable` in pi-worker-docs.ts, `fetchCacheable` in pi-worker-fetch.ts) —
|
|
17
|
+
* a non-answer exits 0 like any other, so a cache keyed on exit code alone
|
|
18
|
+
* memoises the dead end and re-serves it to every later sibling.
|
|
31
19
|
*/
|
|
32
20
|
/**
|
|
33
21
|
* The noun each corpus calls itself IN THE PROMPT. The sentinel is built from
|
|
@@ -51,40 +39,31 @@ export function abstentionSentence(kind) {
|
|
|
51
39
|
* Matches any corpus's abstention. Built from the same table the prompts read, so
|
|
52
40
|
* adding a corpus cannot leave a matcher behind.
|
|
53
41
|
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
* here would miss the real non-answers this exists to catch.
|
|
42
|
+
* A SUBSTRING match, unlike fetch-core's separate `not covered by this page`
|
|
43
|
+
* sentinel, which is anchored. The instructions differ, so the matchers must. The
|
|
44
|
+
* "not covered" rule asks for a partial answer that NAMES what is missing, so a
|
|
45
|
+
* sourced answer legitimately contains the phrase and only an anchored match keeps
|
|
46
|
+
* it from being filed as a coverage miss. Rule 4 below asks for this sentinel
|
|
47
|
+
* INSTEAD of an answer, so nothing sourced can contain it, and a substring match
|
|
48
|
+
* still catches a child that wraps the sentence in an explanation.
|
|
62
49
|
*/
|
|
63
50
|
const ABSTENTION_RE = new RegExp(`unclear\\s+from\\s+this\\s+(${Object.values(NOUNS).join('|')})\\b`, 'i');
|
|
64
|
-
/**
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
* Callers use this for two different decisions and both matter: it must not be
|
|
68
|
-
* SCORED as an answer (typeonly-log, type-only-answer), and it must not be
|
|
69
|
-
* CACHED as one (pi-worker-docs, pi-worker-fetch) — a memoised non-answer is
|
|
70
|
-
* re-served to every later sibling and permanently suppresses escalation.
|
|
71
|
-
*/
|
|
51
|
+
/** True when the child declined to answer rather than answering. See the header
|
|
52
|
+
* for the two decisions this drives. */
|
|
72
53
|
export function isAbstention(text) {
|
|
73
54
|
return ABSTENTION_RE.test(text);
|
|
74
55
|
}
|
|
75
56
|
/**
|
|
76
|
-
* The extraction prompt every corpus shares.
|
|
57
|
+
* The extraction prompt every corpus shares — docs-core.ts and docs-project.ts
|
|
58
|
+
* both build theirs here, differing only in the nouns this signature takes.
|
|
77
59
|
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
* had to guess at. Building the prompt from the same table that recognises its
|
|
82
|
-
* output is the whole point: rule 4 tells the child to write EXACTLY the sentence
|
|
83
|
-
* `isAbstention` looks for, by construction.
|
|
60
|
+
* Rule 4 interpolates `abstentionSentence`, so the sentence the child is told to
|
|
61
|
+
* write is by construction the sentence `isAbstention` looks for. Typing the words
|
|
62
|
+
* instead is what lets the two drift.
|
|
84
63
|
*
|
|
85
64
|
* `subject` is the prose noun ("npm package", "local project's source code");
|
|
86
|
-
* `tag` names both the identity element and the content element,
|
|
87
|
-
* because rules 1, 2 and 4 all refer to `<{tag}-content>`.
|
|
65
|
+
* `tag` names both the identity element and the content element, and the two must
|
|
66
|
+
* match because rules 1, 2 and 4 all refer to `<{tag}-content>`.
|
|
88
67
|
*/
|
|
89
68
|
export function buildExtractionPrompt(opts) {
|
|
90
69
|
const { tag } = opts;
|
|
@@ -7,9 +7,10 @@ export interface BraveSearchOpts {
|
|
|
7
7
|
timeoutMs?: number;
|
|
8
8
|
signal?: AbortSignal;
|
|
9
9
|
/**
|
|
10
|
-
* Injectable fetch.
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* Injectable fetch. All three search providers accept one. Brave gets the most
|
|
11
|
+
* out of it: its status ladder is the widest — three branches (auth,
|
|
12
|
+
* rate-limit, http) against DuckDuckGo's two and Exa's one — and
|
|
13
|
+
* http-request.test.ts drives 401/403/429/503 straight through this seam.
|
|
13
14
|
*/
|
|
14
15
|
fetchImpl?: FetchLike;
|
|
15
16
|
}
|
|
@@ -27,8 +27,11 @@ export async function braveSearch(query, opts) {
|
|
|
27
27
|
'x-subscription-token': opts.apiKey
|
|
28
28
|
}
|
|
29
29
|
}, async (response) => {
|
|
30
|
-
//
|
|
31
|
-
//
|
|
30
|
+
// A rejected key and a rate limit are different problems for the
|
|
31
|
+
// user, so each throws its own MESSAGE rather than the generic
|
|
32
|
+
// `Brave Search HTTP <n>`. That message is what reaches the user:
|
|
33
|
+
// search-core turns any BraveSearchError into
|
|
34
|
+
// `{kind: 'error', message}` and passes the text through verbatim.
|
|
32
35
|
if (response.status === 401 || response.status === 403) {
|
|
33
36
|
throw new BraveSearchError(`Brave Search rejected the key (HTTP ${response.status}). Check BRAVE_SEARCH_API_KEY.`, 'auth', response.status);
|
|
34
37
|
}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* One-line startup hint shown when Brave is the selected search provider but
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* One-line startup hint shown when Brave is the selected search provider but no
|
|
3
|
+
* key is configured. Brave is the only provider that needs one:
|
|
4
|
+
* `SEARCH_PROVIDER_KEY_ENV` in search-types.ts gives exa and ddg an empty var
|
|
5
|
+
* list, so no hint can render for them.
|
|
6
|
+
*
|
|
7
|
+
* It never blocks work. registerSessionHint renders it only in TUI mode and
|
|
8
|
+
* clears it on the first raw keystroke.
|
|
6
9
|
*/
|
|
7
10
|
import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';
|
|
8
11
|
export declare function registerBraveKeyWarning(pi: ExtensionAPI): void;
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* One-line startup hint shown when Brave is the selected search provider but
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* One-line startup hint shown when Brave is the selected search provider but no
|
|
3
|
+
* key is configured. Brave is the only provider that needs one:
|
|
4
|
+
* `SEARCH_PROVIDER_KEY_ENV` in search-types.ts gives exa and ddg an empty var
|
|
5
|
+
* list, so no hint can render for them.
|
|
6
|
+
*
|
|
7
|
+
* It never blocks work. registerSessionHint renders it only in TUI mode and
|
|
8
|
+
* clears it on the first raw keystroke.
|
|
6
9
|
*/
|
|
7
10
|
import { getConfig } from '../config/config.js';
|
|
8
11
|
import { registerSessionHint } from './session-hint.js';
|
|
@@ -10,12 +13,21 @@ const WIDGET_KEY = 'pi-task-brave-warning';
|
|
|
10
13
|
const WARNING = '⚠ pi-task: search provider is Brave but BRAVE_SEARCH_API_KEY is not set — web search '
|
|
11
14
|
+ 'is disabled. Get a free key at https://api.search.brave.com/app/keys or switch '
|
|
12
15
|
+ 'provider in /task-config';
|
|
13
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* The same two env vars, in the same order, that `SEARCH_PROVIDER_KEY_ENV` lists
|
|
18
|
+
* for brave — a second copy, because this runs at session_start with no provider
|
|
19
|
+
* in hand.
|
|
20
|
+
*
|
|
21
|
+
* The two are not byte-equivalent. `??` skips only null/undefined, while
|
|
22
|
+
* `searchProviderKey` skips any falsy value, so `BRAVE_SEARCH_API_KEY=""` with
|
|
23
|
+
* `BRAVE_API_KEY` set makes search work while this still warns. Erring toward
|
|
24
|
+
* showing the hint is the harmless direction.
|
|
25
|
+
*/
|
|
14
26
|
function hasBraveKey() {
|
|
15
27
|
return Boolean(process.env.BRAVE_SEARCH_API_KEY ?? process.env.BRAVE_API_KEY);
|
|
16
28
|
}
|
|
17
29
|
export function registerBraveKeyWarning(pi) {
|
|
18
|
-
// Only the brave provider can be misconfigured
|
|
19
|
-
//
|
|
30
|
+
// Only the brave provider can be misconfigured, so returning null — say
|
|
31
|
+
// nothing — is the answer for every other provider and for a key already set.
|
|
20
32
|
registerSessionHint(pi, WIDGET_KEY, () => getConfig().searchProvider !== 'brave' || hasBraveKey() ? null : { text: WARNING });
|
|
21
33
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Web search
|
|
2
|
+
* Web search by scraping DuckDuckGo's HTML endpoint. No API key: search-types.ts
|
|
3
|
+
* gives `ddg` an empty env-var list.
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* are dropped.
|
|
5
|
+
* `html.duckduckgo.com/html/?q=…` serves an ordinary result page that linkedom
|
|
6
|
+
* parses. Its result links are wrapped in a `//duckduckgo.com/l/?uddg=<encoded>`
|
|
7
|
+
* redirect, which `unwrapDdgRedirect` below decodes so callers get the
|
|
8
|
+
* destination URL rather than the tracker.
|
|
9
9
|
*/
|
|
10
10
|
import { type FetchLike } from './http-request.js';
|
|
11
11
|
import type { SearchResult } from './search-types.js';
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Web search
|
|
2
|
+
* Web search by scraping DuckDuckGo's HTML endpoint. No API key: search-types.ts
|
|
3
|
+
* gives `ddg` an empty env-var list.
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* are dropped.
|
|
5
|
+
* `html.duckduckgo.com/html/?q=…` serves an ordinary result page that linkedom
|
|
6
|
+
* parses. Its result links are wrapped in a `//duckduckgo.com/l/?uddg=<encoded>`
|
|
7
|
+
* redirect, which `unwrapDdgRedirect` below decodes so callers get the
|
|
8
|
+
* destination URL rather than the tracker.
|
|
9
9
|
*/
|
|
10
10
|
import { parseHTML } from 'linkedom';
|
|
11
11
|
import { httpRequest, HttpRequestError } from './http-request.js';
|
|
@@ -13,7 +13,9 @@ const DDG_ENDPOINT = 'https://html.duckduckgo.com/html/';
|
|
|
13
13
|
const DEFAULT_COUNT = 10;
|
|
14
14
|
const MAX_COUNT = 20;
|
|
15
15
|
const DEFAULT_TIMEOUT_MS = 15_000;
|
|
16
|
-
//
|
|
16
|
+
// Required. Without a browser-ish UA the same request answers HTTP 202 with a
|
|
17
|
+
// different page — no `result__a` rows at all, and the words "anomaly" and
|
|
18
|
+
// "challenge" in the body — so the parser would return an empty list.
|
|
17
19
|
const USER_AGENT = 'Mozilla/5.0 (X11; Linux x86_64; rv:127.0) Gecko/20100101 Firefox/127.0';
|
|
18
20
|
export class DdgSearchError extends Error {
|
|
19
21
|
kind;
|
|
@@ -39,7 +41,8 @@ export async function ddgSearch(query, opts = {}) {
|
|
|
39
41
|
accept: 'text/html'
|
|
40
42
|
}
|
|
41
43
|
}, async (response) => {
|
|
42
|
-
//
|
|
44
|
+
// 429 and 403 both mean throttling here, so they share one `kind` and
|
|
45
|
+
// one message rather than falling into the generic HTTP branch.
|
|
43
46
|
if (response.status === 429 || response.status === 403) {
|
|
44
47
|
throw new DdgSearchError(`DuckDuckGo is rate-limiting this client (HTTP ${response.status}). Try again in a moment.`, 'rate-limit', response.status);
|
|
45
48
|
}
|
|
@@ -67,7 +70,9 @@ export function parseDdgHtml(html) {
|
|
|
67
70
|
continue;
|
|
68
71
|
const href = anchor.getAttribute('href');
|
|
69
72
|
const targetUrl = href === null ? null : unwrapDdgRedirect(href);
|
|
70
|
-
// A row whose link never leaves duckduckgo.com is an ad
|
|
73
|
+
// A row whose link never leaves duckduckgo.com is an ad or a module, not a
|
|
74
|
+
// hit. This also drops an `l/?` href carrying no `uddg`, and one whose
|
|
75
|
+
// `uddg` does not decode to a URL.
|
|
71
76
|
if (targetUrl === null)
|
|
72
77
|
continue;
|
|
73
78
|
const title = collapse(anchor.textContent ?? '');
|
|
@@ -80,9 +85,10 @@ export function parseDdgHtml(html) {
|
|
|
80
85
|
return results;
|
|
81
86
|
}
|
|
82
87
|
/**
|
|
83
|
-
* Result hrefs look like `//duckduckgo.com/l/?uddg=<encoded-destination>&rut
|
|
84
|
-
*
|
|
85
|
-
* that
|
|
88
|
+
* Result hrefs look like `//duckduckgo.com/l/?uddg=<encoded-destination>&rut=…`.
|
|
89
|
+
* Returns the decoded destination, a non-DDG href unchanged, or null for a link
|
|
90
|
+
* that stays on duckduckgo.com — no `uddg`, an undecodable one, or an unparseable
|
|
91
|
+
* href.
|
|
86
92
|
*/
|
|
87
93
|
function unwrapDdgRedirect(href) {
|
|
88
94
|
let parsed;
|
|
@@ -35,13 +35,16 @@ CREATE TRIGGER IF NOT EXISTS chunks_ad AFTER DELETE ON chunks BEGIN
|
|
|
35
35
|
END;
|
|
36
36
|
`;
|
|
37
37
|
const req = createRequire(import.meta.url);
|
|
38
|
+
// Branch on the RUNTIME, not on a try/catch: each builtin exists in exactly one
|
|
39
|
+
// of them. Requiring `bun:sqlite` under node throws MODULE_NOT_FOUND, and
|
|
40
|
+
// `node:sqlite` under bun throws ERR_UNKNOWN_BUILTIN_MODULE. `createRequire`
|
|
41
|
+
// keeps both specifiers out of the static import graph, so neither bundler nor
|
|
42
|
+
// type-checker has to resolve the one that is absent.
|
|
38
43
|
function openDb(dbPath) {
|
|
39
44
|
if (typeof globalThis.Bun !== 'undefined') {
|
|
40
|
-
// Bun runtime: use bun:sqlite
|
|
41
45
|
const { Database } = req('bun:sqlite');
|
|
42
46
|
return new Database(dbPath);
|
|
43
47
|
}
|
|
44
|
-
// Node.js runtime: use node:sqlite
|
|
45
48
|
const { DatabaseSync } = req('node:sqlite');
|
|
46
49
|
return new DatabaseSync(dbPath);
|
|
47
50
|
}
|
|
@@ -1,29 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* docs-chunk — cutting source text into retrievable chunks, for
|
|
3
|
-
* docs Worker tool indexes.
|
|
2
|
+
* docs-chunk — cutting source text into retrievable chunks, for both corpora the
|
|
3
|
+
* docs Worker tool indexes: an npm package's `.d.ts` + README (docs-index.ts),
|
|
4
|
+
* and the local project's own `.ts`/`.tsx` (docs-project.ts).
|
|
4
5
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* `chunkDts`/`chunkTs` bodies, and byte-identical `splitAtMatches`/`sliceBytes`,
|
|
9
|
-
* in `docs-index.ts` and `docs-project.ts`. One copy had tests; the other
|
|
10
|
-
* (`docs-project.ts`, 319 lines) had no test file at all and was only ever
|
|
11
|
-
* reached incidentally through the worker's own suite.
|
|
6
|
+
* The chunk boundary is load-bearing for retrieval: a chunk that splits a
|
|
7
|
+
* declaration in half matches on neither half's terms, so one boundary rule for
|
|
8
|
+
* both corpora is the point of this module.
|
|
12
9
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* versions; the project is a cwd key with a max-mtime version and drops its old
|
|
20
|
-
* ones on every re-index), and collapsing them would change one of those
|
|
21
|
-
* behaviours rather than describe them.
|
|
10
|
+
* What is NOT shared: the two INDEX bodies. They key on genuinely different
|
|
11
|
+
* provenance. A package is `(name, version)` with a content hash, and re-indexing
|
|
12
|
+
* runs `DELETE FROM chunks WHERE name = ? AND version = ?`, so older versions
|
|
13
|
+
* survive. The project is a cwd-hash name with a max-mtime version, and
|
|
14
|
+
* re-indexing runs `DELETE FROM chunks WHERE name = ?`, dropping every older
|
|
15
|
+
* version. Collapsing them would change one of those behaviours, not describe it.
|
|
22
16
|
*/
|
|
23
17
|
/**
|
|
24
|
-
* Chunk ceiling.
|
|
25
|
-
*
|
|
26
|
-
*
|
|
18
|
+
* Chunk ceiling, in UTF-8 bytes. Sized against the retrieval budget: retrieved
|
|
19
|
+
* chunks are assembled into `RETRIEVE_CONTENT_BUDGET` (24,000 characters) before
|
|
20
|
+
* going to the extraction child, so this caps any one chunk at about a third of
|
|
21
|
+
* what the child will ever see.
|
|
27
22
|
*/
|
|
28
23
|
export declare const MAX_CHUNK_BYTES: number;
|
|
29
24
|
/**
|
|
@@ -44,30 +39,28 @@ export declare function splitAtMatches(text: string, re: RegExp): string[];
|
|
|
44
39
|
* Cut a string into pieces of at most `maxBytes` UTF-8 bytes, never splitting a
|
|
45
40
|
* character.
|
|
46
41
|
*
|
|
47
|
-
* The cut point is walked BACK to a UTF-8 lead byte first.
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
* `€€€�€€€�…` — corrupted, and one character shorter per slice.
|
|
42
|
+
* The cut point is walked BACK to a UTF-8 lead byte first. Cutting at exactly
|
|
43
|
+
* `maxBytes` and letting `Buffer.toString('utf8')` tidy up does not work: decoding
|
|
44
|
+
* a buffer that ends mid-character yields U+FFFD. That replacement is 3 bytes
|
|
45
|
+
* wide, so the decoded slice measures LONGER than the cut — a 100-byte cut of
|
|
46
|
+
* `€`-dense text decodes to 102 bytes — and advancing by the decoded length then
|
|
47
|
+
* skips past the straddling character entirely.
|
|
54
48
|
*
|
|
55
49
|
* It matters beyond looking wrong: a chunk is quoted back as an `<excerpt>` and
|
|
56
|
-
* checked verbatim against the source, and an excerpt carrying
|
|
57
|
-
* character can never be found, so the answer is flagged as a
|
|
58
|
-
* hallucination. Only reachable on non-ASCII text past the
|
|
59
|
-
* which is why two copies of it survived untested.
|
|
50
|
+
* checked verbatim against the source (`excerptVerified`), and an excerpt carrying
|
|
51
|
+
* a replacement character can never be found, so the answer is flagged as a
|
|
52
|
+
* possible hallucination. Only reachable on non-ASCII text past the chunk ceiling.
|
|
60
53
|
*/
|
|
61
54
|
export declare function sliceBytes(s: string, maxBytes: number): string[];
|
|
62
55
|
/**
|
|
63
56
|
* Chunk a declaration file (`.d.ts`, `.ts`, `.tsx`), one chunk per declaration,
|
|
64
57
|
* each labelled with the file it came from.
|
|
65
58
|
*
|
|
66
|
-
* `relPath` is a MODEL-FACING label and is used exactly as given
|
|
67
|
-
* normalises it to POSIX
|
|
68
|
-
*
|
|
69
|
-
* filesystem, so neither
|
|
70
|
-
* with the caller that has a reason for it.
|
|
59
|
+
* `relPath` is a MODEL-FACING label and is used exactly as given. docs-index.ts
|
|
60
|
+
* normalises it to POSIX (`.replace(/\\/g, '/')`) so a package index is identical
|
|
61
|
+
* across platforms; docs-project.ts passes `path.relative` through with the native
|
|
62
|
+
* separator. It is never re-joined to the filesystem, so neither is wrong — this
|
|
63
|
+
* leaves the choice with the caller that has a reason for it.
|
|
71
64
|
*/
|
|
72
65
|
export declare function chunkDeclarations(content: string, relPath: string): string[];
|
|
73
66
|
/** Chunk a README, one chunk per top-level section, each labelled by heading. */
|