@clinebot/core 0.0.18 → 0.0.21

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 (356) hide show
  1. package/dist/account/cline-account-service.d.ts +3 -2
  2. package/dist/account/cline-account-service.d.ts.map +1 -0
  3. package/dist/account/index.d.ts +1 -0
  4. package/dist/account/index.d.ts.map +1 -0
  5. package/dist/account/rpc.d.ts +1 -0
  6. package/dist/account/rpc.d.ts.map +1 -0
  7. package/dist/account/types.d.ts +1 -0
  8. package/dist/account/types.d.ts.map +1 -0
  9. package/dist/agents/agent-config-loader.d.ts +1 -0
  10. package/dist/agents/agent-config-loader.d.ts.map +1 -0
  11. package/dist/agents/agent-config-parser.d.ts +1 -0
  12. package/dist/agents/agent-config-parser.d.ts.map +1 -0
  13. package/dist/agents/hooks-config-loader.d.ts +1 -0
  14. package/dist/agents/hooks-config-loader.d.ts.map +1 -0
  15. package/dist/agents/index.d.ts +1 -0
  16. package/dist/agents/index.d.ts.map +1 -0
  17. package/dist/agents/plugin-config-loader.d.ts +1 -0
  18. package/dist/agents/plugin-config-loader.d.ts.map +1 -0
  19. package/dist/agents/plugin-loader.d.ts +1 -0
  20. package/dist/agents/plugin-loader.d.ts.map +1 -0
  21. package/dist/agents/plugin-sandbox.d.ts +1 -0
  22. package/dist/agents/plugin-sandbox.d.ts.map +1 -0
  23. package/dist/agents/unified-config-file-watcher.d.ts +1 -0
  24. package/dist/agents/unified-config-file-watcher.d.ts.map +1 -0
  25. package/dist/agents/user-instruction-config-loader.d.ts +1 -0
  26. package/dist/agents/user-instruction-config-loader.d.ts.map +1 -0
  27. package/dist/auth/client.d.ts +1 -0
  28. package/dist/auth/client.d.ts.map +1 -0
  29. package/dist/auth/cline.d.ts +1 -0
  30. package/dist/auth/cline.d.ts.map +1 -0
  31. package/dist/auth/codex.d.ts +1 -0
  32. package/dist/auth/codex.d.ts.map +1 -0
  33. package/dist/auth/oca.d.ts +1 -0
  34. package/dist/auth/oca.d.ts.map +1 -0
  35. package/dist/auth/server.d.ts +1 -0
  36. package/dist/auth/server.d.ts.map +1 -0
  37. package/dist/auth/types.d.ts +1 -0
  38. package/dist/auth/types.d.ts.map +1 -0
  39. package/dist/auth/utils.d.ts +1 -0
  40. package/dist/auth/utils.d.ts.map +1 -0
  41. package/dist/chat/chat-schema.d.ts +13 -12
  42. package/dist/chat/chat-schema.d.ts.map +1 -0
  43. package/dist/index.d.ts +3 -1
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.node.d.ts +2 -0
  46. package/dist/index.node.d.ts.map +1 -0
  47. package/dist/index.node.js +303 -302
  48. package/dist/input/file-indexer.d.ts +1 -0
  49. package/dist/input/file-indexer.d.ts.map +1 -0
  50. package/dist/input/index.d.ts +1 -0
  51. package/dist/input/index.d.ts.map +1 -0
  52. package/dist/input/mention-enricher.d.ts +1 -0
  53. package/dist/input/mention-enricher.d.ts.map +1 -0
  54. package/dist/mcp/config-loader.d.ts +1 -0
  55. package/dist/mcp/config-loader.d.ts.map +1 -0
  56. package/dist/mcp/index.d.ts +1 -0
  57. package/dist/mcp/index.d.ts.map +1 -0
  58. package/dist/mcp/manager.d.ts +1 -0
  59. package/dist/mcp/manager.d.ts.map +1 -0
  60. package/dist/mcp/types.d.ts +1 -0
  61. package/dist/mcp/types.d.ts.map +1 -0
  62. package/dist/providers/local-provider-registry.d.ts +36 -0
  63. package/dist/providers/local-provider-registry.d.ts.map +1 -0
  64. package/dist/providers/local-provider-service.d.ts +2 -1
  65. package/dist/providers/local-provider-service.d.ts.map +1 -0
  66. package/dist/runtime/commands.d.ts +1 -0
  67. package/dist/runtime/commands.d.ts.map +1 -0
  68. package/dist/runtime/hook-file-hooks.d.ts +1 -0
  69. package/dist/runtime/hook-file-hooks.d.ts.map +1 -0
  70. package/dist/runtime/rules.d.ts +1 -0
  71. package/dist/runtime/rules.d.ts.map +1 -0
  72. package/dist/runtime/runtime-builder.d.ts +1 -0
  73. package/dist/runtime/runtime-builder.d.ts.map +1 -0
  74. package/dist/runtime/sandbox/subprocess-sandbox.d.ts +1 -0
  75. package/dist/runtime/sandbox/subprocess-sandbox.d.ts.map +1 -0
  76. package/dist/runtime/session-runtime.d.ts +2 -0
  77. package/dist/runtime/session-runtime.d.ts.map +1 -0
  78. package/dist/runtime/skills.d.ts +1 -0
  79. package/dist/runtime/skills.d.ts.map +1 -0
  80. package/dist/runtime/tool-approval.d.ts +1 -0
  81. package/dist/runtime/tool-approval.d.ts.map +1 -0
  82. package/dist/runtime/workflows.d.ts +1 -0
  83. package/dist/runtime/workflows.d.ts.map +1 -0
  84. package/dist/session/default-session-manager.d.ts +4 -0
  85. package/dist/session/default-session-manager.d.ts.map +1 -0
  86. package/dist/session/file-session-service.d.ts +1 -0
  87. package/dist/session/file-session-service.d.ts.map +1 -0
  88. package/dist/session/rpc-session-service.d.ts +1 -0
  89. package/dist/session/rpc-session-service.d.ts.map +1 -0
  90. package/dist/session/rpc-spawn-lease.d.ts +1 -0
  91. package/dist/session/rpc-spawn-lease.d.ts.map +1 -0
  92. package/dist/session/runtime-oauth-token-manager.d.ts +1 -0
  93. package/dist/session/runtime-oauth-token-manager.d.ts.map +1 -0
  94. package/dist/session/session-agent-events.d.ts +20 -1
  95. package/dist/session/session-agent-events.d.ts.map +1 -0
  96. package/dist/session/session-artifacts.d.ts +1 -0
  97. package/dist/session/session-artifacts.d.ts.map +1 -0
  98. package/dist/session/session-config-builder.d.ts +1 -0
  99. package/dist/session/session-config-builder.d.ts.map +1 -0
  100. package/dist/session/session-graph.d.ts +1 -0
  101. package/dist/session/session-graph.d.ts.map +1 -0
  102. package/dist/session/session-host.d.ts +1 -0
  103. package/dist/session/session-host.d.ts.map +1 -0
  104. package/dist/session/session-manager.d.ts +1 -0
  105. package/dist/session/session-manager.d.ts.map +1 -0
  106. package/dist/session/session-manifest.d.ts +2 -1
  107. package/dist/session/session-manifest.d.ts.map +1 -0
  108. package/dist/session/session-service.d.ts +1 -0
  109. package/dist/session/session-service.d.ts.map +1 -0
  110. package/dist/session/session-team-coordination.d.ts +1 -0
  111. package/dist/session/session-team-coordination.d.ts.map +1 -0
  112. package/dist/session/session-telemetry.d.ts +3 -1
  113. package/dist/session/session-telemetry.d.ts.map +1 -0
  114. package/dist/session/sqlite-rpc-session-backend.d.ts +1 -0
  115. package/dist/session/sqlite-rpc-session-backend.d.ts.map +1 -0
  116. package/dist/session/unified-session-persistence-service.d.ts +1 -0
  117. package/dist/session/unified-session-persistence-service.d.ts.map +1 -0
  118. package/dist/session/utils/helpers.d.ts +1 -0
  119. package/dist/session/utils/helpers.d.ts.map +1 -0
  120. package/dist/session/utils/types.d.ts +1 -0
  121. package/dist/session/utils/types.d.ts.map +1 -0
  122. package/dist/session/utils/usage.d.ts +1 -0
  123. package/dist/session/utils/usage.d.ts.map +1 -0
  124. package/dist/session/workspace-manager.d.ts +1 -0
  125. package/dist/session/workspace-manager.d.ts.map +1 -0
  126. package/dist/session/workspace-manifest.d.ts +1 -0
  127. package/dist/session/workspace-manifest.d.ts.map +1 -0
  128. package/dist/storage/file-team-store.d.ts +1 -0
  129. package/dist/storage/file-team-store.d.ts.map +1 -0
  130. package/dist/storage/provider-settings-legacy-migration.d.ts +1 -0
  131. package/dist/storage/provider-settings-legacy-migration.d.ts.map +1 -0
  132. package/dist/storage/provider-settings-manager.d.ts +1 -0
  133. package/dist/storage/provider-settings-manager.d.ts.map +1 -0
  134. package/dist/storage/sqlite-session-store.d.ts +1 -0
  135. package/dist/storage/sqlite-session-store.d.ts.map +1 -0
  136. package/dist/storage/sqlite-team-store.d.ts +1 -0
  137. package/dist/storage/sqlite-team-store.d.ts.map +1 -0
  138. package/dist/storage/team-store.d.ts +1 -0
  139. package/dist/storage/team-store.d.ts.map +1 -0
  140. package/dist/team/index.d.ts +1 -0
  141. package/dist/team/index.d.ts.map +1 -0
  142. package/dist/team/projections.d.ts +1 -0
  143. package/dist/team/projections.d.ts.map +1 -0
  144. package/dist/telemetry/ITelemetryAdapter.d.ts +1 -0
  145. package/dist/telemetry/ITelemetryAdapter.d.ts.map +1 -0
  146. package/dist/telemetry/LoggerTelemetryAdapter.d.ts +1 -0
  147. package/dist/telemetry/LoggerTelemetryAdapter.d.ts.map +1 -0
  148. package/dist/telemetry/OpenTelemetryAdapter.d.ts +1 -0
  149. package/dist/telemetry/OpenTelemetryAdapter.d.ts.map +1 -0
  150. package/dist/telemetry/OpenTelemetryProvider.d.ts +1 -0
  151. package/dist/telemetry/OpenTelemetryProvider.d.ts.map +1 -0
  152. package/dist/telemetry/TelemetryService.d.ts +1 -0
  153. package/dist/telemetry/TelemetryService.d.ts.map +1 -0
  154. package/dist/telemetry/core-events.d.ts +55 -22
  155. package/dist/telemetry/core-events.d.ts.map +1 -0
  156. package/dist/telemetry/opentelemetry.d.ts +1 -0
  157. package/dist/telemetry/opentelemetry.d.ts.map +1 -0
  158. package/dist/tools/constants.d.ts +1 -0
  159. package/dist/tools/constants.d.ts.map +1 -0
  160. package/dist/tools/definitions.d.ts +8 -1
  161. package/dist/tools/definitions.d.ts.map +1 -0
  162. package/dist/tools/executors/apply-patch-parser.d.ts +1 -0
  163. package/dist/tools/executors/apply-patch-parser.d.ts.map +1 -0
  164. package/dist/tools/executors/apply-patch.d.ts +1 -0
  165. package/dist/tools/executors/apply-patch.d.ts.map +1 -0
  166. package/dist/tools/executors/bash.d.ts +2 -1
  167. package/dist/tools/executors/bash.d.ts.map +1 -0
  168. package/dist/tools/executors/editor.d.ts +1 -0
  169. package/dist/tools/executors/editor.d.ts.map +1 -0
  170. package/dist/tools/executors/file-read.d.ts +1 -0
  171. package/dist/tools/executors/file-read.d.ts.map +1 -0
  172. package/dist/tools/executors/index.d.ts +14 -7
  173. package/dist/tools/executors/index.d.ts.map +1 -0
  174. package/dist/tools/executors/search.d.ts +1 -0
  175. package/dist/tools/executors/search.d.ts.map +1 -0
  176. package/dist/tools/executors/web-fetch.d.ts +1 -0
  177. package/dist/tools/executors/web-fetch.d.ts.map +1 -0
  178. package/dist/tools/helpers.d.ts +15 -0
  179. package/dist/tools/helpers.d.ts.map +1 -0
  180. package/dist/tools/index.d.ts +2 -1
  181. package/dist/tools/index.d.ts.map +1 -0
  182. package/dist/tools/model-tool-routing.d.ts +1 -0
  183. package/dist/tools/model-tool-routing.d.ts.map +1 -0
  184. package/dist/tools/presets.d.ts +1 -0
  185. package/dist/tools/presets.d.ts.map +1 -0
  186. package/dist/tools/schemas.d.ts +41 -0
  187. package/dist/tools/schemas.d.ts.map +1 -0
  188. package/dist/tools/types.d.ts +3 -2
  189. package/dist/tools/types.d.ts.map +1 -0
  190. package/dist/types/common.d.ts +1 -0
  191. package/dist/types/common.d.ts.map +1 -0
  192. package/dist/types/config.d.ts +1 -0
  193. package/dist/types/config.d.ts.map +1 -0
  194. package/dist/types/events.d.ts +1 -0
  195. package/dist/types/events.d.ts.map +1 -0
  196. package/dist/types/provider-settings.d.ts +1 -0
  197. package/dist/types/provider-settings.d.ts.map +1 -0
  198. package/dist/types/sessions.d.ts +1 -0
  199. package/dist/types/sessions.d.ts.map +1 -0
  200. package/dist/types/storage.d.ts +1 -0
  201. package/dist/types/storage.d.ts.map +1 -0
  202. package/dist/types/workspace.d.ts +1 -0
  203. package/dist/types/workspace.d.ts.map +1 -0
  204. package/dist/types.d.ts +1 -0
  205. package/dist/types.d.ts.map +1 -0
  206. package/package.json +8 -6
  207. package/src/account/cline-account-service.test.ts +0 -101
  208. package/src/account/cline-account-service.ts +0 -287
  209. package/src/account/index.ts +0 -22
  210. package/src/account/rpc.test.ts +0 -62
  211. package/src/account/rpc.ts +0 -172
  212. package/src/account/types.ts +0 -98
  213. package/src/agents/agent-config-loader.test.ts +0 -236
  214. package/src/agents/agent-config-loader.ts +0 -108
  215. package/src/agents/agent-config-parser.ts +0 -198
  216. package/src/agents/hooks-config-loader.test.ts +0 -20
  217. package/src/agents/hooks-config-loader.ts +0 -118
  218. package/src/agents/index.ts +0 -85
  219. package/src/agents/plugin-config-loader.test.ts +0 -140
  220. package/src/agents/plugin-config-loader.ts +0 -97
  221. package/src/agents/plugin-loader.test.ts +0 -228
  222. package/src/agents/plugin-loader.ts +0 -172
  223. package/src/agents/plugin-sandbox-bootstrap.ts +0 -445
  224. package/src/agents/plugin-sandbox.test.ts +0 -317
  225. package/src/agents/plugin-sandbox.ts +0 -341
  226. package/src/agents/unified-config-file-watcher.test.ts +0 -196
  227. package/src/agents/unified-config-file-watcher.ts +0 -483
  228. package/src/agents/user-instruction-config-loader.test.ts +0 -158
  229. package/src/agents/user-instruction-config-loader.ts +0 -438
  230. package/src/auth/client.test.ts +0 -40
  231. package/src/auth/client.ts +0 -25
  232. package/src/auth/cline.test.ts +0 -130
  233. package/src/auth/cline.ts +0 -420
  234. package/src/auth/codex.test.ts +0 -170
  235. package/src/auth/codex.ts +0 -491
  236. package/src/auth/oca.test.ts +0 -215
  237. package/src/auth/oca.ts +0 -573
  238. package/src/auth/server.ts +0 -216
  239. package/src/auth/types.ts +0 -81
  240. package/src/auth/utils.test.ts +0 -128
  241. package/src/auth/utils.ts +0 -247
  242. package/src/chat/chat-schema.ts +0 -82
  243. package/src/index.node.ts +0 -285
  244. package/src/index.ts +0 -211
  245. package/src/input/file-indexer.d.ts +0 -11
  246. package/src/input/file-indexer.test.ts +0 -127
  247. package/src/input/file-indexer.ts +0 -327
  248. package/src/input/index.ts +0 -7
  249. package/src/input/mention-enricher.test.ts +0 -85
  250. package/src/input/mention-enricher.ts +0 -122
  251. package/src/mcp/config-loader.test.ts +0 -238
  252. package/src/mcp/config-loader.ts +0 -219
  253. package/src/mcp/index.ts +0 -26
  254. package/src/mcp/manager.test.ts +0 -106
  255. package/src/mcp/manager.ts +0 -262
  256. package/src/mcp/types.ts +0 -88
  257. package/src/providers/local-provider-service.ts +0 -608
  258. package/src/runtime/commands.test.ts +0 -98
  259. package/src/runtime/commands.ts +0 -83
  260. package/src/runtime/hook-file-hooks.test.ts +0 -237
  261. package/src/runtime/hook-file-hooks.ts +0 -859
  262. package/src/runtime/index.ts +0 -37
  263. package/src/runtime/rules.ts +0 -34
  264. package/src/runtime/runtime-builder.team-persistence.test.ts +0 -202
  265. package/src/runtime/runtime-builder.test.ts +0 -371
  266. package/src/runtime/runtime-builder.ts +0 -589
  267. package/src/runtime/runtime-parity.test.ts +0 -143
  268. package/src/runtime/sandbox/subprocess-sandbox.ts +0 -231
  269. package/src/runtime/session-runtime.ts +0 -46
  270. package/src/runtime/skills.ts +0 -44
  271. package/src/runtime/tool-approval.ts +0 -104
  272. package/src/runtime/workflows.test.ts +0 -119
  273. package/src/runtime/workflows.ts +0 -45
  274. package/src/session/default-session-manager.e2e.test.ts +0 -384
  275. package/src/session/default-session-manager.test.ts +0 -1741
  276. package/src/session/default-session-manager.ts +0 -1233
  277. package/src/session/file-session-service.ts +0 -280
  278. package/src/session/index.ts +0 -42
  279. package/src/session/rpc-session-service.ts +0 -107
  280. package/src/session/rpc-spawn-lease.test.ts +0 -49
  281. package/src/session/rpc-spawn-lease.ts +0 -122
  282. package/src/session/runtime-oauth-token-manager.test.ts +0 -137
  283. package/src/session/runtime-oauth-token-manager.ts +0 -272
  284. package/src/session/session-agent-events.ts +0 -159
  285. package/src/session/session-artifacts.ts +0 -106
  286. package/src/session/session-config-builder.ts +0 -113
  287. package/src/session/session-graph.ts +0 -92
  288. package/src/session/session-host.test.ts +0 -29
  289. package/src/session/session-host.ts +0 -242
  290. package/src/session/session-manager.ts +0 -69
  291. package/src/session/session-manifest.ts +0 -29
  292. package/src/session/session-service.team-persistence.test.ts +0 -48
  293. package/src/session/session-service.ts +0 -673
  294. package/src/session/session-team-coordination.ts +0 -229
  295. package/src/session/session-telemetry.ts +0 -95
  296. package/src/session/sqlite-rpc-session-backend.ts +0 -303
  297. package/src/session/unified-session-persistence-service.test.ts +0 -85
  298. package/src/session/unified-session-persistence-service.ts +0 -996
  299. package/src/session/utils/helpers.ts +0 -139
  300. package/src/session/utils/types.ts +0 -57
  301. package/src/session/utils/usage.ts +0 -32
  302. package/src/session/workspace-manager.ts +0 -98
  303. package/src/session/workspace-manifest.ts +0 -100
  304. package/src/storage/artifact-store.ts +0 -1
  305. package/src/storage/file-team-store.ts +0 -257
  306. package/src/storage/index.ts +0 -11
  307. package/src/storage/provider-settings-legacy-migration.test.ts +0 -307
  308. package/src/storage/provider-settings-legacy-migration.ts +0 -689
  309. package/src/storage/provider-settings-manager.test.ts +0 -145
  310. package/src/storage/provider-settings-manager.ts +0 -150
  311. package/src/storage/session-store.ts +0 -1
  312. package/src/storage/sqlite-session-store.ts +0 -275
  313. package/src/storage/sqlite-team-store.ts +0 -454
  314. package/src/storage/team-store.ts +0 -40
  315. package/src/team/index.ts +0 -4
  316. package/src/team/projections.ts +0 -285
  317. package/src/telemetry/ITelemetryAdapter.ts +0 -94
  318. package/src/telemetry/LoggerTelemetryAdapter.test.ts +0 -42
  319. package/src/telemetry/LoggerTelemetryAdapter.ts +0 -114
  320. package/src/telemetry/OpenTelemetryAdapter.test.ts +0 -157
  321. package/src/telemetry/OpenTelemetryAdapter.ts +0 -348
  322. package/src/telemetry/OpenTelemetryProvider.test.ts +0 -113
  323. package/src/telemetry/OpenTelemetryProvider.ts +0 -322
  324. package/src/telemetry/TelemetryService.test.ts +0 -134
  325. package/src/telemetry/TelemetryService.ts +0 -141
  326. package/src/telemetry/core-events.ts +0 -344
  327. package/src/telemetry/opentelemetry.ts +0 -20
  328. package/src/tools/constants.ts +0 -35
  329. package/src/tools/definitions.test.ts +0 -658
  330. package/src/tools/definitions.ts +0 -726
  331. package/src/tools/executors/apply-patch-parser.ts +0 -520
  332. package/src/tools/executors/apply-patch.ts +0 -359
  333. package/src/tools/executors/bash.ts +0 -205
  334. package/src/tools/executors/editor.test.ts +0 -35
  335. package/src/tools/executors/editor.ts +0 -219
  336. package/src/tools/executors/file-read.test.ts +0 -49
  337. package/src/tools/executors/file-read.ts +0 -110
  338. package/src/tools/executors/index.ts +0 -75
  339. package/src/tools/executors/search.ts +0 -278
  340. package/src/tools/executors/web-fetch.ts +0 -259
  341. package/src/tools/index.ts +0 -168
  342. package/src/tools/model-tool-routing.test.ts +0 -86
  343. package/src/tools/model-tool-routing.ts +0 -132
  344. package/src/tools/presets.test.ts +0 -62
  345. package/src/tools/presets.ts +0 -168
  346. package/src/tools/schemas.ts +0 -284
  347. package/src/tools/types.ts +0 -328
  348. package/src/types/common.ts +0 -14
  349. package/src/types/config.ts +0 -84
  350. package/src/types/events.ts +0 -74
  351. package/src/types/index.ts +0 -24
  352. package/src/types/provider-settings.ts +0 -43
  353. package/src/types/sessions.ts +0 -16
  354. package/src/types/storage.ts +0 -64
  355. package/src/types/workspace.ts +0 -7
  356. package/src/types.ts +0 -128
