visionclaw 0.1.219-dev.codex-issue-2805-worker-runtime.3 → 0.1.219-dev.codex-fix-2941-activity-foreground.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 (313) hide show
  1. package/dist/agent/api-proxy.d.ts +16 -0
  2. package/dist/agent/api-proxy.d.ts.map +1 -1
  3. package/dist/agent/api-proxy.js +8 -9
  4. package/dist/agent/api-proxy.js.map +1 -1
  5. package/dist/agent/browser-launcher.d.ts +0 -3
  6. package/dist/agent/browser-launcher.d.ts.map +1 -1
  7. package/dist/agent/browser-launcher.js +43 -101
  8. package/dist/agent/browser-launcher.js.map +1 -1
  9. package/dist/agent/coding-conventions.d.ts +3 -2
  10. package/dist/agent/coding-conventions.d.ts.map +1 -1
  11. package/dist/agent/coding-conventions.js +8 -7
  12. package/dist/agent/coding-conventions.js.map +1 -1
  13. package/dist/agent/command-handlers.d.ts.map +1 -1
  14. package/dist/agent/command-handlers.js +16 -4
  15. package/dist/agent/command-handlers.js.map +1 -1
  16. package/dist/agent/context-action-restart-fallback.d.ts.map +1 -1
  17. package/dist/agent/context-action-restart-fallback.js +3 -9
  18. package/dist/agent/context-action-restart-fallback.js.map +1 -1
  19. package/dist/agent/context-refresh.d.ts.map +1 -1
  20. package/dist/agent/context-refresh.js +7 -0
  21. package/dist/agent/context-refresh.js.map +1 -1
  22. package/dist/agent/context.d.ts.map +1 -1
  23. package/dist/agent/context.js +8 -6
  24. package/dist/agent/context.js.map +1 -1
  25. package/dist/agent/engine-fallback.d.ts +35 -0
  26. package/dist/agent/engine-fallback.d.ts.map +1 -1
  27. package/dist/agent/engine-fallback.js +3 -4
  28. package/dist/agent/engine-fallback.js.map +1 -1
  29. package/dist/agent/engines/browser-tool-policy.d.ts +1 -12
  30. package/dist/agent/engines/browser-tool-policy.d.ts.map +1 -1
  31. package/dist/agent/engines/browser-tool-policy.js +2 -17
  32. package/dist/agent/engines/browser-tool-policy.js.map +1 -1
  33. package/dist/agent/engines/claude/session.d.ts +0 -1
  34. package/dist/agent/engines/claude/session.d.ts.map +1 -1
  35. package/dist/agent/engines/claude/session.js +36 -21
  36. package/dist/agent/engines/claude/session.js.map +1 -1
  37. package/dist/agent/engines/pi/tools.d.ts.map +1 -1
  38. package/dist/agent/engines/pi/tools.js +10 -2
  39. package/dist/agent/engines/pi/tools.js.map +1 -1
  40. package/dist/agent/engines/pi-coding/session.d.ts +0 -3
  41. package/dist/agent/engines/pi-coding/session.d.ts.map +1 -1
  42. package/dist/agent/engines/pi-coding/session.js +22 -19
  43. package/dist/agent/engines/pi-coding/session.js.map +1 -1
  44. package/dist/agent/engines/session-types.d.ts +0 -6
  45. package/dist/agent/engines/session-types.d.ts.map +1 -1
  46. package/dist/agent/external-context/runtime.d.ts.map +1 -1
  47. package/dist/agent/external-context/runtime.js +14 -15
  48. package/dist/agent/external-context/runtime.js.map +1 -1
  49. package/dist/agent/fast-responder.d.ts +20 -4
  50. package/dist/agent/fast-responder.d.ts.map +1 -1
  51. package/dist/agent/fast-responder.js +39 -9
  52. package/dist/agent/fast-responder.js.map +1 -1
  53. package/dist/agent/interrupt-handler.d.ts +18 -4
  54. package/dist/agent/interrupt-handler.d.ts.map +1 -1
  55. package/dist/agent/interrupt-handler.js +284 -49
  56. package/dist/agent/interrupt-handler.js.map +1 -1
  57. package/dist/agent/lab-run-system-prompt.d.ts.map +1 -1
  58. package/dist/agent/lab-run-system-prompt.js +0 -3
  59. package/dist/agent/lab-run-system-prompt.js.map +1 -1
  60. package/dist/agent/lifecycle-trace.d.ts.map +1 -1
  61. package/dist/agent/lifecycle-trace.js +5 -6
  62. package/dist/agent/lifecycle-trace.js.map +1 -1
  63. package/dist/agent/loop.d.ts +1 -4
  64. package/dist/agent/loop.d.ts.map +1 -1
  65. package/dist/agent/loop.js +129 -105
  66. package/dist/agent/loop.js.map +1 -1
  67. package/dist/agent/memory-protocol.d.ts +2 -2
  68. package/dist/agent/memory-protocol.d.ts.map +1 -1
  69. package/dist/agent/memory-protocol.js +15 -3
  70. package/dist/agent/memory-protocol.js.map +1 -1
  71. package/dist/agent/memory-store.d.ts.map +1 -1
  72. package/dist/agent/memory-store.js +1 -11
  73. package/dist/agent/memory-store.js.map +1 -1
  74. package/dist/agent/model-provider.d.ts +0 -2
  75. package/dist/agent/model-provider.d.ts.map +1 -1
  76. package/dist/agent/model-provider.js +25 -31
  77. package/dist/agent/model-provider.js.map +1 -1
  78. package/dist/agent/product-identity.d.ts +4 -5
  79. package/dist/agent/product-identity.d.ts.map +1 -1
  80. package/dist/agent/product-identity.js +6 -7
  81. package/dist/agent/product-identity.js.map +1 -1
  82. package/dist/agent/public-activity.d.ts +1 -1
  83. package/dist/agent/public-activity.d.ts.map +1 -1
  84. package/dist/agent/public-activity.js.map +1 -1
  85. package/dist/agent/reply-watchdog.d.ts +3 -5
  86. package/dist/agent/reply-watchdog.d.ts.map +1 -1
  87. package/dist/agent/reply-watchdog.js +4 -6
  88. package/dist/agent/reply-watchdog.js.map +1 -1
  89. package/dist/agent/session-manager.d.ts +0 -5
  90. package/dist/agent/session-manager.d.ts.map +1 -1
  91. package/dist/agent/session-manager.js +0 -7
  92. package/dist/agent/session-manager.js.map +1 -1
  93. package/dist/agent/state.d.ts +0 -7
  94. package/dist/agent/state.d.ts.map +1 -1
  95. package/dist/agent/state.js +11 -21
  96. package/dist/agent/state.js.map +1 -1
  97. package/dist/agent/stream-handler.d.ts.map +1 -1
  98. package/dist/agent/stream-handler.js +17 -0
  99. package/dist/agent/stream-handler.js.map +1 -1
  100. package/dist/agent/system-prompt.d.ts.map +1 -1
  101. package/dist/agent/system-prompt.js +6 -10
  102. package/dist/agent/system-prompt.js.map +1 -1
  103. package/dist/agent/transcript/transcript-indexer.d.ts +28 -0
  104. package/dist/agent/transcript/transcript-indexer.d.ts.map +1 -1
  105. package/dist/agent/transcript/transcript-indexer.js +57 -58
  106. package/dist/agent/transcript/transcript-indexer.js.map +1 -1
  107. package/dist/agent/usage/usage-report-queue.d.ts.map +1 -1
  108. package/dist/agent/usage/usage-report-queue.js +52 -53
  109. package/dist/agent/usage/usage-report-queue.js.map +1 -1
  110. package/dist/agent/wake-cycle-runner.d.ts +8 -7
  111. package/dist/agent/wake-cycle-runner.d.ts.map +1 -1
  112. package/dist/agent/wake-cycle-runner.js +66 -105
  113. package/dist/agent/wake-cycle-runner.js.map +1 -1
  114. package/dist/builtin-skills/owner-manual/SKILL.md +2 -2
  115. package/dist/calendar/occurrence-ownership.d.ts.map +1 -1
  116. package/dist/calendar/occurrence-ownership.js +5 -6
  117. package/dist/calendar/occurrence-ownership.js.map +1 -1
  118. package/dist/calendar/scheduler.d.ts.map +1 -1
  119. package/dist/calendar/scheduler.js +3 -4
  120. package/dist/calendar/scheduler.js.map +1 -1
  121. package/dist/channels/interface.d.ts +2 -8
  122. package/dist/channels/interface.d.ts.map +1 -1
  123. package/dist/channels/interface.js +1 -8
  124. package/dist/channels/interface.js.map +1 -1
  125. package/dist/channels/manager.d.ts.map +1 -1
  126. package/dist/channels/manager.js +0 -2
  127. package/dist/channels/manager.js.map +1 -1
  128. package/dist/channels/qq-status.d.ts.map +1 -1
  129. package/dist/channels/qq-status.js +1 -0
  130. package/dist/channels/qq-status.js.map +1 -1
  131. package/dist/channels/queue.d.ts.map +1 -1
  132. package/dist/channels/queue.js +0 -8
  133. package/dist/channels/queue.js.map +1 -1
  134. package/dist/channels/telegram-status.d.ts.map +1 -1
  135. package/dist/channels/telegram-status.js +2 -0
  136. package/dist/channels/telegram-status.js.map +1 -1
  137. package/dist/channels/tui.d.ts +2 -2
  138. package/dist/channels/tui.d.ts.map +1 -1
  139. package/dist/channels/tui.js +2 -7
  140. package/dist/channels/tui.js.map +1 -1
  141. package/dist/config/index.d.ts.map +1 -1
  142. package/dist/config/index.js +15 -16
  143. package/dist/config/index.js.map +1 -1
  144. package/dist/config/types.d.ts +8 -16
  145. package/dist/config/types.d.ts.map +1 -1
  146. package/dist/config/types.js +24 -13
  147. package/dist/config/types.js.map +1 -1
  148. package/dist/email/email-route-store.d.ts.map +1 -1
  149. package/dist/email/email-route-store.js +5 -6
  150. package/dist/email/email-route-store.js.map +1 -1
  151. package/dist/i18n/messages.d.ts +0 -12
  152. package/dist/i18n/messages.d.ts.map +1 -1
  153. package/dist/i18n/messages.js +9 -21
  154. package/dist/i18n/messages.js.map +1 -1
  155. package/dist/logger.d.ts +0 -2
  156. package/dist/logger.d.ts.map +1 -1
  157. package/dist/logger.js +1 -19
  158. package/dist/logger.js.map +1 -1
  159. package/dist/threads/active-threads-store.d.ts.map +1 -1
  160. package/dist/threads/active-threads-store.js +27 -8
  161. package/dist/threads/active-threads-store.js.map +1 -1
  162. package/dist/threads/execution-host.d.ts.map +1 -1
  163. package/dist/threads/execution-host.js +0 -1
  164. package/dist/threads/execution-host.js.map +1 -1
  165. package/dist/threads/execution-scheduler.d.ts +0 -4
  166. package/dist/threads/execution-scheduler.d.ts.map +1 -1
  167. package/dist/threads/execution-scheduler.js +2 -38
  168. package/dist/threads/execution-scheduler.js.map +1 -1
  169. package/dist/threads/execution-store.d.ts +5 -20
  170. package/dist/threads/execution-store.d.ts.map +1 -1
  171. package/dist/threads/execution-store.js +8 -45
  172. package/dist/threads/execution-store.js.map +1 -1
  173. package/dist/threads/foreground-task.d.ts +6 -10
  174. package/dist/threads/foreground-task.d.ts.map +1 -1
  175. package/dist/threads/foreground-task.js +26 -34
  176. package/dist/threads/foreground-task.js.map +1 -1
  177. package/dist/threads/foreman-client.d.ts.map +1 -1
  178. package/dist/threads/foreman-client.js +1 -3
  179. package/dist/threads/foreman-client.js.map +1 -1
  180. package/dist/threads/foreman-execution.d.ts +12 -12
  181. package/dist/threads/foreman-execution.d.ts.map +1 -1
  182. package/dist/threads/foreman-execution.js +19 -30
  183. package/dist/threads/foreman-execution.js.map +1 -1
  184. package/dist/threads/foreman-host.d.ts +2 -17
  185. package/dist/threads/foreman-host.d.ts.map +1 -1
  186. package/dist/threads/foreman-host.js +63 -452
  187. package/dist/threads/foreman-host.js.map +1 -1
  188. package/dist/threads/foreman-protocol.d.ts +12 -113
  189. package/dist/threads/foreman-protocol.d.ts.map +1 -1
  190. package/dist/threads/foreman-protocol.js +15 -41
  191. package/dist/threads/foreman-protocol.js.map +1 -1
  192. package/dist/threads/foreman-store.d.ts +10 -44
  193. package/dist/threads/foreman-store.d.ts.map +1 -1
  194. package/dist/threads/foreman-store.js +19 -78
  195. package/dist/threads/foreman-store.js.map +1 -1
  196. package/dist/threads/generic-execution.d.ts +2 -2
  197. package/dist/threads/generic-execution.d.ts.map +1 -1
  198. package/dist/threads/generic-execution.js +6 -9
  199. package/dist/threads/generic-execution.js.map +1 -1
  200. package/dist/threads/ipc.d.ts +0 -3
  201. package/dist/threads/ipc.d.ts.map +1 -1
  202. package/dist/threads/ipc.js.map +1 -1
  203. package/dist/threads/relay-adapter.d.ts +2 -0
  204. package/dist/threads/relay-adapter.d.ts.map +1 -1
  205. package/dist/threads/relay-adapter.js +7 -1
  206. package/dist/threads/relay-adapter.js.map +1 -1
  207. package/dist/threads/supervisor.d.ts +51 -21
  208. package/dist/threads/supervisor.d.ts.map +1 -1
  209. package/dist/threads/supervisor.js +449 -160
  210. package/dist/threads/supervisor.js.map +1 -1
  211. package/dist/threads/task-contracts.d.ts +194 -0
  212. package/dist/threads/task-contracts.d.ts.map +1 -0
  213. package/dist/threads/task-contracts.js +82 -0
  214. package/dist/threads/task-contracts.js.map +1 -0
  215. package/dist/threads/task-sidecar.d.ts +145 -0
  216. package/dist/threads/task-sidecar.d.ts.map +1 -0
  217. package/dist/threads/task-sidecar.js +311 -0
  218. package/dist/threads/task-sidecar.js.map +1 -0
  219. package/dist/threads/task-store.d.ts +172 -0
  220. package/dist/threads/task-store.d.ts.map +1 -0
  221. package/dist/threads/task-store.js +599 -0
  222. package/dist/threads/task-store.js.map +1 -0
  223. package/dist/threads/thread-activity-controller.d.ts +7 -0
  224. package/dist/threads/thread-activity-controller.d.ts.map +1 -1
  225. package/dist/threads/thread-activity-controller.js +52 -2
  226. package/dist/threads/thread-activity-controller.js.map +1 -1
  227. package/dist/threads/thread-task-context.d.ts +40 -0
  228. package/dist/threads/thread-task-context.d.ts.map +1 -0
  229. package/dist/threads/thread-task-context.js +160 -0
  230. package/dist/threads/thread-task-context.js.map +1 -0
  231. package/dist/threads/thread-task-controller.d.ts +122 -0
  232. package/dist/threads/thread-task-controller.d.ts.map +1 -0
  233. package/dist/threads/thread-task-controller.js +276 -0
  234. package/dist/threads/thread-task-controller.js.map +1 -0
  235. package/dist/threads/thread-task-runtime.d.ts +350 -0
  236. package/dist/threads/thread-task-runtime.d.ts.map +1 -0
  237. package/dist/threads/thread-task-runtime.js +896 -0
  238. package/dist/threads/thread-task-runtime.js.map +1 -0
  239. package/dist/threads/worker-entry.d.ts.map +1 -1
  240. package/dist/threads/worker-entry.js +28 -29
  241. package/dist/threads/worker-entry.js.map +1 -1
  242. package/dist/threads/worker-execution-runtime.d.ts +2 -3
  243. package/dist/threads/worker-execution-runtime.d.ts.map +1 -1
  244. package/dist/threads/worker-execution-runtime.js +2 -5
  245. package/dist/threads/worker-execution-runtime.js.map +1 -1
  246. package/dist/tools/delegation.d.ts +2 -75
  247. package/dist/tools/delegation.d.ts.map +1 -1
  248. package/dist/tools/delegation.js +29 -63
  249. package/dist/tools/delegation.js.map +1 -1
  250. package/dist/tools/email.d.ts +1 -1
  251. package/dist/tools/execution-control.d.ts +0 -5
  252. package/dist/tools/execution-control.d.ts.map +1 -1
  253. package/dist/tools/execution-control.js +2 -9
  254. package/dist/tools/execution-control.js.map +1 -1
  255. package/dist/tools/index.d.ts.map +1 -1
  256. package/dist/tools/index.js +6 -2
  257. package/dist/tools/index.js.map +1 -1
  258. package/dist/tools/mcp-manager.d.ts +1 -1
  259. package/dist/tools/memory.d.ts +1 -1
  260. package/dist/tools/notify.d.ts +0 -2
  261. package/dist/tools/notify.d.ts.map +1 -1
  262. package/dist/tools/notify.js +5 -21
  263. package/dist/tools/notify.js.map +1 -1
  264. package/dist/tools/skill.d.ts +1 -1
  265. package/dist/tools/thread-tasks.d.ts +23 -0
  266. package/dist/tools/thread-tasks.d.ts.map +1 -0
  267. package/dist/tools/thread-tasks.js +122 -0
  268. package/dist/tools/thread-tasks.js.map +1 -0
  269. package/dist/tools/totp.d.ts +1 -1
  270. package/dist/tui/request-store.d.ts +1 -1
  271. package/dist/tui/request-store.d.ts.map +1 -1
  272. package/dist/tui/request-store.js +2 -2
  273. package/dist/tui/request-store.js.map +1 -1
  274. package/dist/tui/server.d.ts.map +1 -1
  275. package/dist/tui/server.js +2 -17
  276. package/dist/tui/server.js.map +1 -1
  277. package/dist/utils/restart.d.ts.map +1 -1
  278. package/dist/utils/restart.js +0 -6
  279. package/dist/utils/restart.js.map +1 -1
  280. package/dist-agent/bundle.cjs +2 -2
  281. package/package.json +1 -1
  282. package/dist/agent/runtime-context.d.ts +0 -27
  283. package/dist/agent/runtime-context.d.ts.map +0 -1
  284. package/dist/agent/runtime-context.js +0 -60
  285. package/dist/agent/runtime-context.js.map +0 -1
  286. package/dist/agent/runtime-disclosure-policy.d.ts +0 -6
  287. package/dist/agent/runtime-disclosure-policy.d.ts.map +0 -1
  288. package/dist/agent/runtime-disclosure-policy.js +0 -16
  289. package/dist/agent/runtime-disclosure-policy.js.map +0 -1
  290. package/dist/conversations/lazy-runtime.d.ts +0 -5
  291. package/dist/conversations/lazy-runtime.d.ts.map +0 -1
  292. package/dist/conversations/lazy-runtime.js +0 -12
  293. package/dist/conversations/lazy-runtime.js.map +0 -1
  294. package/dist/conversations/runtime.d.ts +0 -17
  295. package/dist/conversations/runtime.d.ts.map +0 -1
  296. package/dist/conversations/runtime.js +0 -92
  297. package/dist/conversations/runtime.js.map +0 -1
  298. package/dist/conversations/scheduler.d.ts +0 -57
  299. package/dist/conversations/scheduler.d.ts.map +0 -1
  300. package/dist/conversations/scheduler.js +0 -175
  301. package/dist/conversations/scheduler.js.map +0 -1
  302. package/dist/threads/foreman-business.d.ts +0 -9
  303. package/dist/threads/foreman-business.d.ts.map +0 -1
  304. package/dist/threads/foreman-business.js +0 -117
  305. package/dist/threads/foreman-business.js.map +0 -1
  306. package/dist/threads/foreman-delivery.d.ts +0 -5
  307. package/dist/threads/foreman-delivery.d.ts.map +0 -1
  308. package/dist/threads/foreman-delivery.js +0 -21
  309. package/dist/threads/foreman-delivery.js.map +0 -1
  310. package/dist/threads/task-workspace.d.ts +0 -8
  311. package/dist/threads/task-workspace.d.ts.map +0 -1
  312. package/dist/threads/task-workspace.js +0 -52
  313. package/dist/threads/task-workspace.js.map +0 -1
