@namzu/sdk 0.1.4 → 0.1.5-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (324) hide show
  1. package/dist/advisory/executor.d.ts +2 -2
  2. package/dist/advisory/executor.d.ts.map +1 -1
  3. package/dist/advisory/executor.js.map +1 -1
  4. package/dist/agents/AbstractAgent.d.ts +20 -2
  5. package/dist/agents/AbstractAgent.d.ts.map +1 -1
  6. package/dist/agents/AbstractAgent.js +23 -1
  7. package/dist/agents/AbstractAgent.js.map +1 -1
  8. package/dist/agents/PipelineAgent.d.ts.map +1 -1
  9. package/dist/agents/PipelineAgent.js +1 -1
  10. package/dist/agents/PipelineAgent.js.map +1 -1
  11. package/dist/agents/ReactiveAgent.d.ts.map +1 -1
  12. package/dist/agents/ReactiveAgent.js +1 -0
  13. package/dist/agents/ReactiveAgent.js.map +1 -1
  14. package/dist/agents/RouterAgent.d.ts.map +1 -1
  15. package/dist/agents/RouterAgent.js +4 -2
  16. package/dist/agents/RouterAgent.js.map +1 -1
  17. package/dist/agents/SupervisorAgent.d.ts.map +1 -1
  18. package/dist/agents/SupervisorAgent.js +4 -1
  19. package/dist/agents/SupervisorAgent.js.map +1 -1
  20. package/dist/agents/__tests__/lock.test.d.ts +2 -0
  21. package/dist/agents/__tests__/lock.test.d.ts.map +1 -0
  22. package/dist/agents/__tests__/lock.test.js +131 -0
  23. package/dist/agents/__tests__/lock.test.js.map +1 -0
  24. package/dist/agents/index.d.ts +2 -0
  25. package/dist/agents/index.d.ts.map +1 -1
  26. package/dist/agents/index.js +1 -0
  27. package/dist/agents/index.js.map +1 -1
  28. package/dist/agents/lock.d.ts +42 -0
  29. package/dist/agents/lock.d.ts.map +1 -0
  30. package/dist/agents/lock.js +54 -0
  31. package/dist/agents/lock.js.map +1 -0
  32. package/dist/bridge/a2a/message.d.ts.map +1 -1
  33. package/dist/bridge/a2a/message.js.map +1 -1
  34. package/dist/bridge/tools/connector/router.d.ts +4 -5
  35. package/dist/bridge/tools/connector/router.d.ts.map +1 -1
  36. package/dist/bridge/tools/connector/router.js.map +1 -1
  37. package/dist/compaction/__tests__/SlidingWindowManager.test.d.ts +2 -0
  38. package/dist/compaction/__tests__/SlidingWindowManager.test.d.ts.map +1 -0
  39. package/dist/compaction/__tests__/SlidingWindowManager.test.js +113 -0
  40. package/dist/compaction/__tests__/SlidingWindowManager.test.js.map +1 -0
  41. package/dist/compaction/__tests__/dangling.test.d.ts +2 -0
  42. package/dist/compaction/__tests__/dangling.test.d.ts.map +1 -0
  43. package/dist/compaction/__tests__/dangling.test.js +356 -0
  44. package/dist/compaction/__tests__/dangling.test.js.map +1 -0
  45. package/dist/compaction/__tests__/factory.test.d.ts +2 -0
  46. package/dist/compaction/__tests__/factory.test.d.ts.map +1 -0
  47. package/dist/compaction/__tests__/factory.test.js +43 -0
  48. package/dist/compaction/__tests__/factory.test.js.map +1 -0
  49. package/dist/compaction/dangling.d.ts +96 -0
  50. package/dist/compaction/dangling.d.ts.map +1 -0
  51. package/dist/compaction/dangling.js +274 -0
  52. package/dist/compaction/dangling.js.map +1 -0
  53. package/dist/compaction/factory.d.ts +20 -0
  54. package/dist/compaction/factory.d.ts.map +1 -0
  55. package/dist/compaction/factory.js +35 -0
  56. package/dist/compaction/factory.js.map +1 -0
  57. package/dist/compaction/index.d.ts +5 -0
  58. package/dist/compaction/index.d.ts.map +1 -1
  59. package/dist/compaction/index.js +3 -0
  60. package/dist/compaction/index.js.map +1 -1
  61. package/dist/compaction/interface.d.ts +33 -0
  62. package/dist/compaction/interface.d.ts.map +1 -0
  63. package/dist/compaction/interface.js +2 -0
  64. package/dist/compaction/interface.js.map +1 -0
  65. package/dist/compaction/managers/index.d.ts +4 -0
  66. package/dist/compaction/managers/index.d.ts.map +1 -0
  67. package/dist/compaction/managers/index.js +4 -0
  68. package/dist/compaction/managers/index.js.map +1 -0
  69. package/dist/compaction/managers/null.d.ts +12 -0
  70. package/dist/compaction/managers/null.d.ts.map +1 -0
  71. package/dist/compaction/managers/null.js +15 -0
  72. package/dist/compaction/managers/null.js.map +1 -0
  73. package/dist/compaction/managers/slidingWindow.d.ts +27 -0
  74. package/dist/compaction/managers/slidingWindow.d.ts.map +1 -0
  75. package/dist/compaction/managers/slidingWindow.js +41 -0
  76. package/dist/compaction/managers/slidingWindow.js.map +1 -0
  77. package/dist/compaction/managers/structured.d.ts +23 -0
  78. package/dist/compaction/managers/structured.d.ts.map +1 -0
  79. package/dist/compaction/managers/structured.js +144 -0
  80. package/dist/compaction/managers/structured.js.map +1 -0
  81. package/dist/compaction/types.d.ts +1 -1
  82. package/dist/compaction/types.d.ts.map +1 -1
  83. package/dist/config/runtime.d.ts +16 -16
  84. package/dist/config/runtime.js +1 -1
  85. package/dist/config/runtime.js.map +1 -1
  86. package/dist/constants/agent/index.d.ts +1 -1
  87. package/dist/constants/agent/index.d.ts.map +1 -1
  88. package/dist/gateway/local.d.ts +2 -2
  89. package/dist/gateway/local.d.ts.map +1 -1
  90. package/dist/gateway/local.js +10 -1
  91. package/dist/gateway/local.js.map +1 -1
  92. package/dist/index.d.ts +18 -4
  93. package/dist/index.d.ts.map +1 -1
  94. package/dist/index.js +12 -2
  95. package/dist/index.js.map +1 -1
  96. package/dist/manager/agent/lifecycle.d.ts.map +1 -1
  97. package/dist/manager/agent/lifecycle.js +3 -2
  98. package/dist/manager/agent/lifecycle.js.map +1 -1
  99. package/dist/manager/run/persistence.d.ts +1 -2
  100. package/dist/manager/run/persistence.d.ts.map +1 -1
  101. package/dist/manager/run/persistence.js +2 -1
  102. package/dist/manager/run/persistence.js.map +1 -1
  103. package/dist/plugin/__tests__/lifecycle.test.d.ts +2 -0
  104. package/dist/plugin/__tests__/lifecycle.test.d.ts.map +1 -0
  105. package/dist/plugin/__tests__/lifecycle.test.js +332 -0
  106. package/dist/plugin/__tests__/lifecycle.test.js.map +1 -0
  107. package/dist/plugin/lifecycle.d.ts +2 -2
  108. package/dist/plugin/lifecycle.d.ts.map +1 -1
  109. package/dist/plugin/lifecycle.js +28 -2
  110. package/dist/plugin/lifecycle.js.map +1 -1
  111. package/dist/plugin/resolver.d.ts +2 -2
  112. package/dist/plugin/resolver.d.ts.map +1 -1
  113. package/dist/plugin/resolver.js.map +1 -1
  114. package/dist/registry/agent/definitions.d.ts +3 -2
  115. package/dist/registry/agent/definitions.d.ts.map +1 -1
  116. package/dist/registry/agent/definitions.js.map +1 -1
  117. package/dist/registry/tool/execute.d.ts +2 -5
  118. package/dist/registry/tool/execute.d.ts.map +1 -1
  119. package/dist/registry/tool/execute.js.map +1 -1
  120. package/dist/runtime/decision/parser.d.ts.map +1 -1
  121. package/dist/runtime/decision/parser.js +15 -40
  122. package/dist/runtime/decision/parser.js.map +1 -1
  123. package/dist/runtime/query/context-cache.d.ts +3 -3
  124. package/dist/runtime/query/context-cache.d.ts.map +1 -1
  125. package/dist/runtime/query/context-cache.js.map +1 -1
  126. package/dist/runtime/query/context.d.ts +1 -1
  127. package/dist/runtime/query/context.d.ts.map +1 -1
  128. package/dist/runtime/query/context.js.map +1 -1
  129. package/dist/runtime/query/events.js +11 -11
  130. package/dist/runtime/query/events.js.map +1 -1
  131. package/dist/runtime/query/executor.d.ts +4 -2
  132. package/dist/runtime/query/executor.d.ts.map +1 -1
  133. package/dist/runtime/query/executor.js +1 -0
  134. package/dist/runtime/query/executor.js.map +1 -1
  135. package/dist/runtime/query/index.d.ts +5 -3
  136. package/dist/runtime/query/index.d.ts.map +1 -1
  137. package/dist/runtime/query/index.js +2 -1
  138. package/dist/runtime/query/index.js.map +1 -1
  139. package/dist/runtime/query/iteration/index.d.ts +2 -2
  140. package/dist/runtime/query/iteration/index.d.ts.map +1 -1
  141. package/dist/runtime/query/iteration/index.js.map +1 -1
  142. package/dist/runtime/query/iteration/phases/advisory.d.ts.map +1 -1
  143. package/dist/runtime/query/iteration/phases/advisory.js.map +1 -1
  144. package/dist/runtime/query/iteration/phases/checkpoint.d.ts +1 -1
  145. package/dist/runtime/query/iteration/phases/checkpoint.d.ts.map +1 -1
  146. package/dist/runtime/query/iteration/phases/checkpoint.js.map +1 -1
  147. package/dist/runtime/query/iteration/phases/context.d.ts +2 -2
  148. package/dist/runtime/query/iteration/phases/context.d.ts.map +1 -1
  149. package/dist/runtime/query/iteration/phases/plan.d.ts +1 -1
  150. package/dist/runtime/query/iteration/phases/plan.d.ts.map +1 -1
  151. package/dist/runtime/query/iteration/phases/plan.js.map +1 -1
  152. package/dist/runtime/query/prompt.d.ts +2 -2
  153. package/dist/runtime/query/prompt.d.ts.map +1 -1
  154. package/dist/runtime/query/prompt.js.map +1 -1
  155. package/dist/runtime/query/result.d.ts +1 -1
  156. package/dist/runtime/query/result.d.ts.map +1 -1
  157. package/dist/runtime/query/result.js.map +1 -1
  158. package/dist/runtime/query/tooling.d.ts +4 -2
  159. package/dist/runtime/query/tooling.d.ts.map +1 -1
  160. package/dist/runtime/query/tooling.js +1 -0
  161. package/dist/runtime/query/tooling.js.map +1 -1
  162. package/dist/store/conversation/memory.d.ts +1 -1
  163. package/dist/store/conversation/memory.d.ts.map +1 -1
  164. package/dist/store/conversation/memory.js +15 -3
  165. package/dist/store/conversation/memory.js.map +1 -1
  166. package/dist/store/run/disk.d.ts +1 -2
  167. package/dist/store/run/disk.d.ts.map +1 -1
  168. package/dist/store/run/disk.js +21 -13
  169. package/dist/store/run/disk.js.map +1 -1
  170. package/dist/tools/builtins/__tests__/structuredOutput.example.d.ts +140 -0
  171. package/dist/tools/builtins/__tests__/structuredOutput.example.d.ts.map +1 -0
  172. package/dist/tools/builtins/__tests__/structuredOutput.example.js +183 -0
  173. package/dist/tools/builtins/__tests__/structuredOutput.example.js.map +1 -0
  174. package/dist/tools/builtins/__tests__/structuredOutput.test.d.ts +2 -0
  175. package/dist/tools/builtins/__tests__/structuredOutput.test.d.ts.map +1 -0
  176. package/dist/tools/builtins/__tests__/structuredOutput.test.js +224 -0
  177. package/dist/tools/builtins/__tests__/structuredOutput.test.js.map +1 -0
  178. package/dist/tools/builtins/grep.d.ts.map +1 -1
  179. package/dist/tools/builtins/grep.js +1 -2
  180. package/dist/tools/builtins/grep.js.map +1 -1
  181. package/dist/tools/builtins/index.d.ts +1 -0
  182. package/dist/tools/builtins/index.d.ts.map +1 -1
  183. package/dist/tools/builtins/index.js +3 -0
  184. package/dist/tools/builtins/index.js.map +1 -1
  185. package/dist/tools/builtins/ls.d.ts +1 -1
  186. package/dist/tools/builtins/structuredOutput.d.ts +27 -0
  187. package/dist/tools/builtins/structuredOutput.d.ts.map +1 -0
  188. package/dist/tools/builtins/structuredOutput.js +46 -0
  189. package/dist/tools/builtins/structuredOutput.js.map +1 -0
  190. package/dist/tools/task/list.d.ts +1 -1
  191. package/dist/tools/task/list.d.ts.map +1 -1
  192. package/dist/tools/task/list.js.map +1 -1
  193. package/dist/types/agent/base.d.ts +4 -1
  194. package/dist/types/agent/base.d.ts.map +1 -1
  195. package/dist/types/agent/index.d.ts +1 -0
  196. package/dist/types/agent/index.d.ts.map +1 -1
  197. package/dist/types/agent/index.js +1 -0
  198. package/dist/types/agent/index.js.map +1 -1
  199. package/dist/types/agent/manager.d.ts +27 -0
  200. package/dist/types/agent/manager.d.ts.map +1 -0
  201. package/dist/types/agent/manager.js +2 -0
  202. package/dist/types/agent/manager.js.map +1 -0
  203. package/dist/types/agent/reactive.d.ts +2 -2
  204. package/dist/types/agent/reactive.d.ts.map +1 -1
  205. package/dist/types/agent/supervisor.d.ts +2 -2
  206. package/dist/types/agent/supervisor.d.ts.map +1 -1
  207. package/dist/types/agent/task.d.ts +0 -2
  208. package/dist/types/agent/task.d.ts.map +1 -1
  209. package/dist/types/agent/task.js +0 -2
  210. package/dist/types/agent/task.js.map +1 -1
  211. package/dist/types/common/index.d.ts +0 -1
  212. package/dist/types/common/index.d.ts.map +1 -1
  213. package/dist/types/common/index.js +0 -1
  214. package/dist/types/common/index.js.map +1 -1
  215. package/dist/types/hitl/index.d.ts +1 -2
  216. package/dist/types/hitl/index.d.ts.map +1 -1
  217. package/dist/types/hitl/index.js.map +1 -1
  218. package/dist/types/invocation/__tests__/state.test.d.ts +2 -0
  219. package/dist/types/invocation/__tests__/state.test.d.ts.map +1 -0
  220. package/dist/types/invocation/__tests__/state.test.js +167 -0
  221. package/dist/types/invocation/__tests__/state.test.js.map +1 -0
  222. package/dist/types/invocation/index.d.ts +37 -0
  223. package/dist/types/invocation/index.d.ts.map +1 -0
  224. package/dist/types/invocation/index.js +23 -0
  225. package/dist/types/invocation/index.js.map +1 -0
  226. package/dist/types/plugin/index.d.ts +6 -0
  227. package/dist/types/plugin/index.d.ts.map +1 -1
  228. package/dist/types/plugin/index.js +16 -0
  229. package/dist/types/plugin/index.js.map +1 -1
  230. package/dist/types/run/events.d.ts +1 -1
  231. package/dist/types/run/events.d.ts.map +1 -1
  232. package/dist/types/run/index.d.ts +1 -0
  233. package/dist/types/run/index.d.ts.map +1 -1
  234. package/dist/types/run/index.js +1 -0
  235. package/dist/types/run/index.js.map +1 -1
  236. package/dist/types/run/metadata.d.ts +1 -1
  237. package/dist/types/run/metadata.d.ts.map +1 -1
  238. package/dist/types/run/state.d.ts +1 -1
  239. package/dist/types/run/state.d.ts.map +1 -1
  240. package/dist/types/run/stop-reason.d.ts +2 -0
  241. package/dist/types/run/stop-reason.d.ts.map +1 -0
  242. package/dist/types/run/stop-reason.js +2 -0
  243. package/dist/types/run/stop-reason.js.map +1 -0
  244. package/dist/types/structured-output/index.d.ts +51 -0
  245. package/dist/types/structured-output/index.d.ts.map +1 -0
  246. package/dist/types/structured-output/index.js +2 -0
  247. package/dist/types/structured-output/index.js.map +1 -0
  248. package/dist/types/tool/index.d.ts +36 -0
  249. package/dist/types/tool/index.d.ts.map +1 -1
  250. package/package.json +1 -1
  251. package/src/advisory/executor.ts +2 -4
  252. package/src/agents/AbstractAgent.ts +26 -3
  253. package/src/agents/PipelineAgent.ts +1 -1
  254. package/src/agents/ReactiveAgent.ts +1 -0
  255. package/src/agents/RouterAgent.ts +8 -2
  256. package/src/agents/SupervisorAgent.ts +5 -1
  257. package/src/agents/__tests__/lock.test.ts +158 -0
  258. package/src/agents/index.ts +2 -0
  259. package/src/agents/lock.ts +66 -0
  260. package/src/bridge/a2a/message.ts +1 -2
  261. package/src/bridge/tools/connector/router.ts +4 -5
  262. package/src/compaction/__tests__/SlidingWindowManager.test.ts +139 -0
  263. package/src/compaction/__tests__/dangling.test.ts +447 -0
  264. package/src/compaction/__tests__/factory.test.ts +53 -0
  265. package/src/compaction/dangling.ts +321 -0
  266. package/src/compaction/factory.ts +41 -0
  267. package/src/compaction/index.ts +14 -0
  268. package/src/compaction/interface.ts +35 -0
  269. package/src/compaction/managers/index.ts +3 -0
  270. package/src/compaction/managers/null.ts +19 -0
  271. package/src/compaction/managers/slidingWindow.ts +57 -0
  272. package/src/compaction/managers/structured.ts +169 -0
  273. package/src/compaction/types.ts +1 -1
  274. package/src/config/runtime.ts +1 -1
  275. package/src/constants/agent/index.ts +1 -1
  276. package/src/gateway/local.ts +13 -4
  277. package/src/index.ts +38 -1
  278. package/src/manager/agent/lifecycle.ts +3 -2
  279. package/src/manager/run/persistence.ts +3 -8
  280. package/src/plugin/__tests__/lifecycle.test.ts +430 -0
  281. package/src/plugin/lifecycle.ts +32 -6
  282. package/src/plugin/resolver.ts +3 -3
  283. package/src/registry/agent/definitions.ts +3 -2
  284. package/src/registry/tool/execute.ts +2 -5
  285. package/src/runtime/decision/parser.ts +15 -40
  286. package/src/runtime/query/context-cache.ts +3 -4
  287. package/src/runtime/query/context.ts +1 -2
  288. package/src/runtime/query/events.ts +11 -11
  289. package/src/runtime/query/executor.ts +5 -3
  290. package/src/runtime/query/index.ts +11 -4
  291. package/src/runtime/query/iteration/index.ts +2 -2
  292. package/src/runtime/query/iteration/phases/advisory.ts +1 -2
  293. package/src/runtime/query/iteration/phases/checkpoint.ts +1 -2
  294. package/src/runtime/query/iteration/phases/context.ts +2 -2
  295. package/src/runtime/query/iteration/phases/plan.ts +1 -2
  296. package/src/runtime/query/prompt.ts +3 -3
  297. package/src/runtime/query/result.ts +1 -2
  298. package/src/runtime/query/tooling.ts +5 -2
  299. package/src/store/conversation/memory.ts +21 -5
  300. package/src/store/run/disk.ts +18 -16
  301. package/src/tools/builtins/__tests__/structuredOutput.example.ts +221 -0
  302. package/src/tools/builtins/__tests__/structuredOutput.test.ts +275 -0
  303. package/src/tools/builtins/grep.ts +1 -2
  304. package/src/tools/builtins/index.ts +3 -0
  305. package/src/tools/builtins/structuredOutput.ts +55 -0
  306. package/src/tools/task/list.ts +1 -2
  307. package/src/types/agent/base.ts +5 -1
  308. package/src/types/agent/index.ts +1 -0
  309. package/src/types/agent/manager.ts +36 -0
  310. package/src/types/agent/reactive.ts +2 -2
  311. package/src/types/agent/supervisor.ts +2 -2
  312. package/src/types/agent/task.ts +0 -4
  313. package/src/types/common/index.ts +0 -2
  314. package/src/types/hitl/index.ts +1 -2
  315. package/src/types/invocation/__tests__/state.test.ts +210 -0
  316. package/src/types/invocation/index.ts +55 -0
  317. package/src/types/plugin/index.ts +19 -0
  318. package/src/types/run/events.ts +1 -10
  319. package/src/types/run/index.ts +1 -0
  320. package/src/types/run/metadata.ts +1 -1
  321. package/src/types/run/state.ts +1 -1
  322. package/src/types/run/stop-reason.ts +10 -0
  323. package/src/types/structured-output/index.ts +56 -0
  324. package/src/types/tool/index.ts +45 -0
