@dfosco/storyboard 0.6.0-beta.7 → 0.6.0-beta.9
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
CHANGED
|
@@ -549,7 +549,9 @@ export class HotPool {
|
|
|
549
549
|
const poll = setInterval(() => {
|
|
550
550
|
try {
|
|
551
551
|
const paneContent = execSync(
|
|
552
|
-
|
|
552
|
+
// H1: include scrollback so the readiness echo can still be
|
|
553
|
+
// matched after the agent's TUI repaints over it.
|
|
554
|
+
`tmux capture-pane -t "${tmuxName}" -p -S -200`,
|
|
553
555
|
{ encoding: 'utf8', timeout: 1000 }
|
|
554
556
|
)
|
|
555
557
|
// Strip ANSI escape sequences — agent CLIs use heavy formatting
|
|
@@ -3148,7 +3148,8 @@ export function Default() {
|
|
|
3148
3148
|
const poll = setInterval(() => {
|
|
3149
3149
|
if (sent) { clearInterval(poll); return }
|
|
3150
3150
|
try {
|
|
3151
|
-
|
|
3151
|
+
// H1: include scrollback so the echo survives Copilot's TUI repaint.
|
|
3152
|
+
const pane = execSync(`tmux capture-pane -t "${tmuxName}" -p -S -200`, { encoding: 'utf8', timeout: 1000 })
|
|
3152
3153
|
if (pane.includes(readinessSignal)) {
|
|
3153
3154
|
sent = true
|
|
3154
3155
|
clearInterval(poll)
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
25
|
import { execSync } from 'node:child_process'
|
|
26
|
-
import { readFileSync, mkdirSync, writeFileSync, renameSync, existsSync, unlinkSync } from 'node:fs'
|
|
26
|
+
import { readFileSync, mkdirSync, writeFileSync, renameSync, existsSync, unlinkSync, rmSync } from 'node:fs'
|
|
27
27
|
import { resolve, join, dirname } from 'node:path'
|
|
28
28
|
import { fileURLToPath } from 'node:url'
|
|
29
29
|
import { tmpdir } from 'node:os'
|
|
@@ -1164,7 +1164,15 @@ function handleConnection(ws, widgetId, canvasId, prettyName, widgetStartupComma
|
|
|
1164
1164
|
// pool-keyed for the life of the warm process, so the user-level
|
|
1165
1165
|
// hook always writes there, not to the widget-keyed file.)
|
|
1166
1166
|
const postStartup = resolvedAgentCfg?.postStartup || null
|
|
1167
|
-
|
|
1167
|
+
// ── H2 fix: skip readiness re-poll on warm handoff.
|
|
1168
|
+
// The hot pool already verified readiness when it warmed this session.
|
|
1169
|
+
// Re-polling for a one-shot signal like Copilot's "Environment loaded:"
|
|
1170
|
+
// against an already-running TUI always misses (the echo has long
|
|
1171
|
+
// since scrolled off the visible pane, and `tmux capture-pane -p`
|
|
1172
|
+
// returns only the visible region). Falling through to the 30s
|
|
1173
|
+
// timeout fallback was delaying /allow-all, identity, role, and
|
|
1174
|
+
// bindWidget by ~30s for every warm Copilot widget.
|
|
1175
|
+
const readinessSignal = null
|
|
1168
1176
|
setTimeout(() => {
|
|
1169
1177
|
let completed = false
|
|
1170
1178
|
const finalize = () => {
|
|
@@ -1244,16 +1252,26 @@ function handleConnection(ws, widgetId, canvasId, prettyName, widgetStartupComma
|
|
|
1244
1252
|
const envScriptDir = join(cwd, '.storyboard', 'terminals')
|
|
1245
1253
|
try { mkdirSync(envScriptDir, { recursive: true }) } catch { /* empty */ }
|
|
1246
1254
|
const envScriptPath = join(envScriptDir, `${widgetId}.env.sh`)
|
|
1255
|
+
// ── H4: file marker for readiness. Terminal-state-independent —
|
|
1256
|
+
// `existsSync` doesn't care if Copilot's TUI repainted the pane,
|
|
1257
|
+
// entered alt-screen, or scrolled the echo away.
|
|
1258
|
+
const readyFilePath = join(envScriptDir, `${widgetId}.ready`)
|
|
1259
|
+
try { rmSync(readyFilePath, { force: true }) } catch { /* empty */ }
|
|
1247
1260
|
try {
|
|
1248
|
-
//
|
|
1249
|
-
//
|
|
1250
|
-
//
|
|
1251
|
-
|
|
1252
|
-
|
|
1261
|
+
// `touch` fires before the echo so the marker is set the instant
|
|
1262
|
+
// the env script finishes exporting. Echo kept for backwards-
|
|
1263
|
+
// compatible pane-scanning fallback (H1).
|
|
1264
|
+
writeFileSync(
|
|
1265
|
+
envScriptPath,
|
|
1266
|
+
envParts.join('\n') +
|
|
1267
|
+
`\ntouch ${JSON.stringify(readyFilePath)}\necho "Environment loaded:"\n`
|
|
1268
|
+
)
|
|
1253
1269
|
} catch { /* empty */ }
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1270
|
+
// Source env script; the trailing readiness echo MUST remain on
|
|
1271
|
+
// the pane so the post-startup poller can match it. Don't append
|
|
1272
|
+
// a `clear` here — the welcomeCmd that runs next clears the pane
|
|
1273
|
+
// itself before launching the agent.
|
|
1274
|
+
const envSourceCmd = `source ${JSON.stringify(envScriptPath)}`
|
|
1257
1275
|
|
|
1258
1276
|
setTimeout(() => {
|
|
1259
1277
|
try {
|
|
@@ -1336,6 +1354,7 @@ function handleConnection(ws, widgetId, canvasId, prettyName, widgetStartupComma
|
|
|
1336
1354
|
if (sent) return
|
|
1337
1355
|
sent = true
|
|
1338
1356
|
clearInterval(pollInterval)
|
|
1357
|
+
try { rmSync(readyFilePath, { force: true }) } catch { /* empty */ }
|
|
1339
1358
|
setTimeout(() => {
|
|
1340
1359
|
if (postStartup) {
|
|
1341
1360
|
try {
|
|
@@ -1364,14 +1383,21 @@ function handleConnection(ws, widgetId, canvasId, prettyName, widgetStartupComma
|
|
|
1364
1383
|
}
|
|
1365
1384
|
const pollInterval = setInterval(() => {
|
|
1366
1385
|
if (sent) { clearInterval(pollInterval); return }
|
|
1386
|
+
// ── H4: marker file is set the instant env script finishes
|
|
1387
|
+
// exporting, before any TUI starts. Terminal-state-independent.
|
|
1388
|
+
if (existsSync(readyFilePath)) { finalize('file'); return }
|
|
1367
1389
|
try {
|
|
1390
|
+
// ── H1: include 200 lines of scrollback so the echo
|
|
1391
|
+
// can be matched after Copilot's full-screen TUI
|
|
1392
|
+
// repaints over it. `-p` alone returns only the
|
|
1393
|
+
// visible region.
|
|
1368
1394
|
const paneContent = execSync(
|
|
1369
|
-
`tmux capture-pane -t "${tmuxName}" -p`,
|
|
1395
|
+
`tmux capture-pane -t "${tmuxName}" -p -S -200`,
|
|
1370
1396
|
{ encoding: 'utf8', timeout: 1000 }
|
|
1371
1397
|
)
|
|
1372
1398
|
if (paneContent.includes(readinessSignal)) finalize('signal')
|
|
1373
1399
|
} catch { /* empty */ }
|
|
1374
|
-
},
|
|
1400
|
+
}, 1000)
|
|
1375
1401
|
// Fallback: if readiness signal never matches (e.g. resume mode
|
|
1376
1402
|
// doesn't print "Environment loaded:", or the agent shows a
|
|
1377
1403
|
// prompt we can't detect), bind anyway after 30s so the widget
|