@cuongtran001/kanna 1.2.0 → 1.3.1

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.
Files changed (196) hide show
  1. package/dist/client/assets/{arc-BLk1CZKB.js → arc-DXtN3EWJ.js} +1 -1
  2. package/dist/client/assets/{architectureDiagram-3BPJPVTR-B01ZQYte.js → architectureDiagram-3BPJPVTR-CIGqW1z-.js} +1 -1
  3. package/dist/client/assets/{blockDiagram-GPEHLZMM-diL6a3cq.js → blockDiagram-GPEHLZMM-BafNS_H6.js} +1 -1
  4. package/dist/client/assets/{c4Diagram-AAUBKEIU-BY1verL8.js → c4Diagram-AAUBKEIU-YdAgRSis.js} +1 -1
  5. package/dist/client/assets/channel-BEYTrUAm.js +1 -0
  6. package/dist/client/assets/{chunk-2J33WTMH-psdZuajw.js → chunk-2J33WTMH-eiP5y5G-.js} +1 -1
  7. package/dist/client/assets/{chunk-4BX2VUAB-CrwQVBc5.js → chunk-4BX2VUAB-CvLGzPMF.js} +1 -1
  8. package/dist/client/assets/{chunk-55IACEB6-9-aj4jKO.js → chunk-55IACEB6-Y-S9Fb8A.js} +1 -1
  9. package/dist/client/assets/{chunk-727SXJPM-1FNK9_nB.js → chunk-727SXJPM-6sJH1RI9.js} +1 -1
  10. package/dist/client/assets/{chunk-AQP2D5EJ-DFhvd_Ek.js → chunk-AQP2D5EJ-hV1r_IXO.js} +1 -1
  11. package/dist/client/assets/{chunk-FMBD7UC4-B7MLrDa7.js → chunk-FMBD7UC4-CC3L28S5.js} +1 -1
  12. package/dist/client/assets/{chunk-ND2GUHAM-D4Df05QE.js → chunk-ND2GUHAM-BZ3czhdH.js} +1 -1
  13. package/dist/client/assets/{chunk-QZHKN3VN-Iq17Dznm.js → chunk-QZHKN3VN-Bjyd6Yr-.js} +1 -1
  14. package/dist/client/assets/classDiagram-4FO5ZUOK-CvnF5Ud6.js +1 -0
  15. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-CvnF5Ud6.js +1 -0
  16. package/dist/client/assets/{cose-bilkent-S5V4N54A-BmaZhtXy.js → cose-bilkent-S5V4N54A-CfxdDghE.js} +1 -1
  17. package/dist/client/assets/{dagre-BM42HDAG-BCmpfnfP.js → dagre-BM42HDAG-B8CddsbB.js} +1 -1
  18. package/dist/client/assets/{diagram-2AECGRRQ-BIEcKowS.js → diagram-2AECGRRQ-F0yS0fVB.js} +1 -1
  19. package/dist/client/assets/{diagram-5GNKFQAL-CRBABBfv.js → diagram-5GNKFQAL-cW0i0qlh.js} +1 -1
  20. package/dist/client/assets/{diagram-KO2AKTUF-BRxUk16N.js → diagram-KO2AKTUF-BzAKMeJT.js} +1 -1
  21. package/dist/client/assets/{diagram-LMA3HP47-DPBs4xl1.js → diagram-LMA3HP47-qdC4XqIl.js} +1 -1
  22. package/dist/client/assets/{diagram-OG6HWLK6-BE9rgVv8.js → diagram-OG6HWLK6-CGcpi6Dh.js} +1 -1
  23. package/dist/client/assets/{erDiagram-TEJ5UH35-D3vgA1xJ.js → erDiagram-TEJ5UH35-CxNTDicM.js} +1 -1
  24. package/dist/client/assets/{flowDiagram-I6XJVG4X-Cgu-8XXC.js → flowDiagram-I6XJVG4X-qkGa5TqY.js} +1 -1
  25. package/dist/client/assets/{ganttDiagram-6RSMTGT7-CgjXVyTR.js → ganttDiagram-6RSMTGT7-D9mVPoQi.js} +1 -1
  26. package/dist/client/assets/{gitGraphDiagram-PVQCEYII-zD5D8B-i.js → gitGraphDiagram-PVQCEYII-DA0grAXe.js} +1 -1
  27. package/dist/client/assets/{index-DBTdhMbJ.js → index-BqUY1Bm4.js} +1 -1
  28. package/dist/client/assets/index-CLsOWXff.css +32 -0
  29. package/dist/client/assets/{index-zQLLeAfl.js → index-TU1k6ov6.js} +253 -243
  30. package/dist/client/assets/{infoDiagram-5YYISTIA-BaFHqT4u.js → infoDiagram-5YYISTIA-CGljtLs3.js} +1 -1
  31. package/dist/client/assets/{ishikawaDiagram-YF4QCWOH-2yAC1br2.js → ishikawaDiagram-YF4QCWOH-CtC0xrmG.js} +1 -1
  32. package/dist/client/assets/{journeyDiagram-JHISSGLW-DacQ-ND5.js → journeyDiagram-JHISSGLW-URLnVZeN.js} +1 -1
  33. package/dist/client/assets/{kanban-definition-UN3LZRKU-Qlbwm3z4.js → kanban-definition-UN3LZRKU-C2Q6aqQh.js} +1 -1
  34. package/dist/client/assets/{linear-DeafqpgO.js → linear-ByKHtsIx.js} +1 -1
  35. package/dist/client/assets/{mermaid.core-KYiCPJDj.js → mermaid.core-Dl_f2KLc.js} +4 -4
  36. package/dist/client/assets/{mindmap-definition-RKZ34NQL-B1V-T6d3.js → mindmap-definition-RKZ34NQL-CaJ9IDLI.js} +1 -1
  37. package/dist/client/assets/{pieDiagram-4H26LBE5-DOF9OFRu.js → pieDiagram-4H26LBE5-mmYaf2Oa.js} +1 -1
  38. package/dist/client/assets/{quadrantDiagram-W4KKPZXB-lH1mB7ko.js → quadrantDiagram-W4KKPZXB-B_PO6OCC.js} +1 -1
  39. package/dist/client/assets/{requirementDiagram-4Y6WPE33-BaTsJ_sS.js → requirementDiagram-4Y6WPE33-M4huwJt1.js} +1 -1
  40. package/dist/client/assets/{sankeyDiagram-5OEKKPKP-Pfxib5iI.js → sankeyDiagram-5OEKKPKP-B0hb1sWh.js} +1 -1
  41. package/dist/client/assets/{sequenceDiagram-3UESZ5HK-BQMEKkBC.js → sequenceDiagram-3UESZ5HK-BAqSptnA.js} +1 -1
  42. package/dist/client/assets/{stateDiagram-AJRCARHV-CuldNnzp.js → stateDiagram-AJRCARHV-DTNkvPi5.js} +1 -1
  43. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-CVN3oGTF.js +1 -0
  44. package/dist/client/assets/{timeline-definition-PNZ67QCA-DP7u0YAg.js → timeline-definition-PNZ67QCA-MgAjHz8Q.js} +1 -1
  45. package/dist/client/assets/{vennDiagram-CIIHVFJN-QlrjpNeQ.js → vennDiagram-CIIHVFJN-DmKoO51W.js} +1 -1
  46. package/dist/client/assets/{wardley-L42UT6IY-BqdJ_sAM.js → wardley-L42UT6IY-CWcyHnYs.js} +1 -1
  47. package/dist/client/assets/{wardleyDiagram-YWT4CUSO-Wi3kaQ5X.js → wardleyDiagram-YWT4CUSO-BGIGmFP9.js} +1 -1
  48. package/dist/client/assets/{xychartDiagram-2RQKCTM6-D-KXMyCG.js → xychartDiagram-2RQKCTM6-D20YIvEl.js} +1 -1
  49. package/dist/client/index.html +2 -2
  50. package/package.json +1 -1
  51. package/src/server/agent-coordinator-types.ts +158 -0
  52. package/src/server/agent-coordinator.ts +919 -0
  53. package/src/server/agent-deps-builders.ts +425 -0
  54. package/src/server/agent.oauth-rotation.test.ts +101 -0
  55. package/src/server/agent.test.ts +24 -8
  56. package/src/server/agent.ts +35 -4963
  57. package/src/server/auto-continue/read-model.test.ts +2 -2
  58. package/src/server/auto-continue/read-model.ts +4 -1
  59. package/src/server/claude-autocontinue-commands.test.ts +494 -0
  60. package/src/server/claude-autocontinue-commands.ts +256 -0
  61. package/src/server/claude-cancel-handler.test.ts +472 -0
  62. package/src/server/claude-cancel-handler.ts +241 -0
  63. package/src/server/claude-chat-management.test.ts +395 -0
  64. package/src/server/claude-chat-management.ts +251 -0
  65. package/src/server/claude-harness-stream.test.ts +273 -0
  66. package/src/server/claude-harness-stream.ts +158 -0
  67. package/src/server/claude-loop-orch-commands.test.ts +579 -0
  68. package/src/server/claude-loop-orch-commands.ts +466 -0
  69. package/src/server/claude-message-normalizer.test.ts +213 -0
  70. package/src/server/claude-message-normalizer.ts +436 -0
  71. package/src/server/claude-prompt-helpers.test.ts +248 -0
  72. package/src/server/claude-prompt-helpers.ts +139 -0
  73. package/src/server/claude-sdk-queue.test.ts +169 -0
  74. package/src/server/claude-sdk-queue.ts +82 -0
  75. package/src/server/claude-send-command.test.ts +414 -0
  76. package/src/server/claude-send-command.ts +420 -0
  77. package/src/server/claude-session-config-helpers.test.ts +279 -0
  78. package/src/server/claude-session-config-helpers.ts +151 -0
  79. package/src/server/claude-session-config.test.ts +250 -0
  80. package/src/server/claude-session-config.ts +159 -0
  81. package/src/server/claude-session-error-handler.test.ts +498 -0
  82. package/src/server/claude-session-error-handler.ts +361 -0
  83. package/src/server/claude-session-lifecycle.test.ts +669 -0
  84. package/src/server/claude-session-lifecycle.ts +283 -0
  85. package/src/server/claude-session-rebuild.test.ts +224 -0
  86. package/src/server/claude-session-rebuild.ts +101 -0
  87. package/src/server/claude-session-runner.test.ts +523 -0
  88. package/src/server/claude-session-runner.ts +521 -0
  89. package/src/server/claude-session-spawner.test.ts +401 -0
  90. package/src/server/claude-session-spawner.ts +401 -0
  91. package/src/server/claude-session-start.test.ts +205 -0
  92. package/src/server/claude-session-start.ts +277 -0
  93. package/src/server/claude-session-state-queries.test.ts +344 -0
  94. package/src/server/claude-session-state-queries.ts +160 -0
  95. package/src/server/claude-session-state.ts +104 -0
  96. package/src/server/claude-slash-commands.test.ts +277 -0
  97. package/src/server/claude-slash-commands.ts +234 -0
  98. package/src/server/claude-spawn-helpers.test.ts +221 -0
  99. package/src/server/claude-spawn-helpers.ts +179 -0
  100. package/src/server/claude-steer-log.test.ts +243 -0
  101. package/src/server/claude-steer-log.ts +58 -0
  102. package/src/server/claude-subagent-tool-response.test.ts +213 -0
  103. package/src/server/claude-subagent-tool-response.ts +165 -0
  104. package/src/server/claude-subagent-wiring.test.ts +263 -0
  105. package/src/server/claude-subagent-wiring.ts +324 -0
  106. package/src/server/claude-tool-respond.test.ts +321 -0
  107. package/src/server/claude-tool-respond.ts +142 -0
  108. package/src/server/claude-turn-runner.test.ts +310 -0
  109. package/src/server/claude-turn-runner.ts +228 -0
  110. package/src/server/claude-turn-starter.test.ts +266 -0
  111. package/src/server/claude-turn-starter.ts +566 -0
  112. package/src/server/claude-usage-math.test.ts +169 -0
  113. package/src/server/claude-usage-math.ts +132 -0
  114. package/src/server/diff-store-branch-ops.adapter.test.ts +332 -0
  115. package/src/server/diff-store-branch-ops.adapter.ts +602 -0
  116. package/src/server/diff-store-commit-ops.adapter.test.ts +358 -0
  117. package/src/server/diff-store-commit-ops.adapter.ts +258 -0
  118. package/src/server/diff-store-errors.ts +85 -0
  119. package/src/server/diff-store-file-ops.adapter.ts +212 -0
  120. package/src/server/diff-store-git-branch.adapter.ts +686 -0
  121. package/src/server/diff-store-io.adapter.ts +27 -0
  122. package/src/server/diff-store-parse.test.ts +212 -0
  123. package/src/server/diff-store-parse.ts +122 -0
  124. package/src/server/diff-store-state.ts +82 -0
  125. package/src/server/diff-store.ts +76 -1854
  126. package/src/server/event-store-apply.test.ts +280 -0
  127. package/src/server/event-store-apply.ts +132 -0
  128. package/src/server/event-store-chat-lifecycle.test.ts +408 -0
  129. package/src/server/event-store-chat-lifecycle.ts +441 -0
  130. package/src/server/event-store-entity-write.ts +356 -0
  131. package/src/server/event-store-helpers.test.ts +230 -0
  132. package/src/server/event-store-helpers.ts +235 -0
  133. package/src/server/event-store-init.ts +276 -0
  134. package/src/server/event-store-messages.adapter.test.ts +365 -0
  135. package/src/server/event-store-messages.adapter.ts +213 -0
  136. package/src/server/event-store-orch-subscription.ts +124 -0
  137. package/src/server/event-store-orch.test.ts +462 -0
  138. package/src/server/event-store-orch.ts +312 -0
  139. package/src/server/event-store-peripheral-events.adapter.test.ts +254 -0
  140. package/src/server/event-store-peripheral-events.adapter.ts +105 -0
  141. package/src/server/event-store-snapshot.test.ts +597 -0
  142. package/src/server/event-store-snapshot.ts +658 -0
  143. package/src/server/event-store-subagent.test.ts +408 -0
  144. package/src/server/event-store-subagent.ts +198 -0
  145. package/src/server/event-store-tool-requests.test.ts +210 -0
  146. package/src/server/event-store-tool-requests.ts +131 -0
  147. package/src/server/event-store-transcript-write.adapter.ts +285 -0
  148. package/src/server/event-store-write-ops.test.ts +554 -0
  149. package/src/server/event-store-write-ops.ts +529 -0
  150. package/src/server/event-store.test.ts +35 -0
  151. package/src/server/event-store.ts +251 -2284
  152. package/src/server/events.ts +3 -0
  153. package/src/server/harness-types.ts +15 -1
  154. package/src/server/oauth-errors.ts +19 -0
  155. package/src/server/read-models.ts +26 -1
  156. package/src/server/subagent-orchestrator.ts +3 -0
  157. package/src/server/ws-router-agent-ctrl.test.ts +187 -0
  158. package/src/server/ws-router-agent-ctrl.ts +137 -0
  159. package/src/server/ws-router-broadcast.ts +488 -0
  160. package/src/server/ws-router-chat.test.ts +461 -0
  161. package/src/server/ws-router-chat.ts +279 -0
  162. package/src/server/ws-router-defaults.test.ts +283 -0
  163. package/src/server/ws-router-defaults.ts +434 -0
  164. package/src/server/ws-router-diff.test.ts +167 -0
  165. package/src/server/ws-router-diff.ts +250 -0
  166. package/src/server/ws-router-envelope.ts +329 -0
  167. package/src/server/ws-router-misc.test.ts +422 -0
  168. package/src/server/ws-router-misc.ts +265 -0
  169. package/src/server/ws-router-orch.test.ts +190 -0
  170. package/src/server/ws-router-orch.ts +93 -0
  171. package/src/server/ws-router-project.test.ts +403 -0
  172. package/src/server/ws-router-project.ts +256 -0
  173. package/src/server/ws-router-push.test.ts +193 -0
  174. package/src/server/ws-router-push.ts +120 -0
  175. package/src/server/ws-router-settings.test.ts +173 -0
  176. package/src/server/ws-router-settings.ts +416 -0
  177. package/src/server/ws-router-skills.test.ts +194 -0
  178. package/src/server/ws-router-skills.ts +204 -0
  179. package/src/server/ws-router-utils.ts +224 -0
  180. package/src/server/ws-router.ts +339 -2254
  181. package/src/shared/app-settings-types.ts +365 -0
  182. package/src/shared/core-types.ts +27 -0
  183. package/src/shared/git-diff-types.ts +165 -0
  184. package/src/shared/loop-progress.test.ts +106 -0
  185. package/src/shared/loop-progress.ts +88 -0
  186. package/src/shared/mcp-types.ts +105 -0
  187. package/src/shared/provider-model-types.ts +437 -0
  188. package/src/shared/subagent-types.ts +198 -0
  189. package/src/shared/tool-call-types.ts +314 -0
  190. package/src/shared/transcript-types.ts +270 -0
  191. package/src/shared/types.ts +27 -1668
  192. package/dist/client/assets/channel-BMkhjiLI.js +0 -1
  193. package/dist/client/assets/classDiagram-4FO5ZUOK-C5Kg1XOD.js +0 -1
  194. package/dist/client/assets/classDiagram-v2-Q7XG4LA2-C5Kg1XOD.js +0 -1
  195. package/dist/client/assets/index-H6C-f2Vp.css +0 -32
  196. package/dist/client/assets/stateDiagram-v2-BHNVJYJU-Ca2U11af.js +0 -1
