@huseynsnmz/pi-board 0.15.3 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -4
- package/CONTRIBUTING.md +7 -0
- package/README.md +53 -419
- package/SECURITY.md +19 -0
- package/docs/how-it-works.md +31 -0
- package/docs/reference.md +276 -0
- package/package.json +5 -11
- package/src/index.ts +1 -1
- package/src/runtime/service.mjs +12 -0
- package/src/ui/dashboard.ts +14 -8
- package/IMPLEMENTATION_PLAN.md +0 -920
- package/PRD.md +0 -484
- package/PROGRESS.md +0 -142
- package/VERIFY.md +0 -115
- package/docs/BATCH_SELECTION_READ_FLOW.md +0 -277
- package/docs/EXPLORATION.md +0 -187
- package/docs/PTY_ATTACH_IMPLEMENTATION_PLAN.md +0 -581
- package/docs/RELEASE.md +0 -148
- package/docs/superpowers/plans/2026-08-15-screenlog-gc.md +0 -704
- package/docs/superpowers/plans/2026-08-16-attach-double-cursor-jiggle-retry.md +0 -499
- package/docs/superpowers/plans/2026-08-21-attach-coldstart-jiggle-rearm.md +0 -797
- package/docs/superpowers/plans/2026-08-21-dashboard-keypress-lag.md +0 -366
- package/docs/superpowers/plans/2026-08-21-issue-14-needs-input-no-auto-done.md +0 -576
- package/docs/superpowers/plans/2026-08-22-jiggle-shrink-and-hold.md +0 -337
- package/docs/superpowers/plans/2026-08-22-launch-cwd-favorites.md +0 -916
- package/docs/superpowers/plans/2026-08-22-question-tool-grouping.md +0 -211
- package/docs/superpowers/plans/2026-08-22-rename-state-labels.md +0 -182
- package/docs/superpowers/plans/2026-08-22-stable-list-order.md +0 -51
- package/docs/superpowers/plans/2026-08-27-locks-acquirelock-spin.md +0 -614
- package/docs/superpowers/plans/2026-08-27-pty-runner-test-flaky.md +0 -32
- package/docs/superpowers/plans/2026-08-29-code-refs-badges.md +0 -223
- package/docs/superpowers/plans/2026-08-30-circular-navigation.md +0 -308
- package/docs/superpowers/plans/2026-08-30-post-exit-timing-fix.md +0 -37
- package/docs/superpowers/plans/2026-08-30-pty-attach-quality-debt.md +0 -311
- package/docs/superpowers/plans/2026-08-30-readme-v2.md +0 -294
- package/docs/superpowers/plans/2026-09-01-attach-detach-gate-cursor-anchor.md +0 -284
- package/docs/superpowers/plans/2026-09-02-attach-detach-editor-state.md +0 -722
- package/docs/superpowers/plans/2026-09-03-code-refs-pr-backlink-narrow.md +0 -551
- package/docs/superpowers/plans/2026-09-03-detach-gate-glyph-fallback.md +0 -146
- package/docs/superpowers/plans/2026-09-04-evidence-outputpreview.md +0 -209
- package/docs/superpowers/plans/2026-09-04-warm-host-reclaim.md +0 -796
- package/docs/superpowers/plans/2026-09-05-issue-13-drainnextfollowup-pty-probe.md +0 -114
- package/docs/superpowers/plans/2026-09-05-issue-38-windows-wezterm-ime-cursor.md +0 -73
- package/docs/superpowers/plans/2026-09-05-issue-39-truncate-codepoint-boundary.md +0 -143
- package/docs/superpowers/plans/2026-09-05-issue-61-mention-fallback-guards.md +0 -226
- package/docs/superpowers/plans/2026-09-05-issue-63-flaky-manual-completion.md +0 -87
- package/docs/superpowers/plans/2026-09-05-issue-64-changelog-release-helper.md +0 -53
- package/docs/superpowers/plans/2026-09-05-pty-host-stacking-sock-race.md +0 -731
- package/docs/superpowers/plans/2026-09-08-attach-ctrl-left-detach.md +0 -30
- package/docs/superpowers/plans/2026-09-08-dashboard-shrink-repaint.md +0 -68
- package/docs/superpowers/plans/2026-09-08-issue-11-attach-runtime-desync-heal.md +0 -917
- package/docs/superpowers/plans/2026-09-08-legacy-stale-host-recovery.md +0 -125
- package/docs/superpowers/plans/2026-09-08-spawn-async-error-swallow.md +0 -56
- package/docs/superpowers/plans/2026-09-08-stale-model-attach-guard.md +0 -96
- package/docs/superpowers/plans/2026-09-09-claimpid-blocks-replace.md +0 -267
- package/docs/superpowers/plans/2026-09-09-harden-runner-architecture.md +0 -603
- package/docs/superpowers/plans/2026-09-09-single-writer-completion.md +0 -252
- package/docs/superpowers/plans/2026-09-10-reader-consistency.md +0 -115
- package/docs/superpowers/plans/2026-09-14-attach-cursor-dectcem-gate.md +0 -469
- package/docs/superpowers/plans/2026-09-14-attach-snapshot.md +0 -92
- package/docs/superpowers/plans/2026-09-14-host-meta-orphan-lock.md +0 -771
- package/docs/superpowers/plans/2026-09-14-issue-106-terminal-frame-cognition.md +0 -299
- package/docs/superpowers/plans/2026-09-14-issue-113-foreground-preview-race.md +0 -609
- package/docs/superpowers/plans/2026-09-14-terminal-model.md +0 -145
- package/docs/superpowers/plans/2026-09-15-coordinator-pipe-root-normalize.md +0 -224
- package/docs/superpowers/plans/2026-09-15-lease-publish-eprem-reclaim.md +0 -341
- package/docs/superpowers/plans/2026-09-18-detach-anchor-reporter-endpoint.md +0 -875
- package/docs/superpowers/plans/2026-09-20-control-lifecycle.md +0 -116
- package/docs/superpowers/plans/2026-09-20-issue-121-perf-gate-out-of-coverage.md +0 -517
- package/docs/superpowers/plans/2026-09-23-a5-mid-stream-assertion.md +0 -25
- package/docs/superpowers/plans/2026-09-23-cwd-browse-tab-drill.md +0 -393
- package/docs/superpowers/plans/2026-09-23-terminal-query-forwarding.md +0 -51
- package/docs/superpowers/plans/2026-09-23-unconditional-detach-key.md +0 -58
- package/docs/superpowers/plans/2026-09-24-control-reconcile-flake.md +0 -698
- package/docs/superpowers/plans/2026-09-24-probe-attached-clients.md +0 -605
- package/docs/superpowers/plans/2026-09-29-issue-145-holding-semantic-state.md +0 -1183
- package/docs/superpowers/plans/2026-09-29-issue-145-user-verification.md +0 -66
- package/docs/superpowers/plans/2026-09-30-board-delete-key-x-y-confirm.md +0 -649
- package/docs/superpowers/plans/2026-09-30-issue-153-runner-stop-boot-window.md +0 -618
- package/docs/superpowers/plans/2026-09-30-issue-95-test-discipline.md +0 -555
- package/docs/superpowers/plans/2026-09-30-publish-dispatch-tag-check.md +0 -259
- package/docs/superpowers/plans/2026-10-06-attach-settle-scheme-delivery.md +0 -751
- package/docs/superpowers/plans/2026-10-09-issue-167-attach-middle-paste.md +0 -509
- package/docs/superpowers/plans/2026-10-09-issue-169-fullscreen-mouse-ownership.md +0 -346
- package/docs/superpowers/plans/2026-10-10-attach-delete-in-place.md +0 -832
- package/docs/superpowers/plans/2026-10-10-issue-174-selection-mirror-paste-source.md +0 -510
- package/docs/superpowers/plans/2026-10-14-issue-170-attach-reply-guard.md +0 -771
- package/docs/superpowers/plans/2026-10-14-issue-178-accept-registration-race.md +0 -1257
- package/docs/superpowers/plans/2026-10-14-issue-184-snapshot-window-contract.md +0 -657
- package/docs/superpowers/plans/2026-10-14-issue-185-attach-wheel-alt-screen.md +0 -47
- package/docs/superpowers/plans/2026-10-14-issue-187-motionless-drag.md +0 -931
- package/docs/superpowers/plans/2026-10-14-issue-189-mixed-input-segments.md +0 -823
- package/docs/superpowers/plans/2026-10-15-issue-188-x10-mouse-release.md +0 -526
- package/docs/superpowers/plans/2026-10-16-issue-198-attach-wheel-forward-fallback.md +0 -32
- package/docs/superpowers/plans/2026-10-18-attach-wheel-replay-pseudo-scrollback.md +0 -437
- package/docs/superpowers/plans/2026-10-19-issue-213-reply-guard-csi-key.md +0 -257
- package/docs/superpowers/plans/2026-10-19-issue-214-attach-forward-alt-modifier.md +0 -202
- package/docs/superpowers/plans/2026-10-21-issue-221-ask-user-question.md +0 -198
- package/docs/superpowers/plans/2026-10-23-issue-222-attach-ready.md +0 -290
- package/docs/superpowers/plans/2026-10-23-issue-225-monotonic-foreground-state.md +0 -450
- package/docs/superpowers/plans/2026-10-23-issue-229-vim-jkl.md +0 -449
- package/docs/superpowers/plans/2026-10-23-issue-232-attach-bg-only-cells.md +0 -153
- package/docs/superpowers/plans/2026-10-23-lease-heartbeat-staleness.md +0 -537
- package/docs/superpowers/plans/2026-10-23-selected-row-highlight.md +0 -405
- package/docs/superpowers/plans/2026-10-24-issue-217-windows-pid-reuse-stopping.md +0 -367
- package/docs/superpowers/plans/2026-10-24-issue-230-warm-host-background-runs.md +0 -806
- package/docs/superpowers/plans/2026-10-24-issue-238-windows-process-identity-token.md +0 -490
- package/docs/superpowers/plans/2026-10-24-issue-241-coordinator-root-exit.md +0 -461
- package/docs/superpowers/plans/2026-10-25-issue-239-coordinator-replacement-identity.md +0 -504
- package/docs/superpowers/plans/2026-10-26-issue-245-key-hints-doc-sync.md +0 -244
- package/docs/superpowers/plans/2026-10-27-issue-172-fullscreen-selection-clear.md +0 -344
- package/docs/superpowers/plans/2026-10-27-issue-252-select-key-hints.md +0 -209
- package/docs/superpowers/plans/2026-10-28-issue-255-coordinator-endpoint-zombie.md +0 -442
- package/docs/superpowers/plans/2026-10-28-issue-257-legacy-pty-runner-endpoint-cleanup.md +0 -256
- package/docs/superpowers/plans/2026-11-03-cwd-favorites-full-pool.md +0 -266
- package/docs/superpowers/specs/2026-08-15-screenlog-gc-design.md +0 -105
- package/docs/superpowers/specs/2026-08-16-attach-double-cursor-jiggle-retry-design.md +0 -142
- package/docs/superpowers/specs/2026-08-21-attach-coldstart-jiggle-rearm-design.md +0 -110
- package/docs/superpowers/specs/2026-08-21-dashboard-keypress-lag-design.md +0 -59
- package/docs/superpowers/specs/2026-08-21-issue-14-needs-input-no-auto-done-design.md +0 -109
- package/docs/superpowers/specs/2026-08-22-cwd-favorites-design.md +0 -131
- package/docs/superpowers/specs/2026-08-22-jiggle-shrink-and-hold-design.md +0 -111
- package/docs/superpowers/specs/2026-08-22-question-tool-grouping-design.md +0 -114
- package/docs/superpowers/specs/2026-08-22-rename-state-labels-design.md +0 -80
- package/docs/superpowers/specs/2026-08-27-locks-acquirelock-spin-design.md +0 -97
- package/docs/superpowers/specs/2026-08-27-pty-runner-test-flaky-design.md +0 -35
- package/docs/superpowers/specs/2026-08-29-code-refs-badges-design.md +0 -128
- package/docs/superpowers/specs/2026-08-30-circular-navigation-design.md +0 -47
- package/docs/superpowers/specs/2026-08-30-eprm-atomicwrite-race-design.md +0 -77
- package/docs/superpowers/specs/2026-08-30-post-exit-timing-fix-design.md +0 -80
- package/docs/superpowers/specs/2026-08-30-pty-attach-quality-debt-design.md +0 -105
- package/docs/superpowers/specs/2026-08-30-readme-v2-design.md +0 -115
- package/docs/superpowers/specs/2026-09-01-attach-detach-gate-cursor-anchor-design.md +0 -78
- package/docs/superpowers/specs/2026-09-02-attach-detach-editor-state-design.md +0 -120
- package/docs/superpowers/specs/2026-09-03-code-refs-pr-backlink-narrow-design.md +0 -92
- package/docs/superpowers/specs/2026-09-03-detach-gate-glyph-fallback-design.md +0 -99
- package/docs/superpowers/specs/2026-09-04-evidence-outputpreview-design.md +0 -50
- package/docs/superpowers/specs/2026-09-04-warm-host-reclaim-design.md +0 -106
- package/docs/superpowers/specs/2026-09-05-issue-13-drainnextfollowup-pty-probe-design.md +0 -64
- package/docs/superpowers/specs/2026-09-05-issue-38-windows-wezterm-ime-design.md +0 -48
- package/docs/superpowers/specs/2026-09-05-issue-39-truncate-codepoint-boundary-design.md +0 -64
- package/docs/superpowers/specs/2026-09-05-issue-61-mention-fallback-design.md +0 -71
- package/docs/superpowers/specs/2026-09-05-issue-63-flaky-manual-completion-design.md +0 -49
- package/docs/superpowers/specs/2026-09-05-issue-64-changelog-helper-design.md +0 -76
- package/docs/superpowers/specs/2026-09-05-pty-host-stacking-sock-race-design.md +0 -510
- package/docs/superpowers/specs/2026-09-07-issue-11-attach-runtime-desync-heal-design.md +0 -130
- package/docs/superpowers/specs/2026-09-08-attach-ctrl-left-detach-design.md +0 -58
- package/docs/superpowers/specs/2026-09-08-dashboard-shrink-repaint-design.md +0 -52
- package/docs/superpowers/specs/2026-09-08-legacy-stale-host-recovery-design.md +0 -87
- package/docs/superpowers/specs/2026-09-08-spawn-async-error-swallow-design.md +0 -56
- package/docs/superpowers/specs/2026-09-08-stale-model-attach-guard-design.md +0 -79
- package/docs/superpowers/specs/2026-09-09-claimpid-blocks-replace-design.md +0 -147
- package/docs/superpowers/specs/2026-09-09-harden-runner-architecture-design.md +0 -304
- package/docs/superpowers/specs/2026-09-14-attach-cursor-dectcem-gate-design.md +0 -114
- package/docs/superpowers/specs/2026-09-14-host-meta-orphan-lock-design.md +0 -120
- package/docs/superpowers/specs/2026-09-14-issue-106-terminal-frame-cognition-design.md +0 -146
- package/docs/superpowers/specs/2026-09-14-issue-113-foreground-preview-race-design.md +0 -116
- package/docs/superpowers/specs/2026-09-15-coordinator-pipe-root-normalize-design.md +0 -84
- package/docs/superpowers/specs/2026-09-15-lease-publish-eprem-reclaim-design.md +0 -92
- package/docs/superpowers/specs/2026-09-18-detach-anchor-reporter-endpoint-design.md +0 -123
- package/docs/superpowers/specs/2026-09-20-issue-121-perf-gate-out-of-coverage-design.md +0 -204
- package/docs/superpowers/specs/2026-09-23-a5-mid-stream-assertion-design.md +0 -43
- package/docs/superpowers/specs/2026-09-23-cwd-browse-tab-drill-design.md +0 -95
- package/docs/superpowers/specs/2026-09-23-terminal-query-forwarding-design.md +0 -63
- package/docs/superpowers/specs/2026-09-23-unconditional-detach-key-design.md +0 -57
- package/docs/superpowers/specs/2026-09-24-control-reconcile-flake-design.md +0 -98
- package/docs/superpowers/specs/2026-09-24-probe-attached-clients-design.md +0 -190
- package/docs/superpowers/specs/2026-09-29-issue-145-holding-semantic-state-design.md +0 -394
- package/docs/superpowers/specs/2026-09-30-board-delete-key-x-y-confirm-design.md +0 -86
- package/docs/superpowers/specs/2026-09-30-issue-153-runner-stop-boot-window-design.md +0 -75
- package/docs/superpowers/specs/2026-09-30-issue-95-test-discipline-design.md +0 -226
- package/docs/superpowers/specs/2026-09-30-publish-dispatch-tag-check-design.md +0 -129
- package/docs/superpowers/specs/2026-10-06-attach-settle-scheme-delivery-design.md +0 -196
- package/docs/superpowers/specs/2026-10-09-issue-167-attach-middle-paste-design.md +0 -125
- package/docs/superpowers/specs/2026-10-09-issue-169-fullscreen-mouse-ownership-design.md +0 -130
- package/docs/superpowers/specs/2026-10-10-attach-delete-in-place-design.md +0 -224
- package/docs/superpowers/specs/2026-10-10-issue-174-selection-mirror-paste-source-design.md +0 -154
- package/docs/superpowers/specs/2026-10-14-issue-170-attach-reply-guard-design.md +0 -195
- package/docs/superpowers/specs/2026-10-14-issue-178-accept-registration-race-design.md +0 -287
- package/docs/superpowers/specs/2026-10-14-issue-184-snapshot-window-contract-design.md +0 -288
- package/docs/superpowers/specs/2026-10-14-issue-185-attach-wheel-alt-screen-design.md +0 -126
- package/docs/superpowers/specs/2026-10-14-issue-187-motionless-drag-design.md +0 -269
- package/docs/superpowers/specs/2026-10-14-issue-189-mixed-input-segments-design.md +0 -369
- package/docs/superpowers/specs/2026-10-15-issue-188-x10-mouse-release-design.md +0 -252
- package/docs/superpowers/specs/2026-10-16-issue-198-attach-wheel-forward-fallback-design.md +0 -67
- package/docs/superpowers/specs/2026-10-18-attach-wheel-replay-pseudo-scrollback-design.md +0 -250
- package/docs/superpowers/specs/2026-10-19-issue-213-reply-guard-csi-key-design.md +0 -165
- package/docs/superpowers/specs/2026-10-19-issue-214-attach-forward-alt-modifier-design.md +0 -131
- package/docs/superpowers/specs/2026-10-20-issue-221-ask-user-question-design.md +0 -270
- package/docs/superpowers/specs/2026-10-22-issue-225-monotonic-foreground-state-design.md +0 -225
- package/docs/superpowers/specs/2026-10-23-issue-222-attach-ready-design.md +0 -204
- package/docs/superpowers/specs/2026-10-23-issue-229-vim-jkl-design.md +0 -90
- package/docs/superpowers/specs/2026-10-23-issue-232-attach-bg-only-cells-design.md +0 -146
- package/docs/superpowers/specs/2026-10-23-lease-heartbeat-staleness-design.md +0 -271
- package/docs/superpowers/specs/2026-10-23-selected-row-highlight-design.md +0 -191
- package/docs/superpowers/specs/2026-10-24-issue-217-windows-pid-reuse-stopping-design.md +0 -334
- package/docs/superpowers/specs/2026-10-24-issue-230-warm-host-background-runs-design.md +0 -283
- package/docs/superpowers/specs/2026-10-24-issue-238-windows-process-identity-token-design.md +0 -286
- package/docs/superpowers/specs/2026-10-24-issue-241-coordinator-root-exit-design.md +0 -381
- package/docs/superpowers/specs/2026-10-25-issue-239-coordinator-replacement-identity-design.md +0 -331
- package/docs/superpowers/specs/2026-10-26-issue-245-key-hints-doc-sync-design.md +0 -72
- package/docs/superpowers/specs/2026-10-27-issue-172-fullscreen-selection-clear-design.md +0 -248
- package/docs/superpowers/specs/2026-10-27-issue-252-select-key-hints-design.md +0 -95
- package/docs/superpowers/specs/2026-10-28-issue-255-coordinator-endpoint-zombie-design.md +0 -380
- package/docs/superpowers/specs/2026-10-28-issue-257-legacy-pty-runner-endpoint-cleanup-design.md +0 -197
- package/docs/superpowers/specs/2026-11-03-cwd-favorites-full-pool-design.md +0 -106
- package/scripts/budget-audit.mjs +0 -135
- package/scripts/release.mjs +0 -446
- package/scripts/release_helper.mjs +0 -309
- package/scripts/run-perf-gate.mjs +0 -40
- package/scripts/run-tests.mjs +0 -375
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
# Spec: 修复 attach 落定的亮暗交付(issue #161)
|
|
2
|
-
|
|
3
|
-
Drafted: zai-coding-cn/glm-5.3 (selected) · zai-coding-cn/glm-5.3 (physical) @ 2026-10-04T23:59:27+08:00
|
|
4
|
-
Revised: zai-coding-cn/glm-5.3 (selected) · zai-coding-cn/glm-5.3 (physical) @ 2026-10-05T23:16:10+08:00
|
|
5
|
-
Revised: zai-coding-cn/glm-5.3 (selected) · zai-coding-cn/glm-5.3 (physical) @ 2026-10-06T22:29:11+08:00
|
|
6
|
-
|
|
7
|
-
> 状态:v3(第 2 轮复核收敛:真缺口 0,细化 R2-D1~D3 已处置)——⏸ 等待用户确认设计,确认后进步 5(worktree)。
|
|
8
|
-
> 依据:issue #161 正文 + 三轮调研(`research/root-cause-probe-api-rename.md`、
|
|
9
|
-
> `research/fix-surface-api-availability.md`、`research/observability-hook-points.md`)。
|
|
10
|
-
|
|
11
|
-
## 1. 根因(已证实)
|
|
12
|
-
|
|
13
|
-
**落定探测调用的 pi-tui 方法在 pi ≥ 0.99 已改名,调用抛 `TypeError` 被 `catch {}` 吞掉,
|
|
14
|
-
probe 从未执行——settle 时刻的亮暗交付在 pi ≥ 0.99 上恒为 no-op。**
|
|
15
|
-
|
|
16
|
-
证据链:
|
|
17
|
-
|
|
18
|
-
1. `src/ui/pty-attach.ts:664`(origin/main,#165 后)调用
|
|
19
|
-
`this.tui.queryTerminalBackgroundColor({ timeoutMs })`;665-667 行 `catch {}` 静默。
|
|
20
|
-
2. pi-tui 版本考古(npm tarball `tui.d.ts` 核对):0.79.8 / 0.87.1 仅有旧名;
|
|
21
|
-
0.99.0 / 0.99.2 / 1.0.0 / 1.0.2 仅有 `queryTerminalColors`。改名点 = 0.87.1 → 0.99.0。
|
|
22
|
-
3. `pi-coding-agent@0.99.2` 依赖 pi-tui ^0.99.2 → 事发环境(2026-10-01,pi 0.99.2)已改名,
|
|
23
|
-
probe 当时即 no-op;当前本机 pi 1.0.2 亦然。
|
|
24
|
-
4. CI 绿的原因:repo devDeps 钉 0.79.8(旧名在,typecheck 过),测试 fake 只带旧名;
|
|
25
|
-
运行时扩展副本 node_modules/@earendil-works 为空,peer 依赖由宿主 pi 解析(仅新名)
|
|
26
|
-
→ 开发面与运行面漂移。
|
|
27
|
-
5. 排除备选:settle 未触发 / 发送链路断——#165 修的回声残渣证明 settle 钩子在跑;
|
|
28
|
-
「应答被僵尸条目吃掉」是旧实现的失败模式,且根本走不到那一步(调用即抛)。
|
|
29
|
-
6. (复核补强)扩展组件拿到的 tui 是 `createInteractiveTuiReference` 返回的透明 Proxy
|
|
30
|
-
(pi 1.0.2 tui-renderer.js:37-66,get/set/has 全量转发底层 TuiBase)→ 旧名经
|
|
31
|
-
`Reflect.get` 得 undefined、调用即 TypeError(与根因自洽);新名 `queryTerminalColors`
|
|
32
|
-
真实存在且可调(TuiBase 实现,tui.js:1056)——修复假设实现级成立。
|
|
33
|
-
|
|
34
|
-
**#165 合并后的现状**:settle 交付只剩 probe 一条路(D3 已删;D2 桥只转发宿主消费到的
|
|
35
|
-
scheme 事件,救不了落定即错的子 pi)→ 任何「子 pi spawn 时拿不到真实亮暗」的 attach 必现
|
|
36
|
-
恒 dark,直到外部 997 推送。比 issue 提交时更严重(当时 D3 至少还会发裸查询)。
|
|
37
|
-
|
|
38
|
-
## 2. 修复设计
|
|
39
|
-
|
|
40
|
-
### 2.1 决策表
|
|
41
|
-
|
|
42
|
-
| 决策点 | 选择 | 理由 | 被拒备选 |
|
|
43
|
-
|---|---|---|---|
|
|
44
|
-
| 探测 API | feature-detect:优先 `queryTerminalColors`(新),回退 `queryTerminalBackgroundColor`(旧),皆无则不发探测 | peerDependencies 为 `*`,须同时覆盖新旧宿主;#165 后 settle 只剩这条路 | 只用新名(砍掉 ≤0.87 宿主);维持旧名(= 现状,坏的) |
|
|
45
|
-
| 迟到应答 | 新 API 传 `onLateReply`;到达时若组件未 closed **且 scheme 不同于已发送值**才补发一次 997;相同则跳过 | 上游已把迟到应答导向回调(tui.js:1064-1068);997 幂等但重复发送是诊断噪音、违背 #148「一次探测」克制精神 | 忽略迟到(慢链路首拍仍 dark);无去重(每次 attach 可能发两条相同 997) |
|
|
46
|
-
| 「读已解析状态」 | 不做 | 扩展面 TUI 接口无同步 getter(1.0.2 tui.d.ts 无 getTerminalTheme 暴露);宿主进程直连真实终端、队列干净、WezTerm 毫秒级答 OSC 11,一次查询等价可靠 | issue 方向 2 原案(无 API 可用) |
|
|
47
|
-
| scheme 事件缓存重发 | 本期不做 | scheme 事件只在外部推送时触发;board 重启场景宿主是新进程、缓存必空,边际价值低 | 记为后续可选加固(开放问题 2) |
|
|
48
|
-
| 可观测性 | 组件 `onDiagnostic` 发 **DiagnosticEvent 兼容 patch**(code/source/level/message/details),两处构造点 wiring 直传 `appendDiagnostic(root, viewId, patch)` | `normalizeDiagnostic` 只保留 level/code/runId/source/message/details 白名单,其余字段会被丢弃——事件必须在源头就按既有 schema 构造 | 组件直写文件(层穿透、难单测);自定义字段名(会被白名单丢弃,R1-G1) |
|
|
49
|
-
| devDeps 0.79.8 → 1.0.x | 本期不做 | 全量类型面迁移,独立 PR;本期用 feature-detect + 本地结构类型 + 局部 cast | 混入会放大回归面 |
|
|
50
|
-
| kill switch | 保留 `AGENT_BOARD_FORWARD_TERMINAL_QUERIES=0` | 既有回退语义(probe + D2 桥同关) | — |
|
|
51
|
-
|
|
52
|
-
### 2.2 数据流(settle 时刻)
|
|
53
|
-
|
|
54
|
-
```
|
|
55
|
-
finishAttachTransition(现有,不变)
|
|
56
|
-
└─ probeAndReportRealTerminalColorScheme(重写编排)
|
|
57
|
-
├─ resolveProbeApi(this.tui) [新纯函数,terminal-query-sequences.mjs]
|
|
58
|
-
│ api=colors → tui.queryTerminalColors({timeoutMs, onLateReply})
|
|
59
|
-
│ → colors.background → RgbColor
|
|
60
|
-
│ api=background→ tui.queryTerminalBackgroundColor({timeoutMs}) → RgbColor
|
|
61
|
-
│ api=none → 仅诊断事件,不发探测
|
|
62
|
-
├─ colorSchemeForBackgroundRgb(rgb) [现有纯函数,不动]
|
|
63
|
-
├─ toColorSchemeReport(scheme) [现有纯函数,不动]
|
|
64
|
-
├─ this.send({type:"input", data:997…}) [现有通道,不动]
|
|
65
|
-
└─ onDiagnostic(DiagnosticEvent 兼容 patch) [新回调,缺省 no-op]
|
|
66
|
-
onLateReply(colors) →
|
|
67
|
-
closed → 不发送,记 outcome=dropped_closed
|
|
68
|
-
scheme 与已发送值相同 → 不发送,记 outcome=duplicate_skipped
|
|
69
|
-
否则 → 补发一次 997,记 outcome=reported + late:true
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
### 2.3 组件契约与改动面
|
|
73
|
-
|
|
74
|
-
- `src/core/terminal-query-sequences.mjs`(落点钉死,与 `colorSchemeForBackgroundRgb` 同域):
|
|
75
|
-
- `resolveProbeApi(tuiLike) → {api:"colors"|"background"|"none", invoke(tuiLike, timeoutMs, onLateReply?) → Promise<RgbColor|undefined>}`
|
|
76
|
-
—— 纯检测 + 适配器,输入输出 plain object,零副作用。
|
|
77
|
-
- `backgroundRgbFromTerminalColors(colors) → RgbColor|undefined`——纯映射。
|
|
78
|
-
- **编译期类型约束**:本仓编译面是 pi-tui 0.79.8,没有 `TerminalColors` 类型也没有
|
|
79
|
-
`queryTerminalColors` 方法 → 上述两个函数使用**本地定义的最小结构类型**
|
|
80
|
-
(如 `interface TerminalColorsLike { background?: RgbColor }`),**禁止
|
|
81
|
-
`import type { TerminalColors }` 自 pi-tui**;`typeof` 检测与必要的 cast 封装在
|
|
82
|
-
`invoke` 内部,不外溢到编排层。
|
|
83
|
-
- `src/ui/pty-attach.ts`:
|
|
84
|
-
- `PtyAttachOptions`(pty-attach.ts:24,实名)增加可选
|
|
85
|
-
`onDiagnostic?: (event: AttachSettleDiagnosticPatch) => void`(缺省 no-op)。
|
|
86
|
-
- `probeAndReportRealTerminalColorScheme` 重写为上述编排;每步失败写诊断后静默返回
|
|
87
|
-
(保持 #148 的 best-effort 语义,但可观测)。
|
|
88
|
-
- **两处构造点都要注入**(当前为同构重复的两个 `openPtyAttach`,是否去重由 plan 决定,
|
|
89
|
-
去重本身非目标):
|
|
90
|
-
- `src/commands/attach-flow.ts:33-37`(export 的 `openPtyAttach`,供 `attach()` /
|
|
91
|
-
键盘回退路径);
|
|
92
|
-
- `src/commands/agent-board.ts:226-251`(私有 `openPtyAttach`,供 dashboard attach
|
|
93
|
-
动作,构造在 238-240 行)。
|
|
94
|
-
两处工厂闭包均持有 `root` 与 `viewId`,把 `onDiagnostic` 接到
|
|
95
|
-
`appendDiagnostic(root, viewId, patch)`(与现有 `screenLogPath(root, viewId)` 同源)。
|
|
96
|
-
- **诊断事件契约(映射到既有 `DiagnosticEvent` schema,字段名必须落在
|
|
97
|
-
`normalizeDiagnostic` 白名单内,否则会被静默丢弃)**:
|
|
98
|
-
- `code: "attach_settle_scheme"`(snake_case,对齐 `host_crashed` /
|
|
99
|
-
`host_crash_owner_changed` 等现有 code 命名)
|
|
100
|
-
- `source: "attach"`
|
|
101
|
-
- `level`: **「预期交付但失败」才 `"warn"`**——`timeout` / `error` / `no_background` /
|
|
102
|
-
`no_probe_api` / `dropped_disconnected`;正常/预期操作用 `"info"`——`reported` / `duplicate_skipped` /
|
|
103
|
-
`dropped_closed` / `suppressed`(后三者是慢链路正常去重、用户快速 detach、用户主动
|
|
104
|
-
kill switch;标 warn 会污染 `summarizeDiagnostics` 的 warningCount——diagnostics.mjs:60
|
|
105
|
-
计数、store.mjs:425 并入行摘要被 dashboard 消费)
|
|
106
|
-
- `message`: 人类可读摘要(如 `attach settle scheme reported via queryTerminalColors (light)`)
|
|
107
|
-
- `details: { probeApi: "colors"|"background"|"none", outcome:
|
|
108
|
-
"reported"|"timeout"|"error"|"no_background"|"no_probe_api"|"dropped_closed"|
|
|
109
|
-
"dropped_disconnected"|"duplicate_skipped"|"suppressed", report?: "997;1"|"997;2", late?: true }`
|
|
110
|
-
(camelCase,对齐既有 details 键风格如 `ownerChanged`;`report` 仅在发送 997 时携带;
|
|
111
|
-
kill switch 的 suppressed 事件不填 `probeApi`)
|
|
112
|
-
- 组件的 `onDiagnostic` 即以上述 patch 形状发出,wiring 层直接透传给
|
|
113
|
-
`appendDiagnostic(root, viewId, patch)`,不做二次映射。
|
|
114
|
-
- **outcome 判定规则(实现与 A9 的对齐依据)**:
|
|
115
|
-
(a) `timeout` 与 `no_background` 的区分——api=colors 时,返回对象**任一字段**
|
|
116
|
-
(foreground / background / palette 任一项)有值但 `background` 缺失 →
|
|
117
|
-
`no_background`(终端应答了但没答 OSC 11);返回对象**完全为空** → `timeout`
|
|
118
|
-
(pi-tui 1.0.2 tui.d.ts:376-378:未应答的颜色为 undefined,两种情形在返回值层面
|
|
119
|
-
仅靠「是否有任何字段有值」区分);api=background 时 `undefined` → `timeout`
|
|
120
|
-
(旧 API 只查 OSC 11,无法细分)。
|
|
121
|
-
(b) 去重基准 `sentScheme` 初始为 `null`;`null` 不命中 duplicate 判定——保证
|
|
122
|
-
「超时 → 迟到应答补发」这条主路径不被去重吞掉。
|
|
123
|
-
|
|
124
|
-
### 2.4 降级路径
|
|
125
|
-
|
|
126
|
-
- probe 超时 / 异常 / 无 background → 诊断 `outcome=timeout|error|no_background`
|
|
127
|
-
(level `"warn"`;判定规则见 §2.3 outcome 判定规则),不发 997。
|
|
128
|
-
- 无任何 probe API → 诊断 `details.probeApi="none"`、`outcome=no_probe_api`,不发探测。
|
|
129
|
-
- `onLateReply` 到达且组件已 closed → 不发送,诊断 `outcome=dropped_closed`(level `"info"`)。
|
|
130
|
-
- `onLateReply` 的 scheme 与已发送值(`sentScheme`,初始 `null`)相同 → 不发送,诊断
|
|
131
|
-
`outcome=duplicate_skipped`(level `"info"`);不同(含尚未发送过任何值的 `null` 情形,
|
|
132
|
-
即「超时 → 迟到补发」主路径)→ 补发一次 997,诊断 `outcome=reported`、`late:true`。
|
|
133
|
-
- kill switch(`AGENT_BOARD_FORWARD_TERMINAL_QUERIES=0`)→ 全链路跳过,诊断
|
|
134
|
-
`outcome=suppressed`(level `"info"`,不填 `probeApi`),行为回到修复前。
|
|
135
|
-
- 控制.socket 未连接时 settle 交付命中 send() 静默 no-op(硬超时落定、宿主连接慢的角落)→
|
|
136
|
-
不没 sentScheme、不发 997,诊断 `outcome=dropped_disconnected`(level `"warn"`)——同 scheme
|
|
137
|
-
的迟到应答之后重试而非被去重吞掉。(CR round-1 advisory 采纳)
|
|
138
|
-
- 以上降级均不抛错、不影响 attach 主流程。
|
|
139
|
-
|
|
140
|
-
### 2.5 非目标
|
|
141
|
-
|
|
142
|
-
- pi-tui 上游 `pendingTerminalColorQueries` 超时条目累积的队列卫生(上游问题)。
|
|
143
|
-
- WezTerm mode 2031 支持(上游 wezterm/wezterm#6454)。
|
|
144
|
-
- 子 pi 侧 OSC 11 僵尸条目泄漏(上游)。
|
|
145
|
-
- devDependencies 升级到 pi 1.0.x(独立 issue/PR,见开放问题 1)。
|
|
146
|
-
- 复活 #165 删除的 D3 raw replay。
|
|
147
|
-
- 两个 `openPtyAttach` 同构重复的去重(由 plan 自行决定,不作为本期验收项)。
|
|
148
|
-
|
|
149
|
-
## 3. 验收矩阵
|
|
150
|
-
|
|
151
|
-
| ID | 功能点 | 验收方式 | 具体验证 | 通过标准 |
|
|
152
|
-
|----|--------|----------|----------|----------|
|
|
153
|
-
| A1 | 新 API 探测与 997 交付 | 自动化验证(unit) | `node --test test/pty-attach-settle-scheme.test.mjs`:fake tui 仅带 `queryTerminalColors`,light/dark 两个 background 值 | probe 被调用;向 child 各发一次 `997;2` / `997;1` |
|
|
154
|
-
| A2 | 旧 API 回退不回归 | 自动化验证(unit) | 同文件:fake tui 仅带 `queryTerminalBackgroundColor` | 旧路径行为与 #149 语义一致(997 照发) |
|
|
155
|
-
| A3 | 双 API 皆缺 | 自动化验证(unit) | 同文件:fake tui 两者皆无 | 不抛错、不发探测;`onDiagnostic` 收到 `details.probeApi:"none"`、`outcome:"no_probe_api"` |
|
|
156
|
-
| A4 | 颜色→亮暗映射 | 自动化验证(unit) | `colorSchemeForBackgroundRgb` 现有边界用例保持 + `backgroundRgbFromTerminalColors` 新用例 | 亮度阈值与 pi 对齐;`background` 缺失返回 `undefined` |
|
|
157
|
-
| A5 | 迟到应答补发与去重 | 自动化验证(unit) | fake:resolve 后再触发 `onLateReply`(scheme 相同 / 不同两组)+ closed 场景 | scheme 不同补发一次 997 且 `late:true`;相同跳过且 `outcome:"duplicate_skipped"`;closed 后不发送且 `outcome:"dropped_closed"` |
|
|
158
|
-
| A6 | 诊断事件流(schema 兼容) | 自动化验证(unit) | fake `onDiagnostic` 捕获各阶段事件,并**将 patch 过一遍 `normalizeDiagnostic(viewId, patch)` 直证字段不丢** | 事件 `code:"attach_settle_scheme"`、`source:"attach"`、`details.{probeApi,outcome,report?}` 全部在 normalize 后存活;kill switch 下 `details.outcome:"suppressed"` |
|
|
159
|
-
| A7 | 真实 PTY 端到端 | 自动化验证(integration) | 扩展 `test-support/detach-gate-smoke.ts`:新增「tui 仅新 API」场景,真实 node-pty 子进程 | 子 PTY 输入流收到正确 997 恰一次 |
|
|
160
|
-
| A8 | 既有面无回归 | 自动化验证(static + build + integration) | `npm run verify`(= typecheck + perf gate + tests + coverage 阈值 lines 85 / funcs 80 / branches 70 + pack:dry,见 AGENTS.md:20-24) | 全绿;c8 分支阈值由 A1/A2/A3 三向覆盖满足;#165 的 `attach-startup-color-smoke` 不受影响 |
|
|
161
|
-
| A9 | 失败分支不发 997 | 自动化验证(unit) | A1 同文件:fake 分别返回 timeout(resolve 完全为空对象 `{}`,不触发 late)/ throw / 有应答但无 `background`(resolve 含 foreground 的对象) | 三种降级下 sent 均为空;`details.outcome` 分别为 `timeout` / `error` / `no_background`(判定规则见 §2.3) |
|
|
162
|
-
| U1 | 真机 board 重启场景(两条入口路径各验一次) | 用户实测 | 前置(必须):① 快进运行时副本 `git -C ~/.pi/agent/git/github.com/zhuxixi/pi-agent-board pull --ff-only`(至含本修复的 main);② **重启宿主 pi**(扩展随宿主启动加载,不重启跑的是旧代码)。然后:浅色 WezTerm 下宿主 pi 起 board → board 级重启 → **两次 attach 各走一条入口**:① dashboard 选中 view 回车 attach(验 agent-board.ts 构造点);② detach 后用 ← 键盘路径或 bg 命令再 attach(验 attach-flow.ts 构造点);观察子 pi 首帧配色 + 两次各自 view 的 `diagnostics.jsonl` | 两次首帧均 light-warm 系(userMsg ≈ `#f0f0f0` 而非 `#343541`);两次的 diagnostics.jsonl 均出现 `code:"attach_settle_scheme"`、`details.probeApi:"colors"`、`details.report:"997;2"` |
|
|
163
|
-
| U2 | kill switch 回退 | 用户实测 | 同 U1 前置两步 + `AGENT_BOARD_FORWARD_TERMINAL_QUERIES=0` 起宿主,重复 U1 场景 | 无探测(diagnostics `details.outcome:"suppressed"`)、无报错,行为回到修复前(dark) |
|
|
164
|
-
|
|
165
|
-
分层说明:A1-A6/A9 用 unit(行为可由注入 fake 完全证明,不跨进程);A7 必须 fake tui + 真实 PTY
|
|
166
|
-
跨组件,用 integration;A8 是类型/构建/既有集成回归(`npm run verify` 是仓约定的 done 门)。
|
|
167
|
-
U1/U2 依赖真实终端 + 真实宿主 pi 进程与 board 运行时,无法在自动化环境稳定复现(需要真实
|
|
168
|
-
OSC 11 应答方与 board 级重启),故为用户实测;可执行时机 = 修复合入 main 后按前置两步更新
|
|
169
|
-
运行时副本并重启宿主 pi。
|
|
170
|
-
|
|
171
|
-
## 4. 可测性拆分设计(硬约束)
|
|
172
|
-
|
|
173
|
-
- `resolveProbeApi`:纯检测器,落点 `src/core/terminal-query-sequences.mjs`。三种 tui
|
|
174
|
-
形状(仅新 / 仅旧 / 皆无)直测 → 形成 A1/A2/A3 的测试边界:**API 形状判定不依赖真实
|
|
175
|
-
pi-tui**;参数与返回使用本地结构类型(`TerminalColorsLike` 等),禁止 import pi-tui 的
|
|
176
|
-
`TerminalColors`。
|
|
177
|
-
- `backgroundRgbFromTerminalColors`:纯映射函数,独立于探测与发送 → A4 边界。
|
|
178
|
-
- `probeAndReportRealTerminalColorScheme`:编排层,副作用序列(query→map→send→diagnose)。
|
|
179
|
-
unit 测试以注入 fake(tui、send 捕获、onDiagnostic 捕获)断言 → A1/A3/A5/A6/A9 边界:
|
|
180
|
-
**编排测试不碰真 PTY / 真 socket**。
|
|
181
|
-
- 诊断 wiring(两处 openPtyAttach → appendDiagnostic):A6 用 `normalizeDiagnostic` 直证
|
|
182
|
-
组件发出的 patch schema 兼容(字段在白名单内存活);`appendDiagnostic` 本身已有 core
|
|
183
|
-
层测试,不在本期改动范围。
|
|
184
|
-
- A7 的真实 PTY 边界:复用 detach-gate-smoke harness(fake tui + 真 node-pty 子进程),
|
|
185
|
-
不新造 harness。
|
|
186
|
-
- 实现不得把 resolveProbeApi / backgroundRgbFromTerminalColors 的逻辑内联回编排函数
|
|
187
|
-
(保持可测边界);不得在编排层引入对 pi-tui 版本的硬编码判断。
|
|
188
|
-
|
|
189
|
-
## 5. 开放问题
|
|
190
|
-
|
|
191
|
-
1. devDeps 0.79.8 → 1.0.x 迁移:单独开 issue(类型面变化大:TUI 接口、扩展 API 均有改动;
|
|
192
|
-
迁移后本 spec 的本地结构类型与 cast 可去除)。是否现在开,等用户确认。
|
|
193
|
-
2. 「宿主已知 scheme 缓存 + settle 重发」(issue 方向 3 保险丝):本期不做;若 U1 实测发现
|
|
194
|
-
WezTerm 下探测超时率不为零,再评估。
|
|
195
|
-
3. `pendingTerminalColorQueries` 超时条目是否出队(上游细节):不影响本期决策(每 attach
|
|
196
|
-
一次 + onLateReply 的用量与 pi 自身启动探测同量级);如后续观察到吞应答再上游报 bug。
|
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
# Spec: #167 — middle-click paste in attach views under pi ≥1.0 fullscreen TUI
|
|
2
|
-
|
|
3
|
-
Status: DRAFT (awaiting user approval — github-issue-driven step 4 gate)
|
|
4
|
-
Issue: https://github.com/zhuxixi/pi-agent-board/issues/167
|
|
5
|
-
Date: 2026-10-09
|
|
6
|
-
|
|
7
|
-
## Problem (root cause, verified)
|
|
8
|
-
|
|
9
|
-
pi ≥1.0 defaults to fullscreen TUI. `TuiAltScreen` parses and consumes all SGR mouse
|
|
10
|
-
sequences itself; overlays receive mouse only through the normalized `handleMouse(event)`
|
|
11
|
-
API. `PtyAttachComponent` implements only the legacy `handleInput(data)` path, so its
|
|
12
|
-
middle-click paste (`pastePrimarySelection()`, commit a1c5f08) never runs in fullscreen.
|
|
13
|
-
Regular mode (`pi --tui-mode regular`) is unaffected. Evidence: pi-tui 1.1.0
|
|
14
|
-
`tui-alt-screen.js:520-526`, `tui.js:507-532`; research notes in
|
|
15
|
-
`~/.claude/github-issue-driven/zhuxixi/pi-agent-board/issue-167/research/`.
|
|
16
|
-
|
|
17
|
-
## Goal / non-goals
|
|
18
|
-
|
|
19
|
-
**Goal**: middle-click inside the attach view pastes the X11 PRIMARY selection into the
|
|
20
|
-
hosted session in fullscreen mode, with the same semantics and escape hatch as regular
|
|
21
|
-
mode; regular mode behavior unchanged.
|
|
22
|
-
|
|
23
|
-
**Non-goals** (explicitly deferred):
|
|
24
|
-
- X11 PRIMARY *mirroring* of selections made in fullscreen (pi's own fullscreen selection
|
|
25
|
-
writes CLIPBOARD, not PRIMARY). Taking over left-button events would displace pi's
|
|
26
|
-
fullscreen selection — needs its own design (follow-up).
|
|
27
|
-
- #169 (attach teardown disabling outer-terminal mouse reporting) — separate issue/PR.
|
|
28
|
-
- Any wheel behavior change (today's defer-to-overlay fallback keeps working; we return
|
|
29
|
-
`undefined` for wheel).
|
|
30
|
-
|
|
31
|
-
## Design
|
|
32
|
-
|
|
33
|
-
### 1. Pure decision helper (new, `src/core/pty-scroll.mjs` — same module that owns
|
|
34
|
-
`parseMouseInputChunk`)
|
|
35
|
-
|
|
36
|
-
```js
|
|
37
|
-
// Returns the attach-surface action for a normalized pi-tui mouse event.
|
|
38
|
-
// "paste-primary": read X11 PRIMARY and forward as input (middle-click paste).
|
|
39
|
-
// null: not ours — let the event fall through untouched (today's behavior).
|
|
40
|
-
resolveAttachMouseAction(event, { nativePasteEnabled }) → "paste-primary" | null
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
Decision table (`event.type`, `event.button`, `nativePasteEnabled`):
|
|
44
|
-
|
|
45
|
-
| type | button | nativePasteEnabled | result |
|
|
46
|
-
| --- | --- | --- | --- |
|
|
47
|
-
| press | middle | true | `"paste-primary"` |
|
|
48
|
-
| press | middle | false | `null` (kill switch = today's silent drop) |
|
|
49
|
-
| press/release/click | left/right/none | * | `null` (pi fullscreen selection & default behavior untouched) |
|
|
50
|
-
| wheel | any | * | `null` (existing defer-to-`handleInput` path preserved) |
|
|
51
|
-
| move/drag | any | * | `null` |
|
|
52
|
-
|
|
53
|
-
`nativePasteEnabled` is computed once per call site from
|
|
54
|
-
`process.env.AGENT_BOARD_ATTACH_NATIVE_PASTE !== "0"` (same rule as `pastePrimarySelection`).
|
|
55
|
-
|
|
56
|
-
### 2. Component wiring (`src/ui/pty-attach.ts`)
|
|
57
|
-
|
|
58
|
-
```ts
|
|
59
|
-
handleMouse(event: TuiMouseEvent): TuiMouseEventResult | undefined {
|
|
60
|
-
const action = resolveAttachMouseAction(event, {
|
|
61
|
-
nativePasteEnabled: process.env.AGENT_BOARD_ATTACH_NATIVE_PASTE !== "0",
|
|
62
|
-
});
|
|
63
|
-
if (action === "paste-primary") {
|
|
64
|
-
this.clearPendingClick();
|
|
65
|
-
this.clearSelection();
|
|
66
|
-
this.pastePrimarySelection();
|
|
67
|
-
return { handled: true, render: false };
|
|
68
|
-
}
|
|
69
|
-
return undefined;
|
|
70
|
-
}
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
- `{ handled: true }` stops propagation and suppresses alt-screen fallback for that
|
|
74
|
-
press; `render: false` because a successful paste repaints via child output (async)
|
|
75
|
-
and a failed one (no xclip) needs no repaint.
|
|
76
|
-
- Legacy `handleInput` raw path stays exactly as is — regular mode and wheel keep working.
|
|
77
|
-
- No changes to outer-terminal mouse-mode writes (`MOUSE_ENABLE`/`MOUSE_DISABLE`) — #169.
|
|
78
|
-
|
|
79
|
-
### 3. devDependencies bump (enabling task)
|
|
80
|
-
|
|
81
|
-
Bump `@earendil-works/pi-tui` and `@earendil-works/pi-coding-agent` devDeps 0.79.8 → 1.1.0
|
|
82
|
-
(match the runtime the extension actually runs under). Gate: `npm run typecheck` +
|
|
83
|
-
`npm run verify` green. If unrelated type breakage from 0.79.8→1.1.0 turns out to be large,
|
|
84
|
-
fallback: keep 0.79.8 and declare local structural `TuiMouseEvent`/result types in the
|
|
85
|
-
component — decision point recorded in the plan; the bump is the preferred path.
|
|
86
|
-
|
|
87
|
-
## Acceptance matrix
|
|
88
|
-
|
|
89
|
-
| ID | 功能点 | 验收方式 | 具体验证 | 通过标准 |
|
|
90
|
-
|----|--------|----------|----------|----------|
|
|
91
|
-
| A1 | 决策表:哪些归一化鼠标事件触发中键粘贴 | 自动化验证(unit) | `node --test test/pty-scroll.test.mjs`(新增用例直接调 `resolveAttachMouseAction`,覆盖决策表全部行 + `AGENT_BOARD_ATTACH_NATIVE_PASTE=0`) | 新增用例全绿,且现有点击/滚轮用例不回归 |
|
|
92
|
-
| A2 | 组件接线:`PtyAttachComponent.handleMouse` 中键 press → 读 PRIMARY → 作为 input 发往 attach socket;kill switch 时返回 undefined | 自动化验证(integration,组件级场景) | 新增 `test-support/pty-attach-mouse-scenario.ts`(仿 `pty-attach-protocol-scenario.ts`:fake tui + stub-`xclip` 目录 prepend PATH 打印 fixture 文本),由 `test/pty-attach-mouse.test.mjs` 以 `node --experimental-transform-types` 运行并断言 JSON 输出 | ① 中键 press 返回 `{handled:true}` 且 socket 收到 `{type:"input",data:fixture}`;② 同场景 legacy `handleInput` 注入 SGR 中键序列行为一致(regular 路径回归);③ `AGENT_BOARD_ATTACH_NATIVE_PASTE=0` 时 `handleMouse` 返回 undefined 且无 input 发出 |
|
|
93
|
-
| A3 | 类型与构建在 pi-tui 1.1.0 下成立 | 自动化验证(static/build) | `npm run typecheck`、`npm run verify`(含 pack:dry) | 全部通过,无 0.79.8→1.1.0 引入的未处理类型错误 |
|
|
94
|
-
| A4 | 全部既有行为不回归 | 自动化验证(unit+integration,既有套件) | `npm test` / `npm run test:coverage` | 既有测试全绿,覆盖阈值不降 |
|
|
95
|
-
| U1 | 真实终端里 fullscreen 中键粘贴生效 | 用户实测 | ① pi 1.1.0 fullscreen → `/agent-board` → attach;② 在别的 X11 应用选中一段文字;③ 在 attach 视图内中键 | 子 session 收到粘贴文本并有反应;同机 `pi --tui-mode regular` 行为不变;`AGENT_BOARD_ATTACH_NATIVE_PASTE=0` 后中键回到无反应 |
|
|
96
|
-
|
|
97
|
-
## Testability split design (hard constraint for implementation)
|
|
98
|
-
|
|
99
|
-
- **Pure core**: `resolveAttachMouseAction(event, {nativePasteEnabled})` — plain function,
|
|
100
|
-
no I/O, no env reads inside (env read at call site). Unit-tested decision table (A1).
|
|
101
|
-
Test boundary: `src/core/pty-scroll.mjs` exports; tests import only this.
|
|
102
|
-
- **I/O shell**: `PtyAttachComponent.handleMouse` maps action → `pastePrimarySelection()`
|
|
103
|
-
(existing xclip spawn) + result object. Tested at component level (A2) through public
|
|
104
|
-
seams only: `handleMouse`/`handleInput` in, fake tui terminal writes + attach-socket
|
|
105
|
-
messages out, `xclip` stubbed via PATH. No test-only constructor options.
|
|
106
|
-
- The pure function must not be re-coupled into the component (no inline decision logic).
|
|
107
|
-
|
|
108
|
-
## Risks / notes
|
|
109
|
-
|
|
110
|
-
- devDep bump may expose type breakage elsewhere (0.79.8 typings vs 1.1.0). Contained by
|
|
111
|
-
A3 as its own task; fallback documented above.
|
|
112
|
-
- `AGENT_BOARD_ATTACH_MOUSE=0` does not disable the new path (it governs outer-terminal
|
|
113
|
-
mode writes only); in fullscreen the alt screen owns reporting regardless. Documented
|
|
114
|
-
here as intended.
|
|
115
|
-
- Behavior when xclip is absent: unchanged silent no-op (existing contract), but the
|
|
116
|
-
event is still consumed (`handled:true`) — same as regular mode today.
|
|
117
|
-
|
|
118
|
-
## Deliverables
|
|
119
|
-
|
|
120
|
-
- `src/core/pty-scroll.mjs`: + `resolveAttachMouseAction`
|
|
121
|
-
- `src/ui/pty-attach.ts`: + `handleMouse`
|
|
122
|
-
- `package.json`: devDeps bump (pi-tui, pi-coding-agent → 1.1.0)
|
|
123
|
-
- `test/pty-scroll.test.mjs`: decision-table cases
|
|
124
|
-
- `test-support/pty-attach-mouse-scenario.ts` + `test/pty-attach-mouse.test.mjs`
|
|
125
|
-
- CHANGELOG entry via release tooling at release time (not in this PR)
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
Drafted: zai-coding-cn/glm-5.3 (selected) · zai-coding-cn/glm-5.3 (physical) @ 2026-10-09T12:49:41Z
|
|
2
|
-
|
|
3
|
-
# Spec: #169 — detaching from a PTY attach view kills outer-terminal mouse reporting for the rest of the fullscreen session
|
|
4
|
-
|
|
5
|
-
Status: DRAFT (awaiting spec-cross-review, then user approval — github-issue-driven step 4 gates)
|
|
6
|
-
Issue: https://github.com/zhuxixi/pi-agent-board/issues/169
|
|
7
|
-
Repo state baseline: main @ `fee345c` (post-#171)
|
|
8
|
-
|
|
9
|
-
## Problem (root cause, verified from source)
|
|
10
|
-
|
|
11
|
-
In fullscreen mode (pi ≥1.0 default) `TuiAltScreen` owns outer-terminal mouse reporting:
|
|
12
|
-
it writes the enable set `?1000h?1002h?1003h?1004h?1006h` exactly once in
|
|
13
|
-
`beforeTerminalStart` (pi-tui 1.1.0 `tui-alt-screen.js:194`) and disables it only at
|
|
14
|
-
`beforeTerminalStop` (:196-206); there is no re-assert path. `PtyAttachComponent` nonetheless
|
|
15
|
-
manages the outer terminal's mouse mode itself — ctor writes
|
|
16
|
-
`MOUSE_DISABLE`+`MOUSE_ENABLE` (downgrading `?1003h`/`?1004h`), and `close()` writes
|
|
17
|
-
`MOUSE_DISABLE` unconditionally (src/ui/pty-attach.ts:278-280, :1524 via
|
|
18
|
-
`disableMouseScroll` :882 — which, unlike `enableMouseScroll`, has no env guard). After
|
|
19
|
-
the first detach, pi-tui still believes mouse is on but the terminal stopped sending
|
|
20
|
-
events: fullscreen selection / copy-on-select / wheel / click regions are dead until pi
|
|
21
|
-
restarts. In regular mode the same writes are correct (board is the sole mouse owner).
|
|
22
|
-
|
|
23
|
-
Full evidence: `research/2026-10-09-fullscreen-mouse-ownership.md`,
|
|
24
|
-
`research/2026-10-09-repo-state-and-testability.md` in this directory.
|
|
25
|
-
|
|
26
|
-
## Goal / non-goals
|
|
27
|
-
|
|
28
|
-
**Goal**: in fullscreen mode the attach component performs **zero** outer-terminal
|
|
29
|
-
mouse-mode writes for its entire lifecycle (ctor, refresh timers, connect, resize,
|
|
30
|
-
close) — `TuiAltScreen` owns the mode and its full `?1003h/?1004h` set survives
|
|
31
|
-
attach AND detach. Regular-mode behavior (including the
|
|
32
|
-
disable→enable→refresh pairing and the kill-switch semantics) is unchanged. Old pi
|
|
33
|
-
runtimes without `tui.mode` keep today's (regular) behavior.
|
|
34
|
-
|
|
35
|
-
**Non-goals**:
|
|
36
|
-
- X11 PRIMARY mirroring of the fullscreen-native selection (deferred with #167).
|
|
37
|
-
- Any pi-tui change or new re-assert API (unnecessary).
|
|
38
|
-
- Wheel/scroll/selection behavior inside attach views (unchanged by design: deferred
|
|
39
|
-
wheel still reaches `handleInput`; clicks via `handleMouse` from #171).
|
|
40
|
-
- #170 (reply guard), #168 (session resume) — unrelated paths.
|
|
41
|
-
|
|
42
|
-
## Design
|
|
43
|
-
|
|
44
|
-
### 1. Pure decision helper (new, `src/core/pty-scroll.mjs` — mouse helpers live here)
|
|
45
|
-
|
|
46
|
-
```js
|
|
47
|
-
/**
|
|
48
|
-
* Issue #169: whether the attach surface should manage the OUTER terminal's mouse
|
|
49
|
-
* mode. Only in regular TUI mode — in fullscreen, TuiAltScreen owns mouse reporting
|
|
50
|
-
* (asserted once at startup, never re-asserted), so any write from here can only
|
|
51
|
-
* downgrade or kill it. Old runtimes without tui.mode behave as regular.
|
|
52
|
-
* Pure: mode and the env-derived flag are resolved at the call site.
|
|
53
|
-
*/
|
|
54
|
-
shouldOwnOuterMouseMode(mode, mouseEnabled) → boolean // mode !== "fullscreen" && mouseEnabled
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
### 2. Component gating (`src/ui/pty-attach.ts`)
|
|
58
|
-
|
|
59
|
-
All outer-terminal mouse writes funnel through exactly two private methods —
|
|
60
|
-
`enableMouseScroll()` (:852) and `disableMouseScroll()` (:882). Gate both:
|
|
61
|
-
|
|
62
|
-
```ts
|
|
63
|
-
private ownsOuterMouseMode(): boolean {
|
|
64
|
-
return shouldOwnOuterMouseMode(this.tui.mode, this.mouseScrollEnabled());
|
|
65
|
-
}
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
- `enableMouseScroll()`: early-return `if (!this.ownsOuterMouseMode())` (replaces the
|
|
69
|
-
current `mouseScrollEnabled()` check; also gates the `XTSHIFTESCAPE_SELECT` write).
|
|
70
|
-
- `disableMouseScroll()`: early-return `if (!this.ownsOuterMouseMode())` (new guard —
|
|
71
|
-
fixes both the fullscreen teardown kill and the flagged unguarded disable under
|
|
72
|
-
`AGENT_BOARD_ATTACH_MOUSE=0`).
|
|
73
|
-
- `refreshMouseScrollMode()` keeps its `mouseScrollEnabled()` early-return (cheap) and
|
|
74
|
-
its timers call the now-gated `enableMouseScroll` — no other call-site changes
|
|
75
|
-
(ctor :278-280, connect :487, resize :1203, close :1524 all route through the two
|
|
76
|
-
choke points).
|
|
77
|
-
- Comment on `ownsOuterMouseMode()` noting `tui.mode` is typed non-optional but may be
|
|
78
|
-
absent on old runtimes — strict inequality handles it.
|
|
79
|
-
|
|
80
|
-
### Resulting behavior matrix
|
|
81
|
-
|
|
82
|
-
| tui.mode | AGENT_BOARD_ATTACH_MOUSE | ctor/connect/resize writes | close() write | fullscreen session after detach |
|
|
83
|
-
| --- | --- | --- | --- | --- |
|
|
84
|
-
| fullscreen | any | none | none | mouse alive (BUG FIXED) |
|
|
85
|
-
| regular / undefined (old runtime) | on (default) | today's pairing | MOUSE_DISABLE | n/a (unchanged) |
|
|
86
|
-
| regular / undefined | off | none | none | n/a (also fixes unguarded-disable defect) |
|
|
87
|
-
| fullscreen (host built with TuiAltScreenOptions.mouse: false) | n/a | none | none | consistent: host explicitly disabled mouse; nothing owns it — attach-view mouse features inert by host choice |
|
|
88
|
-
|
|
89
|
-
## Acceptance matrix
|
|
90
|
-
|
|
91
|
-
| ID | 功能点 | 验收方式 | 具体验证 | 通过标准 |
|
|
92
|
-
|----|--------|----------|----------|----------|
|
|
93
|
-
| A1 | 鼠标模式所有权决策表(mode × env 开关) | 自动化验证(unit) | `node --test test/pty-scroll.test.mjs`(新增用例直接调 `shouldOwnOuterMouseMode`,覆盖 6 格:fullscreen×on/off、regular×on/off、undefined×on/off) | 新增用例全绿,现有点击/滚轮/中键用例不回归 |
|
|
94
|
-
| A2 | fullscreen 全生命周期零外层鼠标写;regular 配对保留;kill switch 零写 | 自动化验证(integration,组件级场景) | 新增 `test-support/pty-attach-mouse-mode-scenario.ts`(fake tui 的 `terminal.write` 采集字节 + `mode` 可控 + 不可达 socketPath),由 `test/pty-attach-mouse-mode.test.mjs` 以 `node --experimental-transform-types` 运行断言 JSON 输出 | ① `mode:"fullscreen"`:ctor+250ms 刷新窗+close 全程 0 个 `?1000/?1002/?1003/?1004/?1006/XTSHIFTESCAPE` 写;② `mode:"regular"`:ctor 有 `MOUSE_ENABLE`、close 有 `MOUSE_DISABLE`;③ regular+`AGENT_BOARD_ATTACH_MOUSE=0`:全程 0 写(含 close);④ 无 `mode` 字段的 fake tui 行为同 regular |
|
|
95
|
-
| A3 | 类型与构建 | 自动化验证(static/build) | `npm run typecheck`、`npm run verify`(含 pack:dry) | 全部通过 |
|
|
96
|
-
| A4 | 既有行为不回归(含 #171 中键粘贴场景) | 自动化验证(unit+integration,既有套件) | `npm test` / `npm run test:coverage` | 既有测试全绿,覆盖阈值不降 |
|
|
97
|
-
| U1 | 真机:detach 后外层鼠标存活 | 用户实测 | ① pi ≥1.0 fullscreen → `/agent-board` → attach → detach(ctrl+\);② dashboard 里拖选文字 → 应高亮且 copy-on-select 生效;③ 滚轮/点击区域正常;④ attach 中 pi 自身全屏选中/复制正常(`?1003h/?1004h` 不再被降级);⑤ `pi --tui-mode regular` 下 attach→detach 行为与从前一致 | ①–⑤ 全部符合;修复前 ①③④ 在 detach 后失效 |
|
|
98
|
-
|
|
99
|
-
## Testability split design (hard constraint for implementation)
|
|
100
|
-
|
|
101
|
-
- **Pure core**: `shouldOwnOuterMouseMode(mode, mouseEnabled)` — plain function, no env
|
|
102
|
-
reads inside (both args resolved at the call site). Unit-tested decision table (A1).
|
|
103
|
-
Test boundary: `src/core/pty-scroll.mjs` exports; tests import only this.
|
|
104
|
-
- **I/O shell**: `PtyAttachComponent.ownsOuterMouseMode()` reads `this.tui.mode` +
|
|
105
|
-
`this.mouseScrollEnabled()` and delegates to the pure helper; the two write methods
|
|
106
|
-
keep ALL terminal writes. Component-level tests (A2) drive only public seams
|
|
107
|
-
(constructor, time, `dispose()`) and observe `terminal.write` bytes of a fake tui —
|
|
108
|
-
no test-only constructor options, no reading private state.
|
|
109
|
-
- The pure helper must not be re-inlined into the component; the two choke-point methods
|
|
110
|
-
must remain the only places that write mouse-mode/XTSHIFTESCAPE sequences.
|
|
111
|
-
|
|
112
|
-
## Risks / notes
|
|
113
|
-
|
|
114
|
-
- A2 uses an unreachable socketPath to keep the scenario runner-free; if the ctor's
|
|
115
|
-
connect-error path proves non-deterministic for the write-spy assertions, fall back to
|
|
116
|
-
the #171-style real-runner scenario asserting the same facts around a real
|
|
117
|
-
attach/detach (decision at plan time; record whichever is used).
|
|
118
|
-
- Existing scenarios' fake tuis have no `mode` field → they stay on the regular path and
|
|
119
|
-
don't assert terminal bytes → unaffected by the gating.
|
|
120
|
-
- `AGENT_BOARD_ATTACH_MOUSE` semantics in fullscreen become moot (no writes either way);
|
|
121
|
-
document in the behavior matrix rather than redefining the switch.
|
|
122
|
-
|
|
123
|
-
## Deliverables
|
|
124
|
-
|
|
125
|
-
- `src/core/pty-scroll.mjs`: + `shouldOwnOuterMouseMode`
|
|
126
|
-
- `src/ui/pty-attach.ts`: + `ownsOuterMouseMode()` private method; guards in
|
|
127
|
-
`enableMouseScroll` / `disableMouseScroll`
|
|
128
|
-
- `test/pty-scroll.test.mjs`: decision-table cases
|
|
129
|
-
- `test-support/pty-attach-mouse-mode-scenario.ts` + `test/pty-attach-mouse-mode.test.mjs`
|
|
130
|
-
- CHANGELOG entry via release tooling at release time (not in this PR)
|