@zhuxixi/pi-agent-board 0.3.0 → 0.3.1
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/README.md +3 -2
- package/docs/superpowers/plans/2026-08-21-attach-coldstart-jiggle-rearm.md +797 -0
- package/docs/superpowers/plans/2026-08-21-issue-14-needs-input-no-auto-done.md +576 -0
- package/docs/superpowers/plans/2026-08-22-rename-state-labels.md +182 -0
- package/docs/superpowers/plans/2026-08-22-stable-list-order.md +51 -0
- package/docs/superpowers/specs/2026-08-21-attach-coldstart-jiggle-rearm-design.md +106 -0
- package/docs/superpowers/specs/2026-08-21-issue-14-needs-input-no-auto-done-design.md +109 -0
- package/docs/superpowers/specs/2026-08-22-rename-state-labels-design.md +80 -0
- package/package.json +1 -1
- package/runner/job-runner.mjs +28 -4
- package/src/core/auto-state.mjs +62 -7
- package/src/core/derive.mjs +4 -4
- package/src/core/events.mjs +3 -2
- package/src/core/pty-attach-jiggle-controller.mjs +112 -0
- package/src/core/pty-attach-jiggle-retry.mjs +29 -10
- package/src/core/pty-attach-render.mjs +21 -0
- package/src/core/rows.mjs +23 -8
- package/src/core/types.mjs +2 -2
- package/src/runtime/service.mjs +11 -1
- package/src/ui/dashboard.ts +2 -2
- package/src/ui/pty-attach.ts +26 -77
package/src/ui/pty-attach.ts
CHANGED
|
@@ -7,14 +7,8 @@ import type { Component, KeybindingsManager, TUI } from "@earendil-works/pi-tui"
|
|
|
7
7
|
import { CURSOR_MARKER, Key, matchesKey, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
|
|
8
8
|
import { isProbablyEmptyPiInputLine } from "../core/pty-input.mjs";
|
|
9
9
|
import { findHttpUrlAtCells, findWordRangeAtCells } from "../core/pty-links.mjs";
|
|
10
|
-
import { createAttachOutputRenderScheduler, nextAttachRender, shouldScheduleAttachRenderForMessage } from "../core/pty-attach-render.mjs";
|
|
11
|
-
import {
|
|
12
|
-
createJiggleRetryState,
|
|
13
|
-
feedOutput as feedJiggleRetry,
|
|
14
|
-
nextRetryDelay,
|
|
15
|
-
advanceRetry,
|
|
16
|
-
stopRetry,
|
|
17
|
-
} from "../core/pty-attach-jiggle-retry.mjs";
|
|
10
|
+
import { createAttachOutputRenderScheduler, nextAttachRender, projectPtyCursor, shouldScheduleAttachRenderForMessage } from "../core/pty-attach-render.mjs";
|
|
11
|
+
import { createJiggleRetryController } from "../core/pty-attach-jiggle-controller.mjs";
|
|
18
12
|
import { clampInt, parseMouseInputChunk, resolveWheelLines, scrollViewportTop, selectionDragScrollLines } from "../core/pty-scroll.mjs";
|
|
19
13
|
|
|
20
14
|
export type PtyAttachResult = { action: "detached" } | { action: "closed"; exitCode?: number | null };
|
|
@@ -53,6 +47,11 @@ const OSC52_MAX_BYTES = 1_000_000;
|
|
|
53
47
|
const OSC52_CARRY_MAX_BYTES = OSC52_MAX_BYTES + 4096;
|
|
54
48
|
const TERMINAL_PASSTHROUGH_MAX_BYTES = 5_000_000;
|
|
55
49
|
const TERMINAL_PASSTHROUGH_CARRY_MAX_BYTES = TERMINAL_PASSTHROUGH_MAX_BYTES + 4096;
|
|
50
|
+
/** Delay between the shrink and restore halves of a resize jiggle. 200ms keeps
|
|
51
|
+
* the two SIGWINCHs apart well beyond pi-tui's 16ms render throttle, so a busy
|
|
52
|
+
* (booting) child processes them as two separate renders instead of coalescing
|
|
53
|
+
* the pair into a net-zero size change. */
|
|
54
|
+
const JIGGLE_RESTORE_MS = 200;
|
|
56
55
|
const KITTY_IMAGE_PREFIX = "\x1b_G";
|
|
57
56
|
const ITERM2_FILE_PREFIX = "\x1b]1337;File=";
|
|
58
57
|
|
|
@@ -122,10 +121,19 @@ export class PtyAttachComponent implements Component {
|
|
|
122
121
|
private mouseRefreshTimers: Array<ReturnType<typeof setTimeout>> = [];
|
|
123
122
|
// Jiggle retry chain: re-send resize jiggle until we see a full-clear sequence
|
|
124
123
|
// in the PTY output, proving the child pi-tui did a fullRender and the replay
|
|
125
|
-
// garbage has been flushed.
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
private
|
|
124
|
+
// garbage has been flushed. The controller re-arms once on the child TUI's
|
|
125
|
+
// first frame so cold-start attaches get a fresh budget exactly when the
|
|
126
|
+
// child can finally observe a resize (issue #10).
|
|
127
|
+
private readonly jiggleRetry = createJiggleRetryController({
|
|
128
|
+
sendJiggle: () => this.forceChildRedraw(),
|
|
129
|
+
shouldFire: () => !this.closed && this.connected,
|
|
130
|
+
setTimeoutFn: (fn, ms) => {
|
|
131
|
+
const t = setTimeout(fn, ms);
|
|
132
|
+
t.unref?.();
|
|
133
|
+
return t;
|
|
134
|
+
},
|
|
135
|
+
clearTimeoutFn: (t) => clearTimeout(t as ReturnType<typeof setTimeout>),
|
|
136
|
+
});
|
|
129
137
|
private osc52Carry = "";
|
|
130
138
|
private passthroughCarry = "";
|
|
131
139
|
private readonly connectStartedAt = Date.now();
|
|
@@ -318,7 +326,7 @@ export class PtyAttachComponent implements Component {
|
|
|
318
326
|
this.send({ type: "hello", clientId: `ui-${Date.now()}`, wantOutput: true });
|
|
319
327
|
this.sendResize();
|
|
320
328
|
this.forceChildRedraw();
|
|
321
|
-
this.
|
|
329
|
+
this.jiggleRetry.start();
|
|
322
330
|
this.enableMouseScroll();
|
|
323
331
|
this.scheduleRender();
|
|
324
332
|
this.startAttachSettle();
|
|
@@ -804,56 +812,13 @@ export class PtyAttachComponent implements Component {
|
|
|
804
812
|
this.sendResize(cols, rows);
|
|
805
813
|
this.deferAttachSettle();
|
|
806
814
|
}
|
|
807
|
-
},
|
|
815
|
+
}, JIGGLE_RESTORE_MS);
|
|
808
816
|
this.redrawTimer.unref?.();
|
|
809
817
|
}
|
|
810
818
|
|
|
811
|
-
/**
|
|
812
|
-
private startJiggleRetry(): void {
|
|
813
|
-
if (this.jiggleRetryTimer) {
|
|
814
|
-
clearTimeout(this.jiggleRetryTimer);
|
|
815
|
-
this.jiggleRetryTimer = null;
|
|
816
|
-
}
|
|
817
|
-
this.jiggleRetryState = createJiggleRetryState();
|
|
818
|
-
this.clearCarry = "";
|
|
819
|
-
this.scheduleNextJiggleRetry();
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
/** Check socket output for full-clear sequence; cancel retry if found. */
|
|
819
|
+
/** Feed socket output into the jiggle retry controller (clear/frame detection). */
|
|
823
820
|
private checkClearSequence(data: string): void {
|
|
824
|
-
|
|
825
|
-
const result = feedJiggleRetry(this.jiggleRetryState, data, this.clearCarry);
|
|
826
|
-
this.jiggleRetryState = result.state;
|
|
827
|
-
this.clearCarry = result.carry;
|
|
828
|
-
if (result.clearFound) this.cancelJiggleRetry();
|
|
829
|
-
}
|
|
830
|
-
|
|
831
|
-
/** Schedule the next jiggle retry with backoff. */
|
|
832
|
-
private scheduleNextJiggleRetry(): void {
|
|
833
|
-
const delay = nextRetryDelay(this.jiggleRetryState);
|
|
834
|
-
if (delay === null) {
|
|
835
|
-
// Chain exhausted (max retries) — mark stopped so checkClearSequence
|
|
836
|
-
// short-circuits on subsequent output chunks.
|
|
837
|
-
this.jiggleRetryState = stopRetry(this.jiggleRetryState);
|
|
838
|
-
return;
|
|
839
|
-
}
|
|
840
|
-
this.jiggleRetryTimer = setTimeout(() => {
|
|
841
|
-
this.jiggleRetryTimer = null;
|
|
842
|
-
if (this.closed || !this.connected) return;
|
|
843
|
-
this.forceChildRedraw();
|
|
844
|
-
this.jiggleRetryState = advanceRetry(this.jiggleRetryState);
|
|
845
|
-
this.scheduleNextJiggleRetry();
|
|
846
|
-
}, delay);
|
|
847
|
-
this.jiggleRetryTimer.unref?.();
|
|
848
|
-
}
|
|
849
|
-
|
|
850
|
-
/** Cancel the jiggle retry chain. */
|
|
851
|
-
private cancelJiggleRetry(): void {
|
|
852
|
-
if (this.jiggleRetryTimer) {
|
|
853
|
-
clearTimeout(this.jiggleRetryTimer);
|
|
854
|
-
this.jiggleRetryTimer = null;
|
|
855
|
-
}
|
|
856
|
-
this.jiggleRetryState = stopRetry(this.jiggleRetryState);
|
|
821
|
+
this.jiggleRetry.feed(data);
|
|
857
822
|
}
|
|
858
823
|
|
|
859
824
|
private tryScrollBy(linesUp: number): boolean {
|
|
@@ -1009,7 +974,7 @@ export class PtyAttachComponent implements Component {
|
|
|
1009
974
|
// The PTY cursor: xterm buffer cursorY is relative to baseY (the viewport top of
|
|
1010
975
|
// the child terminal); cursorX may equal cols (one past the last cell). Only render
|
|
1011
976
|
// it when it lands inside the projected viewport.
|
|
1012
|
-
const cursor =
|
|
977
|
+
const cursor = projectPtyCursor(buf, start, height);
|
|
1013
978
|
for (let i = start; i < end; i++) {
|
|
1014
979
|
out.push(lineToAnsi(buf.getLine(i), reusable, this.term, i, selection, cursor));
|
|
1015
980
|
}
|
|
@@ -1019,7 +984,7 @@ export class PtyAttachComponent implements Component {
|
|
|
1019
984
|
|
|
1020
985
|
private close(): void {
|
|
1021
986
|
this.closed = true;
|
|
1022
|
-
this.
|
|
987
|
+
this.jiggleRetry.stop();
|
|
1023
988
|
this.disableMouseScroll();
|
|
1024
989
|
this.clearMouseRefreshTimers();
|
|
1025
990
|
this.clearPendingClick();
|
|
@@ -1177,22 +1142,6 @@ function lineToAnsi(
|
|
|
1177
1142
|
return out + "\x1b[0m";
|
|
1178
1143
|
}
|
|
1179
1144
|
|
|
1180
|
-
/**
|
|
1181
|
-
* Resolve the PTY cursor into viewport coordinates. xterm's buffer cursorY is relative
|
|
1182
|
-
* to baseY (the child terminal's viewport top); returns null when the cursor is outside
|
|
1183
|
-
* the projected window (e.g. the user scrolled up into history).
|
|
1184
|
-
*/
|
|
1185
|
-
function cursorInViewport(
|
|
1186
|
-
buf: XtermLike["buffer"]["active"],
|
|
1187
|
-
start: number,
|
|
1188
|
-
height: number,
|
|
1189
|
-
): { row: number; col: number } | null {
|
|
1190
|
-
if (typeof buf.cursorX !== "number" || typeof buf.cursorY !== "number" || buf.cursorX < 0 || buf.cursorY < 0) return null;
|
|
1191
|
-
const row = buf.baseY + buf.cursorY - start;
|
|
1192
|
-
if (row < 0 || row >= height) return null;
|
|
1193
|
-
return { row, col: buf.cursorX };
|
|
1194
|
-
}
|
|
1195
|
-
|
|
1196
1145
|
function attrKey(cell: BufferCellLike, selected = false, isCursor = false): string {
|
|
1197
1146
|
return [
|
|
1198
1147
|
selected ? 1 : 0,
|