Commit 9af3f86
* fix(cli): wait for the startup chat before an OpenTUI turn sends (#11042)
The `E2E Interactive - OpenTUI renderer (bun)` leg goes red on main with no
commit to blame. It failed at 0dd5bf2 and at 39a84c9 — the rewind
classifier refactor this issue names — and passed at 74fe3a6, which carries
that same refactor; 56f75ad failed run 33834473606 and passed run
33836390526 on an identical SHA. The job uploads no artifact and its log is
auth-gated, so the failing test is not readable from the run.
Reproduced locally instead, under CI's own bun pin and its exact command: four
interactive specs fail all three vitest attempts (exit 1, 460s) while the same
four pass under ink on the same machine in 34.8s. The rendered screen names the
cause — `Chat not initialized` sitting where the first prompt should have
opened a turn — and the scripted model server records zero requests, so the
prompt never became a turn at all.
OpenTUI mounts its composer and, from that mount effect, loads the command
registry, which calls `config.initialize()`. `Config.initialize()` sets its own
`initialized` guard before the work runs and reaches
`llmClient.initialize()` -> `startChat()` only near the end, so a prompt
submitted inside that window makes the turn's own `initialize()` throw
"already initialized" — which the turn's catch reads as "already done" and
proceeds. The client has no chat yet, the send dies in `getChat()`, and the
prompt is dropped. ink cannot reach this state: #11000 gates `isInputActive` on
`isConfigInitialized`, so its composer accepts nothing until initialization
completes, and OpenTUI has no equivalent gate.
`OpenTuiSlashDispatcher.ensureCommandsLoaded` already self-heals the same
window for the registry with a bounded poll; the turn path never got one. Wait
for the chat the in-flight initialization creates, on the same budget, so a
config that never finishes still reports its own error instead of hanging the
prompt. Starting the chat directly is not the fix: `LlmClient.initialize()`
guards only on an already-built chat, so a second caller inside the window
would build a second one.
Witness: a turn whose `initialize()` throws "already initialized" against a
client that reports no chat and throws from the send until one appears. With
the wait removed the case fails `Error: Chat not initialized`; restored, the
send runs once and the file's 45 cases pass.
Measured: the leg now exits 0 with 10 files passed and 1 skipped (19 tests),
against 4 failed files before.
* fix(cli): hold the OpenTUI turn until the startup chat has tools (#11042)
Chat existence is not readiness: startChat() assigns the chat and only
then awaits the SessionStart hook, its context and setTools(), so a wait
polling isInitialized() released the session's first prompt before the
tool declarations existed and the turn reached the model with no tools.
Release on the generation config's tools instead — setTools() is the
flight's last stage and always writes them — and give the wait's
deadline branch its own fake-timer witness.
* fix(cli): settle the startup-chat wait on abort and on expiry (#11042)
The wait this branch added is the first multi-second window between an
OpenTUI submit and its send, and it consulted neither the turn's abort
signal nor what its own expiry means.
An Esc pressed inside it was ignored for the whole budget, and nothing
between the loop and sendMessageStream re-checks the signal for text-only
input — applyPromptVisionBridge returns on !hasImageParts before its
abort check, and @-expansion is skipped for non-@ text — so a prompt the
user had already cancelled still fired its UserPromptSubmit hooks and
pushed a history entry. Poll the signal in the loop condition and throw
once it aborts: the throw is what routes the turn through runTurn's
catch, which settles on abort.signal.aborted, where a clean generator
return would have fired onComplete instead.
Expiry also fell through to a send whenever the flight had assigned the
chat but not reached setTools(), so a SessionStart hook slower than the
budget produced a turn answered with zero tool declarations and nothing
on screen to say so. Fail that case with a named error, gated on
isInitialized() so a config that never created a chat still surfaces the
client's own "Chat not initialized".
One existing test changed shape, not assertion: "skips steering when the
turn is aborted" pre-aborted its controller before calling the generator,
a state the production caller cannot reach and which now settles before
the send. It aborts inside the fake stream instead, where a real Esc
arrives. Measured on the pre-round source, neither version witnesses the
tool-response boundary guard — both pass with `|| abort.aborted` deleted
— so no coverage moved.
---------
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
1 parent f747999 commit 9af3f86
2 files changed
Lines changed: 237 additions & 4 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
29 | 29 | | |
30 | 30 | | |
31 | 31 | | |
| 32 | + | |
32 | 33 | | |
33 | 34 | | |
34 | 35 | | |
| |||
188 | 189 | | |
189 | 190 | | |
190 | 191 | | |
| 192 | + | |
191 | 193 | | |
192 | 194 | | |
193 | 195 | | |
194 | | - | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
195 | 205 | | |
196 | 206 | | |
197 | 207 | | |
| |||
276 | 286 | | |
277 | 287 | | |
278 | 288 | | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
| 407 | + | |
| 408 | + | |
| 409 | + | |
| 410 | + | |
| 411 | + | |
| 412 | + | |
| 413 | + | |
| 414 | + | |
| 415 | + | |
| 416 | + | |
| 417 | + | |
| 418 | + | |
| 419 | + | |
| 420 | + | |
| 421 | + | |
| 422 | + | |
| 423 | + | |
| 424 | + | |
| 425 | + | |
| 426 | + | |
| 427 | + | |
| 428 | + | |
| 429 | + | |
| 430 | + | |
| 431 | + | |
| 432 | + | |
| 433 | + | |
| 434 | + | |
| 435 | + | |
| 436 | + | |
| 437 | + | |
| 438 | + | |
| 439 | + | |
| 440 | + | |
| 441 | + | |
| 442 | + | |
| 443 | + | |
| 444 | + | |
| 445 | + | |
| 446 | + | |
| 447 | + | |
| 448 | + | |
| 449 | + | |
| 450 | + | |
| 451 | + | |
| 452 | + | |
| 453 | + | |
| 454 | + | |
| 455 | + | |
| 456 | + | |
| 457 | + | |
| 458 | + | |
| 459 | + | |
| 460 | + | |
| 461 | + | |
| 462 | + | |
| 463 | + | |
| 464 | + | |
| 465 | + | |
| 466 | + | |
| 467 | + | |
279 | 468 | | |
280 | 469 | | |
281 | 470 | | |
| |||
516 | 705 | | |
517 | 706 | | |
518 | 707 | | |
519 | | - | |
| 708 | + | |
| 709 | + | |
520 | 710 | | |
521 | 711 | | |
522 | 712 | | |
523 | 713 | | |
| 714 | + | |
| 715 | + | |
| 716 | + | |
| 717 | + | |
| 718 | + | |
| 719 | + | |
524 | 720 | | |
525 | | - | |
526 | | - | |
527 | 721 | | |
528 | 722 | | |
529 | 723 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
544 | 544 | | |
545 | 545 | | |
546 | 546 | | |
| 547 | + | |
| 548 | + | |
| 549 | + | |
| 550 | + | |
| 551 | + | |
| 552 | + | |
547 | 553 | | |
548 | 554 | | |
549 | 555 | | |
| |||
565 | 571 | | |
566 | 572 | | |
567 | 573 | | |
| 574 | + | |
| 575 | + | |
| 576 | + | |
| 577 | + | |
| 578 | + | |
| 579 | + | |
| 580 | + | |
| 581 | + | |
| 582 | + | |
| 583 | + | |
| 584 | + | |
| 585 | + | |
| 586 | + | |
| 587 | + | |
| 588 | + | |
| 589 | + | |
| 590 | + | |
| 591 | + | |
| 592 | + | |
| 593 | + | |
| 594 | + | |
| 595 | + | |
| 596 | + | |
| 597 | + | |
| 598 | + | |
| 599 | + | |
| 600 | + | |
| 601 | + | |
| 602 | + | |
| 603 | + | |
| 604 | + | |
| 605 | + | |
| 606 | + | |
568 | 607 | | |
569 | 608 | | |
570 | 609 | | |
| |||
0 commit comments