@@ -0,0 +1,256 @@
1
+ /**
2
+ * Standalone auto-continue command handlers for AgentCoordinator.
3
+ *
4
+ * Extracted from agent.ts so the eight related private/public methods live in
5
+ * their own testable module. The coordinator delegates to these functions by
6
+ * passing an object literal that satisfies `AutoContinueCommandDeps`.
7
+ *
8
+ * Side-effect seal: this module contains NO direct IO (no node:fs, no HTTP
9
+ * calls, no Bun primitives). Every effectful operation is injected through
10
+ * the deps interface.
11
+ */
12
+
13
+ import type { ChatAttachment, QueuedChatMessage, TranscriptEntry } from "../shared/types"
14
+ import { AUTO_CONTINUE_EVENT_VERSION, type AutoContinueEvent } from "./auto-continue/events"
15
+ import { deriveChatSchedules } from "./auto-continue/read-model"
16
+ import type { SendMessageOptions } from "./claude-steer-log"
17
+ import { timestamped } from "./claude-message-normalizer"
18
+
19
+ // ---------------------------------------------------------------------------
20
+ // Structural sub-interfaces — only the operations this module calls.
21
+ // ---------------------------------------------------------------------------
22
+
23
+ /** Minimal schedule-manager surface used by emitAutoContinueEvent. */
24
+ interface AutoContinueScheduleManager {
25
+ onEvent(event: AutoContinueEvent): void
26
+ }
27
+
28
+ /** Subset of EventStore used by these handlers. */
29
+ interface AutoContinueCommandStore {
30
+ appendAutoContinueEvent(event: AutoContinueEvent): Promise<void>
31
+ getAutoContinueEvents(chatId: string): AutoContinueEvent[]
32
+ /** Returns null/undefined when the chat does not exist. */
33
+ getChat(chatId: string): { id: string } | null | undefined
34
+ appendMessage(chatId: string, entry: TranscriptEntry): Promise<void>
35
+ }
36
+
37
+ // ---------------------------------------------------------------------------
38
+ // Dependency bundle injected by AgentCoordinator
39
+ // ---------------------------------------------------------------------------
40
+
41
+ export interface AutoContinueCommandDeps {
42
+ /**
43
+ * Per-chat auto-resume override. A `boolean` value in the map takes
44
+ * precedence over the global preference; absence falls back to
45
+ * `getAutoResumePreference()`.
46
+ */
47
+ autoResumeByChat: Pick<Map<string, boolean>, "get">
48
+
49
+ /** Returns the global auto-resume preference when no per-chat override exists. */
50
+ getAutoResumePreference(): boolean
51
+
52
+ /** EventStore — subset used by these handlers. */
53
+ store: AutoContinueCommandStore
54
+
55
+ /**
56
+ * Optional schedule manager that must be notified after every event so it
57
+ * can arm/disarm real timers. Null when no manager is configured.
58
+ */
59
+ scheduleManager: AutoContinueScheduleManager | null
60
+
61
+ /**
62
+ * Emit a state-change event for the given chat. Propagated to WebSocket
63
+ * subscribers so the UI can refresh.
64
+ */
65
+ emitStateChange(chatId: string): void
66
+
67
+ /**
68
+ * Enqueue a prompt as if the user sent it. Used by `fireAutoContinue` to
69
+ * replay the schedule's stored prompt.
70
+ */
71
+ enqueueMessage(
72
+ chatId: string,
73
+ content: string,
74
+ attachments: ChatAttachment[],
75
+ options?: SendMessageOptions,
76
+ ): Promise<QueuedChatMessage>
77
+
78
+ /**
79
+ * Start the next queued message for the chat if there is no active turn.
80
+ * Returns `true` when a message was dequeued and started.
81
+ */
82
+ maybeStartNextQueuedMessage(chatId: string): Promise<boolean>
83
+ }
84
+
85
+ // ---------------------------------------------------------------------------
86
+ // Exported standalone functions
87
+ // ---------------------------------------------------------------------------
88
+
89
+ /**
90
+ * Return the auto-resume preference for a specific chat.
91
+ * Per-chat overrides (set when the chat was created / the send command was
92
+ * issued) take precedence over the global preference.
93
+ */
94
+ export function resolveAutoResumeFor(deps: AutoContinueCommandDeps, chatId: string): boolean {
95
+ const cached = deps.autoResumeByChat.get(chatId)
96
+ if (typeof cached === "boolean") return cached
97
+ return deps.getAutoResumePreference()
98
+ }
99
+
100
+ /**
101
+ * Append an auto-continue event to the store, notify the schedule manager so
102
+ * it can arm/disarm timers, and emit a state-change event.
103
+ */
104
+ export async function emitAutoContinueEvent(
105
+ deps: AutoContinueCommandDeps,
106
+ event: AutoContinueEvent,
107
+ ): Promise<void> {
108
+ await deps.store.appendAutoContinueEvent(event)
109
+ deps.scheduleManager?.onEvent(event)
110
+ deps.emitStateChange(event.chatId)
111
+ }
112
+
113
+ /**
114
+ * Derive the live schedule entry for a given `scheduleId` within a chat.
115
+ * Returns `undefined` when the schedule does not exist.
116
+ */
117
+ export function getChatSchedule(
118
+ deps: AutoContinueCommandDeps,
119
+ chatId: string,
120
+ scheduleId: string,
121
+ ) {
122
+ const events = deps.store.getAutoContinueEvents(chatId)
123
+ return deriveChatSchedules(events, chatId).schedules[scheduleId]
124
+ }
125
+
126
+ /**
127
+ * Guard: throws when `scheduledAt` is not strictly in the future.
128
+ * All acceptance / reschedule paths call this before persisting the event.
129
+ */
130
+ export function requireFuture(scheduledAt: number): void {
131
+ if (scheduledAt <= Date.now()) throw new Error("scheduledAt must be in the future")
132
+ }
133
+
134
+ /**
135
+ * Fire a scheduled auto-continue: replay the stored prompt and start the
136
+ * next queued message. If any step fails, a synthetic error result is
137
+ * appended to the transcript so the failure is visible in the UI.
138
+ */
139
+ export async function fireAutoContinue(
140
+ deps: AutoContinueCommandDeps,
141
+ chatId: string,
142
+ scheduleId: string,
143
+ ): Promise<void> {
144
+ if (!deps.store.getChat(chatId)) return
145
+
146
+ // `subagent_background` deliveries carry the "Read PROGRESS.md" prompt;
147
+ // provider-failure schedules carry none and fall back to the literal "continue".
148
+ const schedule = getChatSchedule(deps, chatId, scheduleId)
149
+ const promptToReplay = schedule?.prompt ?? "continue"
150
+
151
+ const event: AutoContinueEvent = {
152
+ v: AUTO_CONTINUE_EVENT_VERSION,
153
+ kind: "auto_continue_fired",
154
+ timestamp: Date.now(),
155
+ chatId,
156
+ scheduleId,
157
+ }
158
+ try {
159
+ await deps.store.appendAutoContinueEvent(event)
160
+ await deps.enqueueMessage(chatId, promptToReplay, [], { autoContinue: { scheduleId } })
161
+ await deps.maybeStartNextQueuedMessage(chatId)
162
+ } catch (error) {
163
+ const message = error instanceof Error ? error.message : String(error)
164
+ await deps.store.appendMessage(
165
+ chatId,
166
+ timestamped({
167
+ kind: "result",
168
+ subtype: "error",
169
+ isError: true,
170
+ durationMs: 0,
171
+ result: `Auto-continue failed: ${message}`,
172
+ }),
173
+ )
174
+ }
175
+
176
+ deps.emitStateChange(chatId)
177
+ }
178
+
179
+ /**
180
+ * Accept a proposed auto-continue schedule (user or pool-rotation acceptance).
181
+ * Validates that the schedule exists, is still in the `proposed` state, and
182
+ * that `scheduledAt` is in the future before persisting the acceptance event.
183
+ */
184
+ export async function acceptAutoContinue(
185
+ deps: AutoContinueCommandDeps,
186
+ chatId: string,
187
+ scheduleId: string,
188
+ scheduledAt: number,
189
+ ): Promise<void> {
190
+ const schedule = getChatSchedule(deps, chatId, scheduleId)
191
+ if (!schedule) throw new Error("Schedule not found")
192
+ if (schedule.state !== "proposed") throw new Error("Schedule not pending")
193
+ requireFuture(scheduledAt)
194
+
195
+ await emitAutoContinueEvent(deps, {
196
+ v: AUTO_CONTINUE_EVENT_VERSION,
197
+ kind: "auto_continue_accepted",
198
+ timestamp: Date.now(),
199
+ chatId,
200
+ scheduleId,
201
+ scheduledAt,
202
+ tz: schedule.tz,
203
+ source: "user",
204
+ resetAt: schedule.resetAt,
205
+ detectedAt: schedule.detectedAt,
206
+ })
207
+ }
208
+
209
+ /**
210
+ * Reschedule an active (`scheduled`) auto-continue to a new time.
211
+ * Throws when the schedule does not exist or is not in the `scheduled` state.
212
+ */
213
+ export async function rescheduleAutoContinue(
214
+ deps: AutoContinueCommandDeps,
215
+ chatId: string,
216
+ scheduleId: string,
217
+ scheduledAt: number,
218
+ ): Promise<void> {
219
+ const schedule = getChatSchedule(deps, chatId, scheduleId)
220
+ if (!schedule || schedule.state !== "scheduled") throw new Error("Schedule not active")
221
+ requireFuture(scheduledAt)
222
+
223
+ await emitAutoContinueEvent(deps, {
224
+ v: AUTO_CONTINUE_EVENT_VERSION,
225
+ kind: "auto_continue_rescheduled",
226
+ timestamp: Date.now(),
227
+ chatId,
228
+ scheduleId,
229
+ scheduledAt,
230
+ })
231
+ }
232
+
233
+ /**
234
+ * Cancel a proposed or scheduled auto-continue.
235
+ * No-ops silently when the schedule does not exist or is already in a terminal
236
+ * state (`fired` / `cancelled`).
237
+ */
238
+ export async function cancelAutoContinue(
239
+ deps: AutoContinueCommandDeps,
240
+ chatId: string,
241
+ scheduleId: string,
242
+ reason: "user" | "chat_deleted",
243
+ ): Promise<void> {
244
+ const schedule = getChatSchedule(deps, chatId, scheduleId)
245
+ if (!schedule) return
246
+ if (schedule.state !== "proposed" && schedule.state !== "scheduled") return
247
+
248
+ await emitAutoContinueEvent(deps, {
249
+ v: AUTO_CONTINUE_EVENT_VERSION,
250
+ kind: "auto_continue_cancelled",
251
+ timestamp: Date.now(),
252
+ chatId,
253
+ scheduleId,
254
+ reason,
255
+ })
256
+ }