@@ -1,4 +1,4 @@
1
- import type { AgentCapabilities } from '../../types/agent/index.js'
1
+ import type { AgentCapabilities } from '../../types/agent/base.js'
2
2
  import type { AgentManagerConfig } from '../../types/agent/task.js'
3
3
 
4
4
  export const MAX_RECENT_ACTIVITIES = 5
@@ -1,13 +1,15 @@
1
- import type { AgentManager } from '../manager/agent/lifecycle.js'
2
1
  import type { AgentInput } from '../types/agent/base.js'
3
2
  import type { CreateTaskOptions, TaskGateway, TaskHandle } from '../types/agent/gateway.js'
3
+ import type { AgentManagerContract } from '../types/agent/manager.js'
4
4
  import type { AgentTaskContext } from '../types/agent/task.js'
5
5
  import type { TaskId } from '../types/ids/index.js'
6
6
  import { createUserMessage } from '../types/message/index.js'
7
7
  import type { RunEventListener } from '../types/run/events.js'
8
+ import { toErrorMessage } from '../utils/error.js'
9
+ import { getRootLogger } from '../utils/logger.js'
8
10
 
9
11
  export class LocalTaskGateway implements TaskGateway {
10
- private agentManager: AgentManager
12
+ private agentManager: AgentManagerContract
11
13
  private taskContext: AgentTaskContext
12
14
  private listener: RunEventListener | undefined
13
15
  private trackedTaskIds: Set<TaskId> = new Set()
@@ -17,7 +19,7 @@ export class LocalTaskGateway implements TaskGateway {
17
19
  private completionListeners: Set<(handle: TaskHandle) => void> = new Set()
18
20
 
19
21
  constructor(
20
- agentManager: AgentManager,
22
+ agentManager: AgentManagerContract,
21
23
  taskContext: AgentTaskContext,
22
24
  listener?: RunEventListener,
23
25
  parentInput?: Pick<AgentInput, 'taskStore' | 'runtimeToolOverrides'>,
@@ -56,7 +58,14 @@ export class LocalTaskGateway implements TaskGateway {
56
58
  }
57
59
  }
58
60
  })
