@agfpd/iapeer 0.4.57 → 0.4.59
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/package.json +1 -1
- package/src/launch/adapters/codex.ts +22 -5
- package/src/supervisor/daemon.ts +36 -25
package/package.json
CHANGED
|
@@ -21,7 +21,11 @@
|
|
|
21
21
|
// Active 253-261) + the keys the boot loop sends (382-400),
|
|
22
22
|
// newestCodexSessionMtime + codexSessionCwd (200-234), and
|
|
23
23
|
// answerPermissionDialog's codex branch (302-306: Down then Enter) over
|
|
24
|
-
// dialogs.ts codexApprovalActive (39-45).
|
|
24
|
+
// dialogs.ts codexApprovalActive (39-45). NB the permission autopilot was
|
|
25
|
+
// NOT ported (no answerPermissionDialog in this codebase): the permission
|
|
26
|
+
// class is closed STRUCTURALLY instead — every peer runs
|
|
27
|
+
// --dangerously-bypass-approvals-and-sandbox (buildArgs below) and init
|
|
28
|
+
// bakes default_tools_approval_mode="approve", so the dialog never shows.
|
|
25
29
|
//
|
|
26
30
|
// The doctrine -c value is the bare `model_instructions_file=<f>` token: the
|
|
27
31
|
// TOML value-side quotes in codex-start.sh:224 exist only because that string is
|
|
@@ -58,8 +62,8 @@ import type { ControlCommand, ControlPlan, LaunchAdapterConfig, LaunchSpec, Runt
|
|
|
58
62
|
* option 1 ("Yes, continue") is default-highlighted → ['Enter']
|
|
59
63
|
* (watcher.ts:386-390, codex-start.sh:261-264).
|
|
60
64
|
* - 'Hooks need review' — a new/changed plugin hooks.json; "Trust all and
|
|
61
|
-
* continue" is option 2, default
|
|
62
|
-
* (
|
|
65
|
+
* continue" is option 2, default selector (`›`) is on option 1 → a single
|
|
66
|
+
* '2' (select-by-number commits immediately — В41 live capture, 0.139.0).
|
|
63
67
|
*/
|
|
64
68
|
function codexUpdatePromptActive(pane: string): boolean {
|
|
65
69
|
return pane.includes('Update available!') && pane.includes('Press enter to continue')
|
|
@@ -213,12 +217,25 @@ export const codexAdapter: RuntimeAdapter = {
|
|
|
213
217
|
* A visible startup dialog → the keys that clear it (watcher.ts:382-400), else
|
|
214
218
|
* null. Update offer is matched first (it can stack ahead of the trust/hooks
|
|
215
219
|
* modals): decline with ['2','Enter']; dir-trust accepts with ['Enter']; hooks
|
|
216
|
-
* -review
|
|
220
|
+
* -review selects "Trust all and continue" by NUMBER — a single '2'.
|
|
221
|
+
*
|
|
222
|
+
* В41 — hooks-review was a blind ['Down','Enter'] burst: under load a swallowed
|
|
223
|
+
* Down left Enter confirming the WRONG default ("Review hooks" — the panel opens,
|
|
224
|
+
* the wake stalls; the same class claude had and replaced with cursor-verified
|
|
225
|
+
* stepping). CAPTURED LIVE (codex-cli 0.139.0, isolated CODEX_HOME, 03.07): the
|
|
226
|
+
* modal renders numbered options with a moving `›` selector
|
|
227
|
+
* › 1. Review hooks
|
|
228
|
+
* 2. Trust all and continue
|
|
229
|
+
* 3. Continue without trusting (hooks won't run)
|
|
230
|
+
* and a BARE DIGIT '2' selects AND commits option 2 immediately (no Enter needed;
|
|
231
|
+
* trusted_hash verified written). One key — nothing can be swallowed between two.
|
|
232
|
+
* The capture harness itself reproduced the burst failure live: a repeated Enter
|
|
233
|
+
* on a stale dir-trust frame landed on this modal and opened the review panel.
|
|
217
234
|
*/
|
|
218
235
|
bootDialogKeys(pane: string): string[] | null {
|
|
219
236
|
if (codexUpdatePromptActive(pane)) return ['2', 'Enter']
|
|
220
237
|
if (codexDirTrustActive(pane)) return ['Enter']
|
|
221
|
-
if (codexHooksReviewActive(pane)) return ['
|
|
238
|
+
if (codexHooksReviewActive(pane)) return ['2']
|
|
222
239
|
return null
|
|
223
240
|
},
|
|
224
241
|
|
package/src/supervisor/daemon.ts
CHANGED
|
@@ -95,6 +95,10 @@ const NAG_STUCK_MS = 10_000
|
|
|
95
95
|
// so without a budget firstAttach would never complete (frozen attach). On timeout we snapshot the
|
|
96
96
|
// current model; live-forward delivers the rest once the gate lifts.
|
|
97
97
|
const MODEL_STABLE_BUDGET_MS = 500
|
|
98
|
+
/** How long firstAttach waits for the child's SIGWINCH redraw wave to ARRIVE after the attach
|
|
99
|
+
* resize (first bytes; the В25-capped stability drain follows). Signal delivery + a full TUI
|
|
100
|
+
* redraw take tens of ms; a child that answers SIGWINCH with nothing just costs this timeout. */
|
|
101
|
+
const SIGWINCH_REDRAW_WAIT_MS = 300
|
|
98
102
|
|
|
99
103
|
// Bun-native PTY (Bun ≥1.3.5) is not yet in @types/bun — type the slice we use.
|
|
100
104
|
interface PtyChild {
|
|
@@ -422,36 +426,43 @@ export function runSupervisorDaemon(opts: SupervisorDaemonOptions): void {
|
|
|
422
426
|
}
|
|
423
427
|
writeGeometry(runDir, session, c, r) // record current child geometry → sidecar for warm-deliver / ready-gate readers
|
|
424
428
|
}
|
|
425
|
-
// FIRST attach for a client. Order
|
|
429
|
+
// FIRST attach for a client. Order for a clean, coloured, correctly-sized snapshot:
|
|
426
430
|
// 1. DRAIN to stability — pending child bytes were generated under the OLD geometry; parse them BEFORE a
|
|
427
431
|
// model resize (else they rewrap wrong), and capture bytes that land while the client is gated.
|
|
428
|
-
// 2. MODEL resize to the client geometry IF it differs —
|
|
429
|
-
// (xterm.js #3093)
|
|
430
|
-
//
|
|
431
|
-
//
|
|
432
|
-
//
|
|
433
|
-
//
|
|
432
|
+
// 2. MODEL + CHILD resize to the client geometry IF it differs — BOTH, BEFORE the snapshot. serialize()
|
|
433
|
+
// round-trips only at matching dims (xterm.js #3093); and the child's SIGWINCH must fire NOW so its
|
|
434
|
+
// full redraw is INGESTED BY THE MODEL (client still gated) instead of being live-forwarded on top
|
|
435
|
+
// of an already-sent snapshot. The prior order (child resized AFTER the snapshot) was the attach-dup
|
|
436
|
+
// root on a BUSY session (live incident, Артур 03.07): the post-snapshot SIGWINCH redraw arrived as
|
|
437
|
+
// live bytes whose relative cursor-ups no longer matched the client screen — the whole block painted
|
|
438
|
+
// AGAIN below the snapshot (stacked "Composing…" spinners) — and every busy tick in the window before
|
|
439
|
+
// the SIGWINCH was still OLD-width, wrapping into extra phantom rows. Reproduced: 2× spinner + 2×
|
|
440
|
+
// content copies in the client buffer; with this order: 1× each.
|
|
441
|
+
// 3. WAIT for the redraw wave — the same bounded stabilization as step 1 (В25 cap applies: a saturating
|
|
442
|
+
// stream snapshots at the deadline; ticks after the snapshot are already client-width, so they land
|
|
443
|
+
// cleanly via live-forward).
|
|
444
|
+
// 4. SNAPSHOT — serialize + send + lift gate, SYNCHRONOUS (no await between the last stability check and
|
|
445
|
+
// the send), so no live byte interleaves. A same-size reattach touches the child ZERO times → pure
|
|
446
|
+
// snapshot, no re-render (step 2 is a no-op).
|
|
447
|
+
// Wait for the FIRST child bytes after a resize (the SIGWINCH redraw wave arriving), or the
|
|
448
|
+
// timeout for a child that answers SIGWINCH with nothing (a plain pipe-like program). A bare
|
|
449
|
+
// stability check is NOT enough here: signal delivery + redraw generation take milliseconds,
|
|
450
|
+
// and a busy child's tick cadence has quiet gaps — the stability loop returns in such a gap
|
|
451
|
+
// BEFORE the redraw arrives (proven by the repro byte log: the \x1b[NA redraw came as frame #2,
|
|
452
|
+
// after the snapshot), which re-creates the dup exactly.
|
|
453
|
+
const awaitSeqBump = async (timeoutMs: number): Promise<void> => {
|
|
454
|
+
const seq = writeSeq
|
|
455
|
+
const deadline = Date.now() + timeoutMs
|
|
456
|
+
while (writeSeq === seq && Date.now() < deadline) await Bun.sleep(15)
|
|
457
|
+
}
|
|
434
458
|
const firstAttach = async (s: { write(b: Buffer): number }, c: number, r: number): Promise<void> => {
|
|
435
459
|
await awaitModelParsedStable()
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
} catch {
|
|
441
|
-
/* transient */
|
|
442
|
-
}
|
|
443
|
-
cols = c
|
|
444
|
-
rows = r
|
|
445
|
-
}
|
|
446
|
-
sendSnapshot(s) // serialize the model → send → lift gate (synchronous, no interleave)
|
|
447
|
-
if (changed) {
|
|
448
|
-
try {
|
|
449
|
-
child.terminal.resize(c, r) // honest one-SIGWINCH resize (post-snapshot, forwarded live), like native
|
|
450
|
-
} catch {
|
|
451
|
-
/* transient */
|
|
452
|
-
}
|
|
453
|
-
writeGeometry(runDir, session, c, r) // record the new current child geometry for the warm-deliver readers
|
|
460
|
+
if (c !== cols || r !== rows) {
|
|
461
|
+
applyGeometry(c, r) // model + child together — one honest SIGWINCH, BEFORE the snapshot
|
|
462
|
+
await awaitSeqBump(SIGWINCH_REDRAW_WAIT_MS) // the redraw wave ARRIVES (or the child has none)
|
|
463
|
+
await awaitModelParsedStable() // …then drain it to stability (bounded, В25 cap)
|
|
454
464
|
}
|
|
465
|
+
sendSnapshot(s) // serialize the post-redraw model → send → lift gate (synchronous, no interleave)
|
|
455
466
|
}
|
|
456
467
|
// DETACH = NO RESIZE (emulator invariant). A detached hosted TUI keeps its LAST client geometry — it is
|
|
457
468
|
// NEVER shrunk back to serve. The old FINDING-2 revert shrank the child into a tiny viewport, making claude
|