@@ -1,859 +0,0 @@
1
- import { spawn } from "node:child_process";
2
- import { appendFileSync, readFileSync } from "node:fs";
3
- import type {
4
- AgentHooks,
5
- HookEventName,
6
- HookEventPayload,
7
- } from "@clinebot/agents";
8
- import type { BasicLogger, HookSessionContext } from "@clinebot/shared";
9
- import { ensureParentDir } from "@clinebot/shared/storage";
10
- import { listHookConfigFiles } from "../agents/hooks-config-loader";
11
-
12
- type HookContextBase = {
13
- agentId: string;
14
- conversationId: string;
15
- parentAgentId: string | null;
16
- };
17
-
18
- type AgentHookControl = NonNullable<
19
- Awaited<ReturnType<NonNullable<AgentHooks["onToolCallStart"]>>>
20
- >;
21
- type AgentHookRunStartContext = Parameters<
22
- NonNullable<AgentHooks["onRunStart"]>
23
- >[0];
24
- type AgentHookToolCallStartContext = Parameters<
25
- NonNullable<AgentHooks["onToolCallStart"]>
26
- >[0];
27
- type AgentHookToolCallEndContext = Parameters<
28
- NonNullable<AgentHooks["onToolCallEnd"]>
29
- >[0];
30
- type AgentHookTurnEndContext = Parameters<
31
- NonNullable<AgentHooks["onTurnEnd"]>
32
- >[0];
33
- type AgentHookStopErrorContext = Parameters<
34
- NonNullable<AgentHooks["onStopError"]>
35
- >[0];
36
- type AgentHookSessionShutdownContext = Parameters<
37
- NonNullable<AgentHooks["onSessionShutdown"]>
38
- >[0];
39
-
40
- type HookRuntimeOptions = {
41
- cwd: string;
42
- workspacePath: string;
43
- hookLogPath?: string;
44
- rootSessionId?: string;
45
- logger?: BasicLogger;
46
- toolCallTimeoutMs?: number;
47
- };
48
-
49
- function mapParams(input: unknown): Record<string, string> {
50
- if (!input || typeof input !== "object") {
51
- return {};
52
- }
53
- const output: Record<string, string> = {};
54
- for (const [key, value] of Object.entries(input as Record<string, unknown>)) {
55
- output[key] = typeof value === "string" ? value : JSON.stringify(value);
56
- }
57
- return output;
58
- }
59
-
60
- function logHookError(
61
- logger: BasicLogger | undefined,
62
- message: string,
63
- error?: unknown,
64
- ): void {
65
- const detail = error instanceof Error ? `: ${error.message}` : "";
66
- const text = `${message}${detail}`;
67
- if (logger?.warn) {
68
- logger.warn(text);
69
- return;
70
- }
71
- console.warn(text);
72
- }
73
-
74
- function mergeHookControls(
75
- current: AgentHookControl | undefined,
76
- next: AgentHookControl | undefined,
77
- ): AgentHookControl | undefined {
78
- if (!next) {
79
- return current;
80
- }
81
- if (!current) {
82
- return { ...next };
83
- }
84
- const contexts = [current.context, next.context]
85
- .filter(
86
- (value): value is string => typeof value === "string" && value.length > 0,
87
- )
88
- .join("\n");
89
- const appendMessages = [
90
- ...(current.appendMessages ?? []),
91
- ...(next.appendMessages ?? []),
92
- ];
93
- return {
94
- cancel: current.cancel === true || next.cancel === true ? true : undefined,
95
- review: current.review === true || next.review === true ? true : undefined,
96
- context: contexts || undefined,
97
- overrideInput:
98
- next.overrideInput !== undefined
99
- ? next.overrideInput
100
- : current.overrideInput,
101
- systemPrompt:
102
- next.systemPrompt !== undefined
103
- ? next.systemPrompt
104
- : current.systemPrompt,
105
- appendMessages: appendMessages.length > 0 ? appendMessages : undefined,
106
- };
107
- }
108
-
109
- function parseHookControl(value: unknown): AgentHookControl | undefined {
110
- if (!value || typeof value !== "object") {
111
- return undefined;
112
- }
113
- const record = value as Record<string, unknown>;
114
- const context =
115
- typeof record.context === "string"
116
- ? record.context
117
- : typeof record.contextModification === "string"
118
- ? record.contextModification
119
- : typeof record.errorMessage === "string"
120
- ? record.errorMessage
121
- : undefined;
122
- return {
123
- cancel: typeof record.cancel === "boolean" ? record.cancel : undefined,
124
- review: typeof record.review === "boolean" ? record.review : undefined,
125
- context,
126
- overrideInput: Object.hasOwn(record, "overrideInput")
127
- ? record.overrideInput
128
- : undefined,
129
- };
130
- }
131
-
132
- function isAbortReason(reason?: string): boolean {
133
- const value = String(reason ?? "").toLowerCase();
134
- return (
135
- value.includes("cancel") ||
136
- value.includes("abort") ||
137
- value.includes("interrupt")
138
- );
139
- }
140
-
141
- function ensureHookLogDir(filePath: string): void {
142
- ensureParentDir(filePath);
143
- }
144
-
145
- function createPayloadBase(
146
- ctx: HookContextBase,
147
- options: HookRuntimeOptions,
148
- ): Omit<HookEventPayload, "hookName"> {
149
- const userId =
150
- process.env.CLINE_USER_ID?.trim() || process.env.USER?.trim() || "unknown";
151
- const sessionContext: HookSessionContext = {
152
- rootSessionId: options.rootSessionId || ctx.conversationId,
153
- hookLogPath: options.hookLogPath,
154
- };
155
- return {
156
- clineVersion: process.env.CLINE_VERSION?.trim() || "",
157
- timestamp: new Date().toISOString(),
158
- taskId: ctx.conversationId,
159
- sessionContext,
160
- workspaceRoots: options.workspacePath ? [options.workspacePath] : [],
161
- userId,
162
- agent_id: ctx.agentId,
163
- parent_agent_id: ctx.parentAgentId,
164
- } as Omit<HookEventPayload, "hookName">;
165
- }
166
-
167
- type HookCommandMap = Partial<Record<HookEventName, string[][]>>;
168
-
169
- interface HookCommandResult {
170
- exitCode: number | null;
171
- stdout: string;
172
- stderr: string;
173
- parsedJson?: unknown;
174
- parseError?: string;
175
- timedOut?: boolean;
176
- }
177
-
178
- function parseHookStdout(stdout: string): {
179
- parsedJson?: unknown;
180
- parseError?: string;
181
- } {
182
- const trimmed = stdout.trim();
183
- if (!trimmed) {
184
- return {};
185
- }
186
- const lines = trimmed
187
- .split("\n")
188
- .map((line) => line.trim())
189
- .filter(Boolean);
190
- const prefixed = lines
191
- .filter((line) => line.startsWith("HOOK_CONTROL\t"))
192
- .map((line) => line.slice("HOOK_CONTROL\t".length));
193
- const candidate =
194
- prefixed.length > 0 ? prefixed[prefixed.length - 1] : trimmed;
195
- try {
196
- return { parsedJson: JSON.parse(candidate) };
197
- } catch (error) {
198
- return {
199
- parseError:
200
- error instanceof Error
201
- ? error.message
202
- : "Failed to parse hook stdout JSON",
203
- };
204
- }
205
- }
206
-
207
- async function writeToChildStdin(
208
- child: ReturnType<typeof spawn>,
209
- body: string,
210
- ): Promise<void> {
211
- const stdin = child.stdin;
212
- if (!stdin) {
213
- throw new Error("hook command failed to create stdin");
214
- }
215
-
216
- await new Promise<void>((resolve, reject) => {
217
- let settled = false;
218
- const cleanup = () => {
219
- stdin.off("error", onError);
220
- stdin.off("finish", onFinish);
221
- child.off("close", onChildClose);
222
- };
223
- const finish = (error?: Error | null) => {
224
- if (settled) {
225
- return;
226
- }
227
- settled = true;
228
- cleanup();
229
- if (error) {
230
- const code = (error as Error & { code?: string }).code;
231
- if (code === "EPIPE" || code === "ERR_STREAM_DESTROYED") {
232
- resolve();
233
- return;
234
- }
235
- reject(error);
236
- return;
237
- }
238
- resolve();
239
- };
240
- const onError = (error: Error) => finish(error);
241
- const onFinish = () => finish();
242
- const onChildClose = () => finish();
243
- stdin.on("error", onError);
244
- stdin.once("finish", onFinish);
245
- child.once("close", onChildClose);
246
- try {
247
- stdin.end(body);
248
- } catch (error) {
249
- finish(error as Error);
250
- }
251
- });
252
- }
253
-
254
- async function runHookCommand(
255
- payload: HookEventPayload,
256
- options: {
257
- command: string[];
258
- cwd: string;
259
- env?: NodeJS.ProcessEnv;
260
- detached: boolean;
261
- timeoutMs?: number;
262
- },
263
- ): Promise<HookCommandResult | undefined> {
264
- if (options.command.length === 0) {
265
- throw new Error("runHookCommand requires non-empty command");
266
- }
267
- const child = spawn(options.command[0], options.command.slice(1), {
268
- cwd: options.cwd,
269
- env: options.env,
270
- stdio: options.detached
271
- ? ["pipe", "ignore", "ignore"]
272
- : ["pipe", "pipe", "pipe"],
273
- detached: options.detached,
274
- });
275
- const spawned = new Promise<void>((resolve) => {
276
- child.once("spawn", () => resolve());
277
- });
278
- const childError = new Promise<never>((_, reject) => {
279
- child.once("error", (error) => reject(error));
280
- });
281
-
282
- const body = JSON.stringify(payload);
283
- await Promise.race([spawned, childError]);
284
- await writeToChildStdin(child, body);
285
-
286
- if (options.detached) {
287
- child.unref();
288
- return;
289
- }
290
-
291
- if (!child.stdout || !child.stderr) {
292
- throw new Error("hook command failed to create stdout/stderr");
293
- }
294
- let stdout = "";
295
- let stderr = "";
296
- let timedOut = false;
297
- let timeoutId: NodeJS.Timeout | undefined;
298
- child.stdout.on("data", (chunk: Buffer | string) => {
299
- stdout += chunk.toString();
300
- });
301
- child.stderr.on("data", (chunk: Buffer | string) => {
302
- stderr += chunk.toString();
303
- });
304
-
305
- const result = new Promise<HookCommandResult>((resolve) => {
306
- if ((options.timeoutMs ?? 0) > 0) {
307
- timeoutId = setTimeout(() => {
308
- timedOut = true;
309
- child.kill("SIGKILL");
310
- }, options.timeoutMs);
311
- }
312
- child.once("close", (exitCode) => {
313
- if (timeoutId) {
314
- clearTimeout(timeoutId);
315
- }
316
- const { parsedJson, parseError } = parseHookStdout(stdout);
317
- resolve({
318
- exitCode,
319
- stdout,
320
- stderr,
321
- parsedJson,
322
- parseError,
323
- timedOut,
324
- });
325
- });
326
- });
327
- return await Promise.race([result, childError]);
328
- }
329
-
330
- function parseShebangCommand(path: string): string[] | undefined {
331
- try {
332
- const content = readFileSync(path, "utf8");
333
- const firstLine = content.split(/\r?\n/, 1)[0]?.trim();
334
- if (!firstLine?.startsWith("#!")) {
335
- return undefined;
336
- }
337
- const shebang = firstLine.slice(2).trim();
338
- if (!shebang) {
339
- return undefined;
340
- }
341
- const tokens = shebang.split(/\s+/).filter(Boolean);
342
- return tokens.length > 0 ? tokens : undefined;
343
- } catch {
344
- return undefined;
345
- }
346
- }
347
-
348
- function normalizeHookInterpreter(tokens: string[]): string[] | undefined {
349
- if (tokens.length === 0) {
350
- return undefined;
351
- }
352
- const [rawCommand, ...rest] = tokens;
353
- const normalizedCommand = rawCommand.replace(/\\/g, "/").toLowerCase();
354
- const commandName = normalizedCommand.split("/").at(-1) ?? normalizedCommand;
355
-
356
- if (commandName === "env") {
357
- return normalizeHookInterpreter(rest);
358
- }
359
-
360
- if (commandName === "bash" || commandName === "sh" || commandName === "zsh") {
361
- return [commandName, ...rest];
362
- }
363
-
364
- if (commandName === "python3" || commandName === "python") {
365
- return [process.platform === "win32" ? "python" : commandName, ...rest];
366
- }
367
-
368
- return tokens;
369
- }
370
-
371
- function inferHookCommand(path: string): string[] {
372
- const shebang = parseShebangCommand(path);
373
- if (shebang && shebang.length > 0) {
374
- return [...(normalizeHookInterpreter(shebang) ?? shebang), path];
375
- }
376
- const lowered = path.toLowerCase();
377
- if (
378
- lowered.endsWith(".sh") ||
379
- lowered.endsWith(".bash") ||
380
- lowered.endsWith(".zsh")
381
- ) {
382
- return ["bash", path];
383
- }
384
- if (
385
- lowered.endsWith(".js") ||
386
- lowered.endsWith(".mjs") ||
387
- lowered.endsWith(".cjs")
388
- ) {
389
- return ["node", path];
390
- }
391
- if (
392
- lowered.endsWith(".ts") ||
393
- lowered.endsWith(".mts") ||
394
- lowered.endsWith(".cts")
395
- ) {
396
- return ["bun", "run", path];
397
- }
398
- if (lowered.endsWith(".py")) {
399
- return [process.platform === "win32" ? "python" : "python3", path];
400
- }
401
- if (lowered.endsWith(".ps1")) {
402
- return [
403
- process.platform === "win32" ? "powershell" : "pwsh",
404
- "-File",
405
- path,
406
- ];
407
- }
408
- // Default to bash for legacy hook files with no extension/shebang.
409
- return ["bash", path];
410
- }
411
-
412
- function createHookCommandMap(workspacePath: string): HookCommandMap {
413
- const map: HookCommandMap = {};
414
- for (const file of listHookConfigFiles(workspacePath)) {
415
- if (!file.hookEventName) {
416
- continue;
417
- }
418
- const existing = map[file.hookEventName] ?? [];
419
- existing.push(inferHookCommand(file.path));
420
- map[file.hookEventName] = existing;
421
- }
422
- return map;
423
- }
424
-
425
- async function runBlockingHookCommands(options: {
426
- commands: string[][];
427
- payload: HookEventPayload;
428
- cwd: string;
429
- logger?: BasicLogger;
430
- timeoutMs?: number;
431
- }): Promise<AgentHookControl | undefined> {
432
- let merged: AgentHookControl | undefined;
433
- for (const command of options.commands) {
434
- const commandLabel = command.join(" ");
435
- try {
436
- const result = await runHookCommand(options.payload, {
437
- command,
438
- cwd: options.cwd,
439
- env: process.env,
440
- detached: false,
441
- timeoutMs: options.timeoutMs,
442
- });
443
- if (result?.timedOut) {
444
- logHookError(options.logger, `hook command timed out: ${commandLabel}`);
445
- continue;
446
- }
447
- if (result?.parseError) {
448
- logHookError(
449
- options.logger,
450
- `hook command returned invalid JSON control output: ${commandLabel} (${result.parseError})`,
451
- );
452
- continue;
453
- }
454
- merged = mergeHookControls(merged, parseHookControl(result?.parsedJson));
455
- } catch (error) {
456
- logHookError(
457
- options.logger,
458
- `hook command failed: ${commandLabel}`,
459
- error,
460
- );
461
- }
462
- }
463
- return merged;
464
- }
465
-
466
- function runAsyncHookCommands(options: {
467
- commands: string[][];
468
- payload: HookEventPayload;
469
- cwd: string;
470
- logger?: BasicLogger;
471
- }): void {
472
- for (const command of options.commands) {
473
- const commandLabel = command.join(" ");
474
- void runHookCommand(options.payload, {
475
- command,
476
- cwd: options.cwd,
477
- env: process.env,
478
- detached: true,
479
- }).catch((error) => {
480
- logHookError(
481
- options.logger,
482
- `hook command failed: ${commandLabel}`,
483
- error,
484
- );
485
- });
486
- }
487
- }
488
-
489
- export function createHookAuditHooks(options: {
490
- hookLogPath: string;
491
- rootSessionId?: string;
492
- workspacePath: string;
493
- }): AgentHooks {
494
- const runtimeOptions: HookRuntimeOptions = {
495
- cwd: options.workspacePath,
496
- workspacePath: options.workspacePath,
497
- hookLogPath: options.hookLogPath,
498
- rootSessionId: options.rootSessionId,
499
- };
500
-
501
- const append = (payload: HookEventPayload): void => {
502
- const line = `${JSON.stringify({
503
- ts: new Date().toISOString(),
504
- ...payload,
505
- })}\n`;
506
- ensureHookLogDir(options.hookLogPath);
507
- appendFileSync(options.hookLogPath, line, "utf8");
508
- };
509
-
510
- return {
511
- onRunStart: async (ctx: AgentHookRunStartContext) => {
512
- append({
513
- ...createPayloadBase(ctx, runtimeOptions),
514
- hookName: "agent_start",
515
- taskStart: { taskMetadata: {} },
516
- });
517
- append({
518
- ...createPayloadBase(ctx, runtimeOptions),
519
- hookName: "prompt_submit",
520
- userPromptSubmit: {
521
- prompt: ctx.userMessage,
522
- attachments: [],
523
- },
524
- });
525
- return undefined;
526
- },
527
- onToolCallStart: async (ctx: AgentHookToolCallStartContext) => {
528
- append({
529
- ...createPayloadBase(ctx, runtimeOptions),
530
- hookName: "tool_call",
531
- iteration: ctx.iteration,
532
- tool_call: {
533
- id: ctx.call.id,
534
- name: ctx.call.name,
535
- input: ctx.call.input,
536
- },
537
- preToolUse: {
538
- toolName: ctx.call.name,
539
- parameters: mapParams(ctx.call.input),
540
- },
541
- });
542
- return undefined;
543
- },
544
- onToolCallEnd: async (ctx: AgentHookToolCallEndContext) => {
545
- append({
546
- ...createPayloadBase(ctx, runtimeOptions),
547
- hookName: "tool_result",
548
- iteration: ctx.iteration,
549
- tool_result: ctx.record,
550
- postToolUse: {
551
- toolName: ctx.record.name,
552
- parameters: mapParams(ctx.record.input),
553
- result:
554
- typeof ctx.record.output === "string"
555
- ? ctx.record.output
556
- : JSON.stringify(ctx.record.output),
557
- success: !ctx.record.error,
558
- executionTimeMs: ctx.record.durationMs,
559
- },
560
- });
561
- return undefined;
562
- },
563
- onTurnEnd: async (ctx: AgentHookTurnEndContext) => {
564
- append({
565
- ...createPayloadBase(ctx, runtimeOptions),
566
- hookName: "agent_end",
567
- iteration: ctx.iteration,
568
- turn: ctx.turn,
569
- taskComplete: { taskMetadata: {} },
570
- });
571
- return undefined;
572
- },
573
- onStopError: async (ctx: AgentHookStopErrorContext) => {
574
- append({
575
- ...createPayloadBase(ctx, runtimeOptions),
576
- hookName: "agent_error",
577
- iteration: ctx.iteration,
578
- error: {
579
- name: ctx.error.name,
580
- message: ctx.error.message,
581
- stack: ctx.error.stack,
582
- },
583
- });
584
- return undefined;
585
- },
586
- onSessionShutdown: async (ctx: AgentHookSessionShutdownContext) => {
587
- if (isAbortReason(ctx.reason)) {
588
- append({
589
- ...createPayloadBase(ctx, runtimeOptions),
590
- hookName: "agent_abort",
591
- reason: ctx.reason,
592
- taskCancel: { taskMetadata: {} },
593
- });
594
- }
595
- append({
596
- ...createPayloadBase(ctx, runtimeOptions),
597
- hookName: "session_shutdown",
598
- reason: ctx.reason,
599
- });
600
- return undefined;
601
- },
602
- };
603
- }
604
-
605
- export function createHookConfigFileHooks(
606
- options: HookRuntimeOptions,
607
- ): AgentHooks | undefined {
608
- const commandMap = createHookCommandMap(options.workspacePath);
609
- const hasAnyHooks = Object.values(commandMap).some(
610
- (paths) => (paths?.length ?? 0) > 0,
611
- );
612
- if (!hasAnyHooks) {
613
- return undefined;
614
- }
615
-
616
- const runStartPayload = async (
617
- ctx: AgentHookRunStartContext,
618
- ): Promise<void> => {
619
- const agentStart = commandMap.agent_start ?? [];
620
- if (agentStart.length > 0) {
621
- runAsyncHookCommands({
622
- commands: agentStart,
623
- cwd: options.cwd,
624
- logger: options.logger,
625
- payload: {
626
- ...createPayloadBase(ctx, options),
627
- hookName: "agent_start",
628
- taskStart: { taskMetadata: {} },
629
- },
630
- });
631
- }
632
-
633
- const promptSubmit = commandMap.prompt_submit ?? [];
634
- if (promptSubmit.length > 0) {
635
- runAsyncHookCommands({
636
- commands: promptSubmit,
637
- cwd: options.cwd,
638
- logger: options.logger,
639
- payload: {
640
- ...createPayloadBase(ctx, options),
641
- hookName: "prompt_submit",
642
- userPromptSubmit: {
643
- prompt: ctx.userMessage,
644
- attachments: [],
645
- },
646
- },
647
- });
648
- }
649
- };
650
-
651
- const runToolCallStart = async (
652
- ctx: AgentHookToolCallStartContext,
653
- ): Promise<AgentHookControl | undefined> => {
654
- const commandPaths = commandMap.tool_call ?? [];
655
- if (commandPaths.length === 0) {
656
- return undefined;
657
- }
658
- return runBlockingHookCommands({
659
- commands: commandPaths,
660
- cwd: options.cwd,
661
- logger: options.logger,
662
- timeoutMs: options.toolCallTimeoutMs ?? 120000,
663
- payload: {
664
- ...createPayloadBase(ctx, options),
665
- hookName: "tool_call",
666
- iteration: ctx.iteration,
667
- tool_call: {
668
- id: ctx.call.id,
669
- name: ctx.call.name,
670
- input: ctx.call.input,
671
- },
672
- preToolUse: {
673
- toolName: ctx.call.name,
674
- parameters: mapParams(ctx.call.input),
675
- },
676
- },
677
- });
678
- };
679
-
680
- const runToolCallEnd = async (
681
- ctx: AgentHookToolCallEndContext,
682
- ): Promise<void> => {
683
- const commandPaths = commandMap.tool_result ?? [];
684
- if (commandPaths.length === 0) {
685
- return;
686
- }
687
- runAsyncHookCommands({
688
- commands: commandPaths,
689
- cwd: options.cwd,
690
- logger: options.logger,
691
- payload: {
692
- ...createPayloadBase(ctx, options),
693
- hookName: "tool_result",
694
- iteration: ctx.iteration,
695
- tool_result: ctx.record,
696
- postToolUse: {
697
- toolName: ctx.record.name,
698
- parameters: mapParams(ctx.record.input),
699
- result:
700
- typeof ctx.record.output === "string"
701
- ? ctx.record.output
702
- : JSON.stringify(ctx.record.output),
703
- success: !ctx.record.error,
704
- executionTimeMs: ctx.record.durationMs,
705
- },
706
- },
707
- });
708
- };
709
-
710
- const runTurnEnd = async (ctx: AgentHookTurnEndContext): Promise<void> => {
711
- const commandPaths = commandMap.agent_end ?? [];
712
- if (commandPaths.length === 0) {
713
- return;
714
- }
715
- runAsyncHookCommands({
716
- commands: commandPaths,
717
- cwd: options.cwd,
718
- logger: options.logger,
719
- payload: {
720
- ...createPayloadBase(ctx, options),
721
- hookName: "agent_end",
722
- iteration: ctx.iteration,
723
- turn: ctx.turn,
724
- taskComplete: { taskMetadata: {} },
725
- },
726
- });
727
- };
728
-
729
- const runStopError = async (
730
- ctx: AgentHookStopErrorContext,
731
- ): Promise<void> => {
732
- const commandPaths = commandMap.agent_error ?? [];
733
- if (commandPaths.length === 0) {
734
- return;
735
- }
736
- runAsyncHookCommands({
737
- commands: commandPaths,
738
- cwd: options.cwd,
739
- logger: options.logger,
740
- payload: {
741
- ...createPayloadBase(ctx, options),
742
- hookName: "agent_error",
743
- iteration: ctx.iteration,
744
- error: {
745
- name: ctx.error.name,
746
- message: ctx.error.message,
747
- stack: ctx.error.stack,
748
- },
749
- },
750
- });
751
- };
752
-
753
- const runSessionShutdown = async (
754
- ctx: AgentHookSessionShutdownContext,
755
- ): Promise<void> => {
756
- if (isAbortReason(ctx.reason)) {
757
- const abortCommands = commandMap.agent_abort ?? [];
758
- if (abortCommands.length > 0) {
759
- runAsyncHookCommands({
760
- commands: abortCommands,
761
- cwd: options.cwd,
762
- logger: options.logger,
763
- payload: {
764
- ...createPayloadBase(ctx, options),
765
- hookName: "agent_abort",
766
- reason: ctx.reason,
767
- taskCancel: { taskMetadata: {} },
768
- },
769
- });
770
- }
771
- }
772
- const shutdownCommands = commandMap.session_shutdown ?? [];
773
- if (shutdownCommands.length === 0) {
774
- return;
775
- }
776
- runAsyncHookCommands({
777
- commands: shutdownCommands,
778
- cwd: options.cwd,
779
- logger: options.logger,
780
- payload: {
781
- ...createPayloadBase(ctx, options),
782
- hookName: "session_shutdown",
783
- reason: ctx.reason,
784
- },
785
- });
786
- };
787
-
788
- return {
789
- onRunStart: async (ctx: AgentHookRunStartContext) => {
790
- await runStartPayload(ctx);
791
- return undefined;
792
- },
793
- onToolCallStart: async (ctx: AgentHookToolCallStartContext) =>
794
- runToolCallStart(ctx),
795
- onToolCallEnd: async (ctx: AgentHookToolCallEndContext) => {
796
- await runToolCallEnd(ctx);
797
- return undefined;
798
- },
799
- onTurnEnd: async (ctx: AgentHookTurnEndContext) => {
800
- await runTurnEnd(ctx);
801
- return undefined;
802
- },
803
- onStopError: async (ctx: AgentHookStopErrorContext) => {
804
- await runStopError(ctx);
805
- return undefined;
806
- },
807
- onSessionShutdown: async (ctx: AgentHookSessionShutdownContext) => {
808
- await runSessionShutdown(ctx);
809
- return undefined;
810
- },
811
- };
812
- }
813
-
814
- function mergeHookFunction<K extends keyof AgentHooks>(
815
- layers: AgentHooks[],
816
- key: K,
817
- ): AgentHooks[K] | undefined {
818
- const handlers = layers
819
- .map((layer) => layer[key])
820
- .filter((handler) => typeof handler === "function");
821
- if (handlers.length === 0) {
822
- return undefined;
823
- }
824
- return (async (ctx: unknown) => {
825
- let control: AgentHookControl | undefined;
826
- for (const handler of handlers) {
827
- const next = await (handler as (arg: unknown) => unknown)(ctx);
828
- control = mergeHookControls(
829
- control,
830
- next as AgentHookControl | undefined,
831
- );
832
- }
833
- return control;
834
- }) as AgentHooks[K];
835
- }
836
-
837
- export function mergeAgentHooks(
838
- layers: Array<AgentHooks | undefined>,
839
- ): AgentHooks | undefined {
840
- const activeLayers = layers.filter(
841
- (layer): layer is AgentHooks => layer !== undefined,
842
- );
843
- if (activeLayers.length === 0) {
844
- return undefined;
845
- }
846
-
847
- return {
848
- onRunStart: mergeHookFunction(activeLayers, "onRunStart"),
849
- onRunEnd: mergeHookFunction(activeLayers, "onRunEnd"),
850
- onIterationStart: mergeHookFunction(activeLayers, "onIterationStart"),
851
- onIterationEnd: mergeHookFunction(activeLayers, "onIterationEnd"),
852
- onTurnStart: mergeHookFunction(activeLayers, "onTurnStart"),
853
- onTurnEnd: mergeHookFunction(activeLayers, "onTurnEnd"),
854
- onToolCallStart: mergeHookFunction(activeLayers, "onToolCallStart"),
855
- onToolCallEnd: mergeHookFunction(activeLayers, "onToolCallEnd"),
856
- onSessionShutdown: mergeHookFunction(activeLayers, "onSessionShutdown"),
857
- onError: mergeHookFunction(activeLayers, "onError"),
858
- };
859
- }