@cyanautomation/kaseki-agent 1.4.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 (459) hide show
  1. package/.dockerignore +54 -0
  2. package/.eslintignore +11 -0
  3. package/.eslintrc.json +95 -0
  4. package/.github/ISSUE_TEMPLATE/bug_report.md +53 -0
  5. package/.github/ISSUE_TEMPLATE/feature_request.md +53 -0
  6. package/.github/ISSUE_TEMPLATE/security.md +51 -0
  7. package/.github/PULL_REQUEST_TEMPLATE/default.md +71 -0
  8. package/.github/dependabot.yml +38 -0
  9. package/.github/skills/dependency-cache-optimization/SKILL.md +526 -0
  10. package/.github/skills/docker-image-management/SKILL.md +532 -0
  11. package/.github/skills/frontend-design/SKILL.md +782 -0
  12. package/.github/skills/prompt-engineering/SKILL.md +360 -0
  13. package/.github/skills/quality-gate-config/SKILL.md +591 -0
  14. package/.github/skills/result-report-analysis/SKILL.md +576 -0
  15. package/.github/skills/test-automation/SKILL.md +593 -0
  16. package/.github/skills/workflow-diagnosis/SKILL.md +468 -0
  17. package/.github/workflows/build-docker-image.yml +453 -0
  18. package/.github/workflows/release.yml +68 -0
  19. package/.releaserc.json +135 -0
  20. package/CHANGELOG.md +117 -0
  21. package/CLAUDE.md +336 -0
  22. package/CONTRIBUTING.md +339 -0
  23. package/Dockerfile +217 -0
  24. package/README.md +1527 -0
  25. package/STYLE.md +521 -0
  26. package/add-js-extensions.d.ts +9 -0
  27. package/add-js-extensions.d.ts.map +1 -0
  28. package/add-js-extensions.js.map +1 -0
  29. package/dist/add-js-extensions.d.ts +9 -0
  30. package/dist/add-js-extensions.d.ts.map +1 -0
  31. package/dist/add-js-extensions.js +52 -0
  32. package/dist/add-js-extensions.js.map +1 -0
  33. package/dist/ansi-colors.d.ts +26 -0
  34. package/dist/ansi-colors.d.ts.map +1 -0
  35. package/dist/ansi-colors.js +51 -0
  36. package/dist/ansi-colors.js.map +1 -0
  37. package/dist/cli/BaseCommand.d.ts +18 -0
  38. package/dist/cli/BaseCommand.d.ts.map +1 -0
  39. package/dist/cli/BaseCommand.js +31 -0
  40. package/dist/cli/BaseCommand.js.map +1 -0
  41. package/dist/cli/KasekiCLI.d.ts +30 -0
  42. package/dist/cli/KasekiCLI.d.ts.map +1 -0
  43. package/dist/cli/KasekiCLI.js +134 -0
  44. package/dist/cli/KasekiCLI.js.map +1 -0
  45. package/dist/cli/commands/ConfigCommand.d.ts +13 -0
  46. package/dist/cli/commands/ConfigCommand.d.ts.map +1 -0
  47. package/dist/cli/commands/ConfigCommand.js +131 -0
  48. package/dist/cli/commands/ConfigCommand.js.map +1 -0
  49. package/dist/cli/commands/DoctorCommand.d.ts +45 -0
  50. package/dist/cli/commands/DoctorCommand.d.ts.map +1 -0
  51. package/dist/cli/commands/DoctorCommand.js +309 -0
  52. package/dist/cli/commands/DoctorCommand.js.map +1 -0
  53. package/dist/cli/commands/ListCommand.d.ts +9 -0
  54. package/dist/cli/commands/ListCommand.d.ts.map +1 -0
  55. package/dist/cli/commands/ListCommand.js +81 -0
  56. package/dist/cli/commands/ListCommand.js.map +1 -0
  57. package/dist/cli/commands/ReportCommand.d.ts +9 -0
  58. package/dist/cli/commands/ReportCommand.d.ts.map +1 -0
  59. package/dist/cli/commands/ReportCommand.js +98 -0
  60. package/dist/cli/commands/ReportCommand.js.map +1 -0
  61. package/dist/cli/commands/RunCommand.d.ts +13 -0
  62. package/dist/cli/commands/RunCommand.d.ts.map +1 -0
  63. package/dist/cli/commands/RunCommand.js +191 -0
  64. package/dist/cli/commands/RunCommand.js.map +1 -0
  65. package/dist/cli/commands/SecretsCommand.d.ts +9 -0
  66. package/dist/cli/commands/SecretsCommand.d.ts.map +1 -0
  67. package/dist/cli/commands/SecretsCommand.js +109 -0
  68. package/dist/cli/commands/SecretsCommand.js.map +1 -0
  69. package/dist/cli/commands/ServeCommand.d.ts +9 -0
  70. package/dist/cli/commands/ServeCommand.d.ts.map +1 -0
  71. package/dist/cli/commands/ServeCommand.js +50 -0
  72. package/dist/cli/commands/ServeCommand.js.map +1 -0
  73. package/dist/cli/commands/SetupCommand.d.ts +42 -0
  74. package/dist/cli/commands/SetupCommand.d.ts.map +1 -0
  75. package/dist/cli/commands/SetupCommand.js +249 -0
  76. package/dist/cli/commands/SetupCommand.js.map +1 -0
  77. package/dist/cli.d.ts +9 -0
  78. package/dist/cli.d.ts.map +1 -0
  79. package/dist/cli.js +130 -0
  80. package/dist/cli.js.map +1 -0
  81. package/dist/config/ConfigManager.d.ts +395 -0
  82. package/dist/config/ConfigManager.d.ts.map +1 -0
  83. package/dist/config/ConfigManager.js +446 -0
  84. package/dist/config/ConfigManager.js.map +1 -0
  85. package/dist/docker/DockerManager.d.ts +69 -0
  86. package/dist/docker/DockerManager.d.ts.map +1 -0
  87. package/dist/docker/DockerManager.js +266 -0
  88. package/dist/docker/DockerManager.js.map +1 -0
  89. package/dist/event-aggregator.d.ts +71 -0
  90. package/dist/event-aggregator.d.ts.map +1 -0
  91. package/dist/event-aggregator.js +95 -0
  92. package/dist/event-aggregator.js.map +1 -0
  93. package/dist/github-app-token.d.ts +16 -0
  94. package/dist/github-app-token.d.ts.map +1 -0
  95. package/dist/github-app-token.js +148 -0
  96. package/dist/github-app-token.js.map +1 -0
  97. package/dist/idempotency-store.d.ts +61 -0
  98. package/dist/idempotency-store.d.ts.map +1 -0
  99. package/dist/idempotency-store.js +321 -0
  100. package/dist/idempotency-store.js.map +1 -0
  101. package/dist/index.d.ts +25 -0
  102. package/dist/index.d.ts.map +1 -0
  103. package/dist/index.js +31 -0
  104. package/dist/index.js.map +1 -0
  105. package/dist/instance/InstanceManager.d.ts +81 -0
  106. package/dist/instance/InstanceManager.d.ts.map +1 -0
  107. package/dist/instance/InstanceManager.js +220 -0
  108. package/dist/instance/InstanceManager.js.map +1 -0
  109. package/dist/instance-metadata-reader.d.ts +48 -0
  110. package/dist/instance-metadata-reader.d.ts.map +1 -0
  111. package/dist/instance-metadata-reader.js +94 -0
  112. package/dist/instance-metadata-reader.js.map +1 -0
  113. package/dist/instance-state-derivation.d.ts +42 -0
  114. package/dist/instance-state-derivation.d.ts.map +1 -0
  115. package/dist/instance-state-derivation.js +133 -0
  116. package/dist/instance-state-derivation.js.map +1 -0
  117. package/dist/job-scheduler.d.ts +124 -0
  118. package/dist/job-scheduler.d.ts.map +1 -0
  119. package/dist/job-scheduler.js +992 -0
  120. package/dist/job-scheduler.js.map +1 -0
  121. package/dist/kaseki-api-client.d.ts +89 -0
  122. package/dist/kaseki-api-client.d.ts.map +1 -0
  123. package/dist/kaseki-api-client.js +405 -0
  124. package/dist/kaseki-api-client.js.map +1 -0
  125. package/dist/kaseki-api-config.d.ts +34 -0
  126. package/dist/kaseki-api-config.d.ts.map +1 -0
  127. package/dist/kaseki-api-config.js +113 -0
  128. package/dist/kaseki-api-config.js.map +1 -0
  129. package/dist/kaseki-api-routes.d.ts +13 -0
  130. package/dist/kaseki-api-routes.d.ts.map +1 -0
  131. package/dist/kaseki-api-routes.js +559 -0
  132. package/dist/kaseki-api-routes.js.map +1 -0
  133. package/dist/kaseki-api-service-wrapper.d.ts +43 -0
  134. package/dist/kaseki-api-service-wrapper.d.ts.map +1 -0
  135. package/dist/kaseki-api-service-wrapper.js +150 -0
  136. package/dist/kaseki-api-service-wrapper.js.map +1 -0
  137. package/dist/kaseki-api-service.d.ts +16 -0
  138. package/dist/kaseki-api-service.d.ts.map +1 -0
  139. package/dist/kaseki-api-service.js +143 -0
  140. package/dist/kaseki-api-service.js.map +1 -0
  141. package/dist/kaseki-api-types.d.ts +440 -0
  142. package/dist/kaseki-api-types.d.ts.map +1 -0
  143. package/dist/kaseki-api-types.js +64 -0
  144. package/dist/kaseki-api-types.js.map +1 -0
  145. package/dist/kaseki-cli-lib.d.ts +219 -0
  146. package/dist/kaseki-cli-lib.d.ts.map +1 -0
  147. package/dist/kaseki-cli-lib.js +523 -0
  148. package/dist/kaseki-cli-lib.js.map +1 -0
  149. package/dist/kaseki-cli.d.ts +38 -0
  150. package/dist/kaseki-cli.d.ts.map +1 -0
  151. package/dist/kaseki-cli.js +559 -0
  152. package/dist/kaseki-cli.js.map +1 -0
  153. package/dist/kaseki-report.d.ts +3 -0
  154. package/dist/kaseki-report.d.ts.map +1 -0
  155. package/dist/kaseki-report.js +140 -0
  156. package/dist/kaseki-report.js.map +1 -0
  157. package/dist/lib/subprocess-helpers.d.ts +98 -0
  158. package/dist/lib/subprocess-helpers.d.ts.map +1 -0
  159. package/dist/lib/subprocess-helpers.js +136 -0
  160. package/dist/lib/subprocess-helpers.js.map +1 -0
  161. package/dist/logger.d.ts +39 -0
  162. package/dist/logger.d.ts.map +1 -0
  163. package/dist/logger.js +79 -0
  164. package/dist/logger.js.map +1 -0
  165. package/dist/metrics.d.ts +19 -0
  166. package/dist/metrics.d.ts.map +1 -0
  167. package/dist/metrics.js +59 -0
  168. package/dist/metrics.js.map +1 -0
  169. package/dist/middleware/job-lookup.d.ts +27 -0
  170. package/dist/middleware/job-lookup.d.ts.map +1 -0
  171. package/dist/middleware/job-lookup.js +28 -0
  172. package/dist/middleware/job-lookup.js.map +1 -0
  173. package/dist/pi-event-filter.d.ts +3 -0
  174. package/dist/pi-event-filter.d.ts.map +1 -0
  175. package/dist/pi-event-filter.js +126 -0
  176. package/dist/pi-event-filter.js.map +1 -0
  177. package/dist/pi-progress-stream.d.ts +3 -0
  178. package/dist/pi-progress-stream.d.ts.map +1 -0
  179. package/dist/pi-progress-stream.js +205 -0
  180. package/dist/pi-progress-stream.js.map +1 -0
  181. package/dist/pi-progress-summarizer.d.ts +61 -0
  182. package/dist/pi-progress-summarizer.d.ts.map +1 -0
  183. package/dist/pi-progress-summarizer.js +246 -0
  184. package/dist/pi-progress-summarizer.js.map +1 -0
  185. package/dist/pre-flight-validator.d.ts +72 -0
  186. package/dist/pre-flight-validator.d.ts.map +1 -0
  187. package/dist/pre-flight-validator.js +513 -0
  188. package/dist/pre-flight-validator.js.map +1 -0
  189. package/dist/progress-stream-utils.d.ts +3 -0
  190. package/dist/progress-stream-utils.d.ts.map +1 -0
  191. package/dist/progress-stream-utils.js +15 -0
  192. package/dist/progress-stream-utils.js.map +1 -0
  193. package/dist/result-cache.d.ts +52 -0
  194. package/dist/result-cache.d.ts.map +1 -0
  195. package/dist/result-cache.js +134 -0
  196. package/dist/result-cache.js.map +1 -0
  197. package/dist/routes/artifact-routes.d.ts +10 -0
  198. package/dist/routes/artifact-routes.d.ts.map +1 -0
  199. package/dist/routes/artifact-routes.js +126 -0
  200. package/dist/routes/artifact-routes.js.map +1 -0
  201. package/dist/routes/log-routes.d.ts +8 -0
  202. package/dist/routes/log-routes.d.ts.map +1 -0
  203. package/dist/routes/log-routes.js +345 -0
  204. package/dist/routes/log-routes.js.map +1 -0
  205. package/dist/routes/status-routes.d.ts +8 -0
  206. package/dist/routes/status-routes.d.ts.map +1 -0
  207. package/dist/routes/status-routes.js +82 -0
  208. package/dist/routes/status-routes.js.map +1 -0
  209. package/dist/routes/webhook-routes.d.ts +6 -0
  210. package/dist/routes/webhook-routes.d.ts.map +1 -0
  211. package/dist/routes/webhook-routes.js +86 -0
  212. package/dist/routes/webhook-routes.js.map +1 -0
  213. package/dist/run-artifact-metadata-cache.d.ts +42 -0
  214. package/dist/run-artifact-metadata-cache.d.ts.map +1 -0
  215. package/dist/run-artifact-metadata-cache.js +139 -0
  216. package/dist/run-artifact-metadata-cache.js.map +1 -0
  217. package/dist/secret-value-cache.d.ts +13 -0
  218. package/dist/secret-value-cache.d.ts.map +1 -0
  219. package/dist/secret-value-cache.js +44 -0
  220. package/dist/secret-value-cache.js.map +1 -0
  221. package/dist/secrets/SecretsManager.d.ts +80 -0
  222. package/dist/secrets/SecretsManager.d.ts.map +1 -0
  223. package/dist/secrets/SecretsManager.js +306 -0
  224. package/dist/secrets/SecretsManager.js.map +1 -0
  225. package/dist/test-utils.d.ts +55 -0
  226. package/dist/test-utils.d.ts.map +1 -0
  227. package/dist/test-utils.js +48 -0
  228. package/dist/test-utils.js.map +1 -0
  229. package/dist/timestamp-tracker.d.ts +75 -0
  230. package/dist/timestamp-tracker.d.ts.map +1 -0
  231. package/dist/timestamp-tracker.js +121 -0
  232. package/dist/timestamp-tracker.js.map +1 -0
  233. package/dist/utils/failure-artifact-writer.d.ts +29 -0
  234. package/dist/utils/failure-artifact-writer.d.ts.map +1 -0
  235. package/dist/utils/failure-artifact-writer.js +157 -0
  236. package/dist/utils/failure-artifact-writer.js.map +1 -0
  237. package/dist/utils/file-helpers.d.ts +41 -0
  238. package/dist/utils/file-helpers.d.ts.map +1 -0
  239. package/dist/utils/file-helpers.js +143 -0
  240. package/dist/utils/file-helpers.js.map +1 -0
  241. package/dist/utils/http-client-factory.d.ts +46 -0
  242. package/dist/utils/http-client-factory.d.ts.map +1 -0
  243. package/dist/utils/http-client-factory.js +114 -0
  244. package/dist/utils/http-client-factory.js.map +1 -0
  245. package/dist/utils/progress-normalizer.d.ts +13 -0
  246. package/dist/utils/progress-normalizer.d.ts.map +1 -0
  247. package/dist/utils/progress-normalizer.js +57 -0
  248. package/dist/utils/progress-normalizer.js.map +1 -0
  249. package/dist/utils/response-helpers.d.ts +34 -0
  250. package/dist/utils/response-helpers.d.ts.map +1 -0
  251. package/dist/utils/response-helpers.js +78 -0
  252. package/dist/utils/response-helpers.js.map +1 -0
  253. package/dist/utils/route-helpers.d.ts +17 -0
  254. package/dist/utils/route-helpers.d.ts.map +1 -0
  255. package/dist/utils/route-helpers.js +22 -0
  256. package/dist/utils/route-helpers.js.map +1 -0
  257. package/dist/utils/status-response-builder.d.ts +23 -0
  258. package/dist/utils/status-response-builder.d.ts.map +1 -0
  259. package/dist/utils/status-response-builder.js +144 -0
  260. package/dist/utils/status-response-builder.js.map +1 -0
  261. package/dist/utils/type-guards.d.ts +37 -0
  262. package/dist/utils/type-guards.d.ts.map +1 -0
  263. package/dist/utils/type-guards.js +45 -0
  264. package/dist/utils/type-guards.js.map +1 -0
  265. package/dist/utils/utf8-helpers.d.ts +32 -0
  266. package/dist/utils/utf8-helpers.d.ts.map +1 -0
  267. package/dist/utils/utf8-helpers.js +97 -0
  268. package/dist/utils/utf8-helpers.js.map +1 -0
  269. package/dist/utils/webhook-event-builder.d.ts +26 -0
  270. package/dist/utils/webhook-event-builder.d.ts.map +1 -0
  271. package/dist/utils/webhook-event-builder.js +77 -0
  272. package/dist/utils/webhook-event-builder.js.map +1 -0
  273. package/dist/webhook-manager.d.ts +56 -0
  274. package/dist/webhook-manager.d.ts.map +1 -0
  275. package/dist/webhook-manager.js +359 -0
  276. package/dist/webhook-manager.js.map +1 -0
  277. package/docker/workspace-cache/package-lock.json +13 -0
  278. package/docker/workspace-cache/package.json +7 -0
  279. package/docker-compose.yml +53 -0
  280. package/docs/API.md +708 -0
  281. package/docs/BACKLOG.md +19 -0
  282. package/docs/BUILD_STRATEGY.md +404 -0
  283. package/docs/CLI.md +569 -0
  284. package/docs/DEPLOYMENT.md +521 -0
  285. package/docs/DEVELOPMENT.md +459 -0
  286. package/docs/DOCKER_SETUP.md +522 -0
  287. package/docs/ENHANCED_PROGRESS_LOGS.md +264 -0
  288. package/docs/IMPLEMENTATION_SUMMARY.md +549 -0
  289. package/docs/INTEGRATION_EXAMPLE.md +217 -0
  290. package/docs/NPM_SETUP.md +468 -0
  291. package/docs/PHASE1-4_IMPLEMENTATION.md +302 -0
  292. package/docs/PHASE1_COMPLETION.md +192 -0
  293. package/docs/PHASE2_COMPLETION.md +134 -0
  294. package/docs/PHASE6_MIGRATION.md +392 -0
  295. package/docs/PRINTF_SAFETY_FIX.md +282 -0
  296. package/docs/QUALITY_GATES.md +369 -0
  297. package/docs/SETUP_GUIDE.md +482 -0
  298. package/docs/TASK_PROMPT_TEMPLATES.md +533 -0
  299. package/docs/VALIDATION_FIX.md +139 -0
  300. package/docs/VERIFICATION_CHECKLIST.md +335 -0
  301. package/docs/repo-maturity.md +760 -0
  302. package/fix-tests.d.ts +9 -0
  303. package/fix-tests.d.ts.map +1 -0
  304. package/fix-tests.js.map +1 -0
  305. package/fix-tests.ts +53 -0
  306. package/jest.config.ts +31 -0
  307. package/kaseki +183 -0
  308. package/kaseki-agent.sh +1961 -0
  309. package/ops/logrotate/kaseki +10 -0
  310. package/package.json +83 -0
  311. package/perf/README.md +54 -0
  312. package/perf/pi-event-filter.benchmark.test.ts +98 -0
  313. package/run-kaseki-json.test.sh +106 -0
  314. package/run-kaseki.sh +990 -0
  315. package/scripts/allowlist-helper.sh +56 -0
  316. package/scripts/cleanup-kaseki.sh +168 -0
  317. package/scripts/deploy-pi-template.sh +293 -0
  318. package/scripts/docker-entrypoint.sh +71 -0
  319. package/scripts/dry-run-allowlist.sh +161 -0
  320. package/scripts/kaseki-activate.sh +396 -0
  321. package/scripts/kaseki-api.service +62 -0
  322. package/scripts/kaseki-container-entrypoint-wrapper.sh +119 -0
  323. package/scripts/kaseki-container-setup-remote.sh +172 -0
  324. package/scripts/kaseki-container-setup.sh +193 -0
  325. package/scripts/kaseki-healthcheck.sh +95 -0
  326. package/scripts/kaseki-install.sh +50 -0
  327. package/scripts/kaseki-maturity-score.sh +291 -0
  328. package/scripts/kaseki-performance-metrics.sh +122 -0
  329. package/scripts/kaseki-preflight.sh +270 -0
  330. package/scripts/kaseki-setup.sh +265 -0
  331. package/scripts/pi-setup-remote.sh +213 -0
  332. package/scripts/setup-github-labels.sh +42 -0
  333. package/scripts/suggest-allowlist.sh +68 -0
  334. package/scripts/templates/MULTI_HOST_DISTRIBUTED.md +337 -0
  335. package/scripts/templates/REST_API_SERVICE.md +490 -0
  336. package/scripts/templates/SINGLE_HOST_CLI.md +194 -0
  337. package/scripts/test-github-app.sh +248 -0
  338. package/src/add-js-extensions.ts +61 -0
  339. package/src/ansi-colors.test.ts +62 -0
  340. package/src/ansi-colors.ts +67 -0
  341. package/src/cli/BaseCommand.ts +40 -0
  342. package/src/cli/KasekiCLI.ts +154 -0
  343. package/src/cli/commands/ConfigCommand.ts +145 -0
  344. package/src/cli/commands/DoctorCommand.ts +329 -0
  345. package/src/cli/commands/ListCommand.ts +105 -0
  346. package/src/cli/commands/ReportCommand.ts +110 -0
  347. package/src/cli/commands/RunCommand.ts +218 -0
  348. package/src/cli/commands/SecretsCommand.ts +120 -0
  349. package/src/cli/commands/ServeCommand.ts +62 -0
  350. package/src/cli/commands/SetupCommand.ts +301 -0
  351. package/src/cli.ts +138 -0
  352. package/src/config/ConfigManager.ts +476 -0
  353. package/src/docker/DockerManager.ts +319 -0
  354. package/src/docker-entrypoint-packaging.test.ts +33 -0
  355. package/src/event-aggregator.test.ts +117 -0
  356. package/src/event-aggregator.ts +126 -0
  357. package/src/github-app-token.ts +215 -0
  358. package/src/idempotency-store.test.ts +117 -0
  359. package/src/idempotency-store.ts +385 -0
  360. package/src/index.ts +89 -0
  361. package/src/instance/InstanceManager.ts +285 -0
  362. package/src/instance-metadata-reader.test.ts +190 -0
  363. package/src/instance-metadata-reader.ts +129 -0
  364. package/src/instance-state-derivation.test.ts +263 -0
  365. package/src/instance-state-derivation.ts +148 -0
  366. package/src/job-scheduler.test.ts +1236 -0
  367. package/src/job-scheduler.ts +1117 -0
  368. package/src/kaseki-api-client.ts +488 -0
  369. package/src/kaseki-api-config.test.ts +315 -0
  370. package/src/kaseki-api-config.ts +175 -0
  371. package/src/kaseki-api-routes.test.ts +1615 -0
  372. package/src/kaseki-api-routes.ts +643 -0
  373. package/src/kaseki-api-service-wrapper.ts +188 -0
  374. package/src/kaseki-api-service.test.ts +418 -0
  375. package/src/kaseki-api-service.ts +192 -0
  376. package/src/kaseki-api-types.ts +320 -0
  377. package/src/kaseki-cli-lib.test.ts +552 -0
  378. package/src/kaseki-cli-lib.ts +760 -0
  379. package/src/kaseki-cli.ts +682 -0
  380. package/src/kaseki-report.test.ts +118 -0
  381. package/src/kaseki-report.ts +192 -0
  382. package/src/lib/subprocess-helpers.ts +177 -0
  383. package/src/logger.ts +114 -0
  384. package/src/metrics.ts +66 -0
  385. package/src/middleware/job-lookup.test.ts +113 -0
  386. package/src/middleware/job-lookup.ts +45 -0
  387. package/src/pi-event-filter.test.ts +183 -0
  388. package/src/pi-event-filter.ts +183 -0
  389. package/src/pi-progress-stream.ts +287 -0
  390. package/src/pi-progress-summarizer.test.ts +302 -0
  391. package/src/pi-progress-summarizer.ts +287 -0
  392. package/src/pre-flight-validator.test.ts +512 -0
  393. package/src/pre-flight-validator.ts +618 -0
  394. package/src/progress-stream-utils.test.ts +35 -0
  395. package/src/progress-stream-utils.ts +14 -0
  396. package/src/result-cache.test.ts +195 -0
  397. package/src/result-cache.ts +181 -0
  398. package/src/routes/artifact-routes.ts +169 -0
  399. package/src/routes/log-routes.ts +391 -0
  400. package/src/routes/status-routes.ts +92 -0
  401. package/src/routes/webhook-routes.ts +97 -0
  402. package/src/run-artifact-metadata-cache.test.ts +80 -0
  403. package/src/run-artifact-metadata-cache.ts +184 -0
  404. package/src/secret-value-cache.test.ts +66 -0
  405. package/src/secret-value-cache.ts +55 -0
  406. package/src/secrets/SecretsManager.ts +343 -0
  407. package/src/test-utils.ts +81 -0
  408. package/src/timestamp-tracker.test.ts +134 -0
  409. package/src/timestamp-tracker.ts +132 -0
  410. package/src/utils/failure-artifact-writer.ts +187 -0
  411. package/src/utils/file-helpers.test.ts +235 -0
  412. package/src/utils/file-helpers.ts +150 -0
  413. package/src/utils/http-client-factory.test.ts +245 -0
  414. package/src/utils/http-client-factory.ts +157 -0
  415. package/src/utils/progress-normalizer.test.ts +442 -0
  416. package/src/utils/progress-normalizer.ts +68 -0
  417. package/src/utils/response-helpers.test.ts +122 -0
  418. package/src/utils/response-helpers.ts +101 -0
  419. package/src/utils/route-helpers.ts +30 -0
  420. package/src/utils/status-response-builder.ts +159 -0
  421. package/src/utils/type-guards.ts +52 -0
  422. package/src/utils/utf8-helpers.ts +102 -0
  423. package/src/utils/webhook-event-builder.test.ts +143 -0
  424. package/src/utils/webhook-event-builder.ts +87 -0
  425. package/src/webhook-manager.test.ts +152 -0
  426. package/src/webhook-manager.ts +445 -0
  427. package/templates/allowlist-api-route.txt +7 -0
  428. package/templates/allowlist-comprehensive.txt +8 -0
  429. package/templates/allowlist-parser-fix.txt +6 -0
  430. package/templates/allowlist-ui-component.txt +9 -0
  431. package/templates/allowlist-utility.txt +9 -0
  432. package/test/actual-model-metadata.test.sh +102 -0
  433. package/test/dry-run.test.sh +131 -0
  434. package/test/fixtures/kaseki-report-exit-codes/metadata-exit-0.json +1 -0
  435. package/test/fixtures/kaseki-report-exit-codes/metadata-exit-1.json +1 -0
  436. package/test/fixtures/kaseki-report-exit-codes/metadata-exit-invalid.json +1 -0
  437. package/test/fixtures/kaseki-report-exit-codes/metadata-exit-str-0.json +1 -0
  438. package/test/fixtures/kaseki-report-exit-codes/metadata-exit-str-1.json +1 -0
  439. package/test/kaseki-api.integration.test.sh +165 -0
  440. package/test/pi-event-filter-failure.test.sh +83 -0
  441. package/test/printf-safety-focused.test.sh +99 -0
  442. package/test/printf-safety-results/results/restoration.jsonl +10 -0
  443. package/test/printf-safety-results/results/test.jsonl +0 -0
  444. package/test/printf-safety.test.sh +297 -0
  445. package/test/validation-fix.test.sh +79 -0
  446. package/test/validation-integration.test.sh +109 -0
  447. package/tests/allowlist-glob.test.sh +61 -0
  448. package/tests/dependency-cache-key.test.sh +48 -0
  449. package/tests/dependency-restore-mode.test.sh +48 -0
  450. package/tests/doctor-template-parity.test.sh +95 -0
  451. package/tests/github-operations.test.sh +142 -0
  452. package/tests/npm-install-flags.test.sh +58 -0
  453. package/tests/quality-gates.test.sh +178 -0
  454. package/tests/repo-memory.test.sh +103 -0
  455. package/tests/restore-disallowed-changes.test.sh +80 -0
  456. package/tests/validation-missing-npm-scripts.test.sh +93 -0
  457. package/tests/validation-strict-mode.test.sh +118 -0
  458. package/tsconfig.changed.json +7 -0
  459. package/tsconfig.json +39 -0
