@hiai-gg/hiai-opencode 0.1.7 → 0.1.9

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 (1195) hide show
  1. package/AGENTS.md +35 -0
  2. package/ARCHITECTURE.md +39 -0
  3. package/README.md +96 -9
  4. package/dist/agents/coder/agent.d.ts +2 -2
  5. package/dist/agents/dynamic-agent-core-sections.d.ts +3 -1
  6. package/dist/agents/guard/agent.d.ts +4 -15
  7. package/dist/agents/prompt-library/index.d.ts +1 -0
  8. package/dist/agents/prompt-library/integration-map.d.ts +9 -0
  9. package/dist/agents/strategist/system-prompt.d.ts +5 -8
  10. package/dist/agents/sub/agent.d.ts +6 -8
  11. package/dist/agents/sub/index.d.ts +0 -4
  12. package/dist/config/schema/oh-my-opencode-config.d.ts +2 -0
  13. package/dist/config/schema/ralph-loop.d.ts +2 -0
  14. package/dist/hooks/keyword-detector/ultrawork/default.d.ts +1 -1
  15. package/dist/hooks/keyword-detector/ultrawork/gemini.d.ts +1 -1
  16. package/dist/hooks/keyword-detector/ultrawork/gpt.d.ts +1 -1
  17. package/dist/hooks/keyword-detector/ultrawork/intent-gate.d.ts +0 -0
  18. package/dist/hooks/keyword-detector/ultrawork/planner.d.ts +1 -1
  19. package/dist/hooks/todo-continuation-enforcer/auto-loop.d.ts +23 -0
  20. package/dist/hooks/todo-continuation-enforcer/handler.d.ts +5 -0
  21. package/dist/hooks/todo-continuation-enforcer/idle-event.d.ts +5 -0
  22. package/dist/hooks/todo-continuation-enforcer/types.d.ts +18 -0
  23. package/dist/index.js +1532 -4127
  24. package/dist/plugin/hooks/create-continuation-hooks.d.ts +2 -1
  25. package/dist/shared/index.d.ts +1 -0
  26. package/dist/shared/learn-system.d.ts +18 -0
  27. package/hiai-opencode.json +8 -5
  28. package/package.json +3 -3
  29. package/dist/agents/bob/default.d.ts +0 -10
  30. package/dist/agents/bob/gemini.d.ts +0 -21
  31. package/dist/agents/bob/gpt-pro.d.ts +0 -27
  32. package/dist/agents/bob/index.d.ts +0 -11
  33. package/dist/agents/coder/gpt-codex.d.ts +0 -20
  34. package/dist/agents/coder/gpt-pro.d.ts +0 -25
  35. package/dist/agents/guard/gemini-prompt-sections.d.ts +0 -12
  36. package/dist/agents/guard/gemini.d.ts +0 -2
  37. package/dist/agents/guard/gpt-prompt-sections.d.ts +0 -12
  38. package/dist/agents/guard/gpt.d.ts +0 -2
  39. package/dist/agents/strategist/gemini.d.ts +0 -13
  40. package/dist/agents/strategist/gpt.d.ts +0 -12
  41. package/dist/agents/sub/gemini.d.ts +0 -10
  42. package/dist/agents/sub/gpt-codex.d.ts +0 -8
  43. package/dist/agents/sub/gpt-pro.d.ts +0 -11
  44. package/dist/agents/sub/gpt.d.ts +0 -9
  45. package/dist/skills/index.d.ts +0 -7
  46. package/dist/testing/module-mock-lifecycle.d.ts +0 -21
  47. package/src/AGENTS.md +0 -41
  48. package/src/agents/AGENTS.md +0 -74
  49. package/src/agents/agent-builder.ts +0 -50
  50. package/src/agents/agent-skills.ts +0 -70
  51. package/src/agents/bob/AGENTS.md +0 -29
  52. package/src/agents/bob/default.ts +0 -134
  53. package/src/agents/bob/gemini.ts +0 -238
  54. package/src/agents/bob/gpt-pro.ts +0 -432
  55. package/src/agents/bob/index.ts +0 -19
  56. package/src/agents/bob.ts +0 -531
  57. package/src/agents/brainstormer.ts +0 -72
  58. package/src/agents/builtin-agents/agent-overrides.ts +0 -75
  59. package/src/agents/builtin-agents/available-skills.ts +0 -35
  60. package/src/agents/builtin-agents/bob-agent.ts +0 -96
  61. package/src/agents/builtin-agents/coder-agent.ts +0 -98
  62. package/src/agents/builtin-agents/environment-context.ts +0 -16
  63. package/src/agents/builtin-agents/general-agents.ts +0 -122
  64. package/src/agents/builtin-agents/guard-agent.ts +0 -66
  65. package/src/agents/builtin-agents/model-resolution.ts +0 -31
  66. package/src/agents/builtin-agents/resolve-file-uri.ts +0 -42
  67. package/src/agents/builtin-agents.ts +0 -250
  68. package/src/agents/coder/AGENTS.md +0 -34
  69. package/src/agents/coder/agent.ts +0 -162
  70. package/src/agents/coder/gpt-codex.ts +0 -406
  71. package/src/agents/coder/gpt-pro.ts +0 -321
  72. package/src/agents/coder/gpt.ts +0 -255
  73. package/src/agents/coder/index.ts +0 -8
  74. package/src/agents/critic/agent.ts +0 -106
  75. package/src/agents/custom-agent-summaries.ts +0 -61
  76. package/src/agents/designer.ts +0 -70
  77. package/src/agents/dynamic-agent-category-skills-guide.ts +0 -144
  78. package/src/agents/dynamic-agent-core-sections.ts +0 -273
  79. package/src/agents/dynamic-agent-policy-sections.ts +0 -182
  80. package/src/agents/dynamic-agent-prompt-builder.ts +0 -32
  81. package/src/agents/dynamic-agent-prompt-types.ts +0 -24
  82. package/src/agents/dynamic-agent-tool-categorization.ts +0 -45
  83. package/src/agents/env-context.ts +0 -16
  84. package/src/agents/gpt-apply-patch-guard.ts +0 -7
  85. package/src/agents/guard/agent.ts +0 -146
  86. package/src/agents/guard/default-prompt-sections.ts +0 -305
  87. package/src/agents/guard/default.ts +0 -23
  88. package/src/agents/guard/gemini-prompt-sections.ts +0 -293
  89. package/src/agents/guard/gemini.ts +0 -23
  90. package/src/agents/guard/gpt-prompt-sections.ts +0 -296
  91. package/src/agents/guard/gpt.ts +0 -23
  92. package/src/agents/guard/index.ts +0 -2
  93. package/src/agents/guard/prompt-section-builder.ts +0 -104
  94. package/src/agents/guard/shared-prompt.ts +0 -172
  95. package/src/agents/index.ts +0 -5
  96. package/src/agents/platform-adapter.ts +0 -236
  97. package/src/agents/platform-manager.ts +0 -73
  98. package/src/agents/prompt-library/identity.ts +0 -14
  99. package/src/agents/prompt-library/index.ts +0 -7
  100. package/src/agents/prompt-library/intent-gate.ts +0 -149
  101. package/src/agents/prompt-library/orchestration.ts +0 -60
  102. package/src/agents/prompt-library/platform.ts +0 -70
  103. package/src/agents/prompt-library/specialized.ts +0 -39
  104. package/src/agents/prompt-library/strategy.ts +0 -80
  105. package/src/agents/prompt-library/todo-discipline.ts +0 -23
  106. package/src/agents/quality-guardian.ts +0 -76
  107. package/src/agents/researcher.ts +0 -74
  108. package/src/agents/strategist/AGENTS.md +0 -37
  109. package/src/agents/strategist/behavioral-summary.ts +0 -79
  110. package/src/agents/strategist/gemini.ts +0 -334
  111. package/src/agents/strategist/gpt.ts +0 -461
  112. package/src/agents/strategist/high-accuracy-mode.ts +0 -78
  113. package/src/agents/strategist/identity-constraints.ts +0 -336
  114. package/src/agents/strategist/index.ts +0 -6
  115. package/src/agents/strategist/interview-mode.ts +0 -335
  116. package/src/agents/strategist/plan-generation.ts +0 -213
  117. package/src/agents/strategist/plan-template.ts +0 -325
  118. package/src/agents/strategist/system-prompt.ts +0 -68
  119. package/src/agents/sub/agent.ts +0 -141
  120. package/src/agents/sub/default.ts +0 -52
  121. package/src/agents/sub/gemini.ts +0 -194
  122. package/src/agents/sub/gpt-codex.ts +0 -156
  123. package/src/agents/sub/gpt-pro.ts +0 -161
  124. package/src/agents/sub/gpt.ts +0 -157
  125. package/src/agents/sub/index.ts +0 -13
  126. package/src/agents/types.ts +0 -147
  127. package/src/agents/ui.ts +0 -59
  128. package/src/config/data/model-capabilities.json +0 -40690
  129. package/src/config/defaults.ts +0 -230
  130. package/src/config/hiai-opencode.schema.json +0 -12
  131. package/src/config/index.ts +0 -65
  132. package/src/config/loader.test.ts +0 -80
  133. package/src/config/loader.ts +0 -188
  134. package/src/config/model-slots-and-export.test.ts +0 -73
  135. package/src/config/platform-schema.ts +0 -242
  136. package/src/config/schema/agent-definitions.ts +0 -5
  137. package/src/config/schema/agent-names.ts +0 -66
  138. package/src/config/schema/agent-overrides.ts +0 -97
  139. package/src/config/schema/babysitting.ts +0 -7
  140. package/src/config/schema/background-task.ts +0 -29
  141. package/src/config/schema/bob-agent.ts +0 -11
  142. package/src/config/schema/bob.ts +0 -17
  143. package/src/config/schema/browser-automation.ts +0 -24
  144. package/src/config/schema/categories.ts +0 -45
  145. package/src/config/schema/claude-code.ts +0 -13
  146. package/src/config/schema/commands.ts +0 -16
  147. package/src/config/schema/comment-checker.ts +0 -8
  148. package/src/config/schema/dynamic-context-pruning.ts +0 -53
  149. package/src/config/schema/experimental.ts +0 -27
  150. package/src/config/schema/fallback-models.ts +0 -31
  151. package/src/config/schema/fast-apply.ts +0 -14
  152. package/src/config/schema/git-env-prefix.ts +0 -28
  153. package/src/config/schema/git-master.ts +0 -14
  154. package/src/config/schema/hooks.ts +0 -59
  155. package/src/config/schema/index.ts +0 -52
  156. package/src/config/schema/internal/permission.ts +0 -20
  157. package/src/config/schema/model-capabilities.ts +0 -10
  158. package/src/config/schema/notification.ts +0 -8
  159. package/src/config/schema/oh-my-opencode-config.ts +0 -88
  160. package/src/config/schema/openclaw.ts +0 -50
  161. package/src/config/schema/ralph-loop.ts +0 -11
  162. package/src/config/schema/runtime-fallback.ts +0 -18
  163. package/src/config/schema/skill-discovery.ts +0 -25
  164. package/src/config/schema/skills.ts +0 -39
  165. package/src/config/schema/start-work.ts +0 -7
  166. package/src/config/schema/tmux.ts +0 -28
  167. package/src/config/types.ts +0 -221
  168. package/src/create-hooks.ts +0 -93
  169. package/src/create-managers.ts +0 -116
  170. package/src/create-runtime-tmux-config.ts +0 -18
  171. package/src/create-tools.ts +0 -56
  172. package/src/features/background-agent/AGENTS.md +0 -56
  173. package/src/features/background-agent/abort-with-timeout.ts +0 -35
  174. package/src/features/background-agent/background-task-notification-template.ts +0 -74
  175. package/src/features/background-agent/compaction-aware-message-resolver.ts +0 -164
  176. package/src/features/background-agent/concurrency.ts +0 -137
  177. package/src/features/background-agent/constants.ts +0 -58
  178. package/src/features/background-agent/duration-formatter.ts +0 -14
  179. package/src/features/background-agent/error-classifier.ts +0 -83
  180. package/src/features/background-agent/fallback-retry-handler.ts +0 -134
  181. package/src/features/background-agent/index.ts +0 -2
  182. package/src/features/background-agent/loop-detector.ts +0 -102
  183. package/src/features/background-agent/manager.ts +0 -2220
  184. package/src/features/background-agent/opencode-client.ts +0 -3
  185. package/src/features/background-agent/process-cleanup.ts +0 -98
  186. package/src/features/background-agent/remove-task-toast-tracking.ts +0 -8
  187. package/src/features/background-agent/session-existence.ts +0 -57
  188. package/src/features/background-agent/session-idle-event-handler.ts +0 -93
  189. package/src/features/background-agent/session-status-classifier.ts +0 -20
  190. package/src/features/background-agent/spawner/parent-directory-resolver.ts +0 -24
  191. package/src/features/background-agent/spawner.ts +0 -327
  192. package/src/features/background-agent/state.ts +0 -199
  193. package/src/features/background-agent/subagent-spawn-limits.ts +0 -97
  194. package/src/features/background-agent/task-history.ts +0 -79
  195. package/src/features/background-agent/task-poller.ts +0 -225
  196. package/src/features/background-agent/types.ts +0 -100
  197. package/src/features/boulder-state/constants.ts +0 -13
  198. package/src/features/boulder-state/index.ts +0 -4
  199. package/src/features/boulder-state/storage.ts +0 -336
  200. package/src/features/boulder-state/top-level-task.ts +0 -78
  201. package/src/features/boulder-state/types.ts +0 -61
  202. package/src/features/builtin-commands/commands.ts +0 -157
  203. package/src/features/builtin-commands/index.ts +0 -2
  204. package/src/features/builtin-commands/templates/doctor.ts +0 -43
  205. package/src/features/builtin-commands/templates/handoff.ts +0 -177
  206. package/src/features/builtin-commands/templates/init-deep.ts +0 -305
  207. package/src/features/builtin-commands/templates/mcp-status.ts +0 -36
  208. package/src/features/builtin-commands/templates/ralph-loop.ts +0 -66
  209. package/src/features/builtin-commands/templates/refactor.ts +0 -619
  210. package/src/features/builtin-commands/templates/remove-ai-slops.ts +0 -96
  211. package/src/features/builtin-commands/templates/start-work.ts +0 -128
  212. package/src/features/builtin-commands/templates/stop-continuation.ts +0 -13
  213. package/src/features/builtin-commands/types.ts +0 -9
  214. package/src/features/builtin-skills/index.ts +0 -2
  215. package/src/features/builtin-skills/materialize.ts +0 -338
  216. package/src/features/builtin-skills/skills/ai-slop-remover.ts +0 -145
  217. package/src/features/builtin-skills/skills/dev-browser.ts +0 -221
  218. package/src/features/builtin-skills/skills/frontend-ui-ux.ts +0 -79
  219. package/src/features/builtin-skills/skills/git-master-sections/commit-workflow.ts +0 -509
  220. package/src/features/builtin-skills/skills/git-master-sections/history-search-workflow.ts +0 -229
  221. package/src/features/builtin-skills/skills/git-master-sections/overview.ts +0 -64
  222. package/src/features/builtin-skills/skills/git-master-sections/quick-reference.ts +0 -86
  223. package/src/features/builtin-skills/skills/git-master-sections/rebase-workflow.ts +0 -181
  224. package/src/features/builtin-skills/skills/git-master-skill-metadata.ts +0 -4
  225. package/src/features/builtin-skills/skills/git-master.ts +0 -28
  226. package/src/features/builtin-skills/skills/hiai-opencode-setup.ts +0 -69
  227. package/src/features/builtin-skills/skills/index.ts +0 -9
  228. package/src/features/builtin-skills/skills/playwright-cli.ts +0 -268
  229. package/src/features/builtin-skills/skills/playwright.ts +0 -488
  230. package/src/features/builtin-skills/skills/review-work.ts +0 -536
  231. package/src/features/builtin-skills/skills/website-copywriting.ts +0 -41
  232. package/src/features/builtin-skills/skills.test.ts +0 -8
  233. package/src/features/builtin-skills/skills.ts +0 -50
  234. package/src/features/builtin-skills/types.ts +0 -16
  235. package/src/features/claude-code-agent-loader/agent-definitions-loader.ts +0 -87
  236. package/src/features/claude-code-agent-loader/claude-model-mapper.ts +0 -53
  237. package/src/features/claude-code-agent-loader/index.ts +0 -5
  238. package/src/features/claude-code-agent-loader/json-agent-loader.ts +0 -53
  239. package/src/features/claude-code-agent-loader/loader.ts +0 -86
  240. package/src/features/claude-code-agent-loader/opencode-config-agents-reader.ts +0 -125
  241. package/src/features/claude-code-agent-loader/types.ts +0 -31
  242. package/src/features/claude-code-command-loader/index.ts +0 -2
  243. package/src/features/claude-code-command-loader/loader.ts +0 -169
  244. package/src/features/claude-code-command-loader/types.ts +0 -46
  245. package/src/features/claude-code-mcp-loader/configure-allowed-env-vars.ts +0 -48
  246. package/src/features/claude-code-mcp-loader/env-expander.ts +0 -51
  247. package/src/features/claude-code-mcp-loader/index.ts +0 -12
  248. package/src/features/claude-code-mcp-loader/loader.ts +0 -156
  249. package/src/features/claude-code-mcp-loader/scope-filter.ts +0 -17
  250. package/src/features/claude-code-mcp-loader/transformer.ts +0 -57
  251. package/src/features/claude-code-mcp-loader/types.ts +0 -51
  252. package/src/features/claude-code-plugin-loader/agent-loader.ts +0 -59
  253. package/src/features/claude-code-plugin-loader/command-loader.ts +0 -53
  254. package/src/features/claude-code-plugin-loader/discovery.ts +0 -251
  255. package/src/features/claude-code-plugin-loader/hook-loader.ts +0 -26
  256. package/src/features/claude-code-plugin-loader/index.ts +0 -10
  257. package/src/features/claude-code-plugin-loader/loader.ts +0 -134
  258. package/src/features/claude-code-plugin-loader/mcp-server-loader.ts +0 -59
  259. package/src/features/claude-code-plugin-loader/plugin-path-resolver.ts +0 -23
  260. package/src/features/claude-code-plugin-loader/scope-filter.ts +0 -29
  261. package/src/features/claude-code-plugin-loader/skill-loader.ts +0 -62
  262. package/src/features/claude-code-plugin-loader/types.ts +0 -255
  263. package/src/features/claude-code-session-state/index.ts +0 -1
  264. package/src/features/claude-code-session-state/state.ts +0 -154
  265. package/src/features/claude-tasks/session-storage.ts +0 -52
  266. package/src/features/claude-tasks/storage.ts +0 -169
  267. package/src/features/claude-tasks/types.ts +0 -20
  268. package/src/features/context-injector/collector.ts +0 -91
  269. package/src/features/context-injector/index.ts +0 -14
  270. package/src/features/context-injector/injector.ts +0 -167
  271. package/src/features/context-injector/types.ts +0 -91
  272. package/src/features/hook-message-injector/constants.ts +0 -1
  273. package/src/features/hook-message-injector/index.ts +0 -11
  274. package/src/features/hook-message-injector/injector.ts +0 -437
  275. package/src/features/hook-message-injector/types.ts +0 -49
  276. package/src/features/mcp-oauth/AGENTS.md +0 -54
  277. package/src/features/mcp-oauth/callback-server.ts +0 -106
  278. package/src/features/mcp-oauth/dcr.ts +0 -98
  279. package/src/features/mcp-oauth/discovery.ts +0 -134
  280. package/src/features/mcp-oauth/oauth-authorization-flow.ts +0 -150
  281. package/src/features/mcp-oauth/provider.ts +0 -215
  282. package/src/features/mcp-oauth/refresh-mutex.ts +0 -58
  283. package/src/features/mcp-oauth/resource-indicator.ts +0 -16
  284. package/src/features/mcp-oauth/schema.ts +0 -8
  285. package/src/features/mcp-oauth/step-up.ts +0 -79
  286. package/src/features/mcp-oauth/storage.ts +0 -155
  287. package/src/features/opencode-skill-loader/AGENTS.md +0 -59
  288. package/src/features/opencode-skill-loader/allowed-tools-parser.ts +0 -9
  289. package/src/features/opencode-skill-loader/async-loader.ts +0 -213
  290. package/src/features/opencode-skill-loader/blocking.ts +0 -62
  291. package/src/features/opencode-skill-loader/config-source-discovery.ts +0 -114
  292. package/src/features/opencode-skill-loader/discover-worker.ts +0 -56
  293. package/src/features/opencode-skill-loader/git-master-template-injection.ts +0 -150
  294. package/src/features/opencode-skill-loader/index.ts +0 -17
  295. package/src/features/opencode-skill-loader/loaded-skill-from-path.ts +0 -73
  296. package/src/features/opencode-skill-loader/loaded-skill-template-extractor.ts +0 -16
  297. package/src/features/opencode-skill-loader/loader.ts +0 -183
  298. package/src/features/opencode-skill-loader/merger/builtin-skill-converter.ts +0 -26
  299. package/src/features/opencode-skill-loader/merger/config-skill-entry-loader.ts +0 -117
  300. package/src/features/opencode-skill-loader/merger/scope-priority.ts +0 -10
  301. package/src/features/opencode-skill-loader/merger/skill-definition-merger.ts +0 -31
  302. package/src/features/opencode-skill-loader/merger/skills-config-normalizer.ts +0 -19
  303. package/src/features/opencode-skill-loader/merger.ts +0 -96
  304. package/src/features/opencode-skill-loader/skill-content.ts +0 -11
  305. package/src/features/opencode-skill-loader/skill-deduplication.ts +0 -13
  306. package/src/features/opencode-skill-loader/skill-definition-record.ts +0 -11
  307. package/src/features/opencode-skill-loader/skill-directory-loader.ts +0 -112
  308. package/src/features/opencode-skill-loader/skill-discovery.ts +0 -76
  309. package/src/features/opencode-skill-loader/skill-mcp-config.ts +0 -45
  310. package/src/features/opencode-skill-loader/skill-resolution-options.ts +0 -9
  311. package/src/features/opencode-skill-loader/skill-template-resolver.ts +0 -97
  312. package/src/features/opencode-skill-loader/types.ts +0 -38
  313. package/src/features/run-continuation-state/constants.ts +0 -1
  314. package/src/features/run-continuation-state/index.ts +0 -3
  315. package/src/features/run-continuation-state/storage.ts +0 -80
  316. package/src/features/run-continuation-state/types.ts +0 -15
  317. package/src/features/skill-mcp-manager/AGENTS.md +0 -111
  318. package/src/features/skill-mcp-manager/cleanup.ts +0 -153
  319. package/src/features/skill-mcp-manager/connection-type.ts +0 -26
  320. package/src/features/skill-mcp-manager/connection.ts +0 -146
  321. package/src/features/skill-mcp-manager/env-cleaner.ts +0 -59
  322. package/src/features/skill-mcp-manager/error-redaction.ts +0 -47
  323. package/src/features/skill-mcp-manager/http-client.ts +0 -126
  324. package/src/features/skill-mcp-manager/index.ts +0 -2
  325. package/src/features/skill-mcp-manager/manager.ts +0 -178
  326. package/src/features/skill-mcp-manager/oauth-handler.ts +0 -160
  327. package/src/features/skill-mcp-manager/stdio-client.ts +0 -112
  328. package/src/features/skill-mcp-manager/types.ts +0 -96
  329. package/src/features/task-toast-manager/index.ts +0 -2
  330. package/src/features/task-toast-manager/manager.ts +0 -251
  331. package/src/features/task-toast-manager/types.ts +0 -29
  332. package/src/features/tmux-subagent/action-executor-core.ts +0 -82
  333. package/src/features/tmux-subagent/action-executor.ts +0 -137
  334. package/src/features/tmux-subagent/cleanup.ts +0 -42
  335. package/src/features/tmux-subagent/decision-engine.ts +0 -22
  336. package/src/features/tmux-subagent/event-handlers.ts +0 -6
  337. package/src/features/tmux-subagent/grid-planning.ts +0 -137
  338. package/src/features/tmux-subagent/index.ts +0 -16
  339. package/src/features/tmux-subagent/manager.ts +0 -969
  340. package/src/features/tmux-subagent/oldest-agent-pane.ts +0 -37
  341. package/src/features/tmux-subagent/pane-split-availability.ts +0 -77
  342. package/src/features/tmux-subagent/pane-state-parser.ts +0 -135
  343. package/src/features/tmux-subagent/pane-state-querier.ts +0 -76
  344. package/src/features/tmux-subagent/polling-constants.ts +0 -6
  345. package/src/features/tmux-subagent/polling-manager.ts +0 -167
  346. package/src/features/tmux-subagent/polling.ts +0 -183
  347. package/src/features/tmux-subagent/session-created-event.ts +0 -44
  348. package/src/features/tmux-subagent/session-created-handler.ts +0 -175
  349. package/src/features/tmux-subagent/session-deleted-handler.ts +0 -50
  350. package/src/features/tmux-subagent/session-message-count.ts +0 -3
  351. package/src/features/tmux-subagent/session-ready-waiter.ts +0 -44
  352. package/src/features/tmux-subagent/session-status-parser.ts +0 -17
  353. package/src/features/tmux-subagent/spawn-action-decider.ts +0 -147
  354. package/src/features/tmux-subagent/spawn-target-finder.ts +0 -146
  355. package/src/features/tmux-subagent/tmux-grid-constants.ts +0 -57
  356. package/src/features/tmux-subagent/tracked-session-state.ts +0 -29
  357. package/src/features/tmux-subagent/types.ts +0 -54
  358. package/src/features/tool-metadata-store/index.ts +0 -7
  359. package/src/features/tool-metadata-store/store.ts +0 -84
  360. package/src/hooks/agent-usage-reminder/constants.ts +0 -52
  361. package/src/hooks/agent-usage-reminder/hook.ts +0 -134
  362. package/src/hooks/agent-usage-reminder/index.ts +0 -1
  363. package/src/hooks/agent-usage-reminder/storage.ts +0 -42
  364. package/src/hooks/agent-usage-reminder/types.ts +0 -6
  365. package/src/hooks/anthropic-context-window-limit-recovery/AGENTS.md +0 -49
  366. package/src/hooks/anthropic-context-window-limit-recovery/aggressive-truncation-strategy.ts +0 -87
  367. package/src/hooks/anthropic-context-window-limit-recovery/client.ts +0 -21
  368. package/src/hooks/anthropic-context-window-limit-recovery/deduplication-recovery.ts +0 -77
  369. package/src/hooks/anthropic-context-window-limit-recovery/empty-content-recovery-sdk.ts +0 -199
  370. package/src/hooks/anthropic-context-window-limit-recovery/empty-content-recovery.ts +0 -149
  371. package/src/hooks/anthropic-context-window-limit-recovery/executor.ts +0 -83
  372. package/src/hooks/anthropic-context-window-limit-recovery/index.ts +0 -8
  373. package/src/hooks/anthropic-context-window-limit-recovery/message-builder.ts +0 -190
  374. package/src/hooks/anthropic-context-window-limit-recovery/message-storage-directory.ts +0 -40
  375. package/src/hooks/anthropic-context-window-limit-recovery/parser.ts +0 -209
  376. package/src/hooks/anthropic-context-window-limit-recovery/pruning-deduplication.ts +0 -189
  377. package/src/hooks/anthropic-context-window-limit-recovery/pruning-tool-output-truncation.ts +0 -142
  378. package/src/hooks/anthropic-context-window-limit-recovery/pruning-types.ts +0 -44
  379. package/src/hooks/anthropic-context-window-limit-recovery/recovery-hook.test-support.ts +0 -119
  380. package/src/hooks/anthropic-context-window-limit-recovery/recovery-hook.ts +0 -193
  381. package/src/hooks/anthropic-context-window-limit-recovery/recovery-strategy.ts +0 -2
  382. package/src/hooks/anthropic-context-window-limit-recovery/session-timeout-map.ts +0 -20
  383. package/src/hooks/anthropic-context-window-limit-recovery/state.ts +0 -78
  384. package/src/hooks/anthropic-context-window-limit-recovery/storage-paths.ts +0 -6
  385. package/src/hooks/anthropic-context-window-limit-recovery/storage.ts +0 -18
  386. package/src/hooks/anthropic-context-window-limit-recovery/summarize-retry-strategy.ts +0 -218
  387. package/src/hooks/anthropic-context-window-limit-recovery/target-token-truncation.ts +0 -196
  388. package/src/hooks/anthropic-context-window-limit-recovery/tool-part-types.ts +0 -38
  389. package/src/hooks/anthropic-context-window-limit-recovery/tool-result-storage-sdk.ts +0 -123
  390. package/src/hooks/anthropic-context-window-limit-recovery/tool-result-storage.ts +0 -119
  391. package/src/hooks/anthropic-context-window-limit-recovery/types.ts +0 -44
  392. package/src/hooks/anthropic-effort/hook.ts +0 -93
  393. package/src/hooks/anthropic-effort/index.ts +0 -1
  394. package/src/hooks/auto-slash-command/constants.ts +0 -12
  395. package/src/hooks/auto-slash-command/detector.ts +0 -88
  396. package/src/hooks/auto-slash-command/executor.ts +0 -165
  397. package/src/hooks/auto-slash-command/hook.ts +0 -238
  398. package/src/hooks/auto-slash-command/index.ts +0 -7
  399. package/src/hooks/auto-slash-command/processed-command-store.ts +0 -74
  400. package/src/hooks/auto-slash-command/types.ts +0 -42
  401. package/src/hooks/background-notification/hook.ts +0 -54
  402. package/src/hooks/background-notification/index.ts +0 -2
  403. package/src/hooks/background-notification/types.ts +0 -5
  404. package/src/hooks/bash-file-read-guard.ts +0 -44
  405. package/src/hooks/category-skill-reminder/formatter.ts +0 -37
  406. package/src/hooks/category-skill-reminder/hook.ts +0 -142
  407. package/src/hooks/category-skill-reminder/index.ts +0 -1
  408. package/src/hooks/claude-code-hooks/AGENTS.md +0 -41
  409. package/src/hooks/claude-code-hooks/claude-code-hooks-hook.ts +0 -28
  410. package/src/hooks/claude-code-hooks/config-loader.ts +0 -151
  411. package/src/hooks/claude-code-hooks/config.ts +0 -147
  412. package/src/hooks/claude-code-hooks/dispatch-hook.ts +0 -27
  413. package/src/hooks/claude-code-hooks/execute-http-hook.ts +0 -116
  414. package/src/hooks/claude-code-hooks/handlers/chat-message-handler.ts +0 -140
  415. package/src/hooks/claude-code-hooks/handlers/pre-compact-handler.ts +0 -41
  416. package/src/hooks/claude-code-hooks/handlers/session-event-handler.ts +0 -137
  417. package/src/hooks/claude-code-hooks/handlers/tool-execute-after-handler.ts +0 -160
  418. package/src/hooks/claude-code-hooks/handlers/tool-execute-before-handler.ts +0 -93
  419. package/src/hooks/claude-code-hooks/index.ts +0 -1
  420. package/src/hooks/claude-code-hooks/plugin-config.ts +0 -12
  421. package/src/hooks/claude-code-hooks/post-tool-use.ts +0 -195
  422. package/src/hooks/claude-code-hooks/pre-compact.ts +0 -105
  423. package/src/hooks/claude-code-hooks/pre-tool-use.ts +0 -168
  424. package/src/hooks/claude-code-hooks/session-hook-state.ts +0 -17
  425. package/src/hooks/claude-code-hooks/stop.ts +0 -118
  426. package/src/hooks/claude-code-hooks/todo.ts +0 -76
  427. package/src/hooks/claude-code-hooks/tool-input-cache.ts +0 -82
  428. package/src/hooks/claude-code-hooks/transcript.ts +0 -248
  429. package/src/hooks/claude-code-hooks/types.ts +0 -214
  430. package/src/hooks/claude-code-hooks/user-prompt-submit.ts +0 -121
  431. package/src/hooks/comment-checker/cli-runner.ts +0 -127
  432. package/src/hooks/comment-checker/cli.ts +0 -269
  433. package/src/hooks/comment-checker/downloader.ts +0 -170
  434. package/src/hooks/comment-checker/hook.ts +0 -192
  435. package/src/hooks/comment-checker/index.ts +0 -1
  436. package/src/hooks/comment-checker/pending-calls.ts +0 -45
  437. package/src/hooks/comment-checker/types.ts +0 -33
  438. package/src/hooks/compaction-context-injector/compaction-context-prompt.ts +0 -56
  439. package/src/hooks/compaction-context-injector/constants.ts +0 -5
  440. package/src/hooks/compaction-context-injector/hook.ts +0 -164
  441. package/src/hooks/compaction-context-injector/index.ts +0 -1
  442. package/src/hooks/compaction-context-injector/recovery-prompt-config.ts +0 -77
  443. package/src/hooks/compaction-context-injector/recovery.ts +0 -163
  444. package/src/hooks/compaction-context-injector/session-id.ts +0 -8
  445. package/src/hooks/compaction-context-injector/session-prompt-config-resolver.ts +0 -120
  446. package/src/hooks/compaction-context-injector/tail-monitor.ts +0 -52
  447. package/src/hooks/compaction-context-injector/types.ts +0 -25
  448. package/src/hooks/compaction-context-injector/validated-model.ts +0 -47
  449. package/src/hooks/compaction-todo-preserver/hook.ts +0 -127
  450. package/src/hooks/compaction-todo-preserver/index.ts +0 -2
  451. package/src/hooks/context-window-monitor.ts +0 -113
  452. package/src/hooks/delegate-task-retry/guidance.ts +0 -45
  453. package/src/hooks/delegate-task-retry/hook.ts +0 -22
  454. package/src/hooks/delegate-task-retry/index.ts +0 -4
  455. package/src/hooks/delegate-task-retry/patterns.ts +0 -77
  456. package/src/hooks/directory-agents-injector/constants.ts +0 -7
  457. package/src/hooks/directory-agents-injector/finder.ts +0 -38
  458. package/src/hooks/directory-agents-injector/hook.ts +0 -80
  459. package/src/hooks/directory-agents-injector/index.ts +0 -1
  460. package/src/hooks/directory-agents-injector/injector.ts +0 -59
  461. package/src/hooks/directory-agents-injector/storage.ts +0 -8
  462. package/src/hooks/directory-readme-injector/constants.ts +0 -7
  463. package/src/hooks/directory-readme-injector/finder.ts +0 -33
  464. package/src/hooks/directory-readme-injector/hook.ts +0 -80
  465. package/src/hooks/directory-readme-injector/index.ts +0 -1
  466. package/src/hooks/directory-readme-injector/injector.ts +0 -59
  467. package/src/hooks/directory-readme-injector/storage.ts +0 -8
  468. package/src/hooks/edit-error-recovery/hook.ts +0 -58
  469. package/src/hooks/edit-error-recovery/index.ts +0 -5
  470. package/src/hooks/empty-task-response-detector.ts +0 -27
  471. package/src/hooks/fast-apply/hook.ts +0 -11
  472. package/src/hooks/fast-apply/index.ts +0 -1
  473. package/src/hooks/fast-apply/ollama-client.ts +0 -53
  474. package/src/hooks/fast-apply/tool-execute-before-handler.ts +0 -86
  475. package/src/hooks/guard/AGENTS.md +0 -64
  476. package/src/hooks/guard/background-launch-session-tracking.ts +0 -97
  477. package/src/hooks/guard/bob-path.ts +0 -8
  478. package/src/hooks/guard/boulder-continuation-injector.ts +0 -109
  479. package/src/hooks/guard/boulder-session-lineage.ts +0 -44
  480. package/src/hooks/guard/event-handler.ts +0 -104
  481. package/src/hooks/guard/final-wave-approval-gate.ts +0 -47
  482. package/src/hooks/guard/final-wave-plan-state.ts +0 -60
  483. package/src/hooks/guard/guard-hook.ts +0 -27
  484. package/src/hooks/guard/hook-name.ts +0 -1
  485. package/src/hooks/guard/idle-event.ts +0 -341
  486. package/src/hooks/guard/index.ts +0 -3
  487. package/src/hooks/guard/is-abort-error.ts +0 -20
  488. package/src/hooks/guard/recent-model-resolver.ts +0 -89
  489. package/src/hooks/guard/resolve-active-boulder-session.ts +0 -29
  490. package/src/hooks/guard/session-last-agent.ts +0 -153
  491. package/src/hooks/guard/subagent-session-id.ts +0 -54
  492. package/src/hooks/guard/system-reminder-templates.ts +0 -249
  493. package/src/hooks/guard/task-context.ts +0 -45
  494. package/src/hooks/guard/tool-execute-after.ts +0 -209
  495. package/src/hooks/guard/tool-execute-before.ts +0 -102
  496. package/src/hooks/guard/tsconfig.json +0 -9
  497. package/src/hooks/guard/types.ts +0 -45
  498. package/src/hooks/guard/verification-reminders.ts +0 -197
  499. package/src/hooks/guard/write-edit-tool-policy.ts +0 -5
  500. package/src/hooks/hashline-edit-diff-enhancer/hook.ts +0 -106
  501. package/src/hooks/hashline-read-enhancer/hook.ts +0 -193
  502. package/src/hooks/hashline-read-enhancer/index.ts +0 -1
  503. package/src/hooks/index.ts +0 -56
  504. package/src/hooks/interactive-bash-session/constants.ts +0 -13
  505. package/src/hooks/interactive-bash-session/hook.ts +0 -125
  506. package/src/hooks/interactive-bash-session/index.ts +0 -3
  507. package/src/hooks/interactive-bash-session/interactive-bash-session-tracker.ts +0 -119
  508. package/src/hooks/interactive-bash-session/parser.ts +0 -118
  509. package/src/hooks/interactive-bash-session/state-manager.ts +0 -35
  510. package/src/hooks/interactive-bash-session/storage.ts +0 -59
  511. package/src/hooks/interactive-bash-session/tmux-command-parser.ts +0 -125
  512. package/src/hooks/interactive-bash-session/types.ts +0 -11
  513. package/src/hooks/json-error-recovery/hook.ts +0 -58
  514. package/src/hooks/json-error-recovery/index.ts +0 -6
  515. package/src/hooks/keyword-detector/AGENTS.md +0 -57
  516. package/src/hooks/keyword-detector/analyze/default.ts +0 -28
  517. package/src/hooks/keyword-detector/analyze/index.ts +0 -1
  518. package/src/hooks/keyword-detector/constants.ts +0 -45
  519. package/src/hooks/keyword-detector/detector.ts +0 -53
  520. package/src/hooks/keyword-detector/hook.ts +0 -143
  521. package/src/hooks/keyword-detector/index.ts +0 -5
  522. package/src/hooks/keyword-detector/search/default.ts +0 -20
  523. package/src/hooks/keyword-detector/search/index.ts +0 -1
  524. package/src/hooks/keyword-detector/types.ts +0 -4
  525. package/src/hooks/keyword-detector/ultrawork/default.ts +0 -302
  526. package/src/hooks/keyword-detector/ultrawork/gemini.ts +0 -290
  527. package/src/hooks/keyword-detector/ultrawork/gpt.ts +0 -171
  528. package/src/hooks/keyword-detector/ultrawork/index.ts +0 -56
  529. package/src/hooks/keyword-detector/ultrawork/planner.ts +0 -140
  530. package/src/hooks/keyword-detector/ultrawork/source-detector.ts +0 -65
  531. package/src/hooks/legacy-plugin-toast/auto-migrate-runner.ts +0 -2
  532. package/src/hooks/legacy-plugin-toast/auto-migrate.ts +0 -64
  533. package/src/hooks/legacy-plugin-toast/hook.ts +0 -68
  534. package/src/hooks/legacy-plugin-toast/index.ts +0 -1
  535. package/src/hooks/legacy-plugin-toast/plugin-entry-migrator.ts +0 -1
  536. package/src/hooks/model-fallback/chat-message-fallback-handler.ts +0 -74
  537. package/src/hooks/model-fallback/hook.ts +0 -201
  538. package/src/hooks/model-fallback/next-fallback.ts +0 -84
  539. package/src/hooks/non-interactive-env/constants.ts +0 -70
  540. package/src/hooks/non-interactive-env/detector.ts +0 -19
  541. package/src/hooks/non-interactive-env/index.ts +0 -5
  542. package/src/hooks/non-interactive-env/non-interactive-env-hook.ts +0 -73
  543. package/src/hooks/non-interactive-env/types.ts +0 -3
  544. package/src/hooks/preemptive-compaction-degradation-monitor.ts +0 -212
  545. package/src/hooks/preemptive-compaction-no-text-tail.ts +0 -70
  546. package/src/hooks/preemptive-compaction.ts +0 -218
  547. package/src/hooks/question-label-truncator/hook.ts +0 -62
  548. package/src/hooks/question-label-truncator/index.ts +0 -1
  549. package/src/hooks/ralph-loop/AGENTS.md +0 -62
  550. package/src/hooks/ralph-loop/command-arguments.ts +0 -30
  551. package/src/hooks/ralph-loop/completion-handler.ts +0 -65
  552. package/src/hooks/ralph-loop/completion-promise-detector-test-input.ts +0 -23
  553. package/src/hooks/ralph-loop/completion-promise-detector.ts +0 -165
  554. package/src/hooks/ralph-loop/constants.ts +0 -7
  555. package/src/hooks/ralph-loop/continuation-prompt-builder.ts +0 -77
  556. package/src/hooks/ralph-loop/continuation-prompt-injector.ts +0 -91
  557. package/src/hooks/ralph-loop/index.ts +0 -6
  558. package/src/hooks/ralph-loop/iteration-continuation.ts +0 -64
  559. package/src/hooks/ralph-loop/logician-verification-detector.ts +0 -88
  560. package/src/hooks/ralph-loop/loop-session-recovery.ts +0 -33
  561. package/src/hooks/ralph-loop/loop-state-controller.ts +0 -178
  562. package/src/hooks/ralph-loop/message-storage-directory.ts +0 -1
  563. package/src/hooks/ralph-loop/pending-verification-handler.ts +0 -152
  564. package/src/hooks/ralph-loop/ralph-loop-event-handler.ts +0 -231
  565. package/src/hooks/ralph-loop/ralph-loop-hook.ts +0 -90
  566. package/src/hooks/ralph-loop/session-event-handler.ts +0 -56
  567. package/src/hooks/ralph-loop/session-reset-strategy.ts +0 -69
  568. package/src/hooks/ralph-loop/storage.ts +0 -164
  569. package/src/hooks/ralph-loop/types.ts +0 -25
  570. package/src/hooks/ralph-loop/verification-failure-handler.ts +0 -103
  571. package/src/hooks/ralph-loop/with-timeout.ts +0 -20
  572. package/src/hooks/read-image-resizer/hook.ts +0 -209
  573. package/src/hooks/read-image-resizer/image-dimensions.ts +0 -191
  574. package/src/hooks/read-image-resizer/image-resizer.ts +0 -191
  575. package/src/hooks/read-image-resizer/index.ts +0 -1
  576. package/src/hooks/read-image-resizer/png-fallback-resizer.ts +0 -359
  577. package/src/hooks/read-image-resizer/types.ts +0 -16
  578. package/src/hooks/rules-injector/AGENTS.md +0 -53
  579. package/src/hooks/rules-injector/cache.ts +0 -27
  580. package/src/hooks/rules-injector/constants.ts +0 -31
  581. package/src/hooks/rules-injector/finder.ts +0 -3
  582. package/src/hooks/rules-injector/hook.ts +0 -94
  583. package/src/hooks/rules-injector/index.ts +0 -2
  584. package/src/hooks/rules-injector/injector.ts +0 -189
  585. package/src/hooks/rules-injector/matcher.ts +0 -63
  586. package/src/hooks/rules-injector/output-path.ts +0 -22
  587. package/src/hooks/rules-injector/parser.ts +0 -211
  588. package/src/hooks/rules-injector/project-root-finder.ts +0 -36
  589. package/src/hooks/rules-injector/rule-distance.ts +0 -53
  590. package/src/hooks/rules-injector/rule-file-finder.ts +0 -139
  591. package/src/hooks/rules-injector/rule-file-scanner.ts +0 -55
  592. package/src/hooks/rules-injector/storage.ts +0 -59
  593. package/src/hooks/rules-injector/types.ts +0 -57
  594. package/src/hooks/runtime-fallback/AGENTS.md +0 -102
  595. package/src/hooks/runtime-fallback/agent-resolver.ts +0 -50
  596. package/src/hooks/runtime-fallback/auto-retry-signal.ts +0 -32
  597. package/src/hooks/runtime-fallback/auto-retry.ts +0 -228
  598. package/src/hooks/runtime-fallback/chat-message-handler.ts +0 -62
  599. package/src/hooks/runtime-fallback/constants.ts +0 -47
  600. package/src/hooks/runtime-fallback/error-classifier.ts +0 -183
  601. package/src/hooks/runtime-fallback/event-handler.ts +0 -213
  602. package/src/hooks/runtime-fallback/fallback-bootstrap-model.ts +0 -63
  603. package/src/hooks/runtime-fallback/fallback-models.ts +0 -86
  604. package/src/hooks/runtime-fallback/fallback-retry-dispatcher.ts +0 -55
  605. package/src/hooks/runtime-fallback/fallback-state.ts +0 -74
  606. package/src/hooks/runtime-fallback/hook.ts +0 -87
  607. package/src/hooks/runtime-fallback/index.ts +0 -2
  608. package/src/hooks/runtime-fallback/last-user-retry-parts.ts +0 -20
  609. package/src/hooks/runtime-fallback/message-update-handler.ts +0 -168
  610. package/src/hooks/runtime-fallback/retry-model-payload.ts +0 -30
  611. package/src/hooks/runtime-fallback/session-messages.ts +0 -38
  612. package/src/hooks/runtime-fallback/session-status-handler.ts +0 -126
  613. package/src/hooks/runtime-fallback/types.ts +0 -77
  614. package/src/hooks/runtime-fallback/visible-assistant-response.ts +0 -80
  615. package/src/hooks/session-notification-content.ts +0 -145
  616. package/src/hooks/session-notification-formatting.ts +0 -25
  617. package/src/hooks/session-notification-scheduler.ts +0 -188
  618. package/src/hooks/session-notification-sender.ts +0 -117
  619. package/src/hooks/session-notification-utils.ts +0 -80
  620. package/src/hooks/session-notification.ts +0 -219
  621. package/src/hooks/session-recovery/AGENTS.md +0 -59
  622. package/src/hooks/session-recovery/constants.ts +0 -5
  623. package/src/hooks/session-recovery/detect-error-type.ts +0 -102
  624. package/src/hooks/session-recovery/hook.ts +0 -166
  625. package/src/hooks/session-recovery/index.ts +0 -7
  626. package/src/hooks/session-recovery/recover-empty-content-message-sdk.ts +0 -201
  627. package/src/hooks/session-recovery/recover-thinking-block-order.ts +0 -137
  628. package/src/hooks/session-recovery/recover-thinking-disabled-violation.ts +0 -75
  629. package/src/hooks/session-recovery/recover-tool-result-missing.ts +0 -108
  630. package/src/hooks/session-recovery/recover-unavailable-tool.ts +0 -108
  631. package/src/hooks/session-recovery/resume.ts +0 -49
  632. package/src/hooks/session-recovery/storage/empty-messages.ts +0 -47
  633. package/src/hooks/session-recovery/storage/empty-text.ts +0 -118
  634. package/src/hooks/session-recovery/storage/message-dir.ts +0 -1
  635. package/src/hooks/session-recovery/storage/messages-reader.ts +0 -83
  636. package/src/hooks/session-recovery/storage/orphan-thinking-search.ts +0 -43
  637. package/src/hooks/session-recovery/storage/part-content.ts +0 -28
  638. package/src/hooks/session-recovery/storage/part-id.ts +0 -5
  639. package/src/hooks/session-recovery/storage/parts-reader.ts +0 -56
  640. package/src/hooks/session-recovery/storage/text-part-injector.ts +0 -63
  641. package/src/hooks/session-recovery/storage/thinking-block-search.ts +0 -42
  642. package/src/hooks/session-recovery/storage/thinking-prepend.ts +0 -223
  643. package/src/hooks/session-recovery/storage/thinking-strip.ts +0 -67
  644. package/src/hooks/session-recovery/storage.ts +0 -34
  645. package/src/hooks/session-recovery/types.ts +0 -101
  646. package/src/hooks/session-todo-status.ts +0 -20
  647. package/src/hooks/shared/compaction-model-resolver.ts +0 -34
  648. package/src/hooks/shared/shared/compaction-model-resolver.ts +0 -34
  649. package/src/hooks/start-work/context-info-builder.ts +0 -319
  650. package/src/hooks/start-work/index.ts +0 -4
  651. package/src/hooks/start-work/parse-user-request.ts +0 -32
  652. package/src/hooks/start-work/start-work-hook.ts +0 -135
  653. package/src/hooks/start-work/worktree-block.ts +0 -11
  654. package/src/hooks/start-work/worktree-detector.ts +0 -77
  655. package/src/hooks/stop-continuation-guard/hook.ts +0 -122
  656. package/src/hooks/stop-continuation-guard/index.ts +0 -2
  657. package/src/hooks/strategist-md-only/agent-matcher.ts +0 -5
  658. package/src/hooks/strategist-md-only/agent-resolution.ts +0 -70
  659. package/src/hooks/strategist-md-only/constants.ts +0 -78
  660. package/src/hooks/strategist-md-only/hook.ts +0 -82
  661. package/src/hooks/strategist-md-only/index.ts +0 -2
  662. package/src/hooks/strategist-md-only/path-policy.ts +0 -41
  663. package/src/hooks/sub-notepad/constants.ts +0 -29
  664. package/src/hooks/sub-notepad/hook.ts +0 -44
  665. package/src/hooks/sub-notepad/index.ts +0 -3
  666. package/src/hooks/task-reminder/hook.ts +0 -59
  667. package/src/hooks/task-reminder/index.ts +0 -1
  668. package/src/hooks/task-resume-info/hook.ts +0 -39
  669. package/src/hooks/task-resume-info/index.ts +0 -1
  670. package/src/hooks/tasks-todowrite-disabler/constants.ts +0 -30
  671. package/src/hooks/tasks-todowrite-disabler/hook.ts +0 -34
  672. package/src/hooks/tasks-todowrite-disabler/index.ts +0 -2
  673. package/src/hooks/think-mode/detector.ts +0 -59
  674. package/src/hooks/think-mode/hook.ts +0 -76
  675. package/src/hooks/think-mode/index.ts +0 -5
  676. package/src/hooks/think-mode/switcher.ts +0 -100
  677. package/src/hooks/think-mode/types.ts +0 -16
  678. package/src/hooks/thinking-block-validator/hook.ts +0 -181
  679. package/src/hooks/thinking-block-validator/index.ts +0 -1
  680. package/src/hooks/todo-continuation-enforcer/AGENTS.md +0 -65
  681. package/src/hooks/todo-continuation-enforcer/abort-detection.ts +0 -17
  682. package/src/hooks/todo-continuation-enforcer/compaction-guard.ts +0 -39
  683. package/src/hooks/todo-continuation-enforcer/constants.ts +0 -25
  684. package/src/hooks/todo-continuation-enforcer/continuation-injection.ts +0 -222
  685. package/src/hooks/todo-continuation-enforcer/countdown.ts +0 -86
  686. package/src/hooks/todo-continuation-enforcer/handler.ts +0 -99
  687. package/src/hooks/todo-continuation-enforcer/idle-event.ts +0 -225
  688. package/src/hooks/todo-continuation-enforcer/index.ts +0 -59
  689. package/src/hooks/todo-continuation-enforcer/message-directory.ts +0 -1
  690. package/src/hooks/todo-continuation-enforcer/non-idle-events.ts +0 -107
  691. package/src/hooks/todo-continuation-enforcer/pending-question-detection.ts +0 -40
  692. package/src/hooks/todo-continuation-enforcer/resolve-message-info.ts +0 -48
  693. package/src/hooks/todo-continuation-enforcer/session-state.ts +0 -283
  694. package/src/hooks/todo-continuation-enforcer/stagnation-detection.ts +0 -36
  695. package/src/hooks/todo-continuation-enforcer/todo.ts +0 -11
  696. package/src/hooks/todo-continuation-enforcer/token-limit-detection.ts +0 -38
  697. package/src/hooks/todo-continuation-enforcer/types.ts +0 -74
  698. package/src/hooks/todo-description-override/description.ts +0 -28
  699. package/src/hooks/todo-description-override/hook.ts +0 -14
  700. package/src/hooks/todo-description-override/index.ts +0 -1
  701. package/src/hooks/tool-output-truncator.ts +0 -66
  702. package/src/hooks/tool-pair-validator/hook.ts +0 -184
  703. package/src/hooks/tool-pair-validator/index.ts +0 -1
  704. package/src/hooks/unstable-agent-babysitter/index.ts +0 -9
  705. package/src/hooks/unstable-agent-babysitter/task-message-analyzer.ts +0 -110
  706. package/src/hooks/unstable-agent-babysitter/unstable-agent-babysitter-hook.ts +0 -238
  707. package/src/hooks/webfetch-redirect-guard/constants.ts +0 -11
  708. package/src/hooks/webfetch-redirect-guard/hook.ts +0 -123
  709. package/src/hooks/webfetch-redirect-guard/index.ts +0 -1
  710. package/src/hooks/webfetch-redirect-guard/redirect-resolution.ts +0 -89
  711. package/src/hooks/write-existing-file-guard/hook.ts +0 -108
  712. package/src/hooks/write-existing-file-guard/index.ts +0 -1
  713. package/src/hooks/write-existing-file-guard/session-read-permissions.ts +0 -36
  714. package/src/hooks/write-existing-file-guard/tool-execute-before-handler.ts +0 -176
  715. package/src/index.ts +0 -254
  716. package/src/internals/plugins/pty/LICENSE +0 -21
  717. package/src/internals/plugins/pty/constants.ts +0 -7
  718. package/src/internals/plugins/pty/plugin.ts +0 -28
  719. package/src/internals/plugins/pty/pty/buffer.ts +0 -75
  720. package/src/internals/plugins/pty/pty/formatters.ts +0 -22
  721. package/src/internals/plugins/pty/pty/manager.ts +0 -175
  722. package/src/internals/plugins/pty/pty/notification-manager.ts +0 -75
  723. package/src/internals/plugins/pty/pty/output-manager.ts +0 -29
  724. package/src/internals/plugins/pty/pty/permissions.ts +0 -115
  725. package/src/internals/plugins/pty/pty/session-lifecycle.ts +0 -161
  726. package/src/internals/plugins/pty/pty/tools/kill.ts +0 -41
  727. package/src/internals/plugins/pty/pty/tools/kill.txt +0 -25
  728. package/src/internals/plugins/pty/pty/tools/list.ts +0 -25
  729. package/src/internals/plugins/pty/pty/tools/list.txt +0 -22
  730. package/src/internals/plugins/pty/pty/tools/read.ts +0 -234
  731. package/src/internals/plugins/pty/pty/tools/read.txt +0 -39
  732. package/src/internals/plugins/pty/pty/tools/spawn.ts +0 -71
  733. package/src/internals/plugins/pty/pty/tools/spawn.txt +0 -47
  734. package/src/internals/plugins/pty/pty/tools/write.ts +0 -96
  735. package/src/internals/plugins/pty/pty/tools/write.txt +0 -28
  736. package/src/internals/plugins/pty/pty/types.ts +0 -67
  737. package/src/internals/plugins/pty/pty/utils.ts +0 -21
  738. package/src/internals/plugins/pty/pty/wildcard.ts +0 -62
  739. package/src/internals/plugins/pty/shared/constants.ts +0 -7
  740. package/src/internals/plugins/pty/types.ts +0 -7
  741. package/src/internals/plugins/subtask2/LICENSE +0 -128
  742. package/src/internals/plugins/subtask2/commands/index.ts +0 -7
  743. package/src/internals/plugins/subtask2/commands/loader.ts +0 -39
  744. package/src/internals/plugins/subtask2/commands/manifest.ts +0 -60
  745. package/src/internals/plugins/subtask2/commands/resolver.ts +0 -28
  746. package/src/internals/plugins/subtask2/core/plugin.ts +0 -52
  747. package/src/internals/plugins/subtask2/core/state.ts +0 -764
  748. package/src/internals/plugins/subtask2/features/auto.ts +0 -57
  749. package/src/internals/plugins/subtask2/features/index.ts +0 -9
  750. package/src/internals/plugins/subtask2/features/inline-subtasks.ts +0 -205
  751. package/src/internals/plugins/subtask2/features/parallel.ts +0 -148
  752. package/src/internals/plugins/subtask2/features/results.ts +0 -48
  753. package/src/internals/plugins/subtask2/features/returns.ts +0 -273
  754. package/src/internals/plugins/subtask2/features/turns.ts +0 -190
  755. package/src/internals/plugins/subtask2/hooks/command-hooks.ts +0 -283
  756. package/src/internals/plugins/subtask2/hooks/message-hooks.ts +0 -603
  757. package/src/internals/plugins/subtask2/hooks/session-idle-hook.ts +0 -358
  758. package/src/internals/plugins/subtask2/hooks/tool-hooks.ts +0 -309
  759. package/src/internals/plugins/subtask2/loop.ts +0 -122
  760. package/src/internals/plugins/subtask2/parsing/auto.ts +0 -33
  761. package/src/internals/plugins/subtask2/parsing/commands.ts +0 -154
  762. package/src/internals/plugins/subtask2/parsing/frontmatter.ts +0 -20
  763. package/src/internals/plugins/subtask2/parsing/index.ts +0 -10
  764. package/src/internals/plugins/subtask2/parsing/overrides.ts +0 -68
  765. package/src/internals/plugins/subtask2/parsing/parallel.ts +0 -91
  766. package/src/internals/plugins/subtask2/parsing/turns.ts +0 -78
  767. package/src/internals/plugins/subtask2/types.ts +0 -41
  768. package/src/internals/plugins/subtask2/utils/config.ts +0 -100
  769. package/src/internals/plugins/subtask2/utils/index.ts +0 -7
  770. package/src/internals/plugins/subtask2/utils/logger.ts +0 -67
  771. package/src/internals/plugins/subtask2/utils/prompts.ts +0 -117
  772. package/src/lsp/index.ts +0 -16
  773. package/src/mcp/context7.ts +0 -9
  774. package/src/mcp/index.ts +0 -53
  775. package/src/mcp/registry.ts +0 -164
  776. package/src/mcp/types.ts +0 -19
  777. package/src/permissions/index.ts +0 -25
  778. package/src/plugin/AGENTS.md +0 -54
  779. package/src/plugin/available-categories.ts +0 -24
  780. package/src/plugin/chat-headers.ts +0 -141
  781. package/src/plugin/chat-message.ts +0 -307
  782. package/src/plugin/chat-params.ts +0 -182
  783. package/src/plugin/command-execute-before.ts +0 -80
  784. package/src/plugin/event.ts +0 -639
  785. package/src/plugin/hooks/create-continuation-hooks.ts +0 -128
  786. package/src/plugin/hooks/create-core-hooks.ts +0 -47
  787. package/src/plugin/hooks/create-session-hooks.ts +0 -269
  788. package/src/plugin/hooks/create-skill-hooks.ts +0 -50
  789. package/src/plugin/hooks/create-tool-guard-hooks.ts +0 -159
  790. package/src/plugin/hooks/create-transform-hooks.ts +0 -85
  791. package/src/plugin/messages-transform.ts +0 -28
  792. package/src/plugin/normalize-tool-arg-schemas.ts +0 -75
  793. package/src/plugin/recent-synthetic-idles.ts +0 -20
  794. package/src/plugin/session-agent-resolver.ts +0 -37
  795. package/src/plugin/session-status-normalizer.ts +0 -22
  796. package/src/plugin/skill-context.ts +0 -150
  797. package/src/plugin/skill-discovery-config.ts +0 -32
  798. package/src/plugin/startup-diagnostics.ts +0 -27
  799. package/src/plugin/system-transform.ts +0 -6
  800. package/src/plugin/tool-execute-after.ts +0 -178
  801. package/src/plugin/tool-execute-before.ts +0 -222
  802. package/src/plugin/tool-registry.ts +0 -286
  803. package/src/plugin/types.ts +0 -26
  804. package/src/plugin/ultrawork-db-model-override.ts +0 -142
  805. package/src/plugin/ultrawork-model-override.ts +0 -196
  806. package/src/plugin/ultrawork-variant-availability.ts +0 -51
  807. package/src/plugin/unstable-agent-babysitter.ts +0 -41
  808. package/src/plugin-config.ts +0 -314
  809. package/src/plugin-dispose.ts +0 -51
  810. package/src/plugin-handlers/AGENTS.md +0 -92
  811. package/src/plugin-handlers/agent-config-handler.ts +0 -510
  812. package/src/plugin-handlers/agent-key-remapper.ts +0 -39
  813. package/src/plugin-handlers/agent-override-protection.ts +0 -38
  814. package/src/plugin-handlers/agent-priority-order.ts +0 -63
  815. package/src/plugin-handlers/category-config-resolver.ts +0 -9
  816. package/src/plugin-handlers/command-config-handler.ts +0 -115
  817. package/src/plugin-handlers/config-handler.ts +0 -61
  818. package/src/plugin-handlers/index.ts +0 -10
  819. package/src/plugin-handlers/mcp-config-handler.test.ts +0 -63
  820. package/src/plugin-handlers/mcp-config-handler.ts +0 -220
  821. package/src/plugin-handlers/plan-model-inheritance.ts +0 -27
  822. package/src/plugin-handlers/plugin-components-loader.ts +0 -70
  823. package/src/plugin-handlers/provider-config-handler.ts +0 -73
  824. package/src/plugin-handlers/strategist-agent-config-builder.ts +0 -128
  825. package/src/plugin-handlers/tool-config-handler.ts +0 -193
  826. package/src/plugin-interface.ts +0 -83
  827. package/src/plugin-state.ts +0 -18
  828. package/src/shared/AGENTS.md +0 -54
  829. package/src/shared/agent-display-names.test.ts +0 -9
  830. package/src/shared/agent-display-names.ts +0 -187
  831. package/src/shared/agent-tool-restrictions.ts +0 -80
  832. package/src/shared/agent-variant.ts +0 -101
  833. package/src/shared/agents-config-dir.ts +0 -23
  834. package/src/shared/archive-entry-validator.ts +0 -83
  835. package/src/shared/background-output-consumption.ts +0 -69
  836. package/src/shared/binary-downloader.ts +0 -127
  837. package/src/shared/claude-config-dir.ts +0 -16
  838. package/src/shared/closure-protocol.ts +0 -53
  839. package/src/shared/command-executor/embedded-commands.ts +0 -26
  840. package/src/shared/command-executor/execute-command.ts +0 -28
  841. package/src/shared/command-executor/execute-hook-command.ts +0 -129
  842. package/src/shared/command-executor/home-directory.ts +0 -5
  843. package/src/shared/command-executor/resolve-commands-in-text.ts +0 -49
  844. package/src/shared/command-executor/shell-path.ts +0 -27
  845. package/src/shared/command-executor.ts +0 -5
  846. package/src/shared/compaction-agent-config-checkpoint.ts +0 -42
  847. package/src/shared/compaction-marker.ts +0 -61
  848. package/src/shared/config-errors.ts +0 -18
  849. package/src/shared/connected-providers-cache.ts +0 -215
  850. package/src/shared/contains-path.ts +0 -50
  851. package/src/shared/context-limit-resolver.ts +0 -42
  852. package/src/shared/data-path.ts +0 -64
  853. package/src/shared/deep-merge.ts +0 -53
  854. package/src/shared/disabled-tools.ts +0 -19
  855. package/src/shared/dynamic-truncator.ts +0 -222
  856. package/src/shared/external-plugin-detector.ts +0 -139
  857. package/src/shared/fallback-chain-from-models.ts +0 -124
  858. package/src/shared/fallback-model-availability.ts +0 -102
  859. package/src/shared/file-reference-resolver.ts +0 -99
  860. package/src/shared/file-utils.ts +0 -34
  861. package/src/shared/first-message-variant.ts +0 -28
  862. package/src/shared/frontmatter.ts +0 -31
  863. package/src/shared/git-worktree/collect-git-diff-stats.ts +0 -56
  864. package/src/shared/git-worktree/format-file-changes.ts +0 -46
  865. package/src/shared/git-worktree/index.ts +0 -7
  866. package/src/shared/git-worktree/parse-diff-numstat.ts +0 -27
  867. package/src/shared/git-worktree/parse-status-porcelain-line.ts +0 -27
  868. package/src/shared/git-worktree/parse-status-porcelain.ts +0 -15
  869. package/src/shared/git-worktree/types.ts +0 -8
  870. package/src/shared/hook-disabled.ts +0 -22
  871. package/src/shared/index.ts +0 -80
  872. package/src/shared/internal-initiator-marker.ts +0 -18
  873. package/src/shared/is-abort-error.ts +0 -20
  874. package/src/shared/json-file-cache-store.ts +0 -98
  875. package/src/shared/jsonc-parser.ts +0 -98
  876. package/src/shared/known-variants.ts +0 -16
  877. package/src/shared/legacy-plugin-warning.ts +0 -68
  878. package/src/shared/load-opencode-plugins.ts +0 -60
  879. package/src/shared/log-legacy-plugin-startup-warning.ts +0 -44
  880. package/src/shared/logger.ts +0 -56
  881. package/src/shared/mcp-static-export.ts +0 -119
  882. package/src/shared/merge-categories.ts +0 -18
  883. package/src/shared/migrate-legacy-config-file.ts +0 -66
  884. package/src/shared/migrate-legacy-plugin-entry.ts +0 -75
  885. package/src/shared/migration/agent-category.ts +0 -60
  886. package/src/shared/migration/agent-names.ts +0 -108
  887. package/src/shared/migration/config-migration.ts +0 -210
  888. package/src/shared/migration/hook-names.ts +0 -40
  889. package/src/shared/migration/migrations-sidecar.ts +0 -92
  890. package/src/shared/migration/model-versions.ts +0 -50
  891. package/src/shared/migration.ts +0 -5
  892. package/src/shared/mode-routing.test.ts +0 -88
  893. package/src/shared/mode-routing.ts +0 -30
  894. package/src/shared/model-availability.ts +0 -294
  895. package/src/shared/model-capabilities/bundled-snapshot.ts +0 -15
  896. package/src/shared/model-capabilities/get-model-capabilities.ts +0 -140
  897. package/src/shared/model-capabilities/index.ts +0 -9
  898. package/src/shared/model-capabilities/runtime-model-readers.ts +0 -190
  899. package/src/shared/model-capabilities/types.ts +0 -80
  900. package/src/shared/model-capabilities-cache.ts +0 -213
  901. package/src/shared/model-capability-aliases.ts +0 -108
  902. package/src/shared/model-capability-guardrails.ts +0 -149
  903. package/src/shared/model-capability-heuristics.ts +0 -32
  904. package/src/shared/model-error-classifier.ts +0 -214
  905. package/src/shared/model-format-normalizer.ts +0 -20
  906. package/src/shared/model-normalization.ts +0 -8
  907. package/src/shared/model-requirements.ts +0 -26
  908. package/src/shared/model-resolution-pipeline.ts +0 -216
  909. package/src/shared/model-resolution-types.ts +0 -41
  910. package/src/shared/model-resolver.ts +0 -106
  911. package/src/shared/model-sanitizer.ts +0 -12
  912. package/src/shared/model-settings-compatibility.ts +0 -200
  913. package/src/shared/model-suggestion-retry.ts +0 -182
  914. package/src/shared/normalize-sdk-response.ts +0 -36
  915. package/src/shared/opencode-command-dirs.ts +0 -36
  916. package/src/shared/opencode-config-dir-types.ts +0 -15
  917. package/src/shared/opencode-config-dir.ts +0 -135
  918. package/src/shared/opencode-http-api.ts +0 -140
  919. package/src/shared/opencode-message-dir.ts +0 -29
  920. package/src/shared/opencode-server-auth.ts +0 -190
  921. package/src/shared/opencode-storage-detection.ts +0 -34
  922. package/src/shared/opencode-storage-paths.ts +0 -7
  923. package/src/shared/opencode-version.ts +0 -80
  924. package/src/shared/parse-tools-config.ts +0 -25
  925. package/src/shared/pattern-matcher.ts +0 -46
  926. package/src/shared/permission-compat.ts +0 -86
  927. package/src/shared/plugin-command-discovery.ts +0 -28
  928. package/src/shared/plugin-entry-migrator.ts +0 -21
  929. package/src/shared/plugin-identity.ts +0 -8
  930. package/src/shared/port-utils.ts +0 -48
  931. package/src/shared/project-discovery-dirs.ts +0 -101
  932. package/src/shared/prompt-timeout-context.ts +0 -49
  933. package/src/shared/prompt-tools.ts +0 -35
  934. package/src/shared/provider-model-id-transform.ts +0 -58
  935. package/src/shared/question-denied-session-permission.ts +0 -9
  936. package/src/shared/record-type-guard.ts +0 -3
  937. package/src/shared/resolve-agent-definition-paths.ts +0 -22
  938. package/src/shared/retry-status-utils.ts +0 -19
  939. package/src/shared/runtime-plugin-config.ts +0 -98
  940. package/src/shared/safe-create-hook.ts +0 -24
  941. package/src/shared/session-category-registry.ts +0 -27
  942. package/src/shared/session-cursor.ts +0 -108
  943. package/src/shared/session-directory-resolver.ts +0 -41
  944. package/src/shared/session-injected-paths.ts +0 -59
  945. package/src/shared/session-model-state.ts +0 -15
  946. package/src/shared/session-prompt-params-helpers.ts +0 -31
  947. package/src/shared/session-prompt-params-state.ts +0 -37
  948. package/src/shared/session-tools-store.ts +0 -18
  949. package/src/shared/session-utils.ts +0 -25
  950. package/src/shared/shell-env.ts +0 -175
  951. package/src/shared/skill-path-resolver.ts +0 -26
  952. package/src/shared/snake-case.ts +0 -44
  953. package/src/shared/spawn-with-windows-hide.ts +0 -84
  954. package/src/shared/startup-diagnostics.ts +0 -76
  955. package/src/shared/system-directive.ts +0 -67
  956. package/src/shared/task-system-enabled.ts +0 -9
  957. package/src/shared/tmux/constants.ts +0 -12
  958. package/src/shared/tmux/index.ts +0 -3
  959. package/src/shared/tmux/tmux-utils/environment.ts +0 -13
  960. package/src/shared/tmux/tmux-utils/layout.ts +0 -96
  961. package/src/shared/tmux/tmux-utils/pane-close.ts +0 -48
  962. package/src/shared/tmux/tmux-utils/pane-dimensions.ts +0 -28
  963. package/src/shared/tmux/tmux-utils/pane-replace.ts +0 -73
  964. package/src/shared/tmux/tmux-utils/pane-spawn.ts +0 -94
  965. package/src/shared/tmux/tmux-utils/server-health.ts +0 -62
  966. package/src/shared/tmux/tmux-utils/session-spawn.ts +0 -145
  967. package/src/shared/tmux/tmux-utils/window-spawn.ts +0 -93
  968. package/src/shared/tmux/tmux-utils.ts +0 -15
  969. package/src/shared/tmux/types.ts +0 -4
  970. package/src/shared/tool-name.ts +0 -27
  971. package/src/shared/truncate-description.ts +0 -11
  972. package/src/shared/vision-capable-models-cache.ts +0 -17
  973. package/src/shared/write-file-atomically.ts +0 -31
  974. package/src/shared/zip-entry-listing/powershell-zip-entry-listing.ts +0 -99
  975. package/src/shared/zip-entry-listing/python-zip-entry-listing.ts +0 -55
  976. package/src/shared/zip-entry-listing/read-zip-symlink-target.ts +0 -23
  977. package/src/shared/zip-entry-listing/tar-zip-entry-listing.ts +0 -93
  978. package/src/shared/zip-entry-listing/zipinfo-zip-entry-listing.ts +0 -72
  979. package/src/shared/zip-entry-listing.ts +0 -13
  980. package/src/shared/zip-extractor.ts +0 -118
  981. package/src/skills/index.ts +0 -56
  982. package/src/testing/module-mock-lifecycle.ts +0 -143
  983. package/src/tools/AGENTS.md +0 -108
  984. package/src/tools/ast-grep/cli-binary-path-resolution.ts +0 -60
  985. package/src/tools/ast-grep/cli.ts +0 -177
  986. package/src/tools/ast-grep/constants.ts +0 -5
  987. package/src/tools/ast-grep/downloader.ts +0 -119
  988. package/src/tools/ast-grep/environment-check.ts +0 -59
  989. package/src/tools/ast-grep/index.ts +0 -5
  990. package/src/tools/ast-grep/language-support.ts +0 -60
  991. package/src/tools/ast-grep/process-output-timeout.ts +0 -28
  992. package/src/tools/ast-grep/result-formatter.ts +0 -102
  993. package/src/tools/ast-grep/sg-cli-path.ts +0 -102
  994. package/src/tools/ast-grep/sg-compact-json-output.ts +0 -54
  995. package/src/tools/ast-grep/tools.ts +0 -117
  996. package/src/tools/ast-grep/types.ts +0 -60
  997. package/src/tools/background-task/AGENTS.md +0 -53
  998. package/src/tools/background-task/clients.ts +0 -32
  999. package/src/tools/background-task/constants.ts +0 -9
  1000. package/src/tools/background-task/create-background-cancel.ts +0 -115
  1001. package/src/tools/background-task/create-background-output.ts +0 -159
  1002. package/src/tools/background-task/create-background-task.ts +0 -126
  1003. package/src/tools/background-task/delay.ts +0 -3
  1004. package/src/tools/background-task/full-session-format.ts +0 -148
  1005. package/src/tools/background-task/index.ts +0 -8
  1006. package/src/tools/background-task/message-dir.ts +0 -1
  1007. package/src/tools/background-task/session-messages.ts +0 -22
  1008. package/src/tools/background-task/task-result-format.ts +0 -113
  1009. package/src/tools/background-task/task-status-format.ts +0 -72
  1010. package/src/tools/background-task/time-format.ts +0 -30
  1011. package/src/tools/background-task/tools.ts +0 -11
  1012. package/src/tools/background-task/truncate-text.ts +0 -4
  1013. package/src/tools/background-task/types.ts +0 -72
  1014. package/src/tools/call-omo-agent/AGENTS.md +0 -51
  1015. package/src/tools/call-omo-agent/agent-resolver.ts +0 -64
  1016. package/src/tools/call-omo-agent/background-agent-executor.ts +0 -91
  1017. package/src/tools/call-omo-agent/background-executor.ts +0 -98
  1018. package/src/tools/call-omo-agent/completion-poller.ts +0 -65
  1019. package/src/tools/call-omo-agent/constants.ts +0 -23
  1020. package/src/tools/call-omo-agent/index.ts +0 -3
  1021. package/src/tools/call-omo-agent/message-dir.ts +0 -1
  1022. package/src/tools/call-omo-agent/message-processor.ts +0 -86
  1023. package/src/tools/call-omo-agent/message-storage-directory.ts +0 -1
  1024. package/src/tools/call-omo-agent/session-creator.ts +0 -70
  1025. package/src/tools/call-omo-agent/subagent-session-creator.ts +0 -74
  1026. package/src/tools/call-omo-agent/sync-executor.ts +0 -148
  1027. package/src/tools/call-omo-agent/tool-context-with-metadata.ts +0 -10
  1028. package/src/tools/call-omo-agent/tools.ts +0 -199
  1029. package/src/tools/call-omo-agent/types.ts +0 -34
  1030. package/src/tools/delegate-task/AGENTS.md +0 -58
  1031. package/src/tools/delegate-task/anthropic-categories.ts +0 -62
  1032. package/src/tools/delegate-task/available-models.ts +0 -64
  1033. package/src/tools/delegate-task/background-continuation.ts +0 -68
  1034. package/src/tools/delegate-task/background-task.ts +0 -165
  1035. package/src/tools/delegate-task/builtin-categories.ts +0 -35
  1036. package/src/tools/delegate-task/builtin-category-definition.ts +0 -8
  1037. package/src/tools/delegate-task/cancel-unstable-agent-task.ts +0 -19
  1038. package/src/tools/delegate-task/categories.test.ts +0 -87
  1039. package/src/tools/delegate-task/categories.ts +0 -77
  1040. package/src/tools/delegate-task/category-resolver.ts +0 -309
  1041. package/src/tools/delegate-task/constants.ts +0 -351
  1042. package/src/tools/delegate-task/delegated-model-config.ts +0 -20
  1043. package/src/tools/delegate-task/error-formatting.ts +0 -51
  1044. package/src/tools/delegate-task/executor-types.ts +0 -39
  1045. package/src/tools/delegate-task/executor.ts +0 -16
  1046. package/src/tools/delegate-task/fallback-entry-resolution.ts +0 -27
  1047. package/src/tools/delegate-task/fallback-entry-settings.ts +0 -20
  1048. package/src/tools/delegate-task/git-categories.ts +0 -30
  1049. package/src/tools/delegate-task/google-categories.ts +0 -130
  1050. package/src/tools/delegate-task/index.ts +0 -4
  1051. package/src/tools/delegate-task/kimi-categories.ts +0 -40
  1052. package/src/tools/delegate-task/model-selection.ts +0 -201
  1053. package/src/tools/delegate-task/model-string-parser.test.ts +0 -90
  1054. package/src/tools/delegate-task/model-string-parser.ts +0 -63
  1055. package/src/tools/delegate-task/openai-categories.ts +0 -132
  1056. package/src/tools/delegate-task/parent-context-resolver.ts +0 -47
  1057. package/src/tools/delegate-task/prompt-builder.ts +0 -107
  1058. package/src/tools/delegate-task/resolve-call-id.ts +0 -5
  1059. package/src/tools/delegate-task/skill-resolver.ts +0 -22
  1060. package/src/tools/delegate-task/sub-agent.ts +0 -80
  1061. package/src/tools/delegate-task/subagent-discovery.test.ts +0 -123
  1062. package/src/tools/delegate-task/subagent-discovery.ts +0 -152
  1063. package/src/tools/delegate-task/subagent-resolver.ts +0 -242
  1064. package/src/tools/delegate-task/sync-continuation-deps.ts +0 -9
  1065. package/src/tools/delegate-task/sync-continuation.ts +0 -149
  1066. package/src/tools/delegate-task/sync-prompt-sender.ts +0 -137
  1067. package/src/tools/delegate-task/sync-result-fetcher.ts +0 -60
  1068. package/src/tools/delegate-task/sync-session-creator.ts +0 -29
  1069. package/src/tools/delegate-task/sync-session-poller.ts +0 -188
  1070. package/src/tools/delegate-task/sync-task-deps.ts +0 -13
  1071. package/src/tools/delegate-task/sync-task-fallback.ts +0 -68
  1072. package/src/tools/delegate-task/sync-task.ts +0 -243
  1073. package/src/tools/delegate-task/time-formatter.ts +0 -13
  1074. package/src/tools/delegate-task/timing.ts +0 -46
  1075. package/src/tools/delegate-task/token-limiter.ts +0 -123
  1076. package/src/tools/delegate-task/tools.ts +0 -259
  1077. package/src/tools/delegate-task/types.ts +0 -89
  1078. package/src/tools/delegate-task/unstable-agent-task.ts +0 -243
  1079. package/src/tools/glob/cli.ts +0 -206
  1080. package/src/tools/glob/constants.ts +0 -12
  1081. package/src/tools/glob/index.ts +0 -1
  1082. package/src/tools/glob/result-formatter.ts +0 -26
  1083. package/src/tools/glob/tools.ts +0 -49
  1084. package/src/tools/glob/types.ts +0 -23
  1085. package/src/tools/grep/cli.ts +0 -279
  1086. package/src/tools/grep/constants.ts +0 -141
  1087. package/src/tools/grep/downloader.ts +0 -128
  1088. package/src/tools/grep/index.ts +0 -1
  1089. package/src/tools/grep/result-formatter.ts +0 -60
  1090. package/src/tools/grep/tools.ts +0 -75
  1091. package/src/tools/grep/types.ts +0 -42
  1092. package/src/tools/hashline-edit/AGENTS.md +0 -92
  1093. package/src/tools/hashline-edit/autocorrect-replacement-lines.ts +0 -179
  1094. package/src/tools/hashline-edit/constants.ts +0 -10
  1095. package/src/tools/hashline-edit/diff-utils.ts +0 -53
  1096. package/src/tools/hashline-edit/edit-deduplication.ts +0 -43
  1097. package/src/tools/hashline-edit/edit-operation-primitives.ts +0 -126
  1098. package/src/tools/hashline-edit/edit-operations.ts +0 -103
  1099. package/src/tools/hashline-edit/edit-ordering.ts +0 -56
  1100. package/src/tools/hashline-edit/edit-text-normalization.ts +0 -111
  1101. package/src/tools/hashline-edit/file-text-canonicalization.ts +0 -44
  1102. package/src/tools/hashline-edit/formatter-trigger.ts +0 -132
  1103. package/src/tools/hashline-edit/hash-computation.ts +0 -154
  1104. package/src/tools/hashline-edit/hashline-chunk-formatter.ts +0 -52
  1105. package/src/tools/hashline-edit/hashline-edit-diff.ts +0 -31
  1106. package/src/tools/hashline-edit/hashline-edit-executor.ts +0 -197
  1107. package/src/tools/hashline-edit/index.ts +0 -20
  1108. package/src/tools/hashline-edit/normalize-edits.ts +0 -95
  1109. package/src/tools/hashline-edit/tool-description.ts +0 -95
  1110. package/src/tools/hashline-edit/tools.ts +0 -42
  1111. package/src/tools/hashline-edit/types.ts +0 -20
  1112. package/src/tools/hashline-edit/validation.ts +0 -181
  1113. package/src/tools/index.ts +0 -64
  1114. package/src/tools/interactive-bash/constants.ts +0 -18
  1115. package/src/tools/interactive-bash/index.ts +0 -4
  1116. package/src/tools/interactive-bash/tmux-path-resolver.ts +0 -71
  1117. package/src/tools/interactive-bash/tools.ts +0 -136
  1118. package/src/tools/look-at/assistant-message-extractor.ts +0 -67
  1119. package/src/tools/look-at/constants.ts +0 -3
  1120. package/src/tools/look-at/image-converter.ts +0 -164
  1121. package/src/tools/look-at/index.ts +0 -3
  1122. package/src/tools/look-at/look-at-arguments.ts +0 -34
  1123. package/src/tools/look-at/mime-type-inference.ts +0 -94
  1124. package/src/tools/look-at/multimodal-agent-metadata.ts +0 -166
  1125. package/src/tools/look-at/multimodal-fallback-chain.ts +0 -66
  1126. package/src/tools/look-at/session-poller.ts +0 -42
  1127. package/src/tools/look-at/tools.ts +0 -245
  1128. package/src/tools/look-at/types.ts +0 -5
  1129. package/src/tools/lsp/AGENTS.md +0 -70
  1130. package/src/tools/lsp/client.ts +0 -3
  1131. package/src/tools/lsp/config.ts +0 -3
  1132. package/src/tools/lsp/constants.ts +0 -7
  1133. package/src/tools/lsp/diagnostics-tool.ts +0 -75
  1134. package/src/tools/lsp/directory-diagnostics.ts +0 -163
  1135. package/src/tools/lsp/find-references-tool.ts +0 -43
  1136. package/src/tools/lsp/goto-definition-tool.ts +0 -42
  1137. package/src/tools/lsp/index.ts +0 -9
  1138. package/src/tools/lsp/infer-extension.ts +0 -65
  1139. package/src/tools/lsp/language-config.ts +0 -5
  1140. package/src/tools/lsp/language-mappings.ts +0 -171
  1141. package/src/tools/lsp/lsp-client-connection.ts +0 -66
  1142. package/src/tools/lsp/lsp-client-transport.ts +0 -210
  1143. package/src/tools/lsp/lsp-client-wrapper.ts +0 -116
  1144. package/src/tools/lsp/lsp-client.ts +0 -129
  1145. package/src/tools/lsp/lsp-formatters.ts +0 -193
  1146. package/src/tools/lsp/lsp-manager-process-cleanup.ts +0 -83
  1147. package/src/tools/lsp/lsp-manager-temp-directory-cleanup.ts +0 -29
  1148. package/src/tools/lsp/lsp-process.ts +0 -158
  1149. package/src/tools/lsp/lsp-server.ts +0 -217
  1150. package/src/tools/lsp/rename-tools.ts +0 -53
  1151. package/src/tools/lsp/server-config-loader.ts +0 -116
  1152. package/src/tools/lsp/server-definitions.ts +0 -91
  1153. package/src/tools/lsp/server-installation.ts +0 -58
  1154. package/src/tools/lsp/server-path-bases.ts +0 -16
  1155. package/src/tools/lsp/server-resolution.ts +0 -109
  1156. package/src/tools/lsp/symbols-tool.ts +0 -76
  1157. package/src/tools/lsp/tools.ts +0 -5
  1158. package/src/tools/lsp/types.ts +0 -124
  1159. package/src/tools/lsp/workspace-edit.ts +0 -121
  1160. package/src/tools/session-manager/constants.ts +0 -93
  1161. package/src/tools/session-manager/file-storage.ts +0 -203
  1162. package/src/tools/session-manager/index.ts +0 -3
  1163. package/src/tools/session-manager/sdk-storage.ts +0 -135
  1164. package/src/tools/session-manager/sdk-unavailable.ts +0 -43
  1165. package/src/tools/session-manager/session-formatter.ts +0 -199
  1166. package/src/tools/session-manager/storage.ts +0 -161
  1167. package/src/tools/session-manager/tools.ts +0 -197
  1168. package/src/tools/session-manager/types.ts +0 -99
  1169. package/src/tools/shared/semaphore.ts +0 -32
  1170. package/src/tools/skill/constants.ts +0 -14
  1171. package/src/tools/skill/description-formatter.ts +0 -61
  1172. package/src/tools/skill/index.ts +0 -3
  1173. package/src/tools/skill/mcp-capability-formatter.ts +0 -97
  1174. package/src/tools/skill/native-skills.ts +0 -62
  1175. package/src/tools/skill/scope-priority.ts +0 -17
  1176. package/src/tools/skill/skill-body.ts +0 -26
  1177. package/src/tools/skill/skill-matcher.ts +0 -40
  1178. package/src/tools/skill/tools.ts +0 -196
  1179. package/src/tools/skill/types.ts +0 -48
  1180. package/src/tools/skill-mcp/constants.ts +0 -9
  1181. package/src/tools/skill-mcp/index.ts +0 -3
  1182. package/src/tools/skill-mcp/tools.test.ts +0 -44
  1183. package/src/tools/skill-mcp/tools.ts +0 -242
  1184. package/src/tools/skill-mcp/types.ts +0 -8
  1185. package/src/tools/slashcommand/command-discovery.ts +0 -161
  1186. package/src/tools/slashcommand/command-output-formatter.ts +0 -75
  1187. package/src/tools/slashcommand/index.ts +0 -2
  1188. package/src/tools/slashcommand/types.ts +0 -21
  1189. package/src/tools/task/index.ts +0 -7
  1190. package/src/tools/task/task-create.ts +0 -113
  1191. package/src/tools/task/task-get.ts +0 -47
  1192. package/src/tools/task/task-list.ts +0 -79
  1193. package/src/tools/task/task-update.ts +0 -152
  1194. package/src/tools/task/todo-sync.ts +0 -205
  1195. package/src/tools/task/types.ts +0 -77
