@retinue/agentkit 0.1.0

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 (405) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +310 -0
  3. package/dist/adapters/bullmq/consumer.d.ts +33 -0
  4. package/dist/adapters/bullmq/consumer.js +41 -0
  5. package/dist/adapters/bullmq/dispatcher.d.ts +74 -0
  6. package/dist/adapters/bullmq/dispatcher.js +160 -0
  7. package/dist/adapters/bullmq/export.d.ts +31 -0
  8. package/dist/adapters/bullmq/export.js +53 -0
  9. package/dist/adapters/bullmq/extraction.d.ts +42 -0
  10. package/dist/adapters/bullmq/extraction.js +63 -0
  11. package/dist/adapters/bullmq/index.d.ts +13 -0
  12. package/dist/adapters/bullmq/index.js +13 -0
  13. package/dist/adapters/bullmq/lock.d.ts +77 -0
  14. package/dist/adapters/bullmq/lock.js +126 -0
  15. package/dist/adapters/bullmq/queue.d.ts +50 -0
  16. package/dist/adapters/bullmq/queue.js +81 -0
  17. package/dist/adapters/memory/artifact-exports.d.ts +11 -0
  18. package/dist/adapters/memory/artifact-exports.js +102 -0
  19. package/dist/adapters/memory/artifacts.d.ts +15 -0
  20. package/dist/adapters/memory/artifacts.js +134 -0
  21. package/dist/adapters/memory/blobs.d.ts +7 -0
  22. package/dist/adapters/memory/blobs.js +27 -0
  23. package/dist/adapters/memory/evaluation.d.ts +18 -0
  24. package/dist/adapters/memory/evaluation.js +148 -0
  25. package/dist/adapters/memory/files.d.ts +27 -0
  26. package/dist/adapters/memory/files.js +0 -0
  27. package/dist/adapters/memory/flows.d.ts +16 -0
  28. package/dist/adapters/memory/flows.js +117 -0
  29. package/dist/adapters/memory/hitl.d.ts +9 -0
  30. package/dist/adapters/memory/hitl.js +130 -0
  31. package/dist/adapters/memory/idempotency.d.ts +13 -0
  32. package/dist/adapters/memory/idempotency.js +32 -0
  33. package/dist/adapters/memory/index.d.ts +39 -0
  34. package/dist/adapters/memory/index.js +107 -0
  35. package/dist/adapters/memory/knowledge.d.ts +43 -0
  36. package/dist/adapters/memory/knowledge.js +248 -0
  37. package/dist/adapters/memory/mcp.d.ts +9 -0
  38. package/dist/adapters/memory/mcp.js +37 -0
  39. package/dist/adapters/memory/message-store.d.ts +17 -0
  40. package/dist/adapters/memory/message-store.js +70 -0
  41. package/dist/adapters/memory/principal-memory.d.ts +7 -0
  42. package/dist/adapters/memory/principal-memory.js +83 -0
  43. package/dist/adapters/memory/runtime.d.ts +29 -0
  44. package/dist/adapters/memory/runtime.js +0 -0
  45. package/dist/adapters/memory/sessions.d.ts +29 -0
  46. package/dist/adapters/memory/sessions.js +0 -0
  47. package/dist/adapters/memory/skills.d.ts +10 -0
  48. package/dist/adapters/memory/skills.js +41 -0
  49. package/dist/adapters/memory/thread-summary.d.ts +7 -0
  50. package/dist/adapters/memory/thread-summary.js +29 -0
  51. package/dist/adapters/memory/usage-limits.d.ts +13 -0
  52. package/dist/adapters/memory/usage-limits.js +72 -0
  53. package/dist/adapters/memory/usage.d.ts +16 -0
  54. package/dist/adapters/memory/usage.js +279 -0
  55. package/dist/adapters/otel/index.d.ts +111 -0
  56. package/dist/adapters/otel/index.js +133 -0
  57. package/dist/adapters/postgres/artifact-exports.d.ts +12 -0
  58. package/dist/adapters/postgres/artifact-exports.js +117 -0
  59. package/dist/adapters/postgres/artifacts.d.ts +16 -0
  60. package/dist/adapters/postgres/artifacts.js +172 -0
  61. package/dist/adapters/postgres/checkpoint-store.d.ts +16 -0
  62. package/dist/adapters/postgres/checkpoint-store.js +34 -0
  63. package/dist/adapters/postgres/config.d.ts +15 -0
  64. package/dist/adapters/postgres/config.js +187 -0
  65. package/dist/adapters/postgres/conversation-store.d.ts +4 -0
  66. package/dist/adapters/postgres/conversation-store.js +82 -0
  67. package/dist/adapters/postgres/evaluation.d.ts +17 -0
  68. package/dist/adapters/postgres/evaluation.js +193 -0
  69. package/dist/adapters/postgres/file-content.d.ts +30 -0
  70. package/dist/adapters/postgres/file-content.js +111 -0
  71. package/dist/adapters/postgres/files.d.ts +19 -0
  72. package/dist/adapters/postgres/files.js +209 -0
  73. package/dist/adapters/postgres/flows.d.ts +20 -0
  74. package/dist/adapters/postgres/flows.js +206 -0
  75. package/dist/adapters/postgres/hitl.d.ts +5 -0
  76. package/dist/adapters/postgres/hitl.js +247 -0
  77. package/dist/adapters/postgres/index.d.ts +35 -0
  78. package/dist/adapters/postgres/index.js +35 -0
  79. package/dist/adapters/postgres/knowledge.d.ts +48 -0
  80. package/dist/adapters/postgres/knowledge.js +255 -0
  81. package/dist/adapters/postgres/memory.d.ts +14 -0
  82. package/dist/adapters/postgres/memory.js +194 -0
  83. package/dist/adapters/postgres/message-store.d.ts +11 -0
  84. package/dist/adapters/postgres/message-store.js +145 -0
  85. package/dist/adapters/postgres/migrations.d.ts +69 -0
  86. package/dist/adapters/postgres/migrations.js +1594 -0
  87. package/dist/adapters/postgres/pg-executor.d.ts +19 -0
  88. package/dist/adapters/postgres/pg-executor.js +32 -0
  89. package/dist/adapters/postgres/retention.d.ts +26 -0
  90. package/dist/adapters/postgres/retention.js +59 -0
  91. package/dist/adapters/postgres/rollups.d.ts +17 -0
  92. package/dist/adapters/postgres/rollups.js +267 -0
  93. package/dist/adapters/postgres/run-coordinator.d.ts +5 -0
  94. package/dist/adapters/postgres/run-coordinator.js +98 -0
  95. package/dist/adapters/postgres/run-event-log.d.ts +26 -0
  96. package/dist/adapters/postgres/run-event-log.js +30 -0
  97. package/dist/adapters/postgres/run-store.d.ts +4 -0
  98. package/dist/adapters/postgres/run-store.js +199 -0
  99. package/dist/adapters/postgres/schema.d.ts +39 -0
  100. package/dist/adapters/postgres/schema.js +70 -0
  101. package/dist/adapters/postgres/session-state.d.ts +7 -0
  102. package/dist/adapters/postgres/session-state.js +99 -0
  103. package/dist/adapters/postgres/sql.d.ts +8 -0
  104. package/dist/adapters/postgres/sql.js +2 -0
  105. package/dist/adapters/postgres/transaction.d.ts +37 -0
  106. package/dist/adapters/postgres/transaction.js +93 -0
  107. package/dist/adapters/postgres/unit-of-work.d.ts +18 -0
  108. package/dist/adapters/postgres/unit-of-work.js +8 -0
  109. package/dist/adapters/postgres/usage-limits.d.ts +15 -0
  110. package/dist/adapters/postgres/usage-limits.js +136 -0
  111. package/dist/adapters/postgres/usage.d.ts +15 -0
  112. package/dist/adapters/postgres/usage.js +226 -0
  113. package/dist/adapters/redis/index.d.ts +9 -0
  114. package/dist/adapters/redis/index.js +9 -0
  115. package/dist/adapters/redis/realtime.d.ts +74 -0
  116. package/dist/adapters/redis/realtime.js +112 -0
  117. package/dist/adapters/supabase/index.d.ts +88 -0
  118. package/dist/adapters/supabase/index.js +84 -0
  119. package/dist/adapters/supabase/realtime.d.ts +30 -0
  120. package/dist/adapters/supabase/realtime.js +53 -0
  121. package/dist/adapters/supabase/rls.d.ts +99 -0
  122. package/dist/adapters/supabase/rls.js +216 -0
  123. package/dist/adapters/supabase/storage.d.ts +50 -0
  124. package/dist/adapters/supabase/storage.js +207 -0
  125. package/dist/agents/agent.d.ts +66 -0
  126. package/dist/agents/agent.js +209 -0
  127. package/dist/agents/define.d.ts +21 -0
  128. package/dist/agents/define.js +63 -0
  129. package/dist/agents/engine.d.ts +98 -0
  130. package/dist/agents/engine.js +462 -0
  131. package/dist/agents/index.d.ts +50 -0
  132. package/dist/agents/index.js +17 -0
  133. package/dist/artifacts/index.d.ts +114 -0
  134. package/dist/artifacts/index.js +219 -0
  135. package/dist/authorization/index.d.ts +76 -0
  136. package/dist/authorization/index.js +76 -0
  137. package/dist/capabilities/index.d.ts +120 -0
  138. package/dist/capabilities/index.js +167 -0
  139. package/dist/capabilities/runtime.d.ts +89 -0
  140. package/dist/capabilities/runtime.js +84 -0
  141. package/dist/citations/index.d.ts +161 -0
  142. package/dist/citations/index.js +182 -0
  143. package/dist/context/assembler.d.ts +82 -0
  144. package/dist/context/assembler.js +129 -0
  145. package/dist/context/compaction.d.ts +45 -0
  146. package/dist/context/compaction.js +55 -0
  147. package/dist/context/index.d.ts +75 -0
  148. package/dist/context/index.js +17 -0
  149. package/dist/core/content-parts.d.ts +194 -0
  150. package/dist/core/content-parts.js +23 -0
  151. package/dist/core/context.d.ts +51 -0
  152. package/dist/core/context.js +9 -0
  153. package/dist/core/env.d.ts +25 -0
  154. package/dist/core/env.js +41 -0
  155. package/dist/core/errors.d.ts +30 -0
  156. package/dist/core/errors.js +65 -0
  157. package/dist/core/events.d.ts +139 -0
  158. package/dist/core/events.js +99 -0
  159. package/dist/core/ids.d.ts +52 -0
  160. package/dist/core/ids.js +13 -0
  161. package/dist/core/index.d.ts +9 -0
  162. package/dist/core/index.js +9 -0
  163. package/dist/core/tokens.d.ts +22 -0
  164. package/dist/core/tokens.js +22 -0
  165. package/dist/core/validation.d.ts +34 -0
  166. package/dist/core/validation.js +176 -0
  167. package/dist/documents/extraction.d.ts +121 -0
  168. package/dist/documents/extraction.js +293 -0
  169. package/dist/documents/index.d.ts +199 -0
  170. package/dist/documents/index.js +65 -0
  171. package/dist/documents/parsers/pdf.d.ts +47 -0
  172. package/dist/documents/parsers/pdf.js +508 -0
  173. package/dist/documents/parsers/text.d.ts +59 -0
  174. package/dist/documents/parsers/text.js +325 -0
  175. package/dist/documents/read-tool.d.ts +52 -0
  176. package/dist/documents/read-tool.js +109 -0
  177. package/dist/documents/render.d.ts +29 -0
  178. package/dist/documents/render.js +59 -0
  179. package/dist/documents/vision.d.ts +159 -0
  180. package/dist/documents/vision.js +214 -0
  181. package/dist/entries/adapters-bullmq.d.ts +8 -0
  182. package/dist/entries/adapters-bullmq.js +8 -0
  183. package/dist/entries/adapters-otel.d.ts +13 -0
  184. package/dist/entries/adapters-otel.js +13 -0
  185. package/dist/entries/adapters-postgres.d.ts +10 -0
  186. package/dist/entries/adapters-postgres.js +10 -0
  187. package/dist/entries/adapters-redis.d.ts +3 -0
  188. package/dist/entries/adapters-redis.js +3 -0
  189. package/dist/entries/context.d.ts +20 -0
  190. package/dist/entries/context.js +20 -0
  191. package/dist/entries/flows.d.ts +15 -0
  192. package/dist/entries/flows.js +15 -0
  193. package/dist/entries/hitl.d.ts +10 -0
  194. package/dist/entries/hitl.js +10 -0
  195. package/dist/entries/knowledge.d.ts +18 -0
  196. package/dist/entries/knowledge.js +19 -0
  197. package/dist/entries/mcp.d.ts +10 -0
  198. package/dist/entries/mcp.js +10 -0
  199. package/dist/entries/observability.d.ts +14 -0
  200. package/dist/entries/observability.js +16 -0
  201. package/dist/entries/persistence.d.ts +11 -0
  202. package/dist/entries/persistence.js +11 -0
  203. package/dist/entries/providers.d.ts +14 -0
  204. package/dist/entries/providers.js +14 -0
  205. package/dist/entries/runtime.d.ts +13 -0
  206. package/dist/entries/runtime.js +13 -0
  207. package/dist/entries/server.d.ts +24 -0
  208. package/dist/entries/server.js +24 -0
  209. package/dist/entries/tools.d.ts +21 -0
  210. package/dist/entries/tools.js +21 -0
  211. package/dist/entries/usage.d.ts +10 -0
  212. package/dist/entries/usage.js +10 -0
  213. package/dist/evaluation/gate.d.ts +168 -0
  214. package/dist/evaluation/gate.js +180 -0
  215. package/dist/evaluation/graders.d.ts +125 -0
  216. package/dist/evaluation/graders.js +203 -0
  217. package/dist/evaluation/index.d.ts +120 -0
  218. package/dist/evaluation/index.js +183 -0
  219. package/dist/evaluation/judge.d.ts +75 -0
  220. package/dist/evaluation/judge.js +111 -0
  221. package/dist/export/index.d.ts +162 -0
  222. package/dist/export/index.js +363 -0
  223. package/dist/export/markdown.d.ts +19 -0
  224. package/dist/export/markdown.js +29 -0
  225. package/dist/export/pdf.d.ts +73 -0
  226. package/dist/export/pdf.js +407 -0
  227. package/dist/files/context.d.ts +97 -0
  228. package/dist/files/context.js +185 -0
  229. package/dist/files/index.d.ts +210 -0
  230. package/dist/files/index.js +338 -0
  231. package/dist/files/read-tool.d.ts +81 -0
  232. package/dist/files/read-tool.js +163 -0
  233. package/dist/files/turn-parts.d.ts +96 -0
  234. package/dist/files/turn-parts.js +171 -0
  235. package/dist/flows/index.d.ts +270 -0
  236. package/dist/flows/index.js +62 -0
  237. package/dist/flows/interpreter.d.ts +146 -0
  238. package/dist/flows/interpreter.js +426 -0
  239. package/dist/flows/runner.d.ts +145 -0
  240. package/dist/flows/runner.js +270 -0
  241. package/dist/graphql/index.d.ts +8 -0
  242. package/dist/graphql/index.js +8 -0
  243. package/dist/graphql/resolvers.d.ts +237 -0
  244. package/dist/graphql/resolvers.js +253 -0
  245. package/dist/graphql/schema.d.ts +11 -0
  246. package/dist/graphql/schema.js +258 -0
  247. package/dist/graphql/sse.d.ts +77 -0
  248. package/dist/graphql/sse.js +100 -0
  249. package/dist/hitl/approved-execution.d.ts +127 -0
  250. package/dist/hitl/approved-execution.js +177 -0
  251. package/dist/hitl/index.d.ts +79 -0
  252. package/dist/hitl/index.js +12 -0
  253. package/dist/hitl/service.d.ts +221 -0
  254. package/dist/hitl/service.js +268 -0
  255. package/dist/idempotency/index.d.ts +70 -0
  256. package/dist/idempotency/index.js +59 -0
  257. package/dist/index.d.ts +103 -0
  258. package/dist/index.js +60 -0
  259. package/dist/knowledge/chunking.d.ts +57 -0
  260. package/dist/knowledge/chunking.js +158 -0
  261. package/dist/knowledge/index.d.ts +119 -0
  262. package/dist/knowledge/index.js +166 -0
  263. package/dist/knowledge/retrieval.d.ts +146 -0
  264. package/dist/knowledge/retrieval.js +170 -0
  265. package/dist/loadtest/harness.d.ts +168 -0
  266. package/dist/loadtest/harness.js +507 -0
  267. package/dist/loadtest/index.d.ts +13 -0
  268. package/dist/loadtest/index.js +13 -0
  269. package/dist/loadtest/injection.d.ts +89 -0
  270. package/dist/loadtest/injection.js +147 -0
  271. package/dist/loadtest/metrics.d.ts +197 -0
  272. package/dist/loadtest/metrics.js +160 -0
  273. package/dist/loadtest/runbooks.d.ts +28 -0
  274. package/dist/loadtest/runbooks.js +159 -0
  275. package/dist/loadtest/scenario.d.ts +104 -0
  276. package/dist/loadtest/scenario.js +208 -0
  277. package/dist/mcp/egress.d.ts +53 -0
  278. package/dist/mcp/egress.js +115 -0
  279. package/dist/mcp/index.d.ts +93 -0
  280. package/dist/mcp/index.js +33 -0
  281. package/dist/mcp/provider.d.ts +62 -0
  282. package/dist/mcp/provider.js +0 -0
  283. package/dist/models/index.d.ts +98 -0
  284. package/dist/models/index.js +74 -0
  285. package/dist/models/pricing.d.ts +24 -0
  286. package/dist/models/pricing.js +37 -0
  287. package/dist/models/provider-factory.d.ts +31 -0
  288. package/dist/models/provider-factory.js +67 -0
  289. package/dist/models/streaming.d.ts +145 -0
  290. package/dist/models/streaming.js +272 -0
  291. package/dist/models/vision.d.ts +38 -0
  292. package/dist/models/vision.js +62 -0
  293. package/dist/persistence/index.d.ts +1654 -0
  294. package/dist/persistence/index.js +226 -0
  295. package/dist/principal-memory/index.d.ts +106 -0
  296. package/dist/principal-memory/index.js +89 -0
  297. package/dist/retention/index.d.ts +89 -0
  298. package/dist/retention/index.js +70 -0
  299. package/dist/runtime/checkpoint.d.ts +37 -0
  300. package/dist/runtime/checkpoint.js +22 -0
  301. package/dist/runtime/index.d.ts +118 -0
  302. package/dist/runtime/index.js +69 -0
  303. package/dist/runtime/retry.d.ts +95 -0
  304. package/dist/runtime/retry.js +126 -0
  305. package/dist/runtime/serialization.d.ts +85 -0
  306. package/dist/runtime/serialization.js +95 -0
  307. package/dist/runtime/streaming.d.ts +54 -0
  308. package/dist/runtime/streaming.js +115 -0
  309. package/dist/runtime/worker.d.ts +130 -0
  310. package/dist/runtime/worker.js +405 -0
  311. package/dist/security/checklist.d.ts +53 -0
  312. package/dist/security/checklist.js +204 -0
  313. package/dist/security/findings.d.ts +56 -0
  314. package/dist/security/findings.js +168 -0
  315. package/dist/security/index.d.ts +14 -0
  316. package/dist/security/index.js +14 -0
  317. package/dist/security/prompt-safety.d.ts +100 -0
  318. package/dist/security/prompt-safety.js +133 -0
  319. package/dist/server/boot.d.ts +32 -0
  320. package/dist/server/boot.js +36 -0
  321. package/dist/server/cli-worker.d.ts +37 -0
  322. package/dist/server/cli-worker.js +151 -0
  323. package/dist/server/cli.d.ts +27 -0
  324. package/dist/server/cli.js +74 -0
  325. package/dist/server/config.d.ts +42 -0
  326. package/dist/server/config.js +127 -0
  327. package/dist/server/health.d.ts +59 -0
  328. package/dist/server/health.js +90 -0
  329. package/dist/server/host.d.ts +39 -0
  330. package/dist/server/host.js +124 -0
  331. package/dist/server/index.d.ts +15 -0
  332. package/dist/server/index.js +15 -0
  333. package/dist/server/main.d.ts +16 -0
  334. package/dist/server/main.js +31 -0
  335. package/dist/server/sse-route.d.ts +21 -0
  336. package/dist/server/sse-route.js +282 -0
  337. package/dist/skills/index.d.ts +67 -0
  338. package/dist/skills/index.js +31 -0
  339. package/dist/skills/resolver.d.ts +54 -0
  340. package/dist/skills/resolver.js +121 -0
  341. package/dist/teams/index.d.ts +93 -0
  342. package/dist/teams/index.js +207 -0
  343. package/dist/telemetry/index.d.ts +157 -0
  344. package/dist/telemetry/index.js +71 -0
  345. package/dist/telemetry/instrument.d.ts +108 -0
  346. package/dist/telemetry/instrument.js +232 -0
  347. package/dist/telemetry/log-events.d.ts +17 -0
  348. package/dist/telemetry/log-events.js +58 -0
  349. package/dist/telemetry/metrics.d.ts +123 -0
  350. package/dist/telemetry/metrics.js +135 -0
  351. package/dist/telemetry/noop.d.ts +39 -0
  352. package/dist/telemetry/noop.js +143 -0
  353. package/dist/telemetry/redaction.d.ts +64 -0
  354. package/dist/telemetry/redaction.js +153 -0
  355. package/dist/telemetry/spans.d.ts +56 -0
  356. package/dist/telemetry/spans.js +78 -0
  357. package/dist/telemetry/trace-context.d.ts +55 -0
  358. package/dist/telemetry/trace-context.js +60 -0
  359. package/dist/toolkit/compute.d.ts +53 -0
  360. package/dist/toolkit/compute.js +152 -0
  361. package/dist/toolkit/data.d.ts +98 -0
  362. package/dist/toolkit/data.js +235 -0
  363. package/dist/toolkit/http.d.ts +113 -0
  364. package/dist/toolkit/http.js +205 -0
  365. package/dist/toolkit/index.d.ts +21 -0
  366. package/dist/toolkit/index.js +17 -0
  367. package/dist/toolkit/web.d.ts +107 -0
  368. package/dist/toolkit/web.js +147 -0
  369. package/dist/tools/define.d.ts +25 -0
  370. package/dist/tools/define.js +45 -0
  371. package/dist/tools/delegating.d.ts +132 -0
  372. package/dist/tools/delegating.js +211 -0
  373. package/dist/tools/index.d.ts +129 -0
  374. package/dist/tools/index.js +33 -0
  375. package/dist/tools/library/compute.d.ts +11 -0
  376. package/dist/tools/library/compute.js +46 -0
  377. package/dist/tools/library/data.d.ts +16 -0
  378. package/dist/tools/library/data.js +92 -0
  379. package/dist/tools/library/http.d.ts +28 -0
  380. package/dist/tools/library/http.js +71 -0
  381. package/dist/tools/library/index.d.ts +97 -0
  382. package/dist/tools/library/index.js +134 -0
  383. package/dist/tools/library/knowledge.d.ts +39 -0
  384. package/dist/tools/library/knowledge.js +58 -0
  385. package/dist/tools/library/web.d.ts +19 -0
  386. package/dist/tools/library/web.js +65 -0
  387. package/dist/tools/meta-tools.d.ts +19 -0
  388. package/dist/tools/meta-tools.js +36 -0
  389. package/dist/tools/registry.d.ts +146 -0
  390. package/dist/tools/registry.js +291 -0
  391. package/dist/usage/index.d.ts +105 -0
  392. package/dist/usage/index.js +20 -0
  393. package/dist/usage/quota.d.ts +258 -0
  394. package/dist/usage/quota.js +510 -0
  395. package/dist/usage/recorder.d.ts +29 -0
  396. package/dist/usage/recorder.js +96 -0
  397. package/dist/usage/rollups.d.ts +121 -0
  398. package/dist/usage/rollups.js +157 -0
  399. package/dist/worker/export.d.ts +57 -0
  400. package/dist/worker/export.js +81 -0
  401. package/dist/worker/extraction.d.ts +57 -0
  402. package/dist/worker/extraction.js +84 -0
  403. package/dist/worker/main.d.ts +103 -0
  404. package/dist/worker/main.js +159 -0
  405. package/package.json +187 -0