59
- .catch(() => {})
61
+ .catch((err) => {
62
+ getRootLogger()
63
+ .child({ component: 'LocalTaskGateway' })
64
+ .error('Task completion tracking failed', {
65
+ taskId: task.taskId,
66
+ error: toErrorMessage(err),
67
+ })
68
+ })
60
69
 
61
70
  return toHandle(task)
62
71
  }
package/src/index.ts CHANGED
@@ -43,6 +43,8 @@ export * from './types/advisory/index.js'
43
43
  export * from './types/memory/index.js'
44
44
  export * from './types/plugin/index.js'
45
45
  export * from './types/sandbox/index.js'
46
+ export * from './types/structured-output/index.js'
47
+ export * from './types/invocation/index.js'
46
48
 
47
49
  export {
48
50
  AdvisorRegistry,
@@ -106,8 +108,10 @@ export {
106
108
  RouterAgent,
107
109
  SupervisorAgent,
108
110
  defineAgent,
111
+ InvocationLock,
112
+ ConcurrentInvocationError,
109
113
  } from './agents/index.js'
110
- export type { DefineAgentOptions } from './agents/index.js'
114
+ export type { DefineAgentOptions, ConcurrencyMode, Disposable } from './agents/index.js'
111
115
 
112
116
  export { InMemoryStore } from './store/InMemoryStore.js'
113
117
  export type { Identifiable, Timestamped } from './store/InMemoryStore.js'
@@ -184,9 +188,16 @@ export type { ToolExecutionResult } from './registry/tool/execute.js'
184
188
  export { getBuiltinTools } from './tools/builtins/index.js'
185
189
  export { ReadFileTool } from './tools/builtins/read-file.js'
186
190
  export { WriteFileTool } from './tools/builtins/write-file.js'
191
+ export { EditTool } from './tools/builtins/edit.js'
187
192
  export { BashTool } from './tools/builtins/bash.js'
188
193
  export { GlobTool } from './tools/builtins/glob.js'
194
+ export { GrepTool } from './tools/builtins/grep.js'
195
+ export { LsTool } from './tools/builtins/ls.js'
189
196
  export { SearchToolsTool } from './tools/builtins/search-tools.js'
197
+ export {
198
+ createStructuredOutputTool,
199
+ STRUCTURED_OUTPUT_TOOL_NAME,
200
+ } from './tools/builtins/structuredOutput.js'
190
201
 
191
202
  export {
192
203
  TextChunker,
@@ -280,6 +291,23 @@ export {
280
291
  } from './bridge/sse/index.js'
281
292
  export type { MappedStreamEvent } from './bridge/sse/index.js'
282
293
 
294
+ export {
295
+ AgentBus,
296
+ CircuitBreaker,
297
+ FileLockManager,
298
+ EditOwnershipTracker,
299
+ } from './bus/index.js'
300
+ export type { AgentBusConfig } from './bus/index.js'
301
+
302
+ export { VerificationGate, evaluateRule } from './verification/index.js'
303
+ export type { ToolCallContext } from './verification/index.js'
304
+
305
+ export { buildCoordinatorTools } from './tools/coordinator/index.js'
306
+ export type { CoordinatorToolsOptions, TaskLaunchedCallback } from './tools/coordinator/index.js'
307
+
308
+ export { checkLimitsDetailed, buildLimitConfig } from './run/LimitChecker.js'
309
+ export type { LimitCheckerState, LimitCheckResult } from './run/LimitChecker.js'
310
+
283
311
  export { InMemoryCredentialVault } from './vault/index.js'
284
312
 
285
313
  export {
@@ -301,6 +329,13 @@ export {
301
329
  extractFromUserMessage,
302
330
  extractFromAssistantMessage,
303
331
  buildVerifiedSummary,
332
+ findDanglingMessages,
333
+ removeDanglingMessages,
334
+ findSafeTrimIndex,
335
+ NullManager,
336
+ SlidingWindowManager,
337
+ StructuredCompactionManager,
338
+ createConversationManager,
304
339
  } from './compaction/index.js'
305
340
  export type {
306
341
  WorkingState,
@@ -309,4 +344,6 @@ export type {
309
344
  FileAction,
310
345
  ToolResultSlot,
311
346
  CompactionStrategy,
347
+ DanglingResult,
348
+ ConversationManager,
312
349
  } from './compaction/index.js'
@@ -1,3 +1,5 @@
1
+ import { AGENT_MANAGER_DEFAULTS } from '../../constants/agent/index.js'
2
+ import { EMPTY_TOKEN_USAGE } from '../../constants/limits.js'
1
3
  import type { AgentRegistry } from '../../registry/agent/definitions.js'
2
4
  import type { BaseAgentConfig, BaseAgentResult } from '../../types/agent/base.js'
3
5
  import type {
@@ -11,8 +13,7 @@ import type {
11
13
  AgentTaskState,
12
14
  SendMessageOptions,
13
15
  } from '../../types/agent/task.js'
14
- import { AGENT_MANAGER_DEFAULTS, isTerminalAgentTaskState } from '../../types/agent/task.js'
15
- import { EMPTY_TOKEN_USAGE } from '../../types/common/index.js'
16
+ import { isTerminalAgentTaskState } from '../../types/agent/task.js'
16
17
  import type { RunId, TaskId } from '../../types/ids/index.js'
17
18
  import type { Message } from '../../types/message/index.js'
18
19
  import { createChildAbortController } from '../../utils/abort.js'
@@ -1,15 +1,10 @@
1
+ import { EMPTY_TOKEN_USAGE } from '../../constants/limits.js'
1
2
  import { RunDiskStore } from '../../store/run/disk.js'
2
- import {
3
- type CostInfo,
4
- EMPTY_TOKEN_USAGE,
5
- type TokenUsage,
6
- accumulateTokenUsage,
7
- } from '../../types/common/index.js'
3
+ import { type CostInfo, type TokenUsage, accumulateTokenUsage } from '../../types/common/index.js'
8
4
  import type { RunId } from '../../types/ids/index.js'
9
5
  import type { AssistantMessage, Message } from '../../types/message/index.js'
10
6
  import type { EmergencySaveData } from '../../types/run/emergency.js'
11
- import type { AgentRun, StopReason } from '../../types/run/index.js'
12
- import type { RunPersistenceConfig } from '../../types/run/index.js'
7
+ import type { AgentRun, RunPersistenceConfig, StopReason } from '../../types/run/index.js'
13
8
  import { type ModelPricing, ZERO_COST, accumulateCost } from '../../utils/cost.js'
14
9
  import { generateEmergencySaveId } from '../../utils/id.js'
15
10
  import type { Logger } from '../../utils/logger.js'
@@ -0,0 +1,430 @@
1
+ import { beforeEach, describe, expect, it, vi } from 'vitest'
2
+ import type { PluginRegistry } from '../../registry/plugin/index.js'
3
+ import type { PluginId, RunId } from '../../types/ids/index.js'
4
+ import type { PluginHookContext, PluginHookResult } from '../../types/plugin/index.js'
5
+ import type { ToolRegistryContract } from '../../types/tool/index.js'
6
+ import type { Logger } from '../../utils/logger.js'
7
+ import { PluginLifecycleManager } from '../lifecycle.js'
8
+
9
+ describe('PluginLifecycleManager', () => {
10
+ let manager: PluginLifecycleManager
11
+ let pluginRegistry: PluginRegistry
12
+ let toolRegistry: ToolRegistryContract
13
+ let logger: Logger
14
+
15
+ const mockRunId = 'run_test' as RunId
16
+ const mockPluginId = 'plugin_test' as PluginId
17
+
18
+ beforeEach(() => {
19
+ // Create mock registries and logger
20
+ pluginRegistry = {
21
+ register: vi.fn(),
22
+ unregister: vi.fn(),
23
+ getOrThrow: vi.fn(),
24
+ findByName: vi.fn(),
25
+ getAll: vi.fn(() => []),
26
+ } as any
27
+
28
+ toolRegistry = {
29
+ register: vi.fn(),
30
+ unregister: vi.fn(),
31
+ execute: vi.fn(),
32
+ getAll: vi.fn(() => []),
33
+ } as any
34
+
35
+ logger = {
36
+ child: vi.fn(() => ({
37
+ info: vi.fn(),
38
+ warn: vi.fn(),
39
+ error: vi.fn(),
40
+ debug: vi.fn(),
41
+ })),
42
+ info: vi.fn(),
43
+ warn: vi.fn(),
44
+ error: vi.fn(),
45
+ debug: vi.fn(),
46
+ } as any
47
+
48
+ manager = new PluginLifecycleManager({
49
+ pluginRegistry,
50
+ toolRegistry,
51
+ log: logger,
52
+ hookTimeoutMs: 5000,
53
+ })
54
+ })
55
+
56
+ describe('executeHooks', () => {
57
+ it('should return empty array when no hooks registered', async () => {
58
+ const results = await manager.executeHooks('run_start', { runId: mockRunId })
59
+ expect(results).toEqual([])
60
+ })
61
+
62
+ it('should execute all registered hooks for an event', async () => {
63
+ const hook1Handler = vi.fn(async (): Promise<PluginHookResult> => ({ action: 'continue' }))
64
+ const hook2Handler = vi.fn(async (): Promise<PluginHookResult> => ({ action: 'continue' }))
65
+
66
+ // Register hooks manually
67
+ manager['hookHandlers'].set('run_start', [
68
+ { pluginId: 'plugin_1' as PluginId, handler: hook1Handler },
69
+ { pluginId: 'plugin_2' as PluginId, handler: hook2Handler },
70
+ ])
71
+
72
+ const results = await manager.executeHooks('run_start', { runId: mockRunId })
73
+
74
+ expect(results).toHaveLength(2)
75
+ expect(hook1Handler).toHaveBeenCalled()
76
+ expect(hook2Handler).toHaveBeenCalled()
77
+ })
78
+
79
+ it('should handle hook timeout', async () => {
80
+ const slowHandler = vi.fn(
81
+ () =>
82
+ new Promise<PluginHookResult>((resolve) => {
83
+ setTimeout(() => resolve({ action: 'continue' }), 10000)
84
+ }),
85
+ )
86
+
87
+ manager['hookHandlers'].set('run_start', [{ pluginId: mockPluginId, handler: slowHandler }])
88
+
89
+ const managerWithShortTimeout = new PluginLifecycleManager({
90
+ pluginRegistry,
91
+ toolRegistry,
92
+ log: logger,
93
+ hookTimeoutMs: 10, // Very short timeout
94
+ })
95
+
96
+ managerWithShortTimeout['hookHandlers'].set('run_start', [
97
+ { pluginId: mockPluginId, handler: slowHandler },
98
+ ])
99
+
100
+ const results = await managerWithShortTimeout.executeHooks('run_start', {
101
+ runId: mockRunId,
102
+ })
103
+
104
+ expect(results).toHaveLength(1)
105
+ expect(results[0]?.action).toBe('error')
106
+ if (results[0]?.action === 'error') {
107
+ expect(results[0].message).toContain('timeout')
108
+ }
109
+ })
110
+
111
+ describe('Hook ordering semantics', () => {
112
+ it('should execute pre_* hooks in registration order (first registered first)', async () => {
113
+ const executionOrder: string[] = []
114
+
115
+ const handler1 = vi.fn(async (): Promise<PluginHookResult> => {
116
+ executionOrder.push('hook1')
117
+ return { action: 'continue' }
118
+ })
119
+
120
+ const handler2 = vi.fn(async (): Promise<PluginHookResult> => {
121
+ executionOrder.push('hook2')
122
+ return { action: 'continue' }
123
+ })
124
+
125
+ const handler3 = vi.fn(async (): Promise<PluginHookResult> => {
126
+ executionOrder.push('hook3')
127
+ return { action: 'continue' }
128
+ })
129
+
130
+ manager['hookHandlers'].set('pre_tool_use', [
131
+ { pluginId: 'plugin_1' as PluginId, handler: handler1 },
132
+ { pluginId: 'plugin_2' as PluginId, handler: handler2 },
133
+ { pluginId: 'plugin_3' as PluginId, handler: handler3 },
134
+ ])
135
+
136
+ await manager.executeHooks('pre_tool_use', { runId: mockRunId })
137
+
138
+ expect(executionOrder).toEqual(['hook1', 'hook2', 'hook3'])
139
+ })
140
+
141
+ it('should execute post_* hooks in reverse registration order (last registered first)', async () => {
142
+ const executionOrder: string[] = []
143
+
144
+ const handler1 = vi.fn(async (): Promise<PluginHookResult> => {
145
+ executionOrder.push('hook1')
146
+ return { action: 'continue' }
147
+ })
148
+
149
+ const handler2 = vi.fn(async (): Promise<PluginHookResult> => {
150
+ executionOrder.push('hook2')
151
+ return { action: 'continue' }
152
+ })
153
+
154
+ const handler3 = vi.fn(async (): Promise<PluginHookResult> => {
155
+ executionOrder.push('hook3')
156
+ return { action: 'continue' }
157
+ })
158
+
159
+ manager['hookHandlers'].set('post_tool_use', [
160
+ { pluginId: 'plugin_1' as PluginId, handler: handler1 },
161
+ { pluginId: 'plugin_2' as PluginId, handler: handler2 },
162
+ { pluginId: 'plugin_3' as PluginId, handler: handler3 },
163
+ ])
164
+
165
+ await manager.executeHooks('post_tool_use', { runId: mockRunId })
166
+
167
+ // Reverse order: hook3 -> hook2 -> hook1
168
+ expect(executionOrder).toEqual(['hook3', 'hook2', 'hook1'])
169
+ })
170
+
171
+ it('should execute non-pre/post hooks in registration order', async () => {
172
+ const executionOrder: string[] = []
173
+
174
+ const handler1 = vi.fn(async (): Promise<PluginHookResult> => {
175
+ executionOrder.push('hook1')
176
+ return { action: 'continue' }
177
+ })
178
+
179
+ const handler2 = vi.fn(async (): Promise<PluginHookResult> => {
180
+ executionOrder.push('hook2')
181
+ return { action: 'continue' }
182
+ })
183
+
184
+ manager['hookHandlers'].set('run_start', [
185
+ { pluginId: 'plugin_1' as PluginId, handler: handler1 },
186
+ { pluginId: 'plugin_2' as PluginId, handler: handler2 },
187
+ ])
188
+
189
+ await manager.executeHooks('run_start', { runId: mockRunId })
190
+
191
+ expect(executionOrder).toEqual(['hook1', 'hook2'])
192
+ })
193
+ })
194
+
195
+ describe('Flow control: action priority', () => {
196
+ it('should short-circuit on error action', async () => {
197
+ const handler1 = vi.fn(async (): Promise<PluginHookResult> => {
198
+ return { action: 'error', message: 'Hook failed' }
199
+ })
200
+
201
+ const handler2 = vi.fn(async (): Promise<PluginHookResult> => {
202
+ return { action: 'continue' }
203
+ })
204
+
205
+ manager['hookHandlers'].set('run_start', [
206
+ { pluginId: 'plugin_1' as PluginId, handler: handler1 },
207
+ { pluginId: 'plugin_2' as PluginId, handler: handler2 },
208
+ ])
209
+
210
+ const results = await manager.executeHooks('run_start', { runId: mockRunId })
211
+
212
+ expect(results).toHaveLength(1)
213
+ expect(results[0]?.action).toBe('error')
214
+ expect(handler2).not.toHaveBeenCalled()
215
+ })
216
+
217
+ it('should short-circuit on skip action', async () => {
218
+ const handler1 = vi.fn(async (): Promise<PluginHookResult> => {
219
+ return { action: 'skip', reason: 'Condition not met' }
220
+ })
221
+
222
+ const handler2 = vi.fn(async (): Promise<PluginHookResult> => {
223
+ return { action: 'continue' }
224
+ })
225
+
226
+ manager['hookHandlers'].set('pre_tool_use', [
227
+ { pluginId: 'plugin_1' as PluginId, handler: handler1 },
228
+ { pluginId: 'plugin_2' as PluginId, handler: handler2 },
229
+ ])
230
+
231
+ const results = await manager.executeHooks('pre_tool_use', { runId: mockRunId })
232
+
233
+ expect(results).toHaveLength(1)
234
+ expect(results[0]?.action).toBe('skip')
235
+ expect(handler2).not.toHaveBeenCalled()
236
+ })
237
+
238
+ it('should short-circuit and return resume action', async () => {
239
+ const handler1 = vi.fn(async (): Promise<PluginHookResult> => {
240
+ return { action: 'resume', input: 'new_input_value' }
241
+ })
242
+
243
+ const handler2 = vi.fn(async (): Promise<PluginHookResult> => {
244
+ return { action: 'continue' }
245
+ })
246
+
247
+ manager['hookHandlers'].set('pre_llm_call', [
248
+ { pluginId: 'plugin_1' as PluginId, handler: handler1 },
249
+ { pluginId: 'plugin_2' as PluginId, handler: handler2 },
250
+ ])
251
+
252
+ const results = await manager.executeHooks('pre_llm_call', { runId: mockRunId })
253
+
254
+ expect(results).toHaveLength(1)
255
+ expect(results[0]?.action).toBe('resume')
256
+ if (results[0]?.action === 'resume') {
257
+ expect(results[0].input).toBe('new_input_value')
258
+ }
259
+ expect(handler2).not.toHaveBeenCalled()
260
+ })
261
+
262
+ it('should short-circuit and return retry action', async () => {
263
+ const handler1 = vi.fn(async (): Promise<PluginHookResult> => {
264
+ return { action: 'retry' }
265
+ })
266
+
267
+ const handler2 = vi.fn(async (): Promise<PluginHookResult> => {
268
+ return { action: 'continue' }
269
+ })
270
+
271
+ // Use pre_* hook for forward execution order (plugin_1 runs first and short-circuits).
272
+ // post_* hooks run in reverse order for cleanup semantics.
273
+ manager['hookHandlers'].set('pre_llm_call', [
274
+ { pluginId: 'plugin_1' as PluginId, handler: handler1 },
275
+ { pluginId: 'plugin_2' as PluginId, handler: handler2 },
276
+ ])
277
+
278
+ const results = await manager.executeHooks('pre_llm_call', { runId: mockRunId })
279
+
280
+ expect(results).toHaveLength(1)
281
+ expect(results[0]?.action).toBe('retry')
282
+ expect(handler2).not.toHaveBeenCalled()
283
+ })
284
+
285
+ it('should continue executing on modify action', async () => {
286
+ const handler1 = vi.fn(async (): Promise<PluginHookResult> => {
287
+ return { action: 'modify', input: { updated: true } }
288
+ })
289
+
290
+ const handler2 = vi.fn(async (): Promise<PluginHookResult> => {
291
+ return { action: 'continue' }
292
+ })
293
+
294
+ manager['hookHandlers'].set('pre_tool_use', [
295
+ { pluginId: 'plugin_1' as PluginId, handler: handler1 },
296
+ { pluginId: 'plugin_2' as PluginId, handler: handler2 },
297
+ ])
298
+
299
+ const results = await manager.executeHooks('pre_tool_use', { runId: mockRunId })
300
+
301
+ expect(results).toHaveLength(2)
302
+ expect(results[0]?.action).toBe('modify')
303
+ expect(results[1]?.action).toBe('continue')
304
+ expect(handler2).toHaveBeenCalled()
305
+ })
306
+
307
+ it('should continue executing on continue action', async () => {
308
+ const handler1 = vi.fn(async (): Promise<PluginHookResult> => {
309
+ return { action: 'continue' }
310
+ })
311
+
312
+ const handler2 = vi.fn(async (): Promise<PluginHookResult> => {
313
+ return { action: 'continue' }
314
+ })
315
+
316
+ manager['hookHandlers'].set('iteration_start', [
317
+ { pluginId: 'plugin_1' as PluginId, handler: handler1 },
318
+ { pluginId: 'plugin_2' as PluginId, handler: handler2 },
319
+ ])
320
+
321
+ const results = await manager.executeHooks('iteration_start', { runId: mockRunId })
322
+
323
+ expect(results).toHaveLength(2)
324
+ expect(handler1).toHaveBeenCalled()
325
+ expect(handler2).toHaveBeenCalled()
326
+ })
327
+ })
328
+
329
+ describe('Hook context', () => {
330
+ it('should pass correct context to hook handler', async () => {
331
+ let capturedContext: PluginHookContext | null = null
332
+
333
+ const handler = vi.fn(async (ctx: PluginHookContext): Promise<PluginHookResult> => {
334
+ capturedContext = ctx
335
+ return { action: 'continue' }
336
+ })
337
+
338
+ manager['hookHandlers'].set('pre_tool_use', [{ pluginId: mockPluginId, handler }])
339
+
340
+ const contextData = {
341
+ runId: mockRunId,
342
+ toolName: 'test_tool',
343
+ toolInput: { key: 'value' },
344
+ }
345
+
346
+ await manager.executeHooks('pre_tool_use', contextData)
347
+
348
+ expect(capturedContext).not.toBeNull()
349
+ const ctx = capturedContext as unknown as PluginHookContext
350
+ expect(ctx.runId).toBe(mockRunId)
351
+ expect(ctx.pluginId).toBe(mockPluginId)
352
+ expect(ctx.event).toBe('pre_tool_use')
353
+ expect(ctx.toolName).toBe('test_tool')
354
+ expect(ctx.toolInput).toEqual({ key: 'value' })
355
+ })
356
+
357
+ it('should include iteration number in context when provided', async () => {
358
+ let capturedContext: PluginHookContext | null = null
359
+
360
+ const handler = vi.fn(async (ctx: PluginHookContext): Promise<PluginHookResult> => {
361
+ capturedContext = ctx
362
+ return { action: 'continue' }
363
+ })
364
+
365
+ manager['hookHandlers'].set('iteration_end', [{ pluginId: mockPluginId, handler }])
366
+
367
+ await manager.executeHooks('iteration_end', {
368
+ runId: mockRunId,
369
+ iteration: 5,
370
+ })
371
+
372
+ const ctx = capturedContext as unknown as PluginHookContext
373
+ expect(ctx.iteration).toBe(5)
374
+ })
375
+ })
376
+
377
+ describe('Hook execution logging', () => {
378
+ it('should emit hook_executed event with correct metadata', async () => {
379
+ const events: any[] = []
380
+ manager.on((evt) => events.push(evt))
381
+
382
+ const handler = vi.fn(async (): Promise<PluginHookResult> => {
383
+ return { action: 'continue' }
384
+ })
385
+
386
+ manager['hookHandlers'].set('run_start', [{ pluginId: mockPluginId, handler }])
387
+
388
+ await manager.executeHooks('run_start', { runId: mockRunId })
389
+
390
+ const hookExecutedEvents = events.filter((evt) => evt.type === 'plugin_hook_executed')
391
+ expect(hookExecutedEvents).toHaveLength(1)
392
+
393
+ const event = hookExecutedEvents[0]
394
+ expect(event?.pluginId).toBe(mockPluginId)
395
+ expect(event?.hookEvent).toBe('run_start')
396
+ expect(typeof event?.durationMs).toBe('number')
397
+ expect(event?.durationMs).toBeGreaterThanOrEqual(0)
398
+ })
399
+ })
400
+
401
+ describe('Exception handling', () => {
402
+ it('should catch thrown exceptions and return error action', async () => {
403
+ const handler = vi.fn(async (): Promise<PluginHookResult> => {
404
+ throw new Error('Handler crashed')
405
+ })
406
+
407
+ manager['hookHandlers'].set('run_start', [{ pluginId: mockPluginId, handler }])
408
+
409
+ const results = await manager.executeHooks('run_start', { runId: mockRunId })
410
+
411
+ expect(results).toHaveLength(1)
412
+ expect(results[0]?.action).toBe('error')
413
+ if (results[0]?.action === 'error') {
414
+ expect(results[0].message).toContain('Handler crashed')
415
+ }
416
+ })
417
+
418
+ it('should not throw when handler throws', async () => {
419
+ const handler = vi.fn(async (): Promise<PluginHookResult> => {
420
+ throw new Error('Handler failed')
421
+ })
422
+
423
+ manager['hookHandlers'].set('run_start', [{ pluginId: mockPluginId, handler }])
424
+
425
+ const executePromise = manager.executeHooks('run_start', { runId: mockRunId })
426
+ await expect(executePromise).resolves.not.toThrow()
427
+ })
428
+ })
429
+ })
430
+ })
@@ -2,7 +2,6 @@ import { join } from 'node:path'
2
2
  import { pathToFileURL } from 'node:url'
3
3
  import { HOOK_TIMEOUT_MS, PLUGIN_NAMESPACE_SEPARATOR } from '../constants/plugin/index.js'
4
4
  import type { PluginRegistry } from '../registry/plugin/index.js'
5
- import type { ToolRegistry } from '../registry/tool/execute.js'
6
5
  import type { PluginId } from '../types/ids/index.js'
7
6
  import type {
8
7
  PluginDefinition,
@@ -14,7 +13,7 @@ import type {
14
13
  PluginLifecycleEvent,
15
14
  PluginScope,
16
15
  } from '../types/plugin/index.js'
17
- import type { ToolDefinition } from '../types/tool/index.js'
16
+ import type { ToolDefinition, ToolRegistryContract } from '../types/tool/index.js'
18
17
  import { toErrorMessage } from '../utils/error.js'
19
18
  import { generatePluginId } from '../utils/id.js'
20
19
  import type { Logger } from '../utils/logger.js'
@@ -22,14 +21,14 @@ import { loadPluginManifest } from './loader.js'
22
21
 
23
22
  export interface PluginLifecycleManagerConfig {
24
23
  pluginRegistry: PluginRegistry
25
- toolRegistry: ToolRegistry
24
+ toolRegistry: ToolRegistryContract
26
25
  log: Logger
27
26
  hookTimeoutMs?: number
28
27
  }
29
28
 
30
29
  export class PluginLifecycleManager {
31
30
  private pluginRegistry: PluginRegistry
32
- private toolRegistry: ToolRegistry
31
+ private toolRegistry: ToolRegistryContract
33
32
  private listeners: PluginEventListener[] = []
34
33
  private hookHandlers: Map<
35
34
  PluginHookEvent,
@@ -257,7 +256,25 @@ export class PluginLifecycleManager {
257
256
 
258
257
  const results: PluginHookResult[] = []
259
258
 
260
- for (const { pluginId, handler } of handlers) {
259
+ // Determine execution order: post_* hooks run backward (for cleanup semantics)
260
+ const isPost = event.startsWith('post_')
261
+
262
+ // For post_* hooks, we need to process in reverse order (last registered runs first)
263
+ const indicesToProcess: number[] = []
264
+ if (isPost) {
265
+ for (let i = handlers.length - 1; i >= 0; i--) {
266
+ indicesToProcess.push(i)
267
+ }
268
+ } else {
269
+ for (let i = 0; i < handlers.length; i++) {
270
+ indicesToProcess.push(i)
271
+ }
272
+ }
273
+
274
+ for (const idx of indicesToProcess) {
275
+ const hookEntry = handlers[idx]
276
+ if (!hookEntry) continue
277
+ const { pluginId, handler: handlerFn } = hookEntry
261
278
  const hookContext: PluginHookContext = {
262
279
  ...context,
263
280
  pluginId,
@@ -269,7 +286,7 @@ export class PluginLifecycleManager {
269
286
 
270
287
  try {
271
288
  result = await Promise.race([
272
- handler(hookContext),
289
+ handlerFn(hookContext),
273
290
  new Promise<PluginHookResult>((_, reject) =>
274
291
  setTimeout(() => reject(new Error('Hook timeout')), this.hookTimeoutMs),
275
292
  ),
@@ -289,6 +306,15 @@ export class PluginLifecycleManager {
289
306
  })
290
307
 
291
308
  results.push(result)
309
+
310
+ // Handle flow control: check priority order: error > skip > retry > resume > modify > continue
311
+ // Short-circuit on error or skip; return immediately on resume or retry
312
+ if (result.action === 'error' || result.action === 'skip') {
313
+ break
314
+ }
315
+ if (result.action === 'resume' || result.action === 'retry') {
316
+ break
317
+ }
292
318
  }
293
319
 
294
320
  return results