cancel-immediately-after-start-ack.ts 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. import { runStreamCase, StreamEvent } from "../lib/stream-harness"
  2. const LONG_PROMPT =
  3. 'Run exactly this command and do not summarize until it finishes: sleep 12 && echo "done". After it finishes, reply with exactly "done".'
  4. async function main() {
  5. const startRequestId = `start-${Date.now()}`
  6. const cancelRequestId = `cancel-${Date.now()}`
  7. const shutdownRequestId = `shutdown-${Date.now()}`
  8. let initSeen = false
  9. let startAccepted = false
  10. let sentCancel = false
  11. let cancelDone = false
  12. let sentShutdown = false
  13. await runStreamCase({
  14. onEvent(event: StreamEvent, context) {
  15. if (event.type === "system" && event.subtype === "init" && !initSeen) {
  16. initSeen = true
  17. context.sendCommand({
  18. command: "start",
  19. requestId: startRequestId,
  20. prompt: LONG_PROMPT,
  21. })
  22. return
  23. }
  24. if (
  25. event.type === "control" &&
  26. event.subtype === "ack" &&
  27. event.command === "start" &&
  28. event.requestId === startRequestId &&
  29. !startAccepted
  30. ) {
  31. startAccepted = true
  32. context.sendCommand({
  33. command: "cancel",
  34. requestId: cancelRequestId,
  35. })
  36. sentCancel = true
  37. return
  38. }
  39. if (
  40. event.type === "control" &&
  41. event.subtype === "done" &&
  42. event.command === "cancel" &&
  43. event.requestId === cancelRequestId
  44. ) {
  45. if (event.code === "cancel_requested" || event.code === "no_active_task") {
  46. cancelDone = true
  47. if (!sentShutdown) {
  48. context.sendCommand({
  49. command: "shutdown",
  50. requestId: shutdownRequestId,
  51. })
  52. sentShutdown = true
  53. }
  54. }
  55. return
  56. }
  57. if (event.type === "error") {
  58. throw new Error(`unexpected stream error event: ${event.content ?? "unknown error"}`)
  59. }
  60. },
  61. onTimeoutMessage() {
  62. return `timed out waiting for immediate-cancel flow (initSeen=${initSeen}, startAccepted=${startAccepted}, sentCancel=${sentCancel}, cancelDone=${cancelDone}, sentShutdown=${sentShutdown})`
  63. },
  64. })
  65. if (!startAccepted || !sentCancel || !cancelDone || !sentShutdown) {
  66. throw new Error(
  67. `immediate-cancel flow did not complete expected transitions (startAccepted=${startAccepted}, sentCancel=${sentCancel}, cancelDone=${cancelDone}, sentShutdown=${sentShutdown})`,
  68. )
  69. }
  70. }
  71. main().catch((error) => {
  72. console.error(`[FAIL] ${error instanceof Error ? error.message : String(error)}`)
  73. process.exit(1)
  74. })