cancel-active-task.ts 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  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-a-${Date.now()}`
  6. const cancelRequestId = `cancel-${Date.now()}`
  7. const shutdownRequestId = `shutdown-${Date.now()}`
  8. let initSeen = false
  9. let startAccepted = false
  10. let startCommandToolUseSeen = false
  11. let sentCancel = false
  12. let cancelDone = false
  13. let sentShutdown = false
  14. await runStreamCase({
  15. onEvent(event: StreamEvent, context) {
  16. if (event.type === "system" && event.subtype === "init" && !initSeen) {
  17. initSeen = true
  18. context.sendCommand({
  19. command: "start",
  20. requestId: startRequestId,
  21. prompt: LONG_PROMPT,
  22. })
  23. return
  24. }
  25. if (
  26. event.type === "control" &&
  27. event.subtype === "ack" &&
  28. event.command === "start" &&
  29. event.requestId === startRequestId
  30. ) {
  31. startAccepted = true
  32. return
  33. }
  34. if (
  35. event.type === "tool_use" &&
  36. event.subtype === "command" &&
  37. event.done === true &&
  38. event.requestId === startRequestId
  39. ) {
  40. startCommandToolUseSeen = true
  41. }
  42. if (startAccepted && startCommandToolUseSeen && !sentCancel) {
  43. context.sendCommand({
  44. command: "cancel",
  45. requestId: cancelRequestId,
  46. })
  47. sentCancel = true
  48. return
  49. }
  50. if (
  51. event.type === "control" &&
  52. event.subtype === "done" &&
  53. event.command === "cancel" &&
  54. event.requestId === cancelRequestId
  55. ) {
  56. if (event.code === "cancel_requested" || event.code === "no_active_task") {
  57. cancelDone = true
  58. }
  59. return
  60. }
  61. if (cancelDone && !sentShutdown) {
  62. context.sendCommand({
  63. command: "shutdown",
  64. requestId: shutdownRequestId,
  65. })
  66. sentShutdown = true
  67. return
  68. }
  69. if (event.type === "control" && event.subtype === "error" && event.requestId === cancelRequestId) {
  70. throw new Error(
  71. `cancel command failed with code=${event.code ?? "unknown"} content="${event.content ?? ""}"`,
  72. )
  73. }
  74. if (event.type === "error") {
  75. throw new Error(`unexpected stream error event: ${event.content ?? "unknown error"}`)
  76. }
  77. },
  78. onTimeoutMessage() {
  79. return `timed out waiting for cancel flow (initSeen=${initSeen}, startAccepted=${startAccepted}, startCommandToolUseSeen=${startCommandToolUseSeen}, sentCancel=${sentCancel}, cancelDone=${cancelDone}, sentShutdown=${sentShutdown})`
  80. },
  81. })
  82. if (!startAccepted || !startCommandToolUseSeen || !sentCancel || !cancelDone || !sentShutdown) {
  83. throw new Error(
  84. `cancel flow did not complete expected transitions (startAccepted=${startAccepted}, startCommandToolUseSeen=${startCommandToolUseSeen}, sentCancel=${sentCancel}, cancelDone=${cancelDone}, sentShutdown=${sentShutdown})`,
  85. )
  86. }
  87. }
  88. main().catch((error) => {
  89. console.error(`[FAIL] ${error instanceof Error ? error.message : String(error)}`)
  90. process.exit(1)
  91. })