@dfosco/storyboard 0.6.0-beta.8 → 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dfosco/storyboard",
3
- "version": "0.6.0-beta.8",
3
+ "version": "0.6.0-beta.9",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Storyboard prototyping framework — core engine, React integration, and canvas",
@@ -549,7 +549,9 @@ export class HotPool {
549
549
  const poll = setInterval(() => {
550
550
  try {
551
551
  const paneContent = execSync(
552
- `tmux capture-pane -t "${tmuxName}" -p`,
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
- const pane = execSync(`tmux capture-pane -t "${tmuxName}" -p`, { encoding: 'utf8', timeout: 1000 })
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
- const readinessSignal = resolvedAgentCfg?.readinessSignal || null
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,12 +1252,20 @@ 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
- // Trailing echo is the readiness signal the post-startup poller
1249
- // looks for. Without it, the 30s timeout fallback fires before
1250
- // /allow-all, identity, role/broadcast bind are sent — making
1251
- // the agent feel "stuck" for the first half-minute after launch.
1252
- writeFileSync(envScriptPath, envParts.join('\n') + '\necho "Environment loaded:"\n')
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
1270
  // Source env script; the trailing readiness echo MUST remain on
1255
1271
  // the pane so the post-startup poller can match it. Don't append
@@ -1338,6 +1354,7 @@ function handleConnection(ws, widgetId, canvasId, prettyName, widgetStartupComma
1338
1354
  if (sent) return
1339
1355
  sent = true
1340
1356
  clearInterval(pollInterval)
1357
+ try { rmSync(readyFilePath, { force: true }) } catch { /* empty */ }
1341
1358
  setTimeout(() => {
1342
1359
  if (postStartup) {
1343
1360
  try {
@@ -1366,14 +1383,21 @@ function handleConnection(ws, widgetId, canvasId, prettyName, widgetStartupComma
1366
1383
  }
1367
1384
  const pollInterval = setInterval(() => {
1368
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 }
1369
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.
1370
1394
  const paneContent = execSync(
1371
- `tmux capture-pane -t "${tmuxName}" -p`,
1395
+ `tmux capture-pane -t "${tmuxName}" -p -S -200`,
1372
1396
  { encoding: 'utf8', timeout: 1000 }
1373
1397
  )
1374
1398
  if (paneContent.includes(readinessSignal)) finalize('signal')
1375
1399
  } catch { /* empty */ }
1376
- }, 2000)
1400
+ }, 1000)
1377
1401
  // Fallback: if readiness signal never matches (e.g. resume mode
1378
1402
  // doesn't print "Environment loaded:", or the agent shows a
1379
1403
  // prompt we can't detect), bind anyway after 30s so the widget