@@ -1,2220 +0,0 @@
1
-
2
- import type { PluginInput } from "@opencode-ai/plugin"
3
- import { isAgentNotFoundError, FALLBACK_AGENT, buildFallbackBody } from "./spawner"
4
- import type {
5
- BackgroundTask,
6
- LaunchInput,
7
- ResumeInput,
8
- } from "./types"
9
- import { TaskHistory } from "./task-history"
10
- import {
11
- log,
12
- getAgentToolRestrictions,
13
- normalizePromptTools,
14
- normalizeSDKResponse,
15
- promptWithModelSuggestionRetry,
16
- resolveInheritedPromptTools,
17
- createInternalAgentTextPart,
18
- } from "../../shared"
19
- import { applySessionPromptParams } from "../../shared/session-prompt-params-helpers"
20
- import { setSessionTools } from "../../shared/session-tools-store"
21
- import { SessionCategoryRegistry } from "../../shared/session-category-registry"
22
- import { ConcurrencyManager } from "./concurrency"
23
- import type { BackgroundTaskConfig, TmuxConfig } from "../../config/schema"
24
- import { isInsideTmux } from "../../shared/tmux"
25
- import {
26
- shouldRetryError,
27
- hasMoreFallbacks,
28
- } from "../../shared/model-error-classifier"
29
- import {
30
- POLLING_INTERVAL_MS,
31
- TASK_CLEANUP_DELAY_MS,
32
- TASK_TTL_MS,
33
- } from "./constants"
34
-
35
- import { subagentSessions } from "../claude-code-session-state"
36
- import { getTaskToastManager } from "../task-toast-manager"
37
- import { formatDuration } from "./duration-formatter"
38
- import {
39
- buildBackgroundTaskNotificationText,
40
- type BackgroundTaskNotificationTask,
41
- } from "./background-task-notification-template"
42
- import {
43
- isAbortedSessionError,
44
- extractErrorName,
45
- extractErrorMessage,
46
- getSessionErrorMessage,
47
- isRecord,
48
- } from "./error-classifier"
49
- import { tryFallbackRetry } from "./fallback-retry-handler"
50
- import { registerManagerForCleanup, unregisterManagerForCleanup } from "./process-cleanup"
51
- import {
52
- findNearestMessageExcludingCompaction,
53
- resolvePromptContextFromSessionMessages,
54
- } from "./compaction-aware-message-resolver"
55
- import { handleSessionIdleBackgroundEvent } from "./session-idle-event-handler"
56
- import { MESSAGE_STORAGE } from "../hook-message-injector"
57
- import { join } from "node:path"
58
- import { pruneStaleTasksAndNotifications } from "./task-poller"
59
- import { checkAndInterruptStaleTasks } from "./task-poller"
60
- import { removeTaskToastTracking } from "./remove-task-toast-tracking"
61
- import { abortWithTimeout } from "./abort-with-timeout"
62
- import {
63
- MIN_SESSION_GONE_POLLS,
64
- verifySessionExists as verifySessionStillExists,
65
- } from "./session-existence"
66
- import { isActiveSessionStatus, isTerminalSessionStatus } from "./session-status-classifier"
67
- import {
68
- detectRepetitiveToolUse,
69
- recordToolCall,
70
- resolveCircuitBreakerSettings,
71
- type CircuitBreakerSettings,
72
- } from "./loop-detector"
73
- import {
74
- createSubagentDepthLimitError,
75
- createSubagentDescendantLimitError,
76
- getMaxRootSessionSpawnBudget,
77
- getMaxSubagentDepth,
78
- resolveSubagentSpawnContext,
79
- type SubagentSpawnContext,
80
- } from "./subagent-spawn-limits"
81
-
82
- type OpencodeClient = PluginInput["client"]
83
-
84
-
85
- interface MessagePartInfo {
86
- id?: string
87
- sessionID?: string
88
- type?: string
89
- tool?: string
90
- state?: { status?: string; input?: Record<string, unknown> }
91
- }
92
-
93
- interface EventProperties {
94
- sessionID?: string
95
- info?: { id?: string }
96
- [key: string]: unknown
97
- }
98
-
99
- interface Event {
100
- type: string
101
- properties?: EventProperties
102
- }
103
-
104
- function resolveMessagePartInfo(properties: EventProperties | undefined): MessagePartInfo | undefined {
105
- if (!properties || typeof properties !== "object") {
106
- return undefined
107
- }
108
-
109
- const nestedPart = properties.part
110
- if (nestedPart && typeof nestedPart === "object") {
111
- return nestedPart as MessagePartInfo
112
- }
113
-
114
- return properties as MessagePartInfo
115
- }
116
-
117
- interface Todo {
118
- content: string
119
- status: string
120
- priority: string
121
- id: string
122
- }
123
-
124
- interface QueueItem {
125
- task: BackgroundTask
126
- input: LaunchInput
127
- }
128
-
129
- export interface SubagentSessionCreatedEvent {
130
- sessionID: string
131
- parentID: string
132
- title: string
133
- }
134
-
135
- export type OnSubagentSessionCreated = (event: SubagentSessionCreatedEvent) => Promise<void>
136
-
137
- const MAX_TASK_REMOVAL_RESCHEDULES = 6
138
-
139
- export class BackgroundManager {
140
-
141
-
142
- private tasks: Map<string, BackgroundTask>
143
- private notifications: Map<string, BackgroundTask[]>
144
- private pendingNotifications: Map<string, string[]>
145
- private pendingByParent: Map<string, Set<string>> // Track pending tasks per parent for batching
146
- private client: OpencodeClient
147
- private directory: string
148
- private pollingInterval?: ReturnType<typeof setInterval>
149
- private pollingInFlight = false
150
- private concurrencyManager: ConcurrencyManager
151
- private shutdownTriggered = false
152
- private config?: BackgroundTaskConfig
153
- private tmuxEnabled: boolean
154
- private onSubagentSessionCreated?: OnSubagentSessionCreated
155
- private onShutdown?: () => void | Promise<void>
156
-
157
- private queuesByKey: Map<string, QueueItem[]> = new Map()
158
- private processingKeys: Set<string> = new Set()
159
- private completionTimers: Map<string, ReturnType<typeof setTimeout>> = new Map()
160
- private completedTaskSummaries: Map<string, BackgroundTaskNotificationTask[]> = new Map()
161
- private idleDeferralTimers: Map<string, ReturnType<typeof setTimeout>> = new Map()
162
- private notificationQueueByParent: Map<string, Promise<void>> = new Map()
163
- private observedOutputSessions: Set<string> = new Set()
164
- private observedIncompleteTodosBySession: Map<string, boolean> = new Map()
165
- private rootDescendantCounts: Map<string, number>
166
- private preStartDescendantReservations: Set<string>
167
- private enableParentSessionNotifications: boolean
168
- readonly taskHistory = new TaskHistory()
169
- private cachedCircuitBreakerSettings?: CircuitBreakerSettings
170
-
171
- constructor(
172
- ctx: PluginInput,
173
- config?: BackgroundTaskConfig,
174
- options?: {
175
- tmuxConfig?: TmuxConfig
176
- onSubagentSessionCreated?: OnSubagentSessionCreated
177
- onShutdown?: () => void | Promise<void>
178
- enableParentSessionNotifications?: boolean
179
- }
180
- ) {
181
- this.tasks = new Map()
182
- this.notifications = new Map()
183
- this.pendingNotifications = new Map()
184
- this.pendingByParent = new Map()
185
- this.client = ctx.client
186
- this.directory = ctx.directory
187
- this.concurrencyManager = new ConcurrencyManager(config)
188
- this.config = config
189
- this.tmuxEnabled = options?.tmuxConfig?.enabled ?? false
190
- this.onSubagentSessionCreated = options?.onSubagentSessionCreated
191
- this.onShutdown = options?.onShutdown
192
- this.rootDescendantCounts = new Map()
193
- this.preStartDescendantReservations = new Set()
194
- this.enableParentSessionNotifications = options?.enableParentSessionNotifications ?? true
195
- this.registerProcessCleanup()
196
- }
197
-
198
- private async abortSessionWithLogging(sessionID: string, reason: string): Promise<void> {
199
- try {
200
- await abortWithTimeout(this.client, sessionID)
201
- } catch (error) {
202
- log(`[background-agent] Failed to abort session during ${reason}:`, {
203
- sessionID,
204
- error,
205
- })
206
- }
207
- }
208
-
209
- async assertCanSpawn(parentSessionID: string): Promise<SubagentSpawnContext> {
210
- const spawnContext = await resolveSubagentSpawnContext(this.client, parentSessionID, this.directory)
211
- const maxDepth = getMaxSubagentDepth(this.config)
212
- if (spawnContext.childDepth > maxDepth) {
213
- throw createSubagentDepthLimitError({
214
- childDepth: spawnContext.childDepth,
215
- maxDepth,
216
- parentSessionID,
217
- rootSessionID: spawnContext.rootSessionID,
218
- })
219
- }
220
-
221
- const maxRootSessionSpawnBudget = getMaxRootSessionSpawnBudget(this.config)
222
- const descendantCount = this.rootDescendantCounts.get(spawnContext.rootSessionID) ?? 0
223
- if (descendantCount >= maxRootSessionSpawnBudget) {
224
- throw createSubagentDescendantLimitError({
225
- rootSessionID: spawnContext.rootSessionID,
226
- descendantCount,
227
- maxDescendants: maxRootSessionSpawnBudget,
228
- })
229
- }
230
-
231
- return spawnContext
232
- }
233
-
234
- async reserveSubagentSpawn(parentSessionID: string): Promise<{
235
- spawnContext: SubagentSpawnContext
236
- descendantCount: number
237
- commit: () => number
238
- rollback: () => void
239
- }> {
240
- const spawnContext = await this.assertCanSpawn(parentSessionID)
241
- const descendantCount = this.registerRootDescendant(spawnContext.rootSessionID)
242
- let settled = false
243
-
244
- return {
245
- spawnContext,
246
- descendantCount,
247
- commit: () => {
248
- settled = true
249
- return descendantCount
250
- },
251
- rollback: () => {
252
- if (settled) return
253
- settled = true
254
- this.unregisterRootDescendant(spawnContext.rootSessionID)
255
- },
256
- }
257
- }
258
-
259
- private registerRootDescendant(rootSessionID: string): number {
260
- const nextCount = (this.rootDescendantCounts.get(rootSessionID) ?? 0) + 1
261
- this.rootDescendantCounts.set(rootSessionID, nextCount)
262
- return nextCount
263
- }
264
-
265
- private unregisterRootDescendant(rootSessionID: string): void {
266
- const currentCount = this.rootDescendantCounts.get(rootSessionID) ?? 0
267
- if (currentCount <= 1) {
268
- this.rootDescendantCounts.delete(rootSessionID)
269
- return
270
- }
271
-
272
- this.rootDescendantCounts.set(rootSessionID, currentCount - 1)
273
- }
274
-
275
- private markPreStartDescendantReservation(task: BackgroundTask): void {
276
- this.preStartDescendantReservations.add(task.id)
277
- }
278
-
279
- private settlePreStartDescendantReservation(task: BackgroundTask): void {
280
- this.preStartDescendantReservations.delete(task.id)
281
- }
282
-
283
- private rollbackPreStartDescendantReservation(task: BackgroundTask): void {
284
- if (!this.preStartDescendantReservations.delete(task.id)) {
285
- return
286
- }
287
-
288
- if (!task.rootSessionID) {
289
- return
290
- }
291
-
292
- this.unregisterRootDescendant(task.rootSessionID)
293
- }
294
-
295
- async launch(input: LaunchInput): Promise<BackgroundTask> {
296
- log("[background-agent] launch() called with:", {
297
- agent: input.agent,
298
- model: input.model,
299
- description: input.description,
300
- parentSessionID: input.parentSessionID,
301
- })
302
-
303
- if (!input.agent || input.agent.trim() === "") {
304
- throw new Error("Agent parameter is required")
305
- }
306
-
307
- const spawnReservation = await this.reserveSubagentSpawn(input.parentSessionID)
308
-
309
- try {
310
- log("[background-agent] spawn guard passed", {
311
- parentSessionID: input.parentSessionID,
312
- rootSessionID: spawnReservation.spawnContext.rootSessionID,
313
- childDepth: spawnReservation.spawnContext.childDepth,
314
- descendantCount: spawnReservation.descendantCount,
315
- })
316
-
317
- // Create task immediately with status="pending"
318
- const task: BackgroundTask = {
319
- id: `bg_${crypto.randomUUID().slice(0, 8)}`,
320
- status: "pending",
321
- queuedAt: new Date(),
322
- rootSessionID: spawnReservation.spawnContext.rootSessionID,
323
- // Do NOT set startedAt - will be set when running
324
- // Do NOT set sessionID - will be set when running
325
- description: input.description,
326
- prompt: input.prompt,
327
- agent: input.agent,
328
- spawnDepth: spawnReservation.spawnContext.childDepth,
329
- parentSessionID: input.parentSessionID,
330
- parentMessageID: input.parentMessageID,
331
- parentModel: input.parentModel,
332
- parentAgent: input.parentAgent,
333
- parentTools: input.parentTools,
334
- model: input.model,
335
- fallbackChain: input.fallbackChain,
336
- attemptCount: 0,
337
- category: input.category,
338
- }
339
-
340
- this.tasks.set(task.id, task)
341
- this.taskHistory.record(input.parentSessionID, { id: task.id, agent: input.agent, description: input.description, status: "pending", category: input.category })
342
-
343
- // Track for batched notifications immediately (pending state)
344
- if (input.parentSessionID) {
345
- const pending = this.pendingByParent.get(input.parentSessionID) ?? new Set()
346
- pending.add(task.id)
347
- this.pendingByParent.set(input.parentSessionID, pending)
348
- }
349
-
350
- // Add to queue
351
- const key = this.getConcurrencyKeyFromInput(input)
352
- const queue = this.queuesByKey.get(key) ?? []
353
- queue.push({ task, input })
354
- this.queuesByKey.set(key, queue)
355
-
356
- log("[background-agent] Task queued:", { taskId: task.id, key, queueLength: queue.length })
357
-
358
- const toastManager = getTaskToastManager()
359
- if (toastManager) {
360
- toastManager.addTask({
361
- id: task.id,
362
- description: input.description,
363
- agent: input.agent,
364
- isBackground: true,
365
- status: "queued",
366
- skills: input.skills,
367
- })
368
- }
369
-
370
- spawnReservation.commit()
371
- this.markPreStartDescendantReservation(task)
372
-
373
- // Trigger processing (fire-and-forget)
374
- void this.processKey(key)
375
-
376
- return { ...task }
377
- } catch (error) {
378
- spawnReservation.rollback()
379
- throw error
380
- }
381
- }
382
-
383
- private async processKey(key: string): Promise<void> {
384
- if (this.processingKeys.has(key)) {
385
- return
386
- }
387
-
388
- this.processingKeys.add(key)
389
-
390
- try {
391
- const queue = this.queuesByKey.get(key)
392
- while (queue && queue.length > 0) {
393
- const item = queue.shift()
394
- if (!item) {
395
- continue
396
- }
397
-
398
- await this.concurrencyManager.acquire(key)
399
-
400
- if (item.task.status === "cancelled" || item.task.status === "error" || item.task.status === "interrupt") {
401
- this.rollbackPreStartDescendantReservation(item.task)
402
- this.concurrencyManager.release(key)
403
- continue
404
- }
405
-
406
- try {
407
- await this.startTask(item)
408
- } catch (error) {
409
- log("[background-agent] Error starting task:", error)
410
- this.rollbackPreStartDescendantReservation(item.task)
411
-
412
- // Mark task as error so the parent polling loop detects the failure
413
- // instead of leaving it in a zombie "running" state with no prompt sent
414
- item.task.status = "error"
415
- item.task.error = error instanceof Error ? error.message : String(error)
416
- item.task.completedAt = new Date()
417
-
418
- if (item.task.concurrencyKey) {
419
- this.concurrencyManager.release(item.task.concurrencyKey)
420
- item.task.concurrencyKey = undefined
421
- } else {
422
- this.concurrencyManager.release(key)
423
- }
424
-
425
- removeTaskToastTracking(item.task.id)
426
-
427
- // Abort the orphaned session if one was created before the error
428
- if (item.task.sessionID) {
429
- await this.abortSessionWithLogging(item.task.sessionID, "startTask error cleanup")
430
- }
431
-
432
- this.markForNotification(item.task)
433
- this.enqueueNotificationForParent(item.task.parentSessionID, () => this.notifyParentSession(item.task)).catch(err => {
434
- log("[background-agent] Failed to notify on startTask error:", err)
435
- })
436
- }
437
- }
438
- } finally {
439
- this.processingKeys.delete(key)
440
- }
441
- }
442
-
443
- private async startTask(item: QueueItem): Promise<void> {
444
- const { task, input } = item
445
-
446
- log("[background-agent] Starting task:", {
447
- taskId: task.id,
448
- agent: input.agent,
449
- model: input.model,
450
- })
451
-
452
- const concurrencyKey = this.getConcurrencyKeyFromInput(input)
453
-
454
- const parentSession = await this.client.session.get({
455
- path: { id: input.parentSessionID },
456
- query: { directory: this.directory },
457
- }).catch((err) => {
458
- log(`[background-agent] Failed to get parent session: ${err}`)
459
- return null
460
- })
461
- const parentDirectory = parentSession?.data?.directory ?? this.directory
462
- log(`[background-agent] Parent dir: ${parentSession?.data?.directory}, using: ${parentDirectory}`)
463
-
464
- const createResult = await this.client.session.create({
465
- body: {
466
- parentID: input.parentSessionID,
467
- title: `${input.description} (@${input.agent} subagent)`,
468
- ...(input.sessionPermission ? { permission: input.sessionPermission } : {}),
469
- } as Record<string, unknown>,
470
- query: {
471
- directory: parentDirectory,
472
- },
473
- })
474
-
475
- if (createResult.error) {
476
- throw new Error(`Failed to create background session: ${createResult.error}`)
477
- }
478
-
479
- if (!createResult.data?.id) {
480
- throw new Error("Failed to create background session: API returned no session ID")
481
- }
482
-
483
- const sessionID = createResult.data.id
484
-
485
- if (task.status === "cancelled") {
486
- await this.abortSessionWithLogging(sessionID, "cancelled pre-start cleanup")
487
- this.concurrencyManager.release(concurrencyKey)
488
- return
489
- }
490
-
491
- this.settlePreStartDescendantReservation(task)
492
- subagentSessions.add(sessionID)
493
-
494
- log("[background-agent] tmux callback check", {
495
- hasCallback: !!this.onSubagentSessionCreated,
496
- tmuxEnabled: this.tmuxEnabled,
497
- isInsideTmux: isInsideTmux(),
498
- sessionID,
499
- parentID: input.parentSessionID,
500
- })
501
-
502
- if (this.onSubagentSessionCreated && this.tmuxEnabled && isInsideTmux()) {
503
- log("[background-agent] Invoking tmux callback NOW", { sessionID })
504
- await this.onSubagentSessionCreated({
505
- sessionID,
506
- parentID: input.parentSessionID,
507
- title: input.description,
508
- }).catch((err) => {
509
- log("[background-agent] Failed to spawn tmux pane:", err)
510
- })
511
- log("[background-agent] tmux callback completed, waiting 200ms")
512
- await new Promise(r => setTimeout(r, 200))
513
- } else {
514
- log("[background-agent] SKIP tmux callback - conditions not met")
515
- }
516
-
517
- if (this.tasks.get(task.id)?.status === "cancelled") {
518
- await this.abortSessionWithLogging(sessionID, "cancelled during tmux setup")
519
- subagentSessions.delete(sessionID)
520
- if (task.rootSessionID) {
521
- this.unregisterRootDescendant(task.rootSessionID)
522
- }
523
- this.concurrencyManager.release(concurrencyKey)
524
- return
525
- }
526
-
527
- task.status = "running"
528
- task.startedAt = new Date()
529
- task.sessionID = sessionID
530
- task.progress = {
531
- toolCalls: 0,
532
- lastUpdate: new Date(),
533
- }
534
- task.concurrencyKey = concurrencyKey
535
- task.concurrencyGroup = concurrencyKey
536
-
537
- this.taskHistory.record(input.parentSessionID, { id: task.id, sessionID, agent: input.agent, description: input.description, status: "running", category: input.category, startedAt: task.startedAt })
538
- this.startPolling()
539
-
540
- log("[background-agent] Launching task:", { taskId: task.id, sessionID, agent: input.agent })
541
-
542
- const toastManager = getTaskToastManager()
543
- if (toastManager) {
544
- toastManager.updateTask(task.id, "running")
545
- }
546
-
547
- log("[background-agent] Calling prompt (fire-and-forget) for launch with:", {
548
- sessionID,
549
- agent: input.agent,
550
- model: input.model,
551
- hasSkillContent: !!input.skillContent,
552
- promptLength: input.prompt.length,
553
- })
554
-
555
- // Fire-and-forget prompt via promptAsync (no response body needed)
556
- // OpenCode prompt payload accepts model provider/model IDs and top-level variant only.
557
- // Temperature/topP and provider-specific options are applied through chat.params.
558
- const launchModel = input.model
559
- ? {
560
- providerID: input.model.providerID,
561
- modelID: input.model.modelID,
562
- }
563
- : undefined
564
- const launchVariant = input.model?.variant
565
-
566
- if (input.model) {
567
- applySessionPromptParams(sessionID, input.model)
568
- }
569
-
570
- const promptBody = {
571
- agent: input.agent,
572
- ...(launchModel ? { model: launchModel } : {}),
573
- ...(launchVariant ? { variant: launchVariant } : {}),
574
- system: input.skillContent,
575
- tools: (() => {
576
- const tools = {
577
- task: false,
578
- call_omo_agent: true,
579
- question: false,
580
- ...getAgentToolRestrictions(input.agent),
581
- }
582
- setSessionTools(sessionID, tools)
583
- return tools
584
- })(),
585
- parts: [createInternalAgentTextPart(input.prompt)],
586
- }
587
-
588
- promptWithModelSuggestionRetry(this.client, {
589
- path: { id: sessionID },
590
- body: promptBody,
591
- }).catch(async (error) => {
592
- // Retry with fallback agent if the original agent was unregistered (e.g., after a model switch)
593
- if (isAgentNotFoundError(error) && input.agent !== FALLBACK_AGENT) {
594
- log("[background-agent] Agent not found, retrying with fallback agent", {
595
- original: input.agent,
596
- fallback: FALLBACK_AGENT,
597
- taskId: task.id,
598
- })
599
- try {
600
- const fallbackBody = buildFallbackBody(promptBody, FALLBACK_AGENT)
601
- setSessionTools(sessionID, fallbackBody.tools as Record<string, boolean>)
602
- await promptWithModelSuggestionRetry(this.client, {
603
- path: { id: sessionID },
604
- body: fallbackBody,
605
- })
606
- task.agent = FALLBACK_AGENT
607
- return
608
- } catch (retryError) {
609
- log("[background-agent] Fallback agent also failed:", retryError)
610
- }
611
- }
612
-
613
- log("[background-agent] promptAsync error:", error)
614
- const existingTask = this.findBySession(sessionID)
615
- if (existingTask) {
616
- existingTask.status = "interrupt"
617
- const errorMessage = error instanceof Error ? error.message : String(error)
618
- if (errorMessage.includes("agent.name") || errorMessage.includes("undefined") || isAgentNotFoundError(error)) {
619
- existingTask.error = `Agent "${input.agent}" not found. Make sure the agent is registered in your opencode.json or provided by a plugin.`
620
- } else {
621
- existingTask.error = errorMessage
622
- }
623
- existingTask.completedAt = new Date()
624
- if (existingTask.rootSessionID) {
625
- this.unregisterRootDescendant(existingTask.rootSessionID)
626
- }
627
- if (existingTask.concurrencyKey) {
628
- this.concurrencyManager.release(existingTask.concurrencyKey)
629
- existingTask.concurrencyKey = undefined
630
- }
631
-
632
- removeTaskToastTracking(existingTask.id)
633
-
634
- // Abort the session to prevent infinite polling hang
635
- // Awaited to prevent dangling promise during subagent teardown (Bun/WebKit SIGABRT)
636
- await this.abortSessionWithLogging(sessionID, "launch error cleanup")
637
-
638
- this.markForNotification(existingTask)
639
- this.enqueueNotificationForParent(existingTask.parentSessionID, () => this.notifyParentSession(existingTask)).catch(err => {
640
- log("[background-agent] Failed to notify on error:", err)
641
- })
642
- }
643
- })
644
- }
645
-
646
- getTask(id: string): BackgroundTask | undefined {
647
- return this.tasks.get(id)
648
- }
649
-
650
- getTasksByParentSession(sessionID: string): BackgroundTask[] {
651
- const result: BackgroundTask[] = []
652
- for (const task of this.tasks.values()) {
653
- if (task.parentSessionID === sessionID) {
654
- result.push(task)
655
- }
656
- }
657
- return result
658
- }
659
-
660
- getAllDescendantTasks(sessionID: string): BackgroundTask[] {
661
- const result: BackgroundTask[] = []
662
- const directChildren = this.getTasksByParentSession(sessionID)
663
-
664
- for (const child of directChildren) {
665
- result.push(child)
666
- if (child.sessionID) {
667
- const descendants = this.getAllDescendantTasks(child.sessionID)
668
- result.push(...descendants)
669
- }
670
- }
671
-
672
- return result
673
- }
674
-
675
- findBySession(sessionID: string): BackgroundTask | undefined {
676
- for (const task of this.tasks.values()) {
677
- if (task.sessionID === sessionID) {
678
- return task
679
- }
680
- }
681
- return undefined
682
- }
683
-
684
- private getConcurrencyKeyFromInput(input: LaunchInput): string {
685
- if (input.model) {
686
- return `${input.model.providerID}/${input.model.modelID}`
687
- }
688
- return input.agent
689
- }
690
-
691
- /**
692
- * Track a task created elsewhere (e.g., from task) for notification tracking.
693
- * This allows tasks created by other tools to receive the same toast/prompt notifications.
694
- */
695
- async trackTask(input: {
696
- taskId: string
697
- sessionID: string
698
- parentSessionID: string
699
- description: string
700
- agent?: string
701
- parentAgent?: string
702
- concurrencyKey?: string
703
- }): Promise<BackgroundTask> {
704
- const existingTask = this.tasks.get(input.taskId)
705
- if (existingTask) {
706
- // P2 fix: Clean up old parent's pending set BEFORE changing parent
707
- // Otherwise cleanupPendingByParent would use the new parent ID
708
- const parentChanged = input.parentSessionID !== existingTask.parentSessionID
709
- if (parentChanged) {
710
- this.cleanupPendingByParent(existingTask) // Clean from OLD parent
711
- existingTask.parentSessionID = input.parentSessionID
712
- }
713
- if (input.parentAgent !== undefined) {
714
- existingTask.parentAgent = input.parentAgent
715
- }
716
- if (!existingTask.concurrencyGroup) {
717
- existingTask.concurrencyGroup = input.concurrencyKey ?? existingTask.agent
718
- }
719
-
720
- if (existingTask.sessionID) {
721
- subagentSessions.add(existingTask.sessionID)
722
- }
723
- this.startPolling()
724
-
725
- // Track for batched notifications if task is pending or running
726
- if (existingTask.status === "pending" || existingTask.status === "running") {
727
- const pending = this.pendingByParent.get(input.parentSessionID) ?? new Set()
728
- pending.add(existingTask.id)
729
- this.pendingByParent.set(input.parentSessionID, pending)
730
- } else if (!parentChanged) {
731
- // Only clean up if parent didn't change (already cleaned above if it did)
732
- this.cleanupPendingByParent(existingTask)
733
- }
734
-
735
- log("[background-agent] External task already registered:", { taskId: existingTask.id, sessionID: existingTask.sessionID, status: existingTask.status })
736
-
737
- return existingTask
738
- }
739
-
740
- const concurrencyGroup = input.concurrencyKey ?? input.agent ?? "task"
741
-
742
- // Acquire concurrency slot if a key is provided
743
- if (input.concurrencyKey) {
744
- await this.concurrencyManager.acquire(input.concurrencyKey)
745
- }
746
-
747
- const task: BackgroundTask = {
748
- id: input.taskId,
749
- sessionID: input.sessionID,
750
- parentSessionID: input.parentSessionID,
751
- parentMessageID: "",
752
- description: input.description,
753
- prompt: "",
754
- agent: input.agent || "task",
755
- status: "running",
756
- startedAt: new Date(),
757
- progress: {
758
- toolCalls: 0,
759
- lastUpdate: new Date(),
760
- },
761
- parentAgent: input.parentAgent,
762
- concurrencyKey: input.concurrencyKey,
763
- concurrencyGroup,
764
- }
765
-
766
- this.tasks.set(task.id, task)
767
- subagentSessions.add(input.sessionID)
768
- this.startPolling()
769
- this.taskHistory.record(input.parentSessionID, { id: task.id, sessionID: input.sessionID, agent: input.agent || "task", description: input.description, status: "running", startedAt: task.startedAt })
770
-
771
- if (input.parentSessionID) {
772
- const pending = this.pendingByParent.get(input.parentSessionID) ?? new Set()
773
- pending.add(task.id)
774
- this.pendingByParent.set(input.parentSessionID, pending)
775
- }
776
-
777
- log("[background-agent] Registered external task:", { taskId: task.id, sessionID: input.sessionID })
778
-
779
- return task
780
- }
781
-
782
- async resume(input: ResumeInput): Promise<BackgroundTask> {
783
- const existingTask = this.findBySession(input.sessionId)
784
- if (!existingTask) {
785
- throw new Error(`Task not found for session: ${input.sessionId}`)
786
- }
787
-
788
- if (!existingTask.sessionID) {
789
- throw new Error(`Task has no sessionID: ${existingTask.id}`)
790
- }
791
-
792
- if (existingTask.status === "running") {
793
- log("[background-agent] Resume skipped - task already running:", {
794
- taskId: existingTask.id,
795
- sessionID: existingTask.sessionID,
796
- })
797
- return existingTask
798
- }
799
-
800
- const completionTimer = this.completionTimers.get(existingTask.id)
801
- if (completionTimer) {
802
- clearTimeout(completionTimer)
803
- this.completionTimers.delete(existingTask.id)
804
- }
805
-
806
- // Re-acquire concurrency using the persisted concurrency group
807
- const concurrencyKey = existingTask.concurrencyGroup ?? existingTask.agent
808
- await this.concurrencyManager.acquire(concurrencyKey)
809
- existingTask.concurrencyKey = concurrencyKey
810
- existingTask.concurrencyGroup = concurrencyKey
811
-
812
-
813
- existingTask.status = "running"
814
- existingTask.completedAt = undefined
815
- existingTask.error = undefined
816
- existingTask.parentSessionID = input.parentSessionID
817
- existingTask.parentMessageID = input.parentMessageID
818
- existingTask.parentModel = input.parentModel
819
- existingTask.parentAgent = input.parentAgent
820
- if (input.parentTools) {
821
- existingTask.parentTools = input.parentTools
822
- }
823
- // Reset startedAt on resume to prevent immediate completion
824
- // The MIN_IDLE_TIME_MS check uses startedAt, so resumed tasks need fresh timing
825
- existingTask.startedAt = new Date()
826
-
827
- existingTask.progress = {
828
- toolCalls: existingTask.progress?.toolCalls ?? 0,
829
- toolCallWindow: existingTask.progress?.toolCallWindow,
830
- countedToolPartIDs: existingTask.progress?.countedToolPartIDs,
831
- lastUpdate: new Date(),
832
- }
833
-
834
- this.startPolling()
835
- if (existingTask.sessionID) {
836
- subagentSessions.add(existingTask.sessionID)
837
- }
838
-
839
- if (input.parentSessionID) {
840
- const pending = this.pendingByParent.get(input.parentSessionID) ?? new Set()
841
- pending.add(existingTask.id)
842
- this.pendingByParent.set(input.parentSessionID, pending)
843
- }
844
-
845
- const toastManager = getTaskToastManager()
846
- if (toastManager) {
847
- toastManager.addTask({
848
- id: existingTask.id,
849
- description: existingTask.description,
850
- agent: existingTask.agent,
851
- isBackground: true,
852
- })
853
- }
854
-
855
- log("[background-agent] Resuming task:", { taskId: existingTask.id, sessionID: existingTask.sessionID })
856
-
857
- log("[background-agent] Resuming task - calling prompt (fire-and-forget) with:", {
858
- sessionID: existingTask.sessionID,
859
- agent: existingTask.agent,
860
- model: existingTask.model,
861
- promptLength: input.prompt.length,
862
- })
863
-
864
- // Fire-and-forget prompt via promptAsync (no response body needed)
865
- // Resume uses the same PromptInput contract as launch: model IDs plus top-level variant.
866
- const resumeModel = existingTask.model
867
- ? {
868
- providerID: existingTask.model.providerID,
869
- modelID: existingTask.model.modelID,
870
- }
871
- : undefined
872
- const resumeVariant = existingTask.model?.variant
873
-
874
- if (existingTask.model) {
875
- applySessionPromptParams(existingTask.sessionID!, existingTask.model)
876
- }
877
-
878
- this.client.session.promptAsync({
879
- path: { id: existingTask.sessionID },
880
- body: {
881
- agent: existingTask.agent,
882
- ...(resumeModel ? { model: resumeModel } : {}),
883
- ...(resumeVariant ? { variant: resumeVariant } : {}),
884
- tools: (() => {
885
- const tools = {
886
- task: false,
887
- call_omo_agent: true,
888
- question: false,
889
- ...getAgentToolRestrictions(existingTask.agent),
890
- }
891
- setSessionTools(existingTask.sessionID!, tools)
892
- return tools
893
- })(),
894
- parts: [createInternalAgentTextPart(input.prompt)],
895
- },
896
- }).catch(async (error) => {
897
- log("[background-agent] resume prompt error:", error)
898
- existingTask.status = "interrupt"
899
- const errorMessage = error instanceof Error ? error.message : String(error)
900
- existingTask.error = errorMessage
901
- existingTask.completedAt = new Date()
902
- if (existingTask.rootSessionID) {
903
- this.unregisterRootDescendant(existingTask.rootSessionID)
904
- }
905
-
906
- // Release concurrency on error to prevent slot leaks
907
- if (existingTask.concurrencyKey) {
908
- this.concurrencyManager.release(existingTask.concurrencyKey)
909
- existingTask.concurrencyKey = undefined
910
- }
911
-
912
- removeTaskToastTracking(existingTask.id)
913
-
914
- // Abort the session to prevent infinite polling hang
915
- // Awaited to prevent dangling promise during subagent teardown (Bun/WebKit SIGABRT)
916
- if (existingTask.sessionID) {
917
- await this.abortSessionWithLogging(existingTask.sessionID, "resume error cleanup")
918
- }
919
-
920
- this.markForNotification(existingTask)
921
- this.enqueueNotificationForParent(existingTask.parentSessionID, () => this.notifyParentSession(existingTask)).catch(err => {
922
- log("[background-agent] Failed to notify on resume error:", err)
923
- })
924
- })
925
-
926
- return existingTask
927
- }
928
-
929
- private async checkSessionTodos(sessionID: string): Promise<boolean> {
930
- const observedIncompleteTodos = this.observedIncompleteTodosBySession.get(sessionID)
931
- if (observedIncompleteTodos !== undefined) {
932
- return observedIncompleteTodos
933
- }
934
-
935
- try {
936
- const response = await this.client.session.todo({
937
- path: { id: sessionID },
938
- })
939
- const todos = normalizeSDKResponse(response, [] as Todo[], { preferResponseOnMissingData: true })
940
- if (!todos || todos.length === 0) {
941
- this.observedIncompleteTodosBySession.set(sessionID, false)
942
- return false
943
- }
944
-
945
- const incomplete = todos.filter(
946
- (t) => t.status !== "completed" && t.status !== "cancelled"
947
- )
948
- const hasIncompleteTodos = incomplete.length > 0
949
- this.observedIncompleteTodosBySession.set(sessionID, hasIncompleteTodos)
950
- return hasIncompleteTodos
951
- } catch (error) {
952
- log("[background-agent] Failed to check session todos:", {
953
- sessionID,
954
- error,
955
- })
956
- return false
957
- }
958
- }
959
-
960
- private markSessionOutputObserved(sessionID: string): void {
961
- this.observedOutputSessions.add(sessionID)
962
- }
963
-
964
- private clearSessionOutputObserved(sessionID: string): void {
965
- this.observedOutputSessions.delete(sessionID)
966
- }
967
-
968
- private clearSessionTodoObservation(sessionID: string): void {
969
- this.observedIncompleteTodosBySession.delete(sessionID)
970
- }
971
-
972
- private hasOutputSignalFromPart(partInfo: MessagePartInfo | undefined): boolean {
973
- if (!partInfo?.sessionID) return false
974
- if (partInfo.tool) return true
975
- if (partInfo.type === "tool" || partInfo.type === "tool_result") return true
976
- if (partInfo.type === "text" || partInfo.type === "reasoning") return true
977
-
978
- const field = typeof (partInfo as { field?: unknown }).field === "string"
979
- ? (partInfo as { field?: string }).field
980
- : undefined
981
- return field === "text" || field === "reasoning"
982
- }
983
-
984
- handleEvent(event: Event): void {
985
- const props = event.properties
986
-
987
- if (event.type === "message.updated") {
988
- const info = props?.info
989
- if (!info || typeof info !== "object") return
990
-
991
- const sessionID = (info as Record<string, unknown>)["sessionID"]
992
- const role = (info as Record<string, unknown>)["role"]
993
- if (typeof sessionID !== "string") return
994
-
995
- if (role === "tool") {
996
- this.markSessionOutputObserved(sessionID)
997
- }
998
-
999
- if (role !== "assistant") return
1000
-
1001
- const task = this.findBySession(sessionID)
1002
- if (!task || task.status !== "running") return
1003
-
1004
- const assistantError = (info as Record<string, unknown>)["error"]
1005
- if (!assistantError) return
1006
-
1007
- const errorInfo = {
1008
- name: extractErrorName(assistantError),
1009
- message: extractErrorMessage(assistantError),
1010
- }
1011
- void this.tryFallbackRetry(task, errorInfo, "message.updated").catch((error) => {
1012
- log("[background-agent] Error handling message.updated fallback retry:", {
1013
- error,
1014
- taskId: task.id,
1015
- })
1016
- })
1017
- }
1018
-
1019
- if (event.type === "message.part.updated" || event.type === "message.part.delta") {
1020
- const partInfo = resolveMessagePartInfo(props)
1021
- const sessionID = partInfo?.sessionID
1022
- if (!sessionID) return
1023
-
1024
- const task = this.findBySession(sessionID)
1025
- if (!task) return
1026
-
1027
- if (this.hasOutputSignalFromPart(partInfo)) {
1028
- this.markSessionOutputObserved(sessionID)
1029
- }
1030
-
1031
- // Clear any pending idle deferral timer since the task is still active
1032
- const existingTimer = this.idleDeferralTimers.get(task.id)
1033
- if (existingTimer) {
1034
- clearTimeout(existingTimer)
1035
- this.idleDeferralTimers.delete(task.id)
1036
- }
1037
-
1038
- if (!task.progress) {
1039
- task.progress = {
1040
- toolCalls: 0,
1041
- lastUpdate: new Date(),
1042
- }
1043
- }
1044
- task.progress.lastUpdate = new Date()
1045
-
1046
- if (partInfo?.type === "tool" || partInfo?.tool) {
1047
- const countedToolPartIDs = task.progress.countedToolPartIDs ?? new Set<string>()
1048
- const shouldCountToolCall =
1049
- !partInfo.id ||
1050
- partInfo.state?.status !== "running" ||
1051
- !countedToolPartIDs.has(partInfo.id)
1052
-
1053
- if (!shouldCountToolCall) {
1054
- return
1055
- }
1056
-
1057
- if (partInfo.id && partInfo.state?.status === "running") {
1058
- countedToolPartIDs.add(partInfo.id)
1059
- task.progress.countedToolPartIDs = countedToolPartIDs
1060
- }
1061
-
1062
- task.progress.toolCalls += 1
1063
- task.progress.lastTool = partInfo.tool
1064
- const circuitBreaker = this.cachedCircuitBreakerSettings ?? resolveCircuitBreakerSettings(this.config)
1065
- this.cachedCircuitBreakerSettings = circuitBreaker
1066
- if (partInfo.tool) {
1067
- task.progress.toolCallWindow = recordToolCall(
1068
- task.progress.toolCallWindow,
1069
- partInfo.tool,
1070
- circuitBreaker,
1071
- partInfo.state?.input
1072
- )
1073
-
1074
- if (circuitBreaker.enabled) {
1075
- const loopDetection = detectRepetitiveToolUse(task.progress.toolCallWindow)
1076
- if (loopDetection.triggered) {
1077
- log("[background-agent] Circuit breaker: consecutive tool usage detected", {
1078
- taskId: task.id,
1079
- agent: task.agent,
1080
- sessionID,
1081
- toolName: loopDetection.toolName,
1082
- repeatedCount: loopDetection.repeatedCount,
1083
- })
1084
- void this.cancelTask(task.id, {
1085
- source: "circuit-breaker",
1086
- reason: `Subagent called ${loopDetection.toolName} ${loopDetection.repeatedCount} consecutive times (threshold: ${circuitBreaker.consecutiveThreshold}). This usually indicates an infinite loop. The task was automatically cancelled to prevent excessive token usage.`,
1087
- })
1088
- return
1089
- }
1090
- }
1091
- }
1092
-
1093
- const maxToolCalls = circuitBreaker.maxToolCalls
1094
- if (task.progress.toolCalls >= maxToolCalls) {
1095
- log("[background-agent] Circuit breaker: tool call limit reached", {
1096
- taskId: task.id,
1097
- toolCalls: task.progress.toolCalls,
1098
- maxToolCalls,
1099
- agent: task.agent,
1100
- sessionID,
1101
- })
1102
- void this.cancelTask(task.id, {
1103
- source: "circuit-breaker",
1104
- reason: `Subagent exceeded maximum tool call limit (${maxToolCalls}). This usually indicates an infinite loop. The task was automatically cancelled to prevent excessive token usage.`,
1105
- })
1106
- }
1107
- }
1108
- }
1109
-
1110
- if (event.type === "todo.updated") {
1111
- const sessionID = typeof props?.sessionID === "string" ? props.sessionID : undefined
1112
- const todos = Array.isArray(props?.todos) ? props.todos : undefined
1113
- if (!sessionID || !todos) return
1114
-
1115
- const hasIncompleteTodos = todos.some((todo) => {
1116
- if (!todo || typeof todo !== "object") return false
1117
- const status = (todo as { status?: unknown }).status
1118
- return status !== "completed" && status !== "cancelled"
1119
- })
1120
- this.observedIncompleteTodosBySession.set(sessionID, hasIncompleteTodos)
1121
- return
1122
- }
1123
-
1124
- if (event.type === "session.idle") {
1125
- if (!props || typeof props !== "object") return
1126
- handleSessionIdleBackgroundEvent({
1127
- properties: props as Record<string, unknown>,
1128
- findBySession: (id) => this.findBySession(id),
1129
- idleDeferralTimers: this.idleDeferralTimers,
1130
- validateSessionHasOutput: (id) => this.validateSessionHasOutput(id),
1131
- checkSessionTodos: (id) => this.checkSessionTodos(id),
1132
- tryCompleteTask: (task, source) => this.tryCompleteTask(task, source),
1133
- emitIdleEvent: (sessionID) => this.handleEvent({ type: "session.idle", properties: { sessionID } }),
1134
- })
1135
- }
1136
-
1137
- if (event.type === "session.error") {
1138
- const sessionID = typeof props?.sessionID === "string" ? props.sessionID : undefined
1139
- if (!sessionID) return
1140
-
1141
- const task = this.findBySession(sessionID)
1142
- if (!task || task.status !== "running") return
1143
-
1144
- const errorObj = props?.error as { name?: string; message?: string } | undefined
1145
- const errorName = errorObj?.name
1146
- const errorMessage = props ? getSessionErrorMessage(props) : undefined
1147
-
1148
- const errorInfo = { name: errorName, message: errorMessage }
1149
- void this.handleSessionErrorEvent({
1150
- errorInfo,
1151
- errorMessage,
1152
- errorName,
1153
- task,
1154
- }).catch((error) => {
1155
- log("[background-agent] Error handling session.error event:", {
1156
- error,
1157
- taskId: task.id,
1158
- })
1159
- })
1160
- return
1161
- }
1162
-
1163
- if (event.type === "session.deleted") {
1164
- const info = props?.info
1165
- if (!info || typeof info.id !== "string") return
1166
- const sessionID = info.id
1167
- this.clearSessionOutputObserved(sessionID)
1168
- this.clearSessionTodoObservation(sessionID)
1169
-
1170
- const tasksToCancel = new Map<string, BackgroundTask>()
1171
- const directTask = this.findBySession(sessionID)
1172
- if (directTask) {
1173
- tasksToCancel.set(directTask.id, directTask)
1174
- }
1175
- for (const descendant of this.getAllDescendantTasks(sessionID)) {
1176
- tasksToCancel.set(descendant.id, descendant)
1177
- }
1178
-
1179
- this.pendingNotifications.delete(sessionID)
1180
-
1181
- if (tasksToCancel.size === 0) {
1182
- this.clearTaskHistoryWhenParentTasksGone(sessionID)
1183
- return
1184
- }
1185
-
1186
- const parentSessionsToClear = new Set<string>()
1187
-
1188
- const deletedSessionIDs = new Set<string>([sessionID])
1189
- for (const task of tasksToCancel.values()) {
1190
- if (task.sessionID) {
1191
- deletedSessionIDs.add(task.sessionID)
1192
- }
1193
- }
1194
-
1195
- for (const task of tasksToCancel.values()) {
1196
- parentSessionsToClear.add(task.parentSessionID)
1197
-
1198
- if (task.status === "running" || task.status === "pending") {
1199
- void this.cancelTask(task.id, {
1200
- source: "session.deleted",
1201
- reason: "Session deleted",
1202
- }).then(() => {
1203
- if (deletedSessionIDs.has(task.parentSessionID)) {
1204
- this.pendingNotifications.delete(task.parentSessionID)
1205
- }
1206
- }).catch(err => {
1207
- if (deletedSessionIDs.has(task.parentSessionID)) {
1208
- this.pendingNotifications.delete(task.parentSessionID)
1209
- }
1210
- log("[background-agent] Failed to cancel task on session.deleted:", { taskId: task.id, error: err })
1211
- })
1212
- }
1213
- }
1214
-
1215
- for (const parentSessionID of parentSessionsToClear) {
1216
- this.clearTaskHistoryWhenParentTasksGone(parentSessionID)
1217
- }
1218
-
1219
- this.rootDescendantCounts.delete(sessionID)
1220
- SessionCategoryRegistry.remove(sessionID)
1221
- }
1222
-
1223
- if (event.type === "session.status") {
1224
- const sessionID = props?.sessionID as string | undefined
1225
- const status = props?.status as { type?: string; message?: string } | undefined
1226
- if (!sessionID || status?.type !== "retry") return
1227
-
1228
- const task = this.findBySession(sessionID)
1229
- if (!task || task.status !== "running") return
1230
-
1231
- const errorMessage = typeof status.message === "string" ? status.message : undefined
1232
- const errorInfo = { name: "SessionRetry", message: errorMessage }
1233
- void this.tryFallbackRetry(task, errorInfo, "session.status").catch((error) => {
1234
- log("[background-agent] Error handling session.status fallback retry:", {
1235
- error,
1236
- taskId: task.id,
1237
- })
1238
- })
1239
- }
1240
- }
1241
-
1242
- private async handleSessionErrorEvent(args: {
1243
- task: BackgroundTask
1244
- errorInfo: { name?: string; message?: string }
1245
- errorName: string | undefined
1246
- errorMessage: string | undefined
1247
- }): Promise<void> {
1248
- const { task, errorInfo, errorMessage, errorName } = args
1249
-
1250
- // Agent-not-found errors are handled by the prompt catch block with agent fallback.
1251
- // Do not also trigger model fallback retry — that would race with the agent retry.
1252
- if (isAgentNotFoundError({ message: errorInfo.message } as Error)) {
1253
- log("[background-agent] Skipping session.error fallback for agent-not-found (handled by prompt catch)", {
1254
- taskId: task.id,
1255
- errorMessage: errorInfo.message?.slice(0, 100),
1256
- })
1257
- return
1258
- }
1259
-
1260
- if (await this.tryFallbackRetry(task, errorInfo, "session.error")) {
1261
- return
1262
- }
1263
-
1264
- const errorMsg = errorMessage ?? "Session error"
1265
- const canRetry =
1266
- shouldRetryError(errorInfo) &&
1267
- !!task.fallbackChain &&
1268
- hasMoreFallbacks(task.fallbackChain, task.attemptCount ?? 0)
1269
- log("[background-agent] Session error - no retry:", {
1270
- taskId: task.id,
1271
- errorName,
1272
- errorMessage: errorMsg?.slice(0, 100),
1273
- hasFallbackChain: !!task.fallbackChain,
1274
- canRetry,
1275
- })
1276
-
1277
- task.status = "error"
1278
- task.error = errorMsg
1279
- task.completedAt = new Date()
1280
- if (task.rootSessionID) {
1281
- this.unregisterRootDescendant(task.rootSessionID)
1282
- }
1283
- this.taskHistory.record(task.parentSessionID, { id: task.id, sessionID: task.sessionID, agent: task.agent, description: task.description, status: "error", category: task.category, startedAt: task.startedAt, completedAt: task.completedAt })
1284
-
1285
- if (task.concurrencyKey) {
1286
- this.concurrencyManager.release(task.concurrencyKey)
1287
- task.concurrencyKey = undefined
1288
- }
1289
-
1290
- const completionTimer = this.completionTimers.get(task.id)
1291
- if (completionTimer) {
1292
- clearTimeout(completionTimer)
1293
- this.completionTimers.delete(task.id)
1294
- }
1295
-
1296
- const idleTimer = this.idleDeferralTimers.get(task.id)
1297
- if (idleTimer) {
1298
- clearTimeout(idleTimer)
1299
- this.idleDeferralTimers.delete(task.id)
1300
- }
1301
-
1302
- this.cleanupPendingByParent(task)
1303
- this.clearNotificationsForTask(task.id)
1304
- const toastManager = getTaskToastManager()
1305
- if (toastManager) {
1306
- toastManager.removeTask(task.id)
1307
- }
1308
- this.scheduleTaskRemoval(task.id)
1309
- if (task.sessionID) {
1310
- SessionCategoryRegistry.remove(task.sessionID)
1311
- }
1312
-
1313
- this.markForNotification(task)
1314
- this.enqueueNotificationForParent(task.parentSessionID, () => this.notifyParentSession(task)).catch(err => {
1315
- log("[background-agent] Error in notifyParentSession for errored task:", { taskId: task.id, error: err })
1316
- })
1317
- }
1318
-
1319
- private tryFallbackRetry(
1320
- task: BackgroundTask,
1321
- errorInfo: { name?: string; message?: string },
1322
- source: string,
1323
- ): Promise<boolean> {
1324
- const previousSessionID = task.sessionID
1325
- const result = tryFallbackRetry({
1326
- task,
1327
- errorInfo,
1328
- source,
1329
- concurrencyManager: this.concurrencyManager,
1330
- client: this.client,
1331
- idleDeferralTimers: this.idleDeferralTimers,
1332
- queuesByKey: this.queuesByKey,
1333
- processKey: (key: string) => this.processKey(key),
1334
- })
1335
- return result.then((retried) => {
1336
- if (retried && previousSessionID) {
1337
- this.clearSessionOutputObserved(previousSessionID)
1338
- this.clearSessionTodoObservation(previousSessionID)
1339
- subagentSessions.delete(previousSessionID)
1340
- }
1341
- return retried
1342
- })
1343
- }
1344
-
1345
- markForNotification(task: BackgroundTask): void {
1346
- const queue = this.notifications.get(task.parentSessionID) ?? []
1347
- queue.push(task)
1348
- this.notifications.set(task.parentSessionID, queue)
1349
- }
1350
-
1351
- getPendingNotifications(sessionID: string): BackgroundTask[] {
1352
- return this.notifications.get(sessionID) ?? []
1353
- }
1354
-
1355
- clearNotifications(sessionID: string): void {
1356
- this.notifications.delete(sessionID)
1357
- }
1358
-
1359
- queuePendingNotification(sessionID: string | undefined, notification: string): void {
1360
- if (!sessionID) return
1361
- const existingNotifications = this.pendingNotifications.get(sessionID) ?? []
1362
- existingNotifications.push(notification)
1363
- this.pendingNotifications.set(sessionID, existingNotifications)
1364
- }
1365
-
1366
- injectPendingNotificationsIntoChatMessage(output: { parts: Array<{ type: string; text?: string; [key: string]: unknown }> }, sessionID: string): void {
1367
- const pendingNotifications = this.pendingNotifications.get(sessionID)
1368
- if (!pendingNotifications || pendingNotifications.length === 0) {
1369
- return
1370
- }
1371
-
1372
- this.pendingNotifications.delete(sessionID)
1373
- const notificationContent = pendingNotifications.join("\n\n")
1374
- const firstTextPartIndex = output.parts.findIndex((part) => part.type === "text")
1375
-
1376
- if (firstTextPartIndex === -1) {
1377
- output.parts.unshift(createInternalAgentTextPart(notificationContent))
1378
- return
1379
- }
1380
-
1381
- const originalText = output.parts[firstTextPartIndex].text ?? ""
1382
- output.parts[firstTextPartIndex].text = `${notificationContent}\n\n---\n\n${originalText}`
1383
- }
1384
-
1385
- /**
1386
- * Validates that a session has actual assistant/tool output before marking complete.
1387
- * Prevents premature completion when session.idle fires before agent responds.
1388
- */
1389
- private async validateSessionHasOutput(sessionID: string): Promise<boolean> {
1390
- if (this.observedOutputSessions.has(sessionID)) {
1391
- return true
1392
- }
1393
-
1394
- try {
1395
- const response = await this.client.session.messages({
1396
- path: { id: sessionID },
1397
- })
1398
-
1399
- const messages = normalizeSDKResponse(response, [] as Array<{ info?: { role?: string } }>, { preferResponseOnMissingData: true })
1400
-
1401
- // Check for at least one assistant or tool message
1402
- const hasAssistantOrToolMessage = messages.some(
1403
- (m: { info?: { role?: string } }) =>
1404
- m.info?.role === "assistant" || m.info?.role === "tool"
1405
- )
1406
-
1407
- if (!hasAssistantOrToolMessage) {
1408
- log("[background-agent] No assistant/tool messages found in session:", sessionID)
1409
- return false
1410
- }
1411
-
1412
- // OpenCode API uses different part types than Anthropic's API:
1413
- // - "reasoning" with .text property (thinking/reasoning content)
1414
- // - "tool" with .state.output property (tool call results)
1415
- // - "text" with .text property (final text output)
1416
- // - "step-start"/"step-finish" (metadata, no content)
1417
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1418
- const hasContent = messages.some((m: any) => {
1419
- if (m.info?.role !== "assistant" && m.info?.role !== "tool") return false
1420
- const parts = m.parts ?? []
1421
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1422
- return parts.some((p: any) =>
1423
- // Text content (final output)
1424
- (p.type === "text" && p.text && p.text.trim().length > 0) ||
1425
- // Reasoning content (thinking blocks)
1426
- (p.type === "reasoning" && p.text && p.text.trim().length > 0) ||
1427
- // Tool calls (indicates work was done)
1428
- p.type === "tool" ||
1429
- // Tool results (output from executed tools) - important for tool-only tasks
1430
- (p.type === "tool_result" && p.content &&
1431
- (typeof p.content === "string" ? p.content.trim().length > 0 : p.content.length > 0))
1432
- )
1433
- })
1434
-
1435
- if (!hasContent) {
1436
- log("[background-agent] Messages exist but no content found in session:", sessionID)
1437
- return false
1438
- }
1439
-
1440
- this.markSessionOutputObserved(sessionID)
1441
- return true
1442
- } catch (error) {
1443
- log("[background-agent] Error validating session output:", error)
1444
- // On error, allow completion to proceed (don't block indefinitely)
1445
- return true
1446
- }
1447
- }
1448
-
1449
- private clearNotificationsForTask(taskId: string): void {
1450
- for (const [sessionID, tasks] of this.notifications.entries()) {
1451
- const filtered = tasks.filter((t) => t.id !== taskId)
1452
- if (filtered.length === 0) {
1453
- this.notifications.delete(sessionID)
1454
- } else {
1455
- this.notifications.set(sessionID, filtered)
1456
- }
1457
- }
1458
- }
1459
-
1460
- /**
1461
- * Remove task from pending tracking for its parent session.
1462
- * Cleans up the parent entry if no pending tasks remain.
1463
- */
1464
- private cleanupPendingByParent(task: BackgroundTask): void {
1465
- if (!task.parentSessionID) return
1466
- const pending = this.pendingByParent.get(task.parentSessionID)
1467
- if (pending) {
1468
- pending.delete(task.id)
1469
- if (pending.size === 0) {
1470
- this.pendingByParent.delete(task.parentSessionID)
1471
- }
1472
- }
1473
- }
1474
-
1475
- private clearTaskHistoryWhenParentTasksGone(parentSessionID: string | undefined): void {
1476
- if (!parentSessionID) return
1477
- if (this.getTasksByParentSession(parentSessionID).length > 0) return
1478
- this.taskHistory.clearSession(parentSessionID)
1479
- this.completedTaskSummaries.delete(parentSessionID)
1480
- }
1481
-
1482
- private scheduleTaskRemoval(taskId: string, rescheduleCount = 0): void {
1483
- const existingTimer = this.completionTimers.get(taskId)
1484
- if (existingTimer) {
1485
- clearTimeout(existingTimer)
1486
- this.completionTimers.delete(taskId)
1487
- }
1488
-
1489
- const timer = setTimeout(() => {
1490
- this.completionTimers.delete(taskId)
1491
- const task = this.tasks.get(taskId)
1492
- if (!task) return
1493
-
1494
- if (task.parentSessionID) {
1495
- const siblings = this.getTasksByParentSession(task.parentSessionID)
1496
- const runningOrPendingSiblings = siblings.filter(
1497
- sibling => sibling.id !== taskId && (sibling.status === "running" || sibling.status === "pending"),
1498
- )
1499
- const completedAtTimestamp = task.completedAt?.getTime()
1500
- const reachedTaskTtl = completedAtTimestamp !== undefined && (Date.now() - completedAtTimestamp) >= TASK_TTL_MS
1501
- if (runningOrPendingSiblings.length > 0 && rescheduleCount < MAX_TASK_REMOVAL_RESCHEDULES && !reachedTaskTtl) {
1502
- this.scheduleTaskRemoval(taskId, rescheduleCount + 1)
1503
- return
1504
- }
1505
- }
1506
-
1507
- this.clearNotificationsForTask(taskId)
1508
- this.tasks.delete(taskId)
1509
- this.clearTaskHistoryWhenParentTasksGone(task.parentSessionID)
1510
- if (task.sessionID) {
1511
- subagentSessions.delete(task.sessionID)
1512
- SessionCategoryRegistry.remove(task.sessionID)
1513
- }
1514
- log("[background-agent] Removed completed task from memory:", taskId)
1515
- }, TASK_CLEANUP_DELAY_MS)
1516
-
1517
- this.completionTimers.set(taskId, timer)
1518
- }
1519
-
1520
- async cancelTask(
1521
- taskId: string,
1522
- options?: { source?: string; reason?: string; abortSession?: boolean; skipNotification?: boolean }
1523
- ): Promise<boolean> {
1524
- const task = this.tasks.get(taskId)
1525
- if (!task || (task.status !== "running" && task.status !== "pending")) {
1526
- return false
1527
- }
1528
-
1529
- const source = options?.source ?? "cancel"
1530
- const abortSession = options?.abortSession !== false
1531
- const reason = options?.reason
1532
-
1533
- if (task.status === "pending") {
1534
- const key = task.model
1535
- ? `${task.model.providerID}/${task.model.modelID}`
1536
- : task.agent
1537
- const queue = this.queuesByKey.get(key)
1538
- if (queue) {
1539
- const index = queue.findIndex(item => item.task.id === taskId)
1540
- if (index !== -1) {
1541
- queue.splice(index, 1)
1542
- if (queue.length === 0) {
1543
- this.queuesByKey.delete(key)
1544
- }
1545
- }
1546
- }
1547
- this.rollbackPreStartDescendantReservation(task)
1548
- log("[background-agent] Cancelled pending task:", { taskId, key })
1549
- }
1550
-
1551
- const wasRunning = task.status === "running"
1552
- task.status = "cancelled"
1553
- task.completedAt = new Date()
1554
- if (wasRunning && task.rootSessionID) {
1555
- this.unregisterRootDescendant(task.rootSessionID)
1556
- }
1557
- if (reason) {
1558
- task.error = reason
1559
- }
1560
- this.taskHistory.record(task.parentSessionID, { id: task.id, sessionID: task.sessionID, agent: task.agent, description: task.description, status: "cancelled", category: task.category, startedAt: task.startedAt, completedAt: task.completedAt })
1561
-
1562
- if (task.concurrencyKey) {
1563
- this.concurrencyManager.release(task.concurrencyKey)
1564
- task.concurrencyKey = undefined
1565
- }
1566
-
1567
- const existingTimer = this.completionTimers.get(task.id)
1568
- if (existingTimer) {
1569
- clearTimeout(existingTimer)
1570
- this.completionTimers.delete(task.id)
1571
- }
1572
-
1573
- const idleTimer = this.idleDeferralTimers.get(task.id)
1574
- if (idleTimer) {
1575
- clearTimeout(idleTimer)
1576
- this.idleDeferralTimers.delete(task.id)
1577
- }
1578
-
1579
- if (abortSession && task.sessionID) {
1580
- // Awaited to prevent dangling promise during subagent teardown (Bun/WebKit SIGABRT)
1581
- await this.abortSessionWithLogging(task.sessionID, `task cancellation (${source})`)
1582
-
1583
- SessionCategoryRegistry.remove(task.sessionID)
1584
- }
1585
-
1586
- removeTaskToastTracking(task.id)
1587
-
1588
- if (options?.skipNotification) {
1589
- this.cleanupPendingByParent(task)
1590
- this.scheduleTaskRemoval(task.id)
1591
- log(`[background-agent] Task cancelled via ${source} (notification skipped):`, task.id)
1592
- return true
1593
- }
1594
-
1595
- this.markForNotification(task)
1596
-
1597
- try {
1598
- await this.enqueueNotificationForParent(task.parentSessionID, () => this.notifyParentSession(task))
1599
- log(`[background-agent] Task cancelled via ${source}:`, task.id)
1600
- } catch (err) {
1601
- log("[background-agent] Error in notifyParentSession for cancelled task:", { taskId: task.id, error: err })
1602
- }
1603
-
1604
- return true
1605
- }
1606
-
1607
- /**
1608
- * Cancels a pending task by removing it from queue and marking as cancelled.
1609
- * Does NOT abort session (no session exists yet) or release concurrency slot (wasn't acquired).
1610
- */
1611
- cancelPendingTask(taskId: string): boolean {
1612
- const task = this.tasks.get(taskId)
1613
- if (!task || task.status !== "pending") {
1614
- return false
1615
- }
1616
-
1617
- void this.cancelTask(taskId, { source: "cancelPendingTask", abortSession: false })
1618
- return true
1619
- }
1620
-
1621
- private startPolling(): void {
1622
- if (this.pollingInterval) return
1623
-
1624
- this.pollingInterval = setInterval(() => {
1625
- this.pollRunningTasks()
1626
- }, POLLING_INTERVAL_MS)
1627
- this.pollingInterval.unref()
1628
- }
1629
-
1630
- private stopPolling(): void {
1631
- if (this.pollingInterval) {
1632
- clearInterval(this.pollingInterval)
1633
- this.pollingInterval = undefined
1634
- }
1635
- }
1636
-
1637
- private registerProcessCleanup(): void {
1638
- registerManagerForCleanup(this)
1639
- }
1640
-
1641
- private unregisterProcessCleanup(): void {
1642
- unregisterManagerForCleanup(this)
1643
- }
1644
-
1645
-
1646
- /**
1647
- * Get all running tasks (for compaction hook)
1648
- */
1649
- getRunningTasks(): BackgroundTask[] {
1650
- return Array.from(this.tasks.values()).filter(t => t.status === "running")
1651
- }
1652
-
1653
- /**
1654
- * Get all non-running tasks still in memory (for compaction hook)
1655
- */
1656
- getNonRunningTasks(): BackgroundTask[] {
1657
- return Array.from(this.tasks.values()).filter(t => t.status !== "running")
1658
- }
1659
-
1660
- /**
1661
- * Safely complete a task with race condition protection.
1662
- * Returns true if task was successfully completed, false if already completed by another path.
1663
- */
1664
- private async tryCompleteTask(task: BackgroundTask, source: string): Promise<boolean> {
1665
- // Guard: Check if task is still running (could have been completed by another path)
1666
- if (task.status !== "running") {
1667
- log("[background-agent] Task already completed, skipping:", { taskId: task.id, status: task.status, source })
1668
- return false
1669
- }
1670
-
1671
- // Atomically mark as completed to prevent race conditions
1672
- task.status = "completed"
1673
- task.completedAt = new Date()
1674
- this.taskHistory.record(task.parentSessionID, { id: task.id, sessionID: task.sessionID, agent: task.agent, description: task.description, status: "completed", category: task.category, startedAt: task.startedAt, completedAt: task.completedAt })
1675
-
1676
- if (task.rootSessionID) {
1677
- this.unregisterRootDescendant(task.rootSessionID)
1678
- }
1679
-
1680
- removeTaskToastTracking(task.id)
1681
-
1682
- // Release concurrency BEFORE any async operations to prevent slot leaks
1683
- if (task.concurrencyKey) {
1684
- this.concurrencyManager.release(task.concurrencyKey)
1685
- task.concurrencyKey = undefined
1686
- }
1687
-
1688
- this.markForNotification(task)
1689
-
1690
- const idleTimer = this.idleDeferralTimers.get(task.id)
1691
- if (idleTimer) {
1692
- clearTimeout(idleTimer)
1693
- this.idleDeferralTimers.delete(task.id)
1694
- }
1695
-
1696
- if (task.sessionID) {
1697
- // Awaited to prevent dangling promise during subagent teardown (Bun/WebKit SIGABRT)
1698
- await this.abortSessionWithLogging(task.sessionID, `task completion (${source})`)
1699
-
1700
- SessionCategoryRegistry.remove(task.sessionID)
1701
- }
1702
-
1703
- try {
1704
- await this.enqueueNotificationForParent(task.parentSessionID, () => this.notifyParentSession(task))
1705
- log(`[background-agent] Task completed via ${source}:`, task.id)
1706
- } catch (err) {
1707
- log("[background-agent] Error in notifyParentSession:", { taskId: task.id, error: err })
1708
- // Concurrency already released, notification failed but task is complete
1709
- }
1710
-
1711
- return true
1712
- }
1713
-
1714
- private async notifyParentSession(task: BackgroundTask): Promise<void> {
1715
- const duration = formatDuration(task.startedAt ?? new Date(), task.completedAt)
1716
-
1717
- log("[background-agent] notifyParentSession called for task:", task.id)
1718
-
1719
- // Show toast notification
1720
- const toastManager = getTaskToastManager()
1721
- if (toastManager) {
1722
- toastManager.showCompletionToast({
1723
- id: task.id,
1724
- description: task.description,
1725
- duration,
1726
- })
1727
- }
1728
-
1729
- if (!this.completedTaskSummaries.has(task.parentSessionID)) {
1730
- this.completedTaskSummaries.set(task.parentSessionID, [])
1731
- }
1732
- this.completedTaskSummaries.get(task.parentSessionID)!.push({
1733
- id: task.id,
1734
- description: task.description,
1735
- status: task.status,
1736
- error: task.error,
1737
- })
1738
-
1739
- // Update pending tracking and check if all tasks complete
1740
- const pendingSet = this.pendingByParent.get(task.parentSessionID)
1741
- let allComplete = false
1742
- let remainingCount = 0
1743
- if (pendingSet) {
1744
- pendingSet.delete(task.id)
1745
- remainingCount = pendingSet.size
1746
- allComplete = remainingCount === 0
1747
- if (allComplete) {
1748
- this.pendingByParent.delete(task.parentSessionID)
1749
- }
1750
- } else {
1751
- remainingCount = Array.from(this.tasks.values())
1752
- .filter(t => t.parentSessionID === task.parentSessionID && t.id !== task.id && (t.status === "running" || t.status === "pending"))
1753
- .length
1754
- allComplete = remainingCount === 0
1755
- }
1756
-
1757
- const completedTasks = allComplete
1758
- ? (this.completedTaskSummaries.get(task.parentSessionID) ?? [{ id: task.id, description: task.description, status: task.status, error: task.error }])
1759
- : []
1760
-
1761
- if (allComplete) {
1762
- this.completedTaskSummaries.delete(task.parentSessionID)
1763
- }
1764
-
1765
- const statusText = task.status === "completed"
1766
- ? "COMPLETED"
1767
- : task.status === "interrupt"
1768
- ? "INTERRUPTED"
1769
- : task.status === "error"
1770
- ? "ERROR"
1771
- : "CANCELLED"
1772
- const notification = buildBackgroundTaskNotificationText({
1773
- task,
1774
- duration,
1775
- statusText,
1776
- allComplete,
1777
- remainingCount,
1778
- completedTasks,
1779
- })
1780
-
1781
- let agent: string | undefined = task.parentAgent
1782
- let model: { providerID: string; modelID: string } | undefined
1783
- let tools: Record<string, boolean> | undefined = task.parentTools
1784
- let promptContext: ReturnType<typeof resolvePromptContextFromSessionMessages> = null
1785
-
1786
- if (this.enableParentSessionNotifications) {
1787
- try {
1788
- const messagesResp = await this.client.session.messages({ path: { id: task.parentSessionID } })
1789
- const messages = normalizeSDKResponse(messagesResp, [] as Array<{
1790
- info?: {
1791
- agent?: string
1792
- model?: { providerID: string; modelID: string }
1793
- modelID?: string
1794
- providerID?: string
1795
- tools?: Record<string, boolean | "allow" | "deny" | "ask">
1796
- }
1797
- }>)
1798
- promptContext = resolvePromptContextFromSessionMessages(
1799
- messages,
1800
- task.parentSessionID,
1801
- )
1802
- const normalizedTools = isRecord(promptContext?.tools)
1803
- ? normalizePromptTools(promptContext.tools)
1804
- : undefined
1805
-
1806
- if (promptContext?.agent || promptContext?.model || normalizedTools) {
1807
- agent = promptContext?.agent ?? task.parentAgent
1808
- model = promptContext?.model?.providerID && promptContext.model.modelID
1809
- ? { providerID: promptContext.model.providerID, modelID: promptContext.model.modelID }
1810
- : undefined
1811
- tools = normalizedTools ?? tools
1812
- }
1813
- } catch (error) {
1814
- if (isAbortedSessionError(error)) {
1815
- log("[background-agent] Parent session aborted while loading messages; using messageDir fallback:", {
1816
- taskId: task.id,
1817
- parentSessionID: task.parentSessionID,
1818
- })
1819
- }
1820
- const messageDir = join(MESSAGE_STORAGE, task.parentSessionID)
1821
- const currentMessage = messageDir
1822
- ? findNearestMessageExcludingCompaction(messageDir, task.parentSessionID)
1823
- : null
1824
- agent = currentMessage?.agent ?? task.parentAgent
1825
- model = currentMessage?.model?.providerID && currentMessage?.model?.modelID
1826
- ? { providerID: currentMessage.model.providerID, modelID: currentMessage.model.modelID }
1827
- : undefined
1828
- tools = normalizePromptTools(currentMessage?.tools) ?? tools
1829
- }
1830
-
1831
- const resolvedTools = resolveInheritedPromptTools(task.parentSessionID, tools)
1832
-
1833
- log("[background-agent] notifyParentSession context:", {
1834
- taskId: task.id,
1835
- resolvedAgent: agent,
1836
- resolvedModel: model,
1837
- })
1838
-
1839
- const isTaskFailure = task.status === "error" || task.status === "cancelled" || task.status === "interrupt"
1840
- const shouldReply = allComplete || isTaskFailure
1841
-
1842
- const variant = promptContext?.model?.variant
1843
-
1844
- try {
1845
- await this.client.session.promptAsync({
1846
- path: { id: task.parentSessionID },
1847
- body: {
1848
- noReply: !shouldReply,
1849
- ...(agent !== undefined ? { agent } : {}),
1850
- ...(model !== undefined ? { model } : {}),
1851
- ...(variant !== undefined ? { variant } : {}),
1852
- ...(resolvedTools ? { tools: resolvedTools } : {}),
1853
- parts: [createInternalAgentTextPart(notification)],
1854
- },
1855
- })
1856
- log("[background-agent] Sent notification to parent session:", {
1857
- taskId: task.id,
1858
- allComplete,
1859
- isTaskFailure,
1860
- noReply: !shouldReply,
1861
- })
1862
- } catch (error) {
1863
- if (isAbortedSessionError(error)) {
1864
- log("[background-agent] Parent session aborted while sending notification; continuing cleanup:", {
1865
- taskId: task.id,
1866
- parentSessionID: task.parentSessionID,
1867
- })
1868
- this.queuePendingNotification(task.parentSessionID, notification)
1869
- } else {
1870
- log("[background-agent] Failed to send notification:", error)
1871
- }
1872
- }
1873
- } else {
1874
- log("[background-agent] Parent session notifications disabled, skipping prompt injection:", {
1875
- taskId: task.id,
1876
- parentSessionID: task.parentSessionID,
1877
- })
1878
- }
1879
-
1880
- if (task.status !== "running" && task.status !== "pending") {
1881
- this.scheduleTaskRemoval(task.id)
1882
- }
1883
- }
1884
-
1885
- private hasRunningTasks(): boolean {
1886
- for (const task of this.tasks.values()) {
1887
- if (task.status === "running") return true
1888
- }
1889
- return false
1890
- }
1891
-
1892
- private pruneStaleTasksAndNotifications(): void {
1893
- pruneStaleTasksAndNotifications({
1894
- tasks: this.tasks,
1895
- notifications: this.notifications,
1896
- taskTtlMs: this.config?.taskTtlMs,
1897
- onTaskPruned: (taskId, task, errorMessage) => {
1898
- const wasPending = task.status === "pending"
1899
- log("[background-agent] Pruning stale task:", { taskId, status: task.status, age: Math.round(((wasPending ? task.queuedAt?.getTime() : task.startedAt?.getTime()) ? (Date.now() - (wasPending ? task.queuedAt!.getTime() : task.startedAt!.getTime())) : 0) / 1000) + "s" })
1900
- task.status = "error"
1901
- task.error = errorMessage
1902
- task.completedAt = new Date()
1903
- if (!wasPending && task.rootSessionID) {
1904
- this.unregisterRootDescendant(task.rootSessionID)
1905
- }
1906
- this.taskHistory.record(task.parentSessionID, { id: task.id, sessionID: task.sessionID, agent: task.agent, description: task.description, status: "error", category: task.category, startedAt: task.startedAt, completedAt: task.completedAt })
1907
- if (task.concurrencyKey) {
1908
- this.concurrencyManager.release(task.concurrencyKey)
1909
- task.concurrencyKey = undefined
1910
- }
1911
- removeTaskToastTracking(task.id)
1912
- const existingTimer = this.completionTimers.get(taskId)
1913
- if (existingTimer) {
1914
- clearTimeout(existingTimer)
1915
- this.completionTimers.delete(taskId)
1916
- }
1917
- const idleTimer = this.idleDeferralTimers.get(taskId)
1918
- if (idleTimer) {
1919
- clearTimeout(idleTimer)
1920
- this.idleDeferralTimers.delete(taskId)
1921
- }
1922
- if (wasPending) {
1923
- const key = task.model
1924
- ? `${task.model.providerID}/${task.model.modelID}`
1925
- : task.agent
1926
- const queue = this.queuesByKey.get(key)
1927
- if (queue) {
1928
- const index = queue.findIndex((item) => item.task.id === taskId)
1929
- if (index !== -1) {
1930
- queue.splice(index, 1)
1931
- if (queue.length === 0) {
1932
- this.queuesByKey.delete(key)
1933
- }
1934
- }
1935
- }
1936
- }
1937
- this.cleanupPendingByParent(task)
1938
- this.markForNotification(task)
1939
- this.enqueueNotificationForParent(task.parentSessionID, () => this.notifyParentSession(task)).catch(err => {
1940
- log("[background-agent] Error in notifyParentSession for stale-pruned task:", { taskId: task.id, error: err })
1941
- })
1942
- },
1943
- })
1944
- }
1945
-
1946
- private async checkAndInterruptStaleTasks(
1947
- allStatuses: Record<string, { type: string }> = {},
1948
- ): Promise<void> {
1949
- await checkAndInterruptStaleTasks({
1950
- tasks: this.tasks.values(),
1951
- client: this.client,
1952
- directory: this.directory,
1953
- config: this.config,
1954
- concurrencyManager: this.concurrencyManager,
1955
- notifyParentSession: (task) => this.enqueueNotificationForParent(task.parentSessionID, () => this.notifyParentSession(task)),
1956
- sessionStatuses: allStatuses,
1957
- })
1958
- }
1959
-
1960
- private async verifySessionExists(sessionID: string): Promise<boolean> {
1961
- return verifySessionStillExists(this.client, sessionID, this.directory)
1962
- }
1963
-
1964
- private async failCrashedTask(task: BackgroundTask, errorMessage: string): Promise<void> {
1965
- task.status = "error"
1966
- task.error = errorMessage
1967
- task.completedAt = new Date()
1968
- if (task.rootSessionID) {
1969
- this.unregisterRootDescendant(task.rootSessionID)
1970
- }
1971
- this.taskHistory.record(task.parentSessionID, { id: task.id, sessionID: task.sessionID, agent: task.agent, description: task.description, status: "error", category: task.category, startedAt: task.startedAt, completedAt: task.completedAt })
1972
- if (task.concurrencyKey) {
1973
- this.concurrencyManager.release(task.concurrencyKey)
1974
- task.concurrencyKey = undefined
1975
- }
1976
-
1977
- const completionTimer = this.completionTimers.get(task.id)
1978
- if (completionTimer) {
1979
- clearTimeout(completionTimer)
1980
- this.completionTimers.delete(task.id)
1981
- }
1982
- const idleTimer = this.idleDeferralTimers.get(task.id)
1983
- if (idleTimer) {
1984
- clearTimeout(idleTimer)
1985
- this.idleDeferralTimers.delete(task.id)
1986
- }
1987
-
1988
- this.cleanupPendingByParent(task)
1989
- this.clearNotificationsForTask(task.id)
1990
- removeTaskToastTracking(task.id)
1991
- this.scheduleTaskRemoval(task.id)
1992
- if (task.sessionID) {
1993
- SessionCategoryRegistry.remove(task.sessionID)
1994
- }
1995
-
1996
- this.markForNotification(task)
1997
- this.enqueueNotificationForParent(task.parentSessionID, () => this.notifyParentSession(task)).catch(err => {
1998
- log("[background-agent] Error in notifyParentSession for crashed task:", { taskId: task.id, error: err })
1999
- })
2000
- }
2001
-
2002
- private async pollRunningTasks(): Promise<void> {
2003
- if (this.pollingInFlight) return
2004
- this.pollingInFlight = true
2005
- try {
2006
- this.pruneStaleTasksAndNotifications()
2007
-
2008
- const statusResult = await this.client.session.status()
2009
- const allStatuses = normalizeSDKResponse(statusResult, {} as Record<string, { type: string }>)
2010
-
2011
- await this.checkAndInterruptStaleTasks(allStatuses)
2012
-
2013
- for (const task of this.tasks.values()) {
2014
- if (task.status !== "running") continue
2015
-
2016
- const sessionID = task.sessionID
2017
- if (!sessionID) continue
2018
-
2019
- try {
2020
- const sessionStatus = allStatuses[sessionID]
2021
- // Handle retry before checking running state
2022
- if (sessionStatus?.type === "retry") {
2023
- const retryMessage = typeof (sessionStatus as { message?: string }).message === "string"
2024
- ? (sessionStatus as { message?: string }).message
2025
- : undefined
2026
- const errorInfo = { name: "SessionRetry", message: retryMessage }
2027
- if (await this.tryFallbackRetry(task, errorInfo, "polling:session.status")) {
2028
- continue
2029
- }
2030
- }
2031
-
2032
- // Only skip completion when session status is actively running.
2033
- // Unknown or terminal statuses (like "interrupted") fall through to completion.
2034
- if (sessionStatus && isActiveSessionStatus(sessionStatus.type)) {
2035
- log("[background-agent] Session still running, relying on event-based progress:", {
2036
- taskId: task.id,
2037
- sessionID,
2038
- sessionStatus: sessionStatus.type,
2039
- toolCalls: task.progress?.toolCalls ?? 0,
2040
- })
2041
- continue
2042
- }
2043
-
2044
- if (sessionStatus && isTerminalSessionStatus(sessionStatus.type)) {
2045
- await this.tryCompleteTask(task, `polling (terminal session status: ${sessionStatus.type})`)
2046
- continue
2047
- }
2048
-
2049
- if (sessionStatus && sessionStatus.type !== "idle") {
2050
- log("[background-agent] Unknown session status, treating as potentially idle:", {
2051
- taskId: task.id,
2052
- sessionID,
2053
- sessionStatus: sessionStatus.type,
2054
- })
2055
- }
2056
-
2057
- // Session is idle or no longer in status response (completed/disappeared)
2058
- const sessionGoneFromStatus = !sessionStatus
2059
- const sessionGoneThresholdReached = sessionGoneFromStatus
2060
- && (task.consecutiveMissedPolls ?? 0) >= MIN_SESSION_GONE_POLLS
2061
- const completionSource = sessionStatus?.type === "idle"
2062
- ? "polling (idle status)"
2063
- : "polling (session gone from status)"
2064
- const hasValidOutput = await this.validateSessionHasOutput(sessionID)
2065
- if (!hasValidOutput) {
2066
- if (sessionGoneThresholdReached) {
2067
- const sessionExists = await this.verifySessionExists(sessionID)
2068
- if (!sessionExists) {
2069
- log("[background-agent] Session no longer exists (crashed), marking task as error:", task.id)
2070
- await this.failCrashedTask(task, "Subagent session no longer exists (process likely crashed). The session disappeared without producing any output.")
2071
- continue
2072
- }
2073
-
2074
- task.consecutiveMissedPolls = 0
2075
- }
2076
- log("[background-agent] Polling idle/gone but no valid output yet, waiting:", task.id)
2077
- continue
2078
- }
2079
-
2080
- // Re-check status after async operation
2081
- if (task.status !== "running") continue
2082
-
2083
- const hasIncompleteTodos = await this.checkSessionTodos(sessionID)
2084
- if (hasIncompleteTodos) {
2085
- log("[background-agent] Task has incomplete todos via polling, waiting:", task.id)
2086
- continue
2087
- }
2088
-
2089
- await this.tryCompleteTask(task, completionSource)
2090
- } catch (error) {
2091
- log("[background-agent] Poll error for task:", { taskId: task.id, error })
2092
- }
2093
- }
2094
-
2095
- if (!this.hasRunningTasks()) {
2096
- this.stopPolling()
2097
- }
2098
- } finally {
2099
- this.pollingInFlight = false
2100
- }
2101
- }
2102
-
2103
- /**
2104
- * Shutdown the manager gracefully.
2105
- * Cancels all pending concurrency waiters and clears timers.
2106
- * Should be called when the plugin is unloaded.
2107
- */
2108
- async shutdown(): Promise<void> {
2109
- if (this.shutdownTriggered) return
2110
- this.shutdownTriggered = true
2111
- log("[background-agent] Shutting down BackgroundManager")
2112
- this.stopPolling()
2113
- const trackedSessionIDs = new Set<string>()
2114
- const abortRequests: Array<{ sessionID: string; promise: Promise<unknown> }> = []
2115
-
2116
- // Abort all running sessions to prevent zombie processes (#1240)
2117
- for (const task of this.tasks.values()) {
2118
- if (task.sessionID) {
2119
- trackedSessionIDs.add(task.sessionID)
2120
- }
2121
-
2122
- if (task.status === "running" && task.sessionID) {
2123
- abortRequests.push({
2124
- sessionID: task.sessionID,
2125
- promise: abortWithTimeout(this.client, task.sessionID),
2126
- })
2127
- }
2128
- }
2129
-
2130
- if (abortRequests.length > 0) {
2131
- const abortResults = await Promise.allSettled(abortRequests.map((request) => request.promise))
2132
- for (const [index, abortResult] of abortResults.entries()) {
2133
- if (abortResult.status === "fulfilled") continue
2134
-
2135
- log("[background-agent] Error aborting session during shutdown:", {
2136
- error: abortResult.reason,
2137
- sessionID: abortRequests[index]?.sessionID,
2138
- })
2139
- }
2140
- }
2141
-
2142
- // Notify shutdown listeners (e.g., tmux cleanup)
2143
- if (this.onShutdown) {
2144
- try {
2145
- await this.onShutdown()
2146
- } catch (error) {
2147
- log("[background-agent] Error in onShutdown callback:", error)
2148
- }
2149
- }
2150
-
2151
- // Release concurrency for all running tasks
2152
- for (const task of this.tasks.values()) {
2153
- if (task.concurrencyKey) {
2154
- this.concurrencyManager.release(task.concurrencyKey)
2155
- task.concurrencyKey = undefined
2156
- }
2157
- }
2158
-
2159
- for (const timer of this.completionTimers.values()) {
2160
- clearTimeout(timer)
2161
- }
2162
- this.completionTimers.clear()
2163
-
2164
- for (const timer of this.idleDeferralTimers.values()) {
2165
- clearTimeout(timer)
2166
- }
2167
- this.idleDeferralTimers.clear()
2168
-
2169
- for (const sessionID of trackedSessionIDs) {
2170
- subagentSessions.delete(sessionID)
2171
- SessionCategoryRegistry.remove(sessionID)
2172
- }
2173
-
2174
- this.concurrencyManager.clear()
2175
- this.tasks.clear()
2176
- this.notifications.clear()
2177
- this.pendingNotifications.clear()
2178
- this.pendingByParent.clear()
2179
- this.notificationQueueByParent.clear()
2180
- this.rootDescendantCounts.clear()
2181
- this.queuesByKey.clear()
2182
- this.processingKeys.clear()
2183
- this.taskHistory.clearAll()
2184
- this.completedTaskSummaries.clear()
2185
- this.unregisterProcessCleanup()
2186
- log("[background-agent] Shutdown complete")
2187
-
2188
- }
2189
-
2190
- private enqueueNotificationForParent(
2191
- parentSessionID: string | undefined,
2192
- operation: () => Promise<void>
2193
- ): Promise<void> {
2194
- if (!parentSessionID) {
2195
- return operation()
2196
- }
2197
-
2198
- const previous = this.notificationQueueByParent.get(parentSessionID) ?? Promise.resolve()
2199
- const cleanupQueueEntry = (): void => {
2200
- if (this.notificationQueueByParent.get(parentSessionID) === current) {
2201
- this.notificationQueueByParent.delete(parentSessionID)
2202
- }
2203
- }
2204
-
2205
- const current = previous
2206
- .catch((error) => {
2207
- log("[background-agent] Continuing notification queue after previous failure:", {
2208
- parentSessionID,
2209
- error,
2210
- })
2211
- })
2212
- .then(operation)
2213
-
2214
- this.notificationQueueByParent.set(parentSessionID, current)
2215
-
2216
- void current.then(cleanupQueueEntry, cleanupQueueEntry)
2217
-
2218
- return current
2219
- }
2220
- }