@@ -8,7 +8,6 @@
8
8
  import { isEmailChannel, presentsAssistantText } from "../channels/interface.js";
9
9
  import { isCalendarInterruptMessage } from "./owner-initiated-eligibility.js";
10
10
  import { emailAddressesMatch } from "../utils/email.js";
11
- import { EXTERNAL_WAKE_KIND } from "../channels/queue.js";
12
11
  import { nextRecoveryPhase } from "./lifecycle-transitions.js";
13
12
  import { COMPACTION_FAILURE_CONTINUATION, CompactionFailureError } from "./compaction-failure.js";
14
13
  import { ToolOutputRepairError } from "./transcript-output-repair.js";
@@ -33,6 +32,8 @@ import path from "path";
33
32
  import { randomUUID } from "node:crypto";
34
33
  import { runContextRefresh } from "./context-refresh.js";
35
34
  import { loadToolAbortRecoveryChain, saveToolAbortRecoveryChain } from "./recovery-chain.js";
35
+ import { buildThreadTaskContext, executionEvents, isTaskRelevantMessage, recordExecutionEvent, } from "../threads/thread-task-runtime.js";
36
+ import { isThreadTaskContextBlock } from "../threads/thread-task-context.js";
36
37
  import { getWorkerExecutionRuntime } from "../threads/worker-execution-runtime.js";
