@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/remote/push.js
CHANGED
|
@@ -2,23 +2,22 @@ import { appendFileSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
|
|
|
2
2
|
import os from 'node:os';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import webpush from 'web-push';
|
|
5
|
-
/** Resolve the XDG data-home base.
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
/** Resolve the XDG data-home base. Same env-or-homedir shape the docs cache uses
|
|
6
|
+
* (workers/docs-core.ts), but rooted at data-home rather than cache-home: what
|
|
7
|
+
* lives under here is not reconstructible by re-fetching. */
|
|
8
8
|
function dataHome() {
|
|
9
9
|
return process.env.XDG_DATA_HOME?.trim() || path.join(os.homedir(), '.local', 'share');
|
|
10
10
|
}
|
|
11
|
-
/** Where the VAPID keypair is persisted
|
|
12
|
-
*
|
|
11
|
+
/** Where the VAPID keypair is persisted. The browser subscribes with this
|
|
12
|
+
* public key as its applicationServerKey (see ui-script.ts), so the pair has to
|
|
13
|
+
* outlive the process that created it. */
|
|
13
14
|
export function vapidStorePath() {
|
|
14
15
|
return path.join(dataHome(), 'pi-task', 'vapid.json');
|
|
15
16
|
}
|
|
16
17
|
/** Where browser push subscriptions are mirrored to disk (next to vapid.json).
|
|
17
|
-
* Without this
|
|
18
|
-
* device
|
|
19
|
-
*
|
|
20
|
-
* is no longer useful. The in-memory store stays authoritative within a process;
|
|
21
|
-
* this is its durable mirror. */
|
|
18
|
+
* Without this a server restart starts with an empty store and reaches nobody
|
|
19
|
+
* until every device happens to re-register. The in-memory store stays
|
|
20
|
+
* authoritative within a process; this is its durable mirror. */
|
|
22
21
|
export function subscriptionsStorePath() {
|
|
23
22
|
return path.join(dataHome(), 'pi-task', 'subscriptions.json');
|
|
24
23
|
}
|
|
@@ -27,16 +26,17 @@ export function subscriptionsStorePath() {
|
|
|
27
26
|
export function pushLogPath() {
|
|
28
27
|
return process.env.PI_REMOTE_PUSH_LOG?.trim() || '/tmp/pi-task-push.log';
|
|
29
28
|
}
|
|
30
|
-
/** VAPID JWT `sub` claim.
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
29
|
+
/** VAPID JWT `sub` claim. web-push's validateSubject requires a parseable URL
|
|
30
|
+
* whose protocol is `https:` or `mailto:` and throws otherwise, so the default
|
|
31
|
+
* is a real https URL. Override with PI_REMOTE_PUSH_SUBJECT (e.g. your own
|
|
32
|
+
* `mailto:you@domain.com`). */
|
|
34
33
|
export function pushSubject() {
|
|
35
34
|
return process.env.PI_REMOTE_PUSH_SUBJECT?.trim() || 'https://github.com/mjasnikovs/pi-task';
|
|
36
35
|
}
|
|
37
36
|
/** Append a timestamped diagnostic line — but only when PI_REMOTE_PUSH_DEBUG is
|
|
38
|
-
* set, so it stays silent (and test-safe) by default.
|
|
39
|
-
*
|
|
37
|
+
* set, so it stays silent (and test-safe) by default. deliver() swallows every
|
|
38
|
+
* send failure, so this is the only way to see the push service's status
|
|
39
|
+
* codes. */
|
|
40
40
|
export function logPush(line) {
|
|
41
41
|
if (!process.env.PI_REMOTE_PUSH_DEBUG)
|
|
42
42
|
return;
|
|
@@ -49,8 +49,8 @@ export function logPush(line) {
|
|
|
49
49
|
// diagnostics are best-effort; never let logging break a push
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
|
-
/** Short host label for an endpoint, for readable logs
|
|
53
|
-
*
|
|
52
|
+
/** Short host label for an endpoint, for readable logs — a push endpoint's path
|
|
53
|
+
* carries the device's own subscription id and does not belong in a log. */
|
|
54
54
|
function endpointHost(sub) {
|
|
55
55
|
try {
|
|
56
56
|
return new URL(sub.endpoint ?? '').host;
|
|
@@ -82,10 +82,9 @@ export function loadOrCreateVapidKeys(file = vapidStorePath()) {
|
|
|
82
82
|
return keys;
|
|
83
83
|
}
|
|
84
84
|
// In-memory subscription store, keyed by endpoint. Persisted on globalThis so it
|
|
85
|
-
// survives
|
|
86
|
-
//
|
|
87
|
-
//
|
|
88
|
-
// devices that need server push are backgrounded/suspended and won't reload.
|
|
85
|
+
// survives the module being re-evaluated (same pattern as broadcast.ts), and
|
|
86
|
+
// mirrored to disk (subscriptionsStorePath) so it also survives a full process
|
|
87
|
+
// restart — a re-subscribe on page load only helps devices whose page is open.
|
|
89
88
|
const g = globalThis;
|
|
90
89
|
const freshStore = !g.__piRemoteSubs;
|
|
91
90
|
if (!g.__piRemoteSubs)
|
|
@@ -144,8 +143,9 @@ export function clearSubscriptions() {
|
|
|
144
143
|
subs.clear();
|
|
145
144
|
saveSubscriptions();
|
|
146
145
|
}
|
|
147
|
-
/** True when the push service says the subscription is permanently
|
|
148
|
-
*
|
|
146
|
+
/** True when the push service's status says the subscription is permanently
|
|
147
|
+
* gone (404, 410) rather than transiently failing. web-push rejects with the
|
|
148
|
+
* `statusCode` on any non-2xx, which is what this reads. */
|
|
149
149
|
function isGone(err) {
|
|
150
150
|
const code = err?.statusCode;
|
|
151
151
|
return code === 404 || code === 410;
|
|
@@ -182,9 +182,9 @@ function ensureConfigured() {
|
|
|
182
182
|
export function publicKey() {
|
|
183
183
|
return ensureConfigured().publicKey;
|
|
184
184
|
}
|
|
185
|
-
/** Send a notification to all subscribed devices. Best-effort
|
|
186
|
-
* server→push-service→device, so it
|
|
187
|
-
* in-page Notification API
|
|
185
|
+
/** Send a notification to all subscribed devices. Best-effort, and the route is
|
|
186
|
+
* server→push-service→device, so it does not need the page to be open the way
|
|
187
|
+
* the in-page Notification API does. */
|
|
188
188
|
export async function pushNotify(title, body, tag) {
|
|
189
189
|
if (process.env.NODE_ENV === 'test')
|
|
190
190
|
return;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';
|
|
2
2
|
/**
|
|
3
|
-
* Where a plain (non-slash) browser line goes. Exported so
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* Where a plain (non-slash) browser line goes. Exported so
|
|
4
|
+
* test/remote/mid-run-routing.test.ts drives the shipped decision rather than a
|
|
5
|
+
* copy of it — these three branches are the whole of issue #8.
|
|
6
6
|
*/
|
|
7
7
|
export declare function routePlainLine(plain: string, send: (text: string, opts?: {
|
|
8
8
|
deliverAs: 'steer' | 'followUp';
|
package/dist/remote/register.js
CHANGED
|
@@ -12,22 +12,17 @@ if (!_g.__piRemote)
|
|
|
12
12
|
_g.__piRemote = { server: null, send: null, serveResult: null };
|
|
13
13
|
const S = _g.__piRemote;
|
|
14
14
|
/**
|
|
15
|
-
* Where a plain (non-slash) browser line goes. Exported so
|
|
16
|
-
*
|
|
17
|
-
*
|
|
15
|
+
* Where a plain (non-slash) browser line goes. Exported so
|
|
16
|
+
* test/remote/mid-run-routing.test.ts drives the shipped decision rather than a
|
|
17
|
+
* copy of it — these three branches are the whole of issue #8.
|
|
18
18
|
*/
|
|
19
19
|
export function routePlainLine(plain, send) {
|
|
20
20
|
addUserTurn(plain);
|
|
21
21
|
// Read the run flag from SessionState, which is the only thing that owns it.
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
// mirror — so an errored turn or a /new left this branch steering into a turn
|
|
27
|
-
// SessionState already considered finished. And the mirror was a plain module
|
|
28
|
-
// `let` while every other piece of remote state deliberately lives on
|
|
29
|
-
// globalThis to survive jiti re-evaluation, so it silently reset on session
|
|
30
|
-
// switch. Deleting it removed complexity rather than moving it.
|
|
22
|
+
// Three functions clear it — agentEnd, addError and reset — so anything
|
|
23
|
+
// mirroring the flag here would have to track an errored turn and a /new as
|
|
24
|
+
// well as a finished one, and would steer into a turn SessionState already
|
|
25
|
+
// considers over whenever it missed one.
|
|
31
26
|
if (getState().agentRunning) {
|
|
32
27
|
// A live turn: steer it (inject into the current generation) so the
|
|
33
28
|
// nudge lands immediately.
|
|
@@ -35,9 +30,9 @@ export function routePlainLine(plain, send) {
|
|
|
35
30
|
}
|
|
36
31
|
else if (isRunActive()) {
|
|
37
32
|
// Idle, but a task run owns the session — which is most of a run, since
|
|
38
|
-
// the spec phases and every gate are child processes.
|
|
39
|
-
//
|
|
40
|
-
//
|
|
33
|
+
// the spec phases and every gate are child processes. pi documents
|
|
34
|
+
// sendUserMessage as "Always triggers a turn", so sending here opens a
|
|
35
|
+
// SECOND turn beside the run. Hold it for the next task turn instead.
|
|
41
36
|
holdInput(plain);
|
|
42
37
|
}
|
|
43
38
|
else {
|
|
@@ -65,7 +60,9 @@ export function registerRemote(pi) {
|
|
|
65
60
|
}, wsUrl => html(wsUrl), interruptAgent, clearHeldInput);
|
|
66
61
|
// Hands-off HTTPS: point Tailscale serve at our port so phones get a
|
|
67
62
|
// secure context. Best-effort — any failure degrades to the http URL.
|
|
68
|
-
S.serveResult = await ensureTailscaleServe(S.server.port).catch(() => ({
|
|
63
|
+
S.serveResult = await ensureTailscaleServe(S.server.port).catch(() => ({
|
|
64
|
+
state: 'unavailable'
|
|
65
|
+
}));
|
|
69
66
|
return S.server;
|
|
70
67
|
}
|
|
71
68
|
pi.on('session_start', (_event, ctx) => {
|
|
@@ -119,9 +116,10 @@ export function registerRemote(pi) {
|
|
|
119
116
|
});
|
|
120
117
|
publishNotify('Compacting context…', 'info');
|
|
121
118
|
});
|
|
122
|
-
//
|
|
123
|
-
//
|
|
124
|
-
//
|
|
119
|
+
// registerBridgeCommand, not registerRemoteOnlyCommand: this one needs to
|
|
120
|
+
// exist in the terminal AND on the bridge, so `/remote stop` also works typed
|
|
121
|
+
// in the browser — the web UI advertises it, and while a /task-auto run holds
|
|
122
|
+
// the host command loop the browser is the only live input surface.
|
|
125
123
|
registerBridgeCommand(pi, 'remote', {
|
|
126
124
|
description: 'Show the remote QR code & URLs.',
|
|
127
125
|
handler: async (args, ctx) => {
|
package/dist/remote/server.d.ts
CHANGED
|
@@ -25,8 +25,9 @@ type MessageCallback = (text: string) => void;
|
|
|
25
25
|
export declare function getLocalIPs(nets?: NodeJS.Dict<import("node:os").NetworkInterfaceInfo[]>): LocalIPs;
|
|
26
26
|
/** Build the labeled URL lines shown under the QR code. Both Tailscale and LAN
|
|
27
27
|
* when present; a single unlabeled primary URL when neither resolves. The
|
|
28
|
-
* Tailscale line uses the MagicDNS host when known
|
|
29
|
-
*
|
|
28
|
+
* Tailscale line uses the MagicDNS host when known — it resolves to the same
|
|
29
|
+
* node as the raw IP, but it is the name `tailscale cert` and the https URL in
|
|
30
|
+
* tailscale.ts are issued for — falling back to the raw IP. */
|
|
30
31
|
export declare function formatAddresses(ips: LocalIPs, port: number, tsHost?: string): AddressLine[];
|
|
31
32
|
/** Bind the REAL `server` to the first free port at or above `start`, trying up
|
|
32
33
|
* to `max` consecutive ports. On EADDRINUSE we bump the port and re-listen; any
|
|
@@ -35,12 +36,12 @@ export declare function formatAddresses(ips: LocalIPs, port: number, tsHost?: st
|
|
|
35
36
|
*
|
|
36
37
|
* Binding the real server directly (rather than probing a throwaway socket with
|
|
37
38
|
* createServer()/listen()/close() first, then binding the real one) removes a
|
|
38
|
-
* TOCTOU race: between "probe says port free" and "real listen"
|
|
39
|
-
* taken by
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
39
|
+
* TOCTOU race: between "probe says port free" and "real listen" the port can be
|
|
40
|
+
* taken — including by the probe's own socket, if the runtime has not released
|
|
41
|
+
* it yet — so the real bind hits EADDRINUSE on the very port that just tested
|
|
42
|
+
* free. With no 'error' listener on the real server that escapes as an
|
|
43
|
+
* uncaughtException and takes pi down; that is issue #7. Retrying the real bind
|
|
44
|
+
* has no probe and no window. */
|
|
44
45
|
export declare function listenWithRetry(server: import('node:http').Server, start: number, max: number): Promise<number>;
|
|
45
46
|
export declare function startServer(onMessage: MessageCallback, getHtml: (wsUrl: string) => string, onInterrupt?: () => void, onClearHeld?: () => void): Promise<ServerHandle>;
|
|
46
47
|
export {};
|
package/dist/remote/server.js
CHANGED
|
@@ -34,8 +34,9 @@ export function getLocalIPs(nets = networkInterfaces()) {
|
|
|
34
34
|
}
|
|
35
35
|
/** Build the labeled URL lines shown under the QR code. Both Tailscale and LAN
|
|
36
36
|
* when present; a single unlabeled primary URL when neither resolves. The
|
|
37
|
-
* Tailscale line uses the MagicDNS host when known
|
|
38
|
-
*
|
|
37
|
+
* Tailscale line uses the MagicDNS host when known — it resolves to the same
|
|
38
|
+
* node as the raw IP, but it is the name `tailscale cert` and the https URL in
|
|
39
|
+
* tailscale.ts are issued for — falling back to the raw IP. */
|
|
39
40
|
export function formatAddresses(ips, port, tsHost) {
|
|
40
41
|
const out = [];
|
|
41
42
|
if (tsHost)
|
|
@@ -55,12 +56,12 @@ export function formatAddresses(ips, port, tsHost) {
|
|
|
55
56
|
*
|
|
56
57
|
* Binding the real server directly (rather than probing a throwaway socket with
|
|
57
58
|
* createServer()/listen()/close() first, then binding the real one) removes a
|
|
58
|
-
* TOCTOU race: between "probe says port free" and "real listen"
|
|
59
|
-
* taken by
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
59
|
+
* TOCTOU race: between "probe says port free" and "real listen" the port can be
|
|
60
|
+
* taken — including by the probe's own socket, if the runtime has not released
|
|
61
|
+
* it yet — so the real bind hits EADDRINUSE on the very port that just tested
|
|
62
|
+
* free. With no 'error' listener on the real server that escapes as an
|
|
63
|
+
* uncaughtException and takes pi down; that is issue #7. Retrying the real bind
|
|
64
|
+
* has no probe and no window. */
|
|
64
65
|
export function listenWithRetry(server, start, max) {
|
|
65
66
|
return new Promise((resolve, reject) => {
|
|
66
67
|
let port = start;
|
|
@@ -145,12 +146,12 @@ export async function startServer(onMessage, getHtml, onInterrupt, onClearHeld)
|
|
|
145
146
|
// keep-alive / WebSocket connections. Without this, httpServer.close() only
|
|
146
147
|
// stops accepting new connections and waits for existing ones to drain — an
|
|
147
148
|
// open browser tab never drains, so the listening server and its sockets
|
|
148
|
-
// stay as active event-loop handles forever. In
|
|
149
|
-
//
|
|
150
|
-
//
|
|
151
|
-
//
|
|
152
|
-
//
|
|
153
|
-
//
|
|
149
|
+
// stay as active event-loop handles forever. In print mode pi exits by
|
|
150
|
+
// natural event-loop drain: main.js sets `process.exitCode` and returns with
|
|
151
|
+
// no process.exit(), unlike the package-command path right above it which
|
|
152
|
+
// calls process.exit precisely so a bad extension cannot keep a one-shot
|
|
153
|
+
// command alive. Terminating clients and destroying sockets here is what
|
|
154
|
+
// lets the loop drain at all.
|
|
154
155
|
const sockets = new Set();
|
|
155
156
|
httpServer.on('connection', s => {
|
|
156
157
|
sockets.add(s);
|
|
@@ -2,10 +2,11 @@ import { HistoryBuffer } from './history.js';
|
|
|
2
2
|
import type { Turn, Part } from './history.js';
|
|
3
3
|
import type { ContextUsage, PromptMessage, WidgetData } from './protocol.js';
|
|
4
4
|
export interface LiveTurn {
|
|
5
|
-
/** Ordered assistant content
|
|
5
|
+
/** Ordered assistant content — text, thinking and tool parts — for this run. */
|
|
6
6
|
parts: Part[];
|
|
7
|
-
/** Is the trailing text part still accumulating deltas? Closed by text_end
|
|
8
|
-
* a tool start, so the next text begins a fresh segment
|
|
7
|
+
/** Is the trailing text part still accumulating deltas? Closed by text_end, a
|
|
8
|
+
* thinking delta and a tool start, so the next text begins a fresh segment
|
|
9
|
+
* (separate bubble). */
|
|
9
10
|
textOpen: boolean;
|
|
10
11
|
}
|
|
11
12
|
export interface SnapshotMessage {
|
|
@@ -29,7 +30,7 @@ interface SessionState {
|
|
|
29
30
|
taskWidgetData: WidgetData | null;
|
|
30
31
|
prompt: PromptMessage | null;
|
|
31
32
|
context: ContextUsage | null;
|
|
32
|
-
/** Human-readable active model name
|
|
33
|
+
/** Human-readable active model name, as pi reports it, for the header chip. */
|
|
33
34
|
model: string | null;
|
|
34
35
|
/** Lines typed while a task run owned the session, waiting for the next task
|
|
35
36
|
* turn to steer. Mirrors src/task/mid-run-input.ts so the browser can show
|
|
@@ -3,12 +3,15 @@
|
|
|
3
3
|
// Every change funnels through a mutator that (1) updates this object and (2)
|
|
4
4
|
// broadcasts the matching live delta. On (re)connect the server serializes the
|
|
5
5
|
// whole object with snapshot() and the client replaces its entire view. Because
|
|
6
|
-
// the snapshot and the live deltas read/write the same object, they
|
|
7
|
-
// disagree
|
|
8
|
-
// two-task-widget drift the ad-hoc broadcasting used to cause.
|
|
6
|
+
// the snapshot and the live deltas read/write the same object, they cannot
|
|
7
|
+
// disagree.
|
|
9
8
|
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
9
|
+
// updateTool is the one mutator that only broadcasts: a partial tool result is
|
|
10
|
+
// transient, endTool carries the real one, and no browser module listens for
|
|
11
|
+
// `tool_update` at all.
|
|
12
|
+
//
|
|
13
|
+
// State lives on globalThis so it survives the module being re-evaluated, the
|
|
14
|
+
// same pattern broadcast.ts and bridge.ts use.
|
|
12
15
|
import { broadcast as wsBroadcast } from './broadcast.js';
|
|
13
16
|
import { HistoryBuffer } from './history.js';
|
|
14
17
|
const g = globalThis;
|
package/dist/remote/sw.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
/** Service worker source served at /sw.js
|
|
2
|
-
*
|
|
3
|
-
*
|
|
1
|
+
/** Service worker source served at /sw.js (see server.ts's request handler). It
|
|
2
|
+
* must be a real same-origin URL — registering from a `data:` URL is refused
|
|
3
|
+
* with "The URL protocol of the script is not supported" — and serving it at
|
|
4
|
+
* the root path is what gives it root scope: a worker served at `/sw.js`
|
|
5
|
+
* registers with scope `/`, one at `/deep/sw.js` with scope `/deep/`.
|
|
4
6
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* this is the only path that reaches a backgrounded iPhone. */
|
|
7
|
+
* Notifications go through this worker's push event rather than an in-page
|
|
8
|
+
* `new Notification()` so they reach a device whose page is not open. */
|
|
8
9
|
export declare function swJs(): string;
|
package/dist/remote/sw.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
/** Service worker source served at /sw.js
|
|
2
|
-
*
|
|
3
|
-
*
|
|
1
|
+
/** Service worker source served at /sw.js (see server.ts's request handler). It
|
|
2
|
+
* must be a real same-origin URL — registering from a `data:` URL is refused
|
|
3
|
+
* with "The URL protocol of the script is not supported" — and serving it at
|
|
4
|
+
* the root path is what gives it root scope: a worker served at `/sw.js`
|
|
5
|
+
* registers with scope `/`, one at `/deep/sw.js` with scope `/deep/`.
|
|
4
6
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* this is the only path that reaches a backgrounded iPhone. */
|
|
7
|
+
* Notifications go through this worker's push event rather than an in-page
|
|
8
|
+
* `new Notification()` so they reach a device whose page is not open. */
|
|
8
9
|
export function swJs() {
|
|
9
10
|
return `self.addEventListener('install', function () { self.skipWaiting(); });
|
|
10
11
|
self.addEventListener('activate', function (e) { e.waitUntil(self.clients.claim()); });
|
|
@@ -19,7 +19,8 @@ export type ServeResult = {
|
|
|
19
19
|
} | {
|
|
20
20
|
state: 'unavailable';
|
|
21
21
|
};
|
|
22
|
-
/** The MagicDNS host of a serve result, or undefined
|
|
22
|
+
/** The MagicDNS host of a serve result, or undefined for `unavailable` — the one
|
|
23
|
+
* state that carries no host, because the daemon was unreachable or nameless. */
|
|
23
24
|
export declare function hostFromResult(result: ServeResult): string | undefined;
|
|
24
25
|
export interface RemoteUrlPlan {
|
|
25
26
|
/** URL to encode in the QR and announce; the https one when serve is live. */
|
|
@@ -47,5 +48,6 @@ export declare function teardownTailscaleServe(port: number, run?: Run): Promise
|
|
|
47
48
|
/** Pure: pick the primary URL (what the QR encodes) and any hint lines from a
|
|
48
49
|
* serve result. Prefers the MagicDNS host over the raw IP whenever it's known —
|
|
49
50
|
* the https URL when serve is live, else http://<host>:<port> — so the QR and
|
|
50
|
-
* the announced URL carry the tailnet name
|
|
51
|
+
* the announced URL carry the tailnet name, which is what `tailscale cert` and
|
|
52
|
+
* the `--https=443` handler above are issued for. */
|
|
51
53
|
export declare function planRemoteUrls(httpPrimary: string, result: ServeResult, port: number): RemoteUrlPlan;
|
package/dist/remote/tailscale.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { execFile } from 'node:child_process';
|
|
2
|
-
/** The MagicDNS host of a serve result, or undefined
|
|
2
|
+
/** The MagicDNS host of a serve result, or undefined for `unavailable` — the one
|
|
3
|
+
* state that carries no host, because the daemon was unreachable or nameless. */
|
|
3
4
|
export function hostFromResult(result) {
|
|
4
5
|
return result.state === 'unavailable' ? undefined : result.host;
|
|
5
6
|
}
|
|
@@ -83,7 +84,8 @@ export async function teardownTailscaleServe(port, run = defaultRun) {
|
|
|
83
84
|
/** Pure: pick the primary URL (what the QR encodes) and any hint lines from a
|
|
84
85
|
* serve result. Prefers the MagicDNS host over the raw IP whenever it's known —
|
|
85
86
|
* the https URL when serve is live, else http://<host>:<port> — so the QR and
|
|
86
|
-
* the announced URL carry the tailnet name
|
|
87
|
+
* the announced URL carry the tailnet name, which is what `tailscale cert` and
|
|
88
|
+
* the `--https=443` handler above are issued for. */
|
|
87
89
|
export function planRemoteUrls(httpPrimary, result, port) {
|
|
88
90
|
switch (result.state) {
|
|
89
91
|
case 'served':
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
// Generalized single-pass syntax highlighter, shipped as a JS string and
|
|
2
|
-
// concatenated into the page by ui.ts.
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
2
|
+
// concatenated into the page by ui.ts. One string/comment/number/identifier
|
|
3
|
+
// tokenizer, parameterized by a per-language config in HL_LANGS (keyword set +
|
|
4
|
+
// comment markers + string flavors). A language with no entry there falls back
|
|
5
|
+
// to escaped plain text with no spans at all.
|
|
6
6
|
//
|
|
7
|
-
// Depends on escHtml (ui-render.ts) —
|
|
7
|
+
// Depends on escHtml (ui-render.ts) — ui.ts emits renderModule() immediately
|
|
8
|
+
// before highlightModule(), inside one <script>.
|
|
8
9
|
/** Emitted client JS (top-level function declarations + language tables). */
|
|
9
10
|
export function highlightModule() {
|
|
10
11
|
return ` var HL_JS_KW = new Set(('break case catch class const continue debugger default delete do else export '
|
package/dist/remote/ui-render.js
CHANGED
|
@@ -19,10 +19,10 @@ export function renderModule() {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
// Split text into ordered {type:'text'|'code', ...} blocks by scanning lines
|
|
22
|
-
// for a fenced code marker at line
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
//
|
|
22
|
+
// for a fenced code marker at line START — an indented fence is ordinary
|
|
23
|
+
// text. Scanning lines rather than matching one regex over the whole string
|
|
24
|
+
// is also what tolerates an unclosed trailing fence: everything after it
|
|
25
|
+
// becomes the code block.
|
|
26
26
|
function parseBlocks(text) {
|
|
27
27
|
var FENCE = String.fromCharCode(96, 96, 96);
|
|
28
28
|
var lines = String(text == null ? '' : text).split('\\n');
|
package/dist/remote/ui-script.js
CHANGED
|
@@ -21,7 +21,8 @@ export function clientScript(wsUrl) {
|
|
|
21
21
|
if (usage && usage.percent != null) contextFill.style.width = usage.percent + '%';
|
|
22
22
|
setStatusChip(usage);
|
|
23
23
|
}
|
|
24
|
-
// Compact token count for the header chip
|
|
24
|
+
// Compact token count for the header chip. Matches the terminal's
|
|
25
|
+
// formatContextTokens up to 10M; above that this one keeps a decimal.
|
|
25
26
|
function fmtTokens(n) {
|
|
26
27
|
if (n == null) return '';
|
|
27
28
|
if (n < 1000) return String(n);
|
|
@@ -58,11 +59,8 @@ export function clientScript(wsUrl) {
|
|
|
58
59
|
// Last ~20 toasts, newest first, for the bell dropdown history.
|
|
59
60
|
let notifHistory = [];
|
|
60
61
|
let modelName = '';
|
|
61
|
-
//
|
|
62
|
-
//
|
|
63
|
-
// Single authoritative task-widget slot. The snapshot and the live 'widget'
|
|
64
|
-
// delta both set this; null hides the panel. (No more per-key map that could
|
|
65
|
-
// strand an orphaned widget on screen.)
|
|
62
|
+
// Single authoritative task-widget slot — two variables, no keying. The
|
|
63
|
+
// snapshot and the live 'widget' delta both set them; null hides the panel.
|
|
66
64
|
let taskWidgetLines = null;
|
|
67
65
|
let taskWidgetData = null;
|
|
68
66
|
function renderWidgets() {
|
|
@@ -156,10 +154,10 @@ export function clientScript(wsUrl) {
|
|
|
156
154
|
{ name: '/compact', desc: 'Compact context to save tokens' },
|
|
157
155
|
{ name: '/remote stop', desc: 'Stop the remote server' },
|
|
158
156
|
];
|
|
159
|
-
// Every entry above must be dispatchable from the browser
|
|
160
|
-
//
|
|
161
|
-
//
|
|
162
|
-
//
|
|
157
|
+
// Every entry above must be dispatchable from the browser, or picking it
|
|
158
|
+
// only ever toasts "Unknown command: /x". register.test.ts parses this array
|
|
159
|
+
// out of clientScript() and asserts each name is in the bridge's command map
|
|
160
|
+
// (/new excepted, which register.ts dispatches itself).
|
|
163
161
|
let cmdActive = [];
|
|
164
162
|
let cmdIndex = -1;
|
|
165
163
|
|
|
@@ -492,11 +490,13 @@ export function clientScript(wsUrl) {
|
|
|
492
490
|
thinkingText = '';
|
|
493
491
|
}
|
|
494
492
|
|
|
495
|
-
// Pull the human-readable text out of a tool result.
|
|
496
|
-
//
|
|
497
|
-
//
|
|
498
|
-
//
|
|
499
|
-
//
|
|
493
|
+
// Pull the human-readable text out of a tool result. pi types AgentToolResult
|
|
494
|
+
// with content: (TextContent | ImageContent)[], and TextContent is
|
|
495
|
+
// {type:'text', text}, so a result usually arrives as { content: [...] } — or,
|
|
496
|
+
// since the event field is typed \`any\`, as a bare array of those blocks.
|
|
497
|
+
// JSON.stringify would render either as escaped, unreadable JSON. Extract the
|
|
498
|
+
// text blocks and join them; return null when there is nothing text-shaped so
|
|
499
|
+
// the caller falls back to JSON.
|
|
500
500
|
function contentBlocksText(result) {
|
|
501
501
|
const blocks = Array.isArray(result) ? result
|
|
502
502
|
: (result && Array.isArray(result.content)) ? result.content
|
|
@@ -567,8 +567,8 @@ export function clientScript(wsUrl) {
|
|
|
567
567
|
return el;
|
|
568
568
|
}
|
|
569
569
|
|
|
570
|
-
// Render one committed transcript turn. Assistant turns are an ordered list
|
|
571
|
-
// parts
|
|
570
|
+
// Render one committed transcript turn. Assistant turns are an ordered list
|
|
571
|
+
// of parts — text, thinking and tool — so the layout matches the terminal's
|
|
572
572
|
// interleaving instead of one merged blob with tools dumped at the end.
|
|
573
573
|
function renderTurn(t) {
|
|
574
574
|
if (t.error) { addBubble('error', t.text); addTurnTime(t.ts, 'assistant'); return; }
|
|
@@ -745,8 +745,8 @@ export function clientScript(wsUrl) {
|
|
|
745
745
|
}
|
|
746
746
|
|
|
747
747
|
// Register the service worker, subscribe via the Push API, and hand the
|
|
748
|
-
// subscription to the server. The server
|
|
749
|
-
// so
|
|
748
|
+
// subscription to the server. The server, not the page, sends the
|
|
749
|
+
// notification, so delivery does not need this page to be open.
|
|
750
750
|
function subscribePush() {
|
|
751
751
|
return navigator.serviceWorker.register('/sw.js')
|
|
752
752
|
.then(function () { return navigator.serviceWorker.ready; })
|
|
@@ -1214,11 +1214,11 @@ export function clientScript(wsUrl) {
|
|
|
1214
1214
|
window.addEventListener('online', connectNow);
|
|
1215
1215
|
window.addEventListener('focus', connectNow);
|
|
1216
1216
|
|
|
1217
|
-
// Pin the column to
|
|
1218
|
-
//
|
|
1219
|
-
//
|
|
1220
|
-
//
|
|
1221
|
-
//
|
|
1217
|
+
// Pin the column to a stable pixel height instead of leaving it on the
|
|
1218
|
+
// 100dvh fallback in ui-styles.ts (height: var(--app-h, 100dvh)). A rotation
|
|
1219
|
+
// fires several resize events and changes scrollHeight, so coalesce the
|
|
1220
|
+
// updates in one rAF and re-pin to the bottom when the user was already
|
|
1221
|
+
// there.
|
|
1222
1222
|
let appHeightRaf = null;
|
|
1223
1223
|
let appHeightPin = false;
|
|
1224
1224
|
function setAppHeight() {
|