@@ -0,0 +1,147 @@
1
+ /**
2
+ * The failure-injection matrix — AC-3, AC-5.
3
+ *
4
+ * Declared as **data** rather than as a list of test functions, for three reasons that all reduce to the same
5
+ * one: a failure mode that is not in a machine-readable list is a failure mode whose runbook can go missing
6
+ * without anything noticing.
7
+ *
8
+ * - A test asserts that every mode here has a runbook, so a new injector cannot ship undocumented (AC-5).
9
+ * - Each mode carries what recovery *means* for it, so "recovered" is not a judgement call made per run.
10
+ * - `requiresInfrastructure` marks the ones that need something this package cannot start, which is how the
11
+ * honest gap stays visible instead of becoming a quietly skipped case.
12
+ */
13
+ export const FAILURE_MODES = [
14
+ "provider-timeout",
15
+ "provider-rate-limit",
16
+ "redis-unavailable",
17
+ "database-unavailable",
18
+ "database-failover",
19
+ "worker-kill",
20
+ "slow-consumer",
21
+ "overload",
22
+ ];
23
+ export const FAILURE_MATRIX = {
24
+ "provider-timeout": {
25
+ mode: "provider-timeout",
26
+ description: "The model provider accepts the request and never answers.",
27
+ expect: {
28
+ recoversUnattended: true,
29
+ noDataLoss: true,
30
+ noDuplicateExternalAction: true,
31
+ observableSymptom: "model latency p99 climbs to the timeout; retries-total rises; run duration follows",
32
+ mechanism: "runWithRetry with a bounded attempt count, then the run fails with a typed error",
33
+ },
34
+ requiresInfrastructure: false,
35
+ },
36
+ "provider-rate-limit": {
37
+ mode: "provider-rate-limit",
38
+ description: "The provider returns 429 for a sustained period.",
39
+ expect: {
40
+ recoversUnattended: true,
41
+ noDataLoss: true,
42
+ noDuplicateExternalAction: true,
43
+ observableSymptom: "retries-total rises with reason=rate-limited; throughput drops; error rate stays low",
44
+ mechanism: "retry with backoff; the run reports retry-pending so a client can show 'attempt 2 of 5'",
45
+ },
46
+ requiresInfrastructure: false,
47
+ },
48
+ "redis-unavailable": {
49
+ mode: "redis-unavailable",
50
+ description: "The queue's Redis is stopped mid-run and restarted.",
51
+ expect: {
52
+ recoversUnattended: true,
53
+ noDataLoss: true,
54
+ noDuplicateExternalAction: true,
55
+ observableSymptom: "enqueue fails fast with a typed unavailable error; queue depth unreadable; in-flight runs continue",
56
+ mechanism: "enableOfflineQueue:false so an enqueue rejects rather than hanging; the lease reaper re-enqueues once the queue returns",
57
+ },
58
+ requiresInfrastructure: true,
59
+ },
60
+ "database-unavailable": {
61
+ mode: "database-unavailable",
62
+ description: "Postgres is stopped briefly while runs are in flight.",
63
+ expect: {
64
+ recoversUnattended: true,
65
+ noDataLoss: true,
66
+ noDuplicateExternalAction: true,
67
+ observableSymptom: "runs fail with a typed unavailable error; claims stop; the reaper finds expired leases on return",
68
+ mechanism: "the atomic lease claim and the checkpoint, so a run resumes from its last committed step",
69
+ },
70
+ requiresInfrastructure: true,
71
+ },
72
+ "database-failover": {
73
+ mode: "database-failover",
74
+ description: "The primary is promoted away under load.",
75
+ expect: {
76
+ recoversUnattended: true,
77
+ noDataLoss: true,
78
+ noDuplicateExternalAction: true,
79
+ observableSymptom: "a burst of connection errors, then recovery; some runs resume from a checkpoint",
80
+ mechanism: "checkpoint-per-step plus the compare-and-set claim; a half-applied step is never observable",
81
+ },
82
+ requiresInfrastructure: true,
83
+ },
84
+ "worker-kill": {
85
+ mode: "worker-kill",
86
+ description: "A worker is killed without draining, mid-run, holding a lease.",
87
+ expect: {
88
+ recoversUnattended: true,
89
+ noDataLoss: true,
90
+ noDuplicateExternalAction: true,
91
+ observableSymptom: "the run sits until its lease expires, then is claimed by another worker and resumes",
92
+ mechanism: "lease expiry plus the reaper's re-enqueue; the checkpoint prevents re-running a completed tool",
93
+ },
94
+ requiresInfrastructure: false,
95
+ },
96
+ "slow-consumer": {
97
+ mode: "slow-consumer",
98
+ description: "A streaming subscriber reads far slower than events are produced.",
99
+ expect: {
100
+ recoversUnattended: true,
101
+ noDataLoss: true,
102
+ noDuplicateExternalAction: true,
103
+ observableSymptom: "the subscriber falls behind and resumes from its sequence; producer memory stays flat",
104
+ mechanism: "the durable event log and sequence-based catch-up, so a slow reader never holds a producer buffer",
105
+ },
106
+ requiresInfrastructure: false,
107
+ },
108
+ overload: {
109
+ mode: "overload",
110
+ description: "Offered load is driven well past sustainable capacity.",
111
+ expect: {
112
+ recoversUnattended: true,
113
+ noDataLoss: true,
114
+ noDuplicateExternalAction: true,
115
+ observableSymptom: "queue depth rises to its bound; admissions are refused with a typed error; RSS stays flat",
116
+ mechanism: "quota admission before anything is claimed or enqueued, so a refusal holds no slot and no job",
117
+ },
118
+ requiresInfrastructure: false,
119
+ },
120
+ };
121
+ /** The modes this package can inject on its own. The rest need a container or a cluster. */
122
+ export const SELF_CONTAINED_MODES = FAILURE_MODES.filter((mode) => !FAILURE_MATRIX[mode].requiresInfrastructure);
123
+ /**
124
+ * Judge a result against the mode's declared expectation.
125
+ *
126
+ * Separate from running it, so the judgement is testable against a *fabricated* result — including the results
127
+ * that must fail. A verdict function only ever exercised by passing runs is one that might return "passed" for
128
+ * everything, and nothing would reveal that.
129
+ */
130
+ export const judgeInjection = (result) => {
131
+ const expect = FAILURE_MATRIX[result.mode].expect;
132
+ const failures = [];
133
+ if (!result.injected)
134
+ failures.push("the failure was never actually injected, so nothing was tested");
135
+ if (expect.noDataLoss && result.terminal < result.admitted)
136
+ failures.push(`lost work: ${result.admitted} admitted, ${result.terminal} reached a terminal state`);
137
+ // Strictly greater. Equality is the requirement; fewer effects than keys is fine — a run that was refused
138
+ // before its side effect performed none, which is not a duplicate.
139
+ if (expect.noDuplicateExternalAction && result.externalEffects > result.distinctEffectKeys)
140
+ failures.push(`duplicated external action: ${result.externalEffects} effects for ${result.distinctEffectKeys} distinct keys`);
141
+ if (expect.recoversUnattended && result.recoveredMs < 0)
142
+ failures.push("never recovered");
143
+ if (expect.recoversUnattended && result.manualInterventions > 0)
144
+ failures.push(`recovery was not unattended: ${result.manualInterventions} run(s) needed a manual re-drive`);
145
+ return { mode: result.mode, passed: failures.length === 0, failures };
146
+ };
147
+ //# sourceMappingURL=injection.js.map
@@ -0,0 +1,197 @@
1
+ /**
2
+ * Measurement for the load and soak harness — REQ-033 (#144).
3
+ *
4
+ * Pure functions, so the *conclusions* the harness draws are testable without running a load test. A leak
5
+ * detector that has only ever been exercised by the run it is judging is a detector nobody can trust: it will
6
+ * say "no leak" and there is no way to know whether it would ever say anything else.
7
+ *
8
+ * Everything here is deliberately conservative in one direction — it would rather report a problem that is not
9
+ * there than miss one. A load test that flatters the system is worse than no load test, because it converts an
10
+ * unknown into a false belief.
11
+ */
12
+ /**
13
+ * A latency summary.
14
+ *
15
+ * Percentiles, and `max`, and **no mean**. A mean latency is the number that hides the problem: a p99 of eight
16
+ * seconds behind a mean of 200ms is the normal shape of a system that is failing for one user in a hundred, and
17
+ * the mean says everything is fine. `count` travels with them because a p99 over 40 samples is four data points
18
+ * and should not be quoted as though it were a property of the system.
19
+ */
20
+ export type LatencySummary = {
21
+ readonly count: number;
22
+ readonly p50: number;
23
+ readonly p95: number;
24
+ readonly p99: number;
25
+ readonly max: number;
26
+ };
27
+ /**
28
+ * Nearest-rank percentile on a sorted copy.
29
+ *
30
+ * Nearest-rank rather than interpolated, because an interpolated p99 reports a latency **nobody experienced**.
31
+ * For a capacity envelope the honest question is "what did the slowest one percent actually see", and that has
32
+ * to be a real observation.
33
+ */
34
+ export declare const percentile: (sorted: readonly number[], fraction: number) => number;
35
+ export declare const summarizeLatency: (samples: readonly number[]) => LatencySummary;
36
+ export type ThroughputSummary = {
37
+ readonly completed: number;
38
+ readonly failed: number;
39
+ readonly refused: number;
40
+ readonly durationMs: number;
41
+ /** Completions per second. The number an operator sizes a deployment on. */
42
+ readonly completedPerSecond: number;
43
+ /**
44
+ * Failures as a fraction of *attempts*, with refusals excluded from the numerator.
45
+ *
46
+ * A refusal is the system working: it is the honest "no" that AC-4 asks for. Counting refusals as errors would
47
+ * make a correctly back-pressuring system look broken exactly when it is behaving best, and the natural
48
+ * response to that graph is to remove the backpressure.
49
+ */
50
+ readonly errorRate: number;
51
+ /** Refusals as a fraction of attempts. Separate, because it means something different. */
52
+ readonly refusalRate: number;
53
+ };
54
+ export declare const summarizeThroughput: (input: {
55
+ readonly completed: number;
56
+ readonly failed: number;
57
+ readonly refused: number;
58
+ readonly durationMs: number;
59
+ }) => ThroughputSummary;
60
+ /** One resource observation. Sampled on a timer during a run. */
61
+ export type ResourceSample = {
62
+ readonly atMs: number;
63
+ readonly rssBytes: number;
64
+ readonly heapUsedBytes: number;
65
+ /** Server-side connection count, when the harness can see it. Connection exhaustion is a real soak failure. */
66
+ readonly dbConnections?: number;
67
+ /** Jobs waiting. The other half of "bounded queueing". */
68
+ readonly queueDepth?: number;
69
+ };
70
+ export type GrowthVerdict = {
71
+ /** Bytes per hour, from a least-squares fit over the samples. */
72
+ readonly bytesPerHour: number;
73
+ readonly firstQuartileMean: number;
74
+ readonly lastQuartileMean: number;
75
+ readonly samples: number;
76
+ readonly observedMs: number;
77
+ readonly leaking: boolean;
78
+ /**
79
+ * Why the verdict is what it is — including "the run was too short to tell".
80
+ *
81
+ * The important arm. A short run producing "no leak" is the single most misleading output this harness could
82
+ * have, because it looks exactly like a passing result. AC-2 asks for a multi-hour soak precisely because a
83
+ * short burst hides slow growth, so anything shorter must say so rather than reporting a clean bill of health.
84
+ */
85
+ readonly reason: "leaking" | "stable" | "insufficient-samples" | "insufficient-duration";
86
+ };
87
+ /**
88
+ * The threshold: 32 MiB per hour.
89
+ *
90
+ * Chosen to be well above JIT warm-up, heap fragmentation and a pool filling to its configured size, and well
91
+ * below anything that survives a night. A process leaking at this rate grows by 768 MiB a day, which is an
92
+ * incident; one growing at a tenth of it is noise a stricter threshold would report every run, and a detector
93
+ * that cries wolf is one people stop reading.
94
+ */
95
+ export declare const LEAK_BYTES_PER_HOUR: number;
96
+ /** Below this, a fit is fitting noise. */
97
+ export declare const MIN_GROWTH_SAMPLES = 12;
98
+ /** Below this, growth is indistinguishable from warm-up whatever the fit says. */
99
+ export declare const MIN_GROWTH_DURATION_MS: number;
100
+ /**
101
+ * The share of a run treated as warm-up and excluded from the fit.
102
+ *
103
+ * Every real process ramps at the start: the JIT compiles, connection pools fill to their configured size, caches
104
+ * populate. That ramp is not a leak, and it is steep — steeper than any leak worth catching — so a fit including
105
+ * it reports a leak on **every single run**.
106
+ *
107
+ * A quarter. Enough to clear the ramp on runs of any length, and it still leaves three quarters of the samples to
108
+ * fit, which is where a leak would show anyway: a leak that only exists during warm-up is a warm-up.
109
+ */
110
+ export declare const WARMUP_FRACTION = 0.25;
111
+ /**
112
+ * Least-squares slope of RSS against time, after discarding warm-up, plus a quartile comparison.
113
+ *
114
+ * Both, because each catches what the other misses. A fit is robust to a single spike; a quartile comparison is
115
+ * robust to a ramp but fooled by one outlier. A verdict of "leaking" needs *both*.
116
+ *
117
+ * The warm-up exclusion was not in the first version, and the test for it failed: a 20-second ramp to 500 MiB
118
+ * followed by a flat hour fit at ~420 MiB/h and was reported as a leak. My own comment claimed the quartile check
119
+ * handled it, and it did not — the first quartile *was* the ramp, so its mean was low and the comparison agreed
120
+ * with the fit. Two checks that share a blind spot are one check.
121
+ */
122
+ export declare const detectGrowth: (samples: readonly ResourceSample[]) => GrowthVerdict;
123
+ /**
124
+ * Where behaviour degrades, and how — AC-1.
125
+ *
126
+ * The criterion asks for the *manner* as well as the point, so this is a union rather than a number. "Throughput
127
+ * peaked at 40/s" tells an operator nothing about whether the next request queues or fails, and those need
128
+ * completely different responses.
129
+ */
130
+ export type DegradationMode =
131
+ /** Latency rose, everything still completed. The good failure: work is queueing. */
132
+ "graceful-queueing"
133
+ /** The system said no, on purpose. Also good — capacity was reached and it was honest about it. */
134
+ | "honest-refusal"
135
+ /** Requests started failing. The bad one. */
136
+ | "errors"
137
+ /**
138
+ * Work was accepted faster than it was finished.
139
+ *
140
+ * Distinct from `graceful-queueing`, which is a latency rise with completions keeping up. This is a *backlog*:
141
+ * the system is accepting more than it can serve, and the queue is absorbing the difference. Graceful only for
142
+ * as long as the queue has room.
143
+ */
144
+ | "backlog"
145
+ /** Nothing degraded within the range driven; the envelope's upper bound was not found. */
146
+ | "not-reached";
147
+ export type LoadStep = {
148
+ /** Offered load, in run admissions attempted per second. */
149
+ readonly offeredPerSecond: number;
150
+ readonly latency: LatencySummary;
151
+ readonly throughput: ThroughputSummary;
152
+ readonly peakRssBytes: number;
153
+ readonly peakQueueDepth: number;
154
+ /** Distinct reasons admissions were rejected, capped. A count with no reason costs a reproduction. */
155
+ readonly admitFailures?: readonly string[];
156
+ /** Runs that never reached a terminal state before the settle timeout. Apart from `failed` on purpose. */
157
+ readonly stuck?: number;
158
+ /** Which non-terminal statuses they were in. A count alone does not say what to investigate. */
159
+ readonly stuckByStatus?: Readonly<Record<string, number>>;
160
+ };
161
+ export type Envelope = {
162
+ readonly steps: readonly LoadStep[];
163
+ /** The highest step that stayed inside the latency and error budget. */
164
+ readonly sustainablePerSecond: number;
165
+ readonly degradesAt: number | null;
166
+ readonly mode: DegradationMode;
167
+ readonly budget: LoadBudget;
168
+ };
169
+ export type LoadBudget = {
170
+ /** The p99 an operator would accept. Above it, the step is outside the envelope however much completed. */
171
+ readonly p99Ms: number;
172
+ /** The error rate an operator would accept. Refusals are not errors. */
173
+ readonly errorRate: number;
174
+ /**
175
+ * How far completions may fall short of offered load before the step is not sustained.
176
+ *
177
+ * The check latency alone does not make. A step can post an excellent p99 while completing a third of what was
178
+ * offered, because the work that never got picked up contributes no latency sample at all — the fast runs are
179
+ * measured and the queued ones are invisible. My first version of this had exactly that hole and reported
180
+ * "sustainable 20/s, p99 5ms" for a step whose queue reached 101 jobs and whose real throughput was 6.9/s.
181
+ *
182
+ * 0.9: completions must be within ten percent of offered load. Not 1.0, because the drain window makes the
183
+ * ratio slightly noisy at low rates and a knife-edge threshold would make the envelope flap.
184
+ */
185
+ readonly minCompletionRatio: number;
186
+ };
187
+ export declare const DEFAULT_LOAD_BUDGET: LoadBudget;
188
+ /**
189
+ * Read an envelope off a staircase of load steps.
190
+ *
191
+ * The first failing step is the one reported, not the worst: an operator needs the point at which behaviour
192
+ * *starts* to go wrong, and later steps are already past it. And the mode is read from that step rather than
193
+ * from the whole run, because a system that queues at 40/s and errors at 80/s degrades gracefully — the abrupt
194
+ * behaviour further out is a different fact.
195
+ */
196
+ export declare const readEnvelope: (steps: readonly LoadStep[], budget?: LoadBudget) => Envelope;
197
+ //# sourceMappingURL=metrics.d.ts.map
@@ -0,0 +1,160 @@
1
+ /**
2
+ * Measurement for the load and soak harness — REQ-033 (#144).
3
+ *
4
+ * Pure functions, so the *conclusions* the harness draws are testable without running a load test. A leak
5
+ * detector that has only ever been exercised by the run it is judging is a detector nobody can trust: it will
6
+ * say "no leak" and there is no way to know whether it would ever say anything else.
7
+ *
8
+ * Everything here is deliberately conservative in one direction — it would rather report a problem that is not
9
+ * there than miss one. A load test that flatters the system is worse than no load test, because it converts an
10
+ * unknown into a false belief.
11
+ */
12
+ /**
13
+ * Nearest-rank percentile on a sorted copy.
14
+ *
15
+ * Nearest-rank rather than interpolated, because an interpolated p99 reports a latency **nobody experienced**.
16
+ * For a capacity envelope the honest question is "what did the slowest one percent actually see", and that has
17
+ * to be a real observation.
18
+ */
19
+ export const percentile = (sorted, fraction) => {
20
+ if (sorted.length === 0)
21
+ return Number.NaN;
22
+ const rank = Math.ceil(fraction * sorted.length);
23
+ const index = Math.min(sorted.length - 1, Math.max(0, rank - 1));
24
+ return sorted[index];
25
+ };
26
+ export const summarizeLatency = (samples) => {
27
+ const sorted = [...samples].sort((a, b) => a - b);
28
+ return {
29
+ count: sorted.length,
30
+ p50: percentile(sorted, 0.5),
31
+ p95: percentile(sorted, 0.95),
32
+ p99: percentile(sorted, 0.99),
33
+ max: sorted.length === 0 ? Number.NaN : sorted[sorted.length - 1],
34
+ };
35
+ };
36
+ export const summarizeThroughput = (input) => {
37
+ const attempts = input.completed + input.failed + input.refused;
38
+ const seconds = input.durationMs / 1_000;
39
+ return {
40
+ completed: input.completed,
41
+ failed: input.failed,
42
+ refused: input.refused,
43
+ durationMs: input.durationMs,
44
+ completedPerSecond: seconds > 0 ? input.completed / seconds : 0,
45
+ errorRate: attempts > 0 ? input.failed / attempts : 0,
46
+ refusalRate: attempts > 0 ? input.refused / attempts : 0,
47
+ };
48
+ };
49
+ /**
50
+ * The threshold: 32 MiB per hour.
51
+ *
52
+ * Chosen to be well above JIT warm-up, heap fragmentation and a pool filling to its configured size, and well
53
+ * below anything that survives a night. A process leaking at this rate grows by 768 MiB a day, which is an
54
+ * incident; one growing at a tenth of it is noise a stricter threshold would report every run, and a detector
55
+ * that cries wolf is one people stop reading.
56
+ */
57
+ export const LEAK_BYTES_PER_HOUR = 32 * 1024 * 1024;
58
+ /** Below this, a fit is fitting noise. */
59
+ export const MIN_GROWTH_SAMPLES = 12;
60
+ /** Below this, growth is indistinguishable from warm-up whatever the fit says. */
61
+ export const MIN_GROWTH_DURATION_MS = 5 * 60 * 1_000;
62
+ const mean = (values) => values.length === 0 ? Number.NaN : values.reduce((a, b) => a + b, 0) / values.length;
63
+ /**
64
+ * The share of a run treated as warm-up and excluded from the fit.
65
+ *
66
+ * Every real process ramps at the start: the JIT compiles, connection pools fill to their configured size, caches
67
+ * populate. That ramp is not a leak, and it is steep — steeper than any leak worth catching — so a fit including
68
+ * it reports a leak on **every single run**.
69
+ *
70
+ * A quarter. Enough to clear the ramp on runs of any length, and it still leaves three quarters of the samples to
71
+ * fit, which is where a leak would show anyway: a leak that only exists during warm-up is a warm-up.
72
+ */
73
+ export const WARMUP_FRACTION = 0.25;
74
+ /**
75
+ * Least-squares slope of RSS against time, after discarding warm-up, plus a quartile comparison.
76
+ *
77
+ * Both, because each catches what the other misses. A fit is robust to a single spike; a quartile comparison is
78
+ * robust to a ramp but fooled by one outlier. A verdict of "leaking" needs *both*.
79
+ *
80
+ * The warm-up exclusion was not in the first version, and the test for it failed: a 20-second ramp to 500 MiB
81
+ * followed by a flat hour fit at ~420 MiB/h and was reported as a leak. My own comment claimed the quartile check
82
+ * handled it, and it did not — the first quartile *was* the ramp, so its mean was low and the comparison agreed
83
+ * with the fit. Two checks that share a blind spot are one check.
84
+ */
85
+ export const detectGrowth = (samples) => {
86
+ const first = samples[0];
87
+ const last = samples[samples.length - 1];
88
+ const observedMs = first !== undefined && last !== undefined ? last.atMs - first.atMs : 0;
89
+ // Both the fit and the comparison run on the post-warm-up window, so they cannot share the ramp as a blind spot.
90
+ const warmup = Math.floor(samples.length * WARMUP_FRACTION);
91
+ const steady = samples.slice(warmup);
92
+ const quartile = Math.max(1, Math.floor(steady.length / 4));
93
+ const firstQuartileMean = mean(steady.slice(0, quartile).map((s) => s.rssBytes));
94
+ const lastQuartileMean = mean(steady.slice(-quartile).map((s) => s.rssBytes));
95
+ if (samples.length < MIN_GROWTH_SAMPLES)
96
+ return { bytesPerHour: Number.NaN, firstQuartileMean, lastQuartileMean, samples: samples.length, observedMs, leaking: false, reason: "insufficient-samples" };
97
+ if (observedMs < MIN_GROWTH_DURATION_MS)
98
+ return { bytesPerHour: Number.NaN, firstQuartileMean, lastQuartileMean, samples: samples.length, observedMs, leaking: false, reason: "insufficient-duration" };
99
+ const t0 = steady[0]?.atMs ?? 0;
100
+ const xs = steady.map((s) => (s.atMs - t0) / 3_600_000);
101
+ const ys = steady.map((s) => s.rssBytes);
102
+ const xBar = mean(xs);
103
+ const yBar = mean(ys);
104
+ let num = 0;
105
+ let den = 0;
106
+ for (const [i, x] of xs.entries()) {
107
+ num += (x - xBar) * (ys[i] - yBar);
108
+ den += (x - xBar) ** 2;
109
+ }
110
+ const bytesPerHour = den === 0 ? 0 : num / den;
111
+ const leaking = bytesPerHour > LEAK_BYTES_PER_HOUR && lastQuartileMean > firstQuartileMean;
112
+ return {
113
+ bytesPerHour,
114
+ firstQuartileMean,
115
+ lastQuartileMean,
116
+ samples: samples.length,
117
+ observedMs,
118
+ leaking,
119
+ reason: leaking ? "leaking" : "stable",
120
+ };
121
+ };
122
+ export const DEFAULT_LOAD_BUDGET = { p99Ms: 5_000, errorRate: 0.01, minCompletionRatio: 0.9 };
123
+ /**
124
+ * Read an envelope off a staircase of load steps.
125
+ *
126
+ * The first failing step is the one reported, not the worst: an operator needs the point at which behaviour
127
+ * *starts* to go wrong, and later steps are already past it. And the mode is read from that step rather than
128
+ * from the whole run, because a system that queues at 40/s and errors at 80/s degrades gracefully — the abrupt
129
+ * behaviour further out is a different fact.
130
+ */
131
+ export const readEnvelope = (steps, budget = DEFAULT_LOAD_BUDGET) => {
132
+ let sustainable = 0;
133
+ for (const step of steps) {
134
+ const withinLatency = step.latency.count > 0 && step.latency.p99 <= budget.p99Ms;
135
+ const withinErrors = step.throughput.errorRate <= budget.errorRate;
136
+ // A step with refusals is *not* sustainable: capacity was reached, and calling it sustainable would tell an
137
+ // operator to size for a load at which some users are being turned away.
138
+ const noRefusals = step.throughput.refused === 0;
139
+ // And completions must keep up with the offer. Without this, a step that accepted 160/s and finished 55/s
140
+ // reports a fine p99 — because the runs that never got picked up contribute no sample — and the envelope
141
+ // recommends a capacity the system does not have.
142
+ const keepingUp = step.offeredPerSecond === 0 ||
143
+ step.throughput.completedPerSecond / step.offeredPerSecond >= budget.minCompletionRatio;
144
+ if (withinLatency && withinErrors && noRefusals && keepingUp)
145
+ sustainable = step.offeredPerSecond;
146
+ else
147
+ return {
148
+ steps,
149
+ sustainablePerSecond: sustainable,
150
+ degradesAt: step.offeredPerSecond,
151
+ // Ordered by severity, so the reported mode is the worst thing happening at the first failing step.
152
+ // Errors first: a step that both errors and backlogs is an erroring step, and reporting the backlog would
153
+ // point an operator at capacity when the problem is failure.
154
+ mode: !withinErrors ? "errors" : !noRefusals ? "honest-refusal" : !keepingUp ? "backlog" : "graceful-queueing",
155
+ budget,
156
+ };
157
+ }
158
+ return { steps, sustainablePerSecond: sustainable, degradesAt: null, mode: "not-reached", budget };
159
+ };
160
+ //# sourceMappingURL=metrics.js.map
@@ -0,0 +1,28 @@
1
+ /**
2
+ * The runbooks — AC-5: "runbooks exist for every failure mode observed in testing".
3
+ *
4
+ * Kept as data next to the failure matrix, so a test can assert the two agree. A runbook in a wiki is a runbook
5
+ * that drifts from the code silently, and the drift is discovered during the incident it was written for.
6
+ *
7
+ * Each one is written for someone woken at 3am: what they will see, what to check first, what to do, and — the
8
+ * part usually missing — **what not to do**. Most of the damage in an incident comes from a well-intentioned
9
+ * action that fights the recovery mechanism, and "do not restart the workers, the reaper is already handling it"
10
+ * is the sentence that saves the night.
11
+ */
12
+ import { type FailureMode } from "./injection.js";
13
+ export type Runbook = {
14
+ readonly mode: FailureMode;
15
+ /** What an operator sees on a dashboard, in the order they will see it. */
16
+ readonly symptoms: readonly string[];
17
+ /** Which metric or log confirms it is this and not something that looks like it. */
18
+ readonly confirm: readonly string[];
19
+ readonly action: readonly string[];
20
+ /** The tempting action that makes it worse. */
21
+ readonly doNot: readonly string[];
22
+ /** How long unattended recovery takes, so an operator knows whether to wait. */
23
+ readonly expectedRecovery: string;
24
+ };
25
+ export declare const RUNBOOKS: Readonly<Record<FailureMode, Runbook>>;
26
+ /** Every mode has a runbook. Asserted, so a new injector cannot ship undocumented. */
27
+ export declare const modesWithoutRunbook: () => readonly FailureMode[];
28
+ //# sourceMappingURL=runbooks.d.ts.map