37
38
  const AUTH_PATTERNS = /\b(401|402|403|authentication|billing|unauthorized|forbidden|api.?key)\b/i;
38
39
  // Fatal environment/installation problems that won't fix themselves on retry.
@@ -276,15 +277,15 @@ export function isSharedCredentialFailure(err) {
276
277
  }
277
278
  return classifyError(err) === "provider_failed" && !isEngineScopedFailure(err);
278
279
  }
279
- function formatOwnerErrorNotice(category, consecutiveErrors, errMsg) {
280
+ function formatOwnerErrorNotice(category, consecutiveErrors, sleepMin, errMsg) {
280
281
  if (category === "fatal") {
281
- return "Agent encountered an internal runtime error. This wake has stopped; the next message or heartbeat will retry.";
282
+ return `Agent encountered an internal runtime error. Sleeping ${sleepMin}m before retrying.`;
282
283
  }
283
284
  if (category === "local") {
284
285
  // Name the cause. The old text blamed the model provider for an error the
285
286
  // provider never saw, which is what made the original alert storm
286
287
  // impossible to diagnose from the notification alone.
287
- return `Agent hit a local session-coordination error (not a provider failure): ${errMsg ?? "unknown cause"}. This wake has stopped; the next message or heartbeat will retry.`;
288
+ return `Agent hit a local session-coordination error (not a provider failure): ${errMsg ?? "unknown cause"}. Sleeping ${sleepMin}m before retrying.`;
288
289
  }
289
290
  if (category === "request_invalid") {
290
291
  return `Agent stopped this wake because the provider rejected a malformed request (${errMsg ?? "unknown cause"}).`;
@@ -295,7 +296,7 @@ function formatOwnerErrorNotice(category, consecutiveErrors, errMsg) {
295
296
  if (category === "empty_turn") {
296
297
  return "The assistant returned another empty turn without confirming completion. The task may be incomplete. I stopped automatic continuation to avoid repeating work; existing results should be checked before continuing.";
297
298
  }
298
- return `Agent encountered an internal model/provider error ${consecutiveErrors} times in a row. This wake has stopped; the next message or heartbeat will retry.`;
299
+ return `Agent encountered an internal model/provider error ${consecutiveErrors} times in a row. Sleeping ${sleepMin}m before retrying.`;
299
300
  }
300
301
  function isPromptTooLong(err) {
301
302
  const terminalReason = getTerminalReason(err);
@@ -363,6 +364,7 @@ export const MAX_EMPTY_TURN_RETRIES = 1;
363
364
  const ERROR_RETRY_COMPACT_USED_PCT = 0.7;
364
365
  const BASE_DELAY_MS = 5_000;
365
366
  const MAX_DELAY_MS = 5 * 60_000;
367
+ const FATAL_SLEEP_MS = 5 * 60_000;
366
368
  /** Max times to retry the LLM call on stream silence timeout before aborting. */
367
369
  export const MAX_SILENCE_RETRIES = 3;
368
370
  /** Bounded retries for transient stream/network disconnects in any wake cycle. */
@@ -377,6 +379,22 @@ export const MAX_OUTPUT_RECOVERY_ATTEMPTS = 2;
377
379
  const SILENCE_TIMEOUT_SCHEDULE_MS = [300_000, 600_000, 900_000];
378
380
  /** Decreasing effort levels per attempt to reduce thinking overhead on retries. */
379
381
  const EFFORT_SCHEDULE = ["xhigh", "high", "medium"];
382
+ /**
383
+ * A wake retried on a reset session re-sends its first message, whose thread
384
+ * task block (#1996) was built before the reset's reconcile. Swap it for a
385
+ * fresh one so the new session sees exactly one, current copy. Without a
386
+ * task runtime this returns the blocks unchanged.
387
+ */
388
+ export function withFreshThreadTaskBlock(blocks) {
389
+ const fresh = buildThreadTaskContext();
390
+ const index = blocks.findIndex((block) => block.type === "text" && isThreadTaskContextBlock(block.text));
391
+ if (index === -1)
392
+ return fresh ? [...blocks, { type: "text", text: fresh }] : blocks;
393
+ const rest = blocks.filter((_, i) => i !== index);
394
+ if (fresh)
395
+ rest.splice(index, 0, { type: "text", text: fresh });
396
+ return rest;
397
+ }
380
398
  export class WakeCycleRunner {
381
399
  externalContextRegistry;
382
400
  deps;
@@ -388,12 +406,6 @@ export class WakeCycleRunner {
388
406
  localCoordinationErrors = 0;
389
407
  /** Empty Claude assistant turns in this wake (not counted as network failures). */
390
408
  emptyTurnRetries = 0;
391
- /**
392
- * Set once a call's silence ceiling is spent. Silence retries live inside
393
- * the call, so the outer error handler must not spend another backoff on
394
- * the same call. Cleared with the next wake.
395
- */
396
- silenceRecoveryExhausted = false;
397
409
  /** Recovering only while a spendable bucket is in use. Reset when the wake ends. */
398
410
  recoveryPhase = "Running";
399
411
  /**
@@ -494,7 +506,6 @@ export class WakeCycleRunner {
494
506
  const { config, ownerConfig, session, activityTracker, interruptHandler, messageQueue } = this.deps;
495
507
  this.malformedRequestRefreshAttempted = false;
496
508
  this.emptyTurnRetries = 0;
497
- this.silenceRecoveryExhausted = false;
498
509
  this.recoveryPhase = "Running";
499
510
  // --- Capture transcript start lines for post-cycle indexing ---
500
511
  // Recovery can rotate the session to a new transcript, so anchor by
@@ -502,6 +513,7 @@ export class WakeCycleRunner {
502
513
  // entry.
503
514
  const wakeCycleId = generateWakeCycleId();
504
515
  /** Numbers model turns within this wake for their task-event keys. */
516
+ let responseSeq = 0;
505
517
  let awaitingUser = false;
506
518
  /** The model ended the wake with an accepted finish (not a timeout or exhausted recovery). */
507
519
  let wakeFinished = false;
@@ -539,7 +551,10 @@ export class WakeCycleRunner {
539
551
  // failover. If the session cannot be rotated, stay on the current engine
540
552
  // and try again next wake rather than replaying its history elsewhere.
541
553
  if (recovered) {
542
- if (!await this.rotateSessionForEngineSwitch(session, String(activeBefore), recovered.recovered, "recovery")) {
554
+ if (await this.rotateSessionForEngineSwitch(session, String(activeBefore), recovered.recovered, "recovery")) {
555
+ contentBlocks = withFreshThreadTaskBlock(contentBlocks);
556
+ }
557
+ else {
543
558
  setFallbackEngine(fallbackBefore);
544
559
  }
545
560
  }
@@ -577,7 +592,7 @@ export class WakeCycleRunner {
577
592
  // Retry this inner business turn only for transient stream/socket failures.
578
593
  while (transientStreamRetries <= MAX_TRANSIENT_STREAM_RETRIES) {
579
594
  try {
580
- silenceLoop: for (let silenceAttempt = 1; silenceAttempt <= MAX_SILENCE_RETRIES; silenceAttempt++) {
595
+ for (let silenceAttempt = 1; silenceAttempt <= MAX_SILENCE_RETRIES; silenceAttempt++) {
581
596
  const scheduledSilenceMs = SILENCE_TIMEOUT_SCHEDULE_MS[silenceAttempt - 1] ?? SILENCE_TIMEOUT_SCHEDULE_MS[SILENCE_TIMEOUT_SCHEDULE_MS.length - 1];
582
597
  // Pass the scheduled bound. Pi auto-compaction starts after the
583
598
  // stream is already open (system/compaction_start); processAgentStream
@@ -600,6 +615,9 @@ export class WakeCycleRunner {
600
615
  lastReplyText = streamResult.lastAssistantText;
601
616
  lastReplyPresented = streamResult.lastAssistantTextPresented === true;
602
617
  }
618
+ // One event per model turn with its complete prose; the task
619
+ // sidecar folds it into progress. No-op without a task runtime.
620
+ recordExecutionEvent(executionEvents.response(`${wakeCycleId}:${responseSeq++}`, streamResult.assistantText));
603
621
  // A successful SDK envelope can still contain a legitimate empty
604
622
  // model turn. Apply the same task-state policy as SDK diagnostics.
605
623
  if (!this.deps.singleShot && streamResult.emptyTurn && !streamResult.finishCalled && !streamResult.toolTimeout) {
@@ -640,37 +658,22 @@ export class WakeCycleRunner {
640
658
  `skipping the remaining ${MAX_SILENCE_RETRIES - silenceAttempt} silence retries`);
641
659
  }
642
660
  }
643
- // silenceAttempt counts the tries already made in this call. A dead
644
- // engine does not get the remaining ones: that is Stopping, not
645
- // another Recovering retry.
646
- const silencePhase = nextRecoveryPhase({
647
- from: this.recoveryPhase,
648
- bucket: "silence",
649
- spent: engineAnswersProbe ? silenceAttempt : MAX_SILENCE_RETRIES,
650
- });
651
- if (silencePhase === "Recovering")
652
- this.recoveryPhase = "Recovering";
653
- if (silencePhase === "Recovering" && engineAnswersProbe) {
661
+ if (silenceAttempt < MAX_SILENCE_RETRIES && engineAnswersProbe) {
654
662
  const nextTimeoutMs = SILENCE_TIMEOUT_SCHEDULE_MS[silenceAttempt] ?? SILENCE_TIMEOUT_SCHEDULE_MS[SILENCE_TIMEOUT_SCHEDULE_MS.length - 1];
655
663
  const nextEffort = tierEffort
656
664
  ? getLlmRetryEffort(tierEffort, silenceAttempt)
657
665
  : effortSchedule[silenceAttempt];
658
666
  logger.system(`Stream silence timeout after ${silenceTimeoutMs / 1000}s (attempt ${silenceAttempt}/${MAX_SILENCE_RETRIES}) — engine still answering, retrying LLM call with ${nextTimeoutMs / 1000}s timeout and effort=${nextEffort}...`);
659
- // Re-send the same messageContent on the next attempt of this call.
660
- continue silenceLoop;
667
+ // Re-send the same messageContent on next iteration
668
+ continue;
661
669
  }
662
670
  // Retries exhausted, or cut short because the engine failed its
663
- // health probe. Both are Stopping for this call. A hot engine
664
- // switch or a successful compaction below continues this wake, so
665
- // the outer handler is only blocked once this call is actually
666
- // abandoned.
671
+ // health probe.
667
672
  logger.err(engineAnswersProbe
668
673
  ? `Stream silence timeout: all ${MAX_SILENCE_RETRIES} attempts exhausted — aborting`
669
674
  : `Stream silence timeout on attempt ${silenceAttempt}/${MAX_SILENCE_RETRIES} with a failed health probe — aborting`);
670
- if (this.deps.singleShot) {
671
- this.silenceRecoveryExhausted = true;
675
+ if (this.deps.singleShot)
672
676
  throw streamErr;
673
- }
674
677
  const locale = resolveLocale(ownerConfig.language);
675
678
  // Report what actually happened: a probe-driven short circuit may
676
679
  // have spent only one of the three attempts, and telling the owner
@@ -687,6 +690,7 @@ export class WakeCycleRunner {
687
690
  const degraded = await this.degradeActiveEngine(engineFallback, "silence_exhausted", session);
688
691
  if (degraded === "hot") {
689
692
  consecutiveErrors = 0;
693
+ contentBlocks = withFreshThreadTaskBlock(contentBlocks);
690
694
  continue wakeLoop;
691
695
  }
692
696
  if (degraded === "restarting") {
@@ -705,6 +709,9 @@ export class WakeCycleRunner {
705
709
  try {
706
710
  await activeSession.requestCompaction();
707
711
  consecutiveErrors = 0;
712
+ // The compacted session must see the tasks as the compaction
713
+ // reconcile just left them, not the wake-start block (#1996).
714
+ contentBlocks = withFreshThreadTaskBlock(contentBlocks);
708
715
  continue wakeLoop;
709
716
  }
710
717
  catch (compactErr) {
@@ -721,7 +728,6 @@ export class WakeCycleRunner {
721
728
  ...taskContinuationRouting(this.deps.agentState.currentTaskReplyTarget),
722
729
  });
723
730
  logger.system("Enqueued interrupted-task recovery message for next wake cycle");
724
- this.silenceRecoveryExhausted = true;
725
731
  throw streamErr; // propagate so the outer handler records the error
726
732
  }
727
733
  }
@@ -729,18 +735,7 @@ export class WakeCycleRunner {
729
735
  }
730
736
  catch (streamErr) {
731
737
  // TODO: guard retry against simultaneous stop requests and settle the failed input generator before retrying; both are rare stream/stop races.
732
- // transientStreamRetries counts the disconnects already retried in
733
- // this turn. Another one is Recovering; the ceiling throws, and
734
- // the outer handler records it instead of retrying this turn.
735
- const networkPhase = isTransientStreamFailure(streamErr)
736
- ? nextRecoveryPhase({
737
- from: this.recoveryPhase,
738
- bucket: "network",
739
- spent: transientStreamRetries,
740
- })
741
- : null;
742
- if (networkPhase === "Recovering") {
743
- this.recoveryPhase = "Recovering";
738
+ if (isTransientStreamFailure(streamErr) && transientStreamRetries < MAX_TRANSIENT_STREAM_RETRIES) {
744
739
  transientStreamRetries++;
745
740
  logger.system(`Transient stream failure (retry ${transientStreamRetries}/${MAX_TRANSIENT_STREAM_RETRIES}) — retrying the current business turn...`);
746
741
  continue;
@@ -818,16 +813,7 @@ export class WakeCycleRunner {
818
813
  if (!this.deps.singleShot &&
819
814
  endedWithoutLifecycleAction &&
820
815
  runningSubagents.length === 0) {
821
- // prematureEndContinuations counts the continuations already sent in
822
- // this wake. One more is Recovering; the ceiling is Stopping, and
823
- // this wake ends instead of starting another continuation.
824
- const prematurePhase = nextRecoveryPhase({
825
- from: this.recoveryPhase,
826
- bucket: "premature_result",
827
- spent: prematureEndContinuations,
828
- });
829
- if (prematurePhase === "Recovering") {
830
- this.recoveryPhase = "Recovering";
816
+ if (prematureEndContinuations < MAX_PREMATURE_END_CONTINUATIONS) {
831
817
  prematureEndContinuations++;
832
818
  logger.warn(`Turn ended without finish — continuing (${prematureEndContinuations}/${MAX_PREMATURE_END_CONTINUATIONS})`);
833
819
  messageContent = [{
@@ -894,18 +880,9 @@ export class WakeCycleRunner {
894
880
  }
895
881
  }
896
882
  else {
897
- // The persisted count is this chain's own budget. It survives
898
- // wakes and restarts, so a new wake must not refill it.
899
- const toolAbortPhase = nextRecoveryPhase({
900
- from: this.recoveryPhase,
901
- bucket: "tool_abort",
902
- spent: this.toolAbortRecoveries,
903
- });
904
883
  this.toolAbortRecoveryChainId ??= randomUUID();
905
884
  this.toolAbortRecoveries += 1;
906
- const retryable = toolAbortPhase === "Recovering";
907
- if (retryable)
908
- this.recoveryPhase = "Recovering";
885
+ const retryable = this.toolAbortRecoveries <= MAX_TOOL_ABORT_RECOVERIES;
909
886
  if (!retryable)
910
887
  this.toolAbortRecoveryExhausted = true;
911
888
  this.persistToolAbortRecoveryChain();
@@ -966,7 +943,7 @@ export class WakeCycleRunner {
966
943
  // Resumes, external wakes and calendars may carry a delivery target
967
944
  // without requesting a reply. Mid-turn work uses explicit notify_user
968
945
  // for its result; a quick acknowledgment does not enable this fallback.
969
- const replyMessages = messages.filter((message) => message.channel !== "system" && message.meta?.kind !== EXTERNAL_WAKE_KIND && !isCalendarInterruptMessage(message));
946
+ const replyMessages = messages.filter((message) => isTaskRelevantMessage(message) && !isCalendarInterruptMessage(message));
970
947
  const fallbackEligible = !this.deps.singleShot &&
971
948
  !streamResult.toolTimeout &&
972
949
  !skipBecauseNotified &&
@@ -1110,14 +1087,21 @@ export class WakeCycleRunner {
1110
1087
  if (errorResult.kind === "continue_with_message") {
1111
1088
  consecutiveErrors = errorResult.consecutiveErrors;
1112
1089
  contentBlocks = [{ type: "text", text: errorResult.message }];
1090
+ // A rebuilt session starts without the thread's tasks (#1996).
1091
+ if (errorResult.newSession)
1092
+ contentBlocks = withFreshThreadTaskBlock(contentBlocks);
1113
1093
  continue;
1114
1094
  }
1115
1095
  if (errorResult.kind === "continue_with_reset") {
1116
1096
  consecutiveErrors = 0;
1097
+ contentBlocks = withFreshThreadTaskBlock(contentBlocks);
1117
1098
  continue;
1118
1099
  }
1119
1100
  if (errorResult.kind === "continue_preserve_count") {
1120
1101
  consecutiveErrors = errorResult.consecutiveErrors;
1102
+ // Invalid-json recovery may have compacted (#1996): resend the
1103
+ // current task block. A no-op when nothing changed.
1104
+ contentBlocks = withFreshThreadTaskBlock(contentBlocks);
1121
1105
  continue;
1122
1106
  }
1123
1107
  consecutiveErrors = errorResult.consecutiveErrors;
@@ -1197,12 +1181,6 @@ export class WakeCycleRunner {
1197
1181
  logger.system(err.message);
1198
1182
  return { kind: "done", consecutiveErrors };
1199
1183
  }
1200
- if (this.silenceRecoveryExhausted) {
1201
- // The call already spent its own silence ceiling and moved to Stopping.
1202
- // Another backoff here would replay that call instead of ending it.
1203
- logger.system("Stream silence ceiling already spent — ending this wake cycle");
1204
- return { kind: "done", consecutiveErrors: consecutiveErrors + 1 };
1205
- }
1206
1184
  const errMsg = err instanceof Error ? err.message : String(err);
1207
1185
  const category = classifyError(err);
1208
1186
  // A local coordination error never reached a provider, so it must not
@@ -1470,15 +1448,16 @@ export class WakeCycleRunner {
1470
1448
  return { kind: "continue_preserve_count", consecutiveErrors };
1471
1449
  }
1472
1450
  // Retries exhausted: tell the owner what actually broke, naming the
1473
- // local cause instead of blaming the model provider. The wake ends here.
1474
- // Sleeping would keep the foreground slot busy until the timer fired.
1451
+ // local cause instead of blaming the model provider.
1452
+ const sleepMin = FATAL_SLEEP_MS / 60_000;
1475
1453
  logger.warn(`Notifying owner about persistent local coordination error: ${errMsg}`);
1476
1454
  try {
1477
- await notifyOwner(formatOwnerErrorNotice(category, consecutiveErrors, errMsg), config);
1455
+ await notifyOwner(formatOwnerErrorNotice(category, consecutiveErrors, sleepMin, errMsg), config);
1478
1456
  }
1479
1457
  catch (notifyErr) {
1480
1458
  logger.warn(`Failed to send error notification to owner: ${notifyErr instanceof Error ? notifyErr.message : String(notifyErr)}`);
1481
1459
  }
1460
+ await new Promise((r) => setTimeout(r, FATAL_SLEEP_MS));
1482
1461
  return { kind: "done", consecutiveErrors };
1483
1462
  }
1484
1463
  // --- Known malformed Responses replay ---
@@ -1517,7 +1496,7 @@ export class WakeCycleRunner {
1517
1496
  }
1518
1497
  logger.err(`Malformed Responses replay recovery failed — ending wake: ${errMsg}`);
1519
1498
  try {
1520
- await notifyOwner(formatOwnerErrorNotice(category, consecutiveErrors, errMsg), config);
1499
+ await notifyOwner(formatOwnerErrorNotice(category, consecutiveErrors, 0, errMsg), config);
1521
1500
  }
1522
1501
  catch (notifyErr) {
1523
1502
  logger.warn(`Failed to send error notification to owner: ${notifyErr instanceof Error ? notifyErr.message : String(notifyErr)}`);
@@ -1546,7 +1525,7 @@ export class WakeCycleRunner {
1546
1525
  if (phase === "Stopping") {
1547
1526
  logger.warn(`Empty-turn continuation did not confirm task completion: ${errMsg}`);
1548
1527
  try {
1549
- await notifyOwner(formatOwnerErrorNotice(category, this.emptyTurnRetries, errMsg), config);
1528
+ await notifyOwner(formatOwnerErrorNotice(category, this.emptyTurnRetries, 0, errMsg), config);
1550
1529
  }
1551
1530
  catch (notifyErr) {
1552
1531
  logger.warn(`Failed to send empty-turn notification to owner: ${notifyErr instanceof Error ? notifyErr.message : String(notifyErr)}`);
@@ -1615,22 +1594,10 @@ export class WakeCycleRunner {
1615
1594
  logger.system("No alternate engine available for transient network failures; keeping consecutiveErrors and the cooldown");
1616
1595
  }
1617
1596
  }
1618
- // consecutiveErrors counts the failures already seen in this wake. The
1619
- // notify threshold is the unknown bucket, not the network disconnect
1620
- // ceiling. Reaching it is Stopping: notify, then end the wake. The next
1621
- // attempt waits for a queued message or the proactive timer, not a sleep
1622
- // that holds this slot.
1623
- const unknownPhase = category === "fatal"
1624
- ? "Stopping"
1625
- : nextRecoveryPhase({
1626
- from: this.recoveryPhase,
1627
- bucket: "unknown",
1628
- spent: consecutiveErrors,
1629
- });
1630
- if (unknownPhase === "Recovering")
1631
- this.recoveryPhase = "Recovering";
1632
- if (unknownPhase === "Stopping") {
1633
- const notice = formatOwnerErrorNotice(category, consecutiveErrors);
1597
+ if (category === "fatal" ||
1598
+ consecutiveErrors >= MAX_RETRIES_BEFORE_NOTIFY) {
1599
+ const sleepMin = FATAL_SLEEP_MS / 60_000;
1600
+ const notice = formatOwnerErrorNotice(category, consecutiveErrors, sleepMin);
1634
1601
  logger.warn(`Notifying owner about repeated agent error: ${errMsg}`);
1635
1602
  try {
1636
1603
  await notifyOwner(notice, config);
@@ -1639,6 +1606,7 @@ export class WakeCycleRunner {
1639
1606
  logger.warn(`Failed to send error notification to owner: ${notifyErr instanceof Error ? notifyErr.message : String(notifyErr)}`);
1640
1607
  }
1641
1608
  consecutiveErrors = 0;
1609
+ await new Promise((r) => setTimeout(r, FATAL_SLEEP_MS));
1642
1610
  }
1643
1611
  else {
1644
1612
  try {
@@ -1717,15 +1685,8 @@ export class WakeCycleRunner {
1717
1685
  // Two recovery turns total: repair, then one refresh. The second branch is
1718
1686
  // gated by the ceiling, so raising MAX_OUTPUT_RECOVERY_ATTEMPTS is what
1719
1687
  // allows another turn. Advancing before I/O keeps a failed repair from
1720
- // resetting the allowance. The two kinds keep separate counts.
1721
- const outputPhase = nextRecoveryPhase({
1722
- from: this.recoveryPhase,
1723
- bucket: recoveryKind === "policy_refused" ? "policy_refusal" : "oversized",
1724
- spent: attempts[recoveryKind],
1725
- });
1726
- if (outputPhase === "Recovering")
1727
- this.recoveryPhase = "Recovering";
1728
- if (attempts[recoveryKind] === 0 && outputPhase === "Recovering") {
1688
+ // resetting the allowance.
1689
+ if (attempts[recoveryKind] === 0) {
1729
1690
  attempts[recoveryKind]++;
1730
1691
  if (!compactionAlreadyFailed) {
1731
1692
  try {
@@ -1744,7 +1705,7 @@ export class WakeCycleRunner {
1744
1705
  logger.system("SDK auto-compaction already failed — skipping same-session transcript repair and rotating context");
1745
1706
  }
1746
1707
  }
1747
- if (attempts[recoveryKind] < MAX_OUTPUT_RECOVERY_ATTEMPTS && outputPhase === "Recovering") {
1708
+ if (attempts[recoveryKind] < MAX_OUTPUT_RECOVERY_ATTEMPTS) {
1748
1709
  attempts[recoveryKind]++;
1749
1710
  const previousId = session.activeSession.getSessionId();
1750
1711
  try {