@@ -0,0 +1,287 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from 'fs';
4
+ import readline from 'readline';
5
+ import { sanitizeToolName } from './progress-stream-utils.js';
6
+ import {
7
+ formatProgressMessage,
8
+ EventSampler,
9
+ extractFilePath,
10
+ detectError,
11
+ formatElapsed,
12
+ truncate,
13
+ } from './pi-progress-summarizer.js';
14
+ import { ANSI_COLORS, stripAnsi } from './ansi-colors.js';
15
+
16
+ interface EventCountMap {
17
+ [key: string]: number;
18
+ }
19
+
20
+ interface PiEvent {
21
+ type?: string;
22
+ event?: string;
23
+ name?: string;
24
+ kind?: string;
25
+ tool_name?: string;
26
+ toolName?: string;
27
+ tool?: { name: string };
28
+ call?: { name: string };
29
+ message?: { content?: any };
30
+ }
31
+
32
+ interface ProgressPayload {
33
+ timestamp: string;
34
+ updatedAt: string;
35
+ stage: string;
36
+ message: string;
37
+ percentComplete?: number;
38
+ summary?: string;
39
+ [key: string]: any;
40
+ }
41
+
42
+ const progressJsonlPath = process.argv[2] || '/results/progress.jsonl';
43
+ const progressLogPath = process.argv[3] || '/results/progress.log';
44
+ const streamToStdout = process.env.KASEKI_STREAM_PROGRESS !== '0';
45
+ const enableSummarization = process.env.KASEKI_PROGRESS_SUMMARIZATION !== '0';
46
+
47
+ const counts: EventCountMap = {};
48
+ let toolStartCount = 0;
49
+ let toolEndCount = 0;
50
+ let messageUpdateCount = 0;
51
+ let lastHeartbeat = 0;
52
+ let streamOpen = true;
53
+ const startTime = Date.now();
54
+
55
+ // Sample 1 in every 15 message_update events
56
+ const messageSampler = new EventSampler(15);
57
+
58
+ function append(file: string, text: string): void {
59
+ fs.appendFileSync(file, text);
60
+ }
61
+
62
+ function eventType(event: PiEvent | any): string {
63
+ return event?.type || event?.event || event?.name || event?.kind || 'unknown';
64
+ }
65
+
66
+ function toolName(event: PiEvent | any): string {
67
+ const candidates = [
68
+ event?.tool_name,
69
+ event?.toolName,
70
+ event?.tool?.name,
71
+ event?.name,
72
+ event?.call?.name,
73
+ ];
74
+
75
+ for (const candidate of candidates) {
76
+ if (typeof candidate !== 'string') {
77
+ continue;
78
+ }
79
+ const sanitized = sanitizeToolLabel(candidate);
80
+ if (sanitized !== 'tool') {
81
+ return sanitized;
82
+ }
83
+ }
84
+
85
+ return 'tool';
86
+ }
87
+
88
+ function sanitizeToolLabel(value: string): string {
89
+ return sanitizeToolName(value);
90
+ }
91
+
92
+ function emit(stage: string, message: string, extra: Record<string, any> = {}): void {
93
+ const now = new Date().toISOString();
94
+ const payload: ProgressPayload = {
95
+ timestamp: now,
96
+ updatedAt: now,
97
+ stage,
98
+ message,
99
+ ...extra,
100
+ };
101
+
102
+ // For JSONL, strip ANSI codes
103
+ const cleanMessage = stripAnsi(message);
104
+ const cleanPayload = { ...payload, message: cleanMessage };
105
+
106
+ append(progressJsonlPath, `${JSON.stringify(cleanPayload)}\n`);
107
+ const line = `[progress] ${stage}: ${message}\n`;
108
+ append(progressLogPath, line);
109
+ if (streamToStdout) {
110
+ process.stdout.write(line);
111
+ }
112
+ }
113
+
114
+ function eventTotal(): number {
115
+ return Object.values(counts).reduce((sum, count) => sum + count, 0);
116
+ }
117
+
118
+ function maybeHeartbeat(force: boolean = false, reason: string = 'events'): void {
119
+ const now = Date.now();
120
+ if (!force && now - lastHeartbeat < 15000) {
121
+ return;
122
+ }
123
+ lastHeartbeat = now;
124
+
125
+ const elapsed = formatElapsed(startTime);
126
+ const workingMsg = `working; events=${eventTotal()}, tool starts=${toolStartCount}, tool ends=${toolEndCount}`;
127
+ const message = `${workingMsg} | ${ANSI_COLORS.DIM}${elapsed} elapsed${ANSI_COLORS.RESET}`;
128
+
129
+ emit('pi coding agent', message, {
130
+ counts,
131
+ toolStartCount,
132
+ toolEndCount,
133
+ messageUpdateCount,
134
+ reason,
135
+ });
136
+ }
137
+
138
+ /**
139
+ * Emit enhanced summary for tool execution
140
+ */
141
+ function emitToolSummary(event: PiEvent, tool: string, isStart: boolean): void {
142
+ if (!enableSummarization) {
143
+ return;
144
+ }
145
+
146
+ const elapsed = formatElapsed(startTime);
147
+ const action = extractFilePath(tool) || tool;
148
+ const { hasError } = detectError(JSON.stringify(event).substring(0, 500));
149
+ const level = hasError ? ('error' as const) : undefined;
150
+
151
+ const message = formatProgressMessage(
152
+ 'pi tool',
153
+ `${isStart ? 'start' : 'end'} ${action}`,
154
+ undefined,
155
+ level,
156
+ elapsed
157
+ );
158
+
159
+ if (message && message.length > 0) {
160
+ emit('pi tool', stripAnsi(message.substring(13)), { level });
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Emit enhanced summary for message updates
166
+ */
167
+ function emitMessageSummary(event: PiEvent): void {
168
+ if (!enableSummarization || !messageSampler.shouldEmit()) {
169
+ return;
170
+ }
171
+
172
+ const elapsed = formatElapsed(startTime);
173
+ // Try to extract a brief insight from the message
174
+ let detail: string | undefined;
175
+
176
+ if (event.message?.content) {
177
+ const content = event.message.content;
178
+ const contentText = Array.isArray(content)
179
+ ? content.map((c: any) => (c.text || c.content || '').substring(0, 50)).join(' ')
180
+ : String(content).substring(0, 100);
181
+
182
+ if (contentText.length > 10) {
183
+ detail = truncate(contentText, 60);
184
+ }
185
+ }
186
+
187
+ if (detail) {
188
+ const message = formatProgressMessage(
189
+ 'pi coding agent',
190
+ 'processing',
191
+ detail,
192
+ undefined,
193
+ elapsed
194
+ );
195
+
196
+ if (message && message.length > 0) {
197
+ emit('pi coding agent', stripAnsi(message.substring(13)), { type: 'message_update' });
198
+ }
199
+ }
200
+ }
201
+
202
+ emit('pi coding agent', 'started');
203
+ const heartbeatTimer = setInterval(() => {
204
+ if (streamOpen) {
205
+ maybeHeartbeat(true, 'timer');
206
+ }
207
+ }, 30000);
208
+
209
+ const rl = readline.createInterface({
210
+ input: process.stdin,
211
+ crlfDelay: Infinity,
212
+ });
213
+
214
+ rl.on('line', (line: string) => {
215
+ if (line.trim().length === 0) {
216
+ return;
217
+ }
218
+
219
+ let event: any;
220
+ try {
221
+ event = JSON.parse(line);
222
+ } catch {
223
+ counts.invalid_json = (counts.invalid_json || 0) + 1;
224
+ maybeHeartbeat();
225
+ return;
226
+ }
227
+
228
+ const type = eventType(event);
229
+ counts[type] = (counts[type] || 0) + 1;
230
+
231
+ if (type === 'tool_execution_start' || type === 'toolcall_start') {
232
+ const tool = toolName(event);
233
+ toolStartCount += 1;
234
+
235
+ const startMsg = `started ${tool}`;
236
+ emit('pi tool', startMsg, { type, toolStartCount });
237
+
238
+ if (enableSummarization) {
239
+ emitToolSummary(event, tool, true);
240
+ }
241
+ } else if (type === 'tool_execution_end' || type === 'toolcall_end') {
242
+ const tool = toolName(event);
243
+ toolEndCount += 1;
244
+
245
+ const endMsg = `finished ${tool}`;
246
+ emit('pi tool', endMsg, { type, toolEndCount });
247
+
248
+ if (enableSummarization) {
249
+ emitToolSummary(event, tool, false);
250
+ }
251
+ } else if (type === 'message_update') {
252
+ messageUpdateCount += 1;
253
+
254
+ if (enableSummarization) {
255
+ emitMessageSummary(event);
256
+ }
257
+ } else if (type === 'agent_start') {
258
+ messageSampler.reset();
259
+ emit('pi coding agent', 'agent started', { type });
260
+ } else if (type === 'agent_end') {
261
+ emit('pi coding agent', 'agent finished', { type });
262
+ } else if (type === 'auto_retry_start') {
263
+ emit(
264
+ 'pi coding agent',
265
+ `${ANSI_COLORS.YELLOW}auto retry started${ANSI_COLORS.RESET}`,
266
+ { type }
267
+ );
268
+ } else if (type === 'auto_retry_end') {
269
+ emit('pi coding agent', 'auto retry finished', { type });
270
+ }
271
+
272
+ maybeHeartbeat();
273
+ });
274
+
275
+ rl.on('close', () => {
276
+ streamOpen = false;
277
+ clearInterval(heartbeatTimer);
278
+ maybeHeartbeat(true, 'close');
279
+
280
+ const finalElapsed = formatElapsed(startTime);
281
+ emit('pi coding agent', `event stream ended | ${ANSI_COLORS.DIM}${finalElapsed} total${ANSI_COLORS.RESET}`, {
282
+ counts,
283
+ toolStartCount,
284
+ toolEndCount,
285
+ messageUpdateCount,
286
+ });
287
+ });
@@ -0,0 +1,302 @@
1
+ import { describe, it, expect } from '@jest/globals';
2
+ import { ANSI_COLORS } from '../src/ansi-colors';
3
+ import {
4
+ extractFilePath,
5
+ extractDecision,
6
+ detectError,
7
+ summarizeEvent,
8
+ formatElapsed,
9
+ truncate,
10
+ formatProgressMessage,
11
+ EventSampler,
12
+ } from '../src/pi-progress-summarizer';
13
+
14
+ describe('pi-progress-summarizer', () => {
15
+ describe('extractFilePath', () => {
16
+ it('extracts path from read_file tool', () => {
17
+ const result = extractFilePath('read_file', 'reading /workspaces/kaseki-agent/src/parser.ts');
18
+ expect(result).toMatch(/src\/parser/);
19
+ });
20
+
21
+ it('extracts path from grep_search tool', () => {
22
+ const result = extractFilePath('grep_search', 'searching in src/handlers.ts');
23
+ expect(result).toMatch(/handlers/);
24
+ });
25
+
26
+ it('shortens absolute paths', () => {
27
+ const result = extractFilePath('write_file', 'write /workspaces/kaseki-agent/src/index.ts');
28
+ expect(result).toMatch(/src\/index/);
29
+ expect(result).not.toMatch(/workspaces/);
30
+ });
31
+
32
+ it('handles relative paths when content is provided', () => {
33
+ const result = extractFilePath('read_file', './lib/utils.ts');
34
+ expect(result).toBe('lib/utils.ts');
35
+ });
36
+
37
+ it('maps tool names to operations when no path in content', () => {
38
+ expect(extractFilePath('bash')).toBe('bash');
39
+ expect(extractFilePath('grep')).toBe('grep');
40
+ expect(extractFilePath('ls')).toBe('ls');
41
+ });
42
+
43
+ it('returns null for unknown tools without content', () => {
44
+ expect(extractFilePath('unknown_tool')).toBeNull();
45
+ });
46
+
47
+ it('truncates long paths with ellipsis', () => {
48
+ const longPath = '/workspaces/kaseki-agent/src/deeply/nested/folder/structure/index.ts';
49
+ const result = extractFilePath('read_file', longPath);
50
+ if (result) {
51
+ expect(result.length).toBeLessThanOrEqual(40);
52
+ if (result.includes('…')) {
53
+ expect(result).toMatch(/…\//);
54
+ }
55
+ }
56
+ });
57
+ });
58
+
59
+ describe('extractDecision', () => {
60
+ it('detects decision keywords in content', () => {
61
+ expect(extractDecision('I will create a new file')).toContain('create');
62
+ expect(extractDecision('Let me fix this bug')).toContain('fix');
63
+ expect(extractDecision('I need to modify the handler')).toContain('modify');
64
+ });
65
+
66
+ it('returns null for content without keywords', () => {
67
+ expect(extractDecision('This is just some information')).toBeNull();
68
+ expect(extractDecision('I am reading the file')).toBeNull();
69
+ });
70
+
71
+ it('returns null for empty content', () => {
72
+ expect(extractDecision('')).toBeNull();
73
+ expect(extractDecision(undefined)).toBeNull();
74
+ });
75
+
76
+ it('extracts context around keyword', () => {
77
+ const result = extractDecision('Now I will implement the parser with error handling');
78
+ expect(result).toBeTruthy();
79
+ if (result) {
80
+ expect(result).toContain('implement');
81
+ }
82
+ });
83
+
84
+ it('handles case-insensitive matching', () => {
85
+ expect(extractDecision('I WILL CREATE a new module')).toContain('CREATE');
86
+ expect(extractDecision('FiX the parser')).toContain('FiX');
87
+ });
88
+ });
89
+
90
+ describe('detectError', () => {
91
+ it('detects error patterns in content', () => {
92
+ const result = detectError('Error: Cannot read property');
93
+ expect(result.hasError).toBe(true);
94
+ expect(result.snippet).toContain('Error');
95
+ });
96
+
97
+ it('detects failure patterns', () => {
98
+ const result = detectError('Test FAILED: assertion failed');
99
+ expect(result.hasError).toBe(true);
100
+ });
101
+
102
+ it('detects exit code errors', () => {
103
+ const result = detectError('Process exited with code 1');
104
+ expect(result.hasError).toBe(true);
105
+ });
106
+
107
+ it('returns false for successful content', () => {
108
+ const result = detectError('Successfully created file index.ts');
109
+ expect(result.hasError).toBe(false);
110
+ });
111
+
112
+ it('returns false for empty content', () => {
113
+ expect(detectError('').hasError).toBe(false);
114
+ expect(detectError(undefined).hasError).toBe(false);
115
+ });
116
+
117
+ it('extracts context snippet around error', () => {
118
+ const result = detectError('Some output before the actual Error: something bad happened here');
119
+ expect(result.snippet).toBeTruthy();
120
+ if (result.snippet) {
121
+ expect(result.snippet).toContain('Error');
122
+ }
123
+ });
124
+ });
125
+
126
+ describe('formatElapsed', () => {
127
+ it('formats seconds only for times under 60s', () => {
128
+ const now = Date.now();
129
+ const elapsed = formatElapsed(now - 30000); // 30 seconds
130
+ expect(elapsed).toBe('30s');
131
+ });
132
+
133
+ it('formats minutes and seconds', () => {
134
+ const now = Date.now();
135
+ const elapsed = formatElapsed(now - 125000); // 2m 5s
136
+ expect(elapsed).toMatch(/\dm \ds/);
137
+ });
138
+
139
+ it('formats longer durations', () => {
140
+ const now = Date.now();
141
+ const elapsed = formatElapsed(now - 605000); // 10m 5s
142
+ expect(elapsed).toBe('10m 5s');
143
+ });
144
+ });
145
+
146
+ describe('truncate', () => {
147
+ it('returns text as-is if within limit', () => {
148
+ const text = 'short text';
149
+ expect(truncate(text, 20)).toBe(text);
150
+ });
151
+
152
+ it('truncates text exceeding limit', () => {
153
+ const text = 'This is a very long text that exceeds the limit';
154
+ const result = truncate(text, 20);
155
+ expect(result.length).toBeLessThanOrEqual(20);
156
+ expect(result).toContain('…');
157
+ });
158
+
159
+ it('handles undefined input', () => {
160
+ expect(truncate(undefined, 20)).toBe('');
161
+ });
162
+
163
+ it('uses default max length of 100', () => {
164
+ const text = 'a'.repeat(150);
165
+ const result = truncate(text);
166
+ expect(result.length).toBeLessThanOrEqual(100);
167
+ });
168
+ });
169
+
170
+ describe('formatProgressMessage', () => {
171
+ it('formats basic message with stage and action', () => {
172
+ const msg = formatProgressMessage('pi tool', 'read parser.ts');
173
+ expect(msg).toContain('[progress]');
174
+ expect(msg).toContain('pi tool');
175
+ expect(msg).toContain('read parser.ts');
176
+ });
177
+
178
+ it('includes detail when provided', () => {
179
+ const msg = formatProgressMessage('pi tool', 'read parser.ts', 'checking structure');
180
+ expect(msg).toContain('checking structure');
181
+ });
182
+
183
+ it('includes elapsed time when provided', () => {
184
+ const msg = formatProgressMessage('pi tool', 'read parser.ts', undefined, undefined, '1m 23s');
185
+ expect(msg).toContain('1m 23s');
186
+ });
187
+
188
+ it('applies color for error level if available', () => {
189
+ const msg = formatProgressMessage('pi tool', 'bash npm test', undefined, 'error');
190
+ // When not in TTY, ANSI_COLORS will be empty, so just verify message structure
191
+ expect(msg).toContain('[progress]');
192
+ expect(msg).toContain('bash npm test');
193
+ // If colors are available, they should be included
194
+ if (ANSI_COLORS.RED !== '') {
195
+ expect(msg).toContain('\x1b[31m');
196
+ }
197
+ });
198
+
199
+ it('applies color for warn level if available', () => {
200
+ const msg = formatProgressMessage('pi tool', 'auto retry', undefined, 'warn');
201
+ expect(msg).toContain('[progress]');
202
+ expect(msg).toContain('auto retry');
203
+ // If colors are available, they should be included
204
+ if (ANSI_COLORS.YELLOW !== '') {
205
+ expect(msg).toContain('\x1b[33m');
206
+ }
207
+ });
208
+
209
+ it('has no color for info level', () => {
210
+ const msg = formatProgressMessage('pi tool', 'read file', undefined, 'info');
211
+ // Should not have color codes for info level (colors are optional for info)
212
+ // Just verify it doesn't crash
213
+ expect(msg).toContain('[progress]');
214
+ });
215
+ });
216
+
217
+ describe('EventSampler', () => {
218
+ it('emits at configured rate', () => {
219
+ const sampler = new EventSampler(5); // Emit every 5th event
220
+ expect(sampler.shouldEmit()).toBe(false);
221
+ expect(sampler.shouldEmit()).toBe(false);
222
+ expect(sampler.shouldEmit()).toBe(false);
223
+ expect(sampler.shouldEmit()).toBe(false);
224
+ expect(sampler.shouldEmit()).toBe(true); // 5th event
225
+ });
226
+
227
+ it('resets counter on reset()', () => {
228
+ const sampler = new EventSampler(3);
229
+ sampler.shouldEmit();
230
+ sampler.shouldEmit();
231
+ sampler.shouldEmit(); // 3rd event would be true
232
+ sampler.reset();
233
+ expect(sampler.shouldEmit()).toBe(false); // Counter reset
234
+ });
235
+
236
+ it('defaults to rate of 10', () => {
237
+ const sampler = new EventSampler();
238
+ let emitCount = 0;
239
+ for (let i = 0; i < 50; i++) {
240
+ if (sampler.shouldEmit()) emitCount++;
241
+ }
242
+ // Should emit 5 times out of 50 (rate 10)
243
+ expect(emitCount).toBe(5);
244
+ });
245
+
246
+ it('enforces minimum rate of 1 when given 0', () => {
247
+ const sampler = new EventSampler(0);
248
+ // With rate 1, every event is emitted
249
+ expect(sampler.shouldEmit()).toBe(true); // 1st event
250
+ expect(sampler.shouldEmit()).toBe(true); // 2nd event
251
+ expect(sampler.shouldEmit()).toBe(true); // 3rd event
252
+ });
253
+ });
254
+
255
+ describe('summarizeEvent', () => {
256
+ it('extracts file path from event', () => {
257
+ const event = {
258
+ tool_name: 'read_file',
259
+ message: { content: [{ text: '/src/parser.ts' }] },
260
+ };
261
+ const summary = summarizeEvent(event, 'read_file', Date.now() - 5000);
262
+ expect(summary).toBeTruthy();
263
+ if (summary && summary.action) {
264
+ expect(summary.action).toMatch(/read|parser/);
265
+ }
266
+ });
267
+
268
+ it('includes elapsed time', () => {
269
+ const event = { type: 'message_update' };
270
+ const startTime = Date.now() - 65000; // 65 seconds ago
271
+ const summary = summarizeEvent(event, 'agent', startTime);
272
+ expect(summary?.elapsed).toMatch(/1m/);
273
+ });
274
+
275
+ it('marks errors with error level', () => {
276
+ const event = {
277
+ type: 'tool_execution_end',
278
+ message: { content: [{ text: 'Error: failed to read file' }] },
279
+ };
280
+ const summary = summarizeEvent(event, 'read_file', Date.now() - 5000);
281
+ expect(summary?.level).toBe('error');
282
+ });
283
+
284
+ it('extracts decision keywords from content', () => {
285
+ const event = {
286
+ type: 'message_update',
287
+ message: {
288
+ content: [{ text: 'I will create a new file for the parser implementation' }],
289
+ },
290
+ };
291
+ const summary = summarizeEvent(event, 'agent', Date.now() - 5000);
292
+ expect(summary?.detail).toContain('create');
293
+ });
294
+
295
+ it('returns null for events with no extractable data', () => {
296
+ const event = { type: 'unknown' };
297
+ const summary = summarizeEvent(event, 'unknown_tool', Date.now() - 5000);
298
+ // Should return an object (elapsed is always added)
299
+ expect(summary).toBeTruthy();
300
+ });
301
+ });
302
+ });