@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,293 @@
1
+ /**
2
+ * The extraction pipeline (#131).
3
+ *
4
+ * The parsers do the reading; this decides *when*, *within what bounds*, and *what is recorded when it goes
5
+ * wrong* — which is the part that makes extraction safe to run on a shared worker tier.
6
+ *
7
+ * Four properties, each answering an AC:
8
+ *
9
+ * - **It never throws for a document problem** (AC-4). Every outcome is recorded on the file: `extracted` with
10
+ * a reference, or `failed` with a typed reason and a sentence the user reads. An extractor that threw would
11
+ * leave the file looking unextracted, which is indistinguishable from "not got round to it yet" — and the
12
+ * user would see an empty document rather than a reason.
13
+ * - **It is bounded in every dimension a document can grow** (AC-3): bytes read, pages, extracted text,
14
+ * blocks, and wall clock. The timeout is enforced *here* rather than trusted to the parser, because a
15
+ * parser stuck in a loop is exactly the case a parser-side check does not catch.
16
+ * - **It is enqueued, not awaited** (AC-2). `requestExtraction` puts a job on the queue and returns; an
17
+ * enqueue failure marks the file `pending` and is reported, never propagated — an attachment that uploaded
18
+ * fine must not fail because the extraction queue was briefly unreachable.
19
+ * - **The result is stored by reference and read in windows** (AC-6). The `ExtractedDocument` goes to
20
+ * `BlobStore` and the file carries the ref. Nothing loads it into context; `read_document` reads a bounded
21
+ * window, exactly as #130 established for the raw bytes.
22
+ */
23
+ import { AgentPlatformError } from "../core/errors.js";
24
+ import { DEFAULT_EXTRACTION_LIMITS, isExtractionFailure } from "./index.js";
25
+ import {} from "./index.js";
26
+ /** The media type without its parameters, lowercased. `text/plain; charset=utf-8` is `text/plain`. */
27
+ const normaliseMediaType = (mediaType) => mediaType.split(";")[0]?.trim().toLowerCase() ?? "";
28
+ /**
29
+ * Read at most `maxBytes` from the content store.
30
+ *
31
+ * Refuses past the limit rather than truncating, because a half-read document is not a shorter document: a
32
+ * PDF's structure is at the end, and half of one is malformed rather than partial. The distinction matters
33
+ * enough that `too-large` is its own failure reason.
34
+ */
35
+ const readBounded = async (bytes, maxBytes) => {
36
+ const chunks = [];
37
+ let size = 0;
38
+ for await (const chunk of bytes) {
39
+ size += chunk.byteLength;
40
+ // Stops pulling, so a hostile object is not drained into memory before being rejected.
41
+ if (size > maxBytes)
42
+ return "too-large";
43
+ chunks.push(chunk);
44
+ }
45
+ const joined = new Uint8Array(size);
46
+ let offset = 0;
47
+ for (const chunk of chunks) {
48
+ joined.set(chunk, offset);
49
+ offset += chunk.byteLength;
50
+ }
51
+ return joined;
52
+ };
53
+ /**
54
+ * Run a parser under a wall-clock ceiling.
55
+ *
56
+ * The race is here rather than inside the parser because a parser caught in a loop cannot check its own
57
+ * clock. This does not *stop* the runaway work — nothing in a single-threaded runtime can — but it stops the
58
+ * pipeline waiting on it, so the file gets a `timed-out` record and the queue moves on. Said plainly because
59
+ * the alternative reading of this code is that it kills the parser, and it does not.
60
+ */
61
+ const withTimeout = async (work, timeoutMs) => {
62
+ let timer;
63
+ const timeout = new Promise((resolve) => {
64
+ timer = setTimeout(() => resolve({
65
+ reason: "timed-out",
66
+ message: `Extraction took longer than ${Math.round(timeoutMs / 1000)} seconds and was stopped.`,
67
+ }), timeoutMs);
68
+ });
69
+ try {
70
+ return await Promise.race([work, timeout]);
71
+ }
72
+ finally {
73
+ // Without this the timer holds the event loop open for the full duration on every successful extraction,
74
+ // which turns a fast worker into one that exits slowly.
75
+ if (timer !== undefined)
76
+ clearTimeout(timer);
77
+ }
78
+ };
79
+ export const createExtractionService = (deps) => {
80
+ const limits = deps.limits ?? DEFAULT_EXTRACTION_LIMITS;
81
+ const clock = deps.clock ?? (() => new Date().toISOString());
82
+ const log = deps.log ?? (() => { });
83
+ /** Built once. Answers "is this type supported" without reading a byte, which is what makes AC-4's `unsupported-type` a cheap decision. */
84
+ const byMediaType = new Map();
85
+ for (const parser of deps.parsers)
86
+ for (const mediaType of parser.mediaTypes)
87
+ byMediaType.set(normaliseMediaType(mediaType), parser);
88
+ const fallbackByMediaType = new Map();
89
+ for (const parser of deps.fallbackParsers ?? [])
90
+ for (const mediaType of parser.mediaTypes)
91
+ fallbackByMediaType.set(normaliseMediaType(mediaType), parser);
92
+ const record = async (tenantId, id, extraction) => {
93
+ const { recorded } = await deps.metadata.recordExtraction({ tenantId, id, extraction });
94
+ // Not recorded means the file went away mid-extraction. Logged rather than thrown: the work is wasted,
95
+ // nothing is inconsistent, and a worker that threw here would retry a file that no longer exists.
96
+ if (!recorded)
97
+ log("extraction outcome dropped: file no longer exists", { tenantId, id });
98
+ return extraction;
99
+ };
100
+ return {
101
+ limits,
102
+ /** Types this pipeline can read at all. Exposed so an upload path can decide not to enqueue at all. */
103
+ supportedMediaTypes() {
104
+ return [...byMediaType.keys()];
105
+ },
106
+ /**
107
+ * Ask for extraction, without waiting for it — AC-2.
108
+ *
109
+ * Marks the file `pending` first, then enqueues. That order matters: the reverse would let a worker pick
110
+ * the job up, extract, and record its outcome *before* this function overwrote it with `pending`, losing a
111
+ * completed extraction to a race that only shows up under load.
112
+ */
113
+ async requestExtraction(context, id, mediaType) {
114
+ if (!byMediaType.has(normaliseMediaType(mediaType))) {
115
+ // Not a failure: nobody asked for this type to be readable. `skipped` says "we are not going to", which
116
+ // is a different sentence from "we tried and could not".
117
+ await record(context.tenantId, id, {
118
+ state: "skipped",
119
+ failureMessage: `${mediaType} is not a document type this platform extracts text from.`,
120
+ at: clock(),
121
+ });
122
+ return { enqueued: false, state: "skipped" };
123
+ }
124
+ await record(context.tenantId, id, { state: "pending", at: clock() });
125
+ if (deps.dispatcher === undefined)
126
+ return { enqueued: false, state: "pending" };
127
+ try {
128
+ await deps.dispatcher.enqueueExtraction({ tenantId: context.tenantId, fileId: id });
129
+ return { enqueued: true, state: "pending" };
130
+ }
131
+ catch (error) {
132
+ // Swallowed on purpose, and this is the one place in the module where that is right: the upload
133
+ // succeeded, the file is safe, and the row stays `pending` for `sweepStuckExtractions` to re-enqueue.
134
+ // Propagating would fail an upload because a queue was briefly unreachable.
135
+ log("extraction enqueue failed; left pending for the sweep", { tenantId: context.tenantId, id, error });
136
+ return { enqueued: false, state: "pending" };
137
+ }
138
+ },
139
+ /**
140
+ * Do the extraction. Called by the worker, and directly by a single-process host.
141
+ *
142
+ * Returns the record it wrote rather than throwing, for every document-shaped problem. The only throw is
143
+ * for a *file* that is not there, which is a caller error rather than a document one.
144
+ */
145
+ async extract(job) {
146
+ const { tenantId, fileId } = job;
147
+ const file = await deps.metadata.get({ tenantId, id: fileId });
148
+ if (file === null)
149
+ throw new AgentPlatformError({ code: "not_found", message: "no such file", retryable: false });
150
+ if (file.state !== "stored")
151
+ // A file mid-upload or mid-delete has no stable bytes to read. Not an error and not a failure — the
152
+ // sweep will find it again if it settles.
153
+ return record(tenantId, fileId, {
154
+ state: "skipped",
155
+ failureMessage: "The file was not in a readable state when extraction ran.",
156
+ at: clock(),
157
+ });
158
+ const mediaType = normaliseMediaType(file.mediaType);
159
+ const parser = byMediaType.get(mediaType);
160
+ if (parser === undefined)
161
+ return record(tenantId, fileId, {
162
+ state: "skipped",
163
+ failureMessage: `${file.mediaType} is not a document type this platform extracts text from.`,
164
+ at: clock(),
165
+ });
166
+ // `running` before the work, so a crash leaves evidence. Without it a worker that died mid-parse would
167
+ // leave the file `pending` and the sweep would re-enqueue it forever, poisoning the queue with the one
168
+ // document that reliably kills a worker.
169
+ await record(tenantId, fileId, { state: "running", at: clock() });
170
+ const stream = await deps.content.readFile({ tenantId, contentKey: file.contentKey });
171
+ if (stream === null)
172
+ return record(tenantId, fileId, {
173
+ state: "failed",
174
+ failureReason: "malformed",
175
+ failureMessage: "The file's contents are missing from storage.",
176
+ at: clock(),
177
+ });
178
+ const bytes = await readBounded(stream, limits.maxBytes);
179
+ if (bytes === "too-large")
180
+ return record(tenantId, fileId, {
181
+ state: "failed",
182
+ failureReason: "too-large",
183
+ // The limit named, so the user knows what to do rather than guessing.
184
+ failureMessage: `That document is larger than the ${limits.maxBytes} byte extraction limit.`,
185
+ at: clock(),
186
+ });
187
+ let outcome;
188
+ try {
189
+ outcome = await withTimeout(parser.parse({ bytes, mediaType, limits }), limits.timeoutMs);
190
+ }
191
+ catch (error) {
192
+ // A *thrown* error is the parser being broken, not the document being unreadable. Recorded as
193
+ // `malformed` with a generic sentence, and logged with the detail — a stack trace is not a user's
194
+ // problem, and the two must not be confused in a report.
195
+ log("parser threw", { tenantId, fileId, parser: parser.id, error });
196
+ return record(tenantId, fileId, {
197
+ state: "failed",
198
+ failureReason: "malformed",
199
+ failureMessage: "That document could not be read.",
200
+ at: clock(),
201
+ });
202
+ }
203
+ // #132. A scan is the one failure worth a second, more expensive attempt — and only that one. Retrying
204
+ // an encrypted or malformed document through OCR would spend money to reach the same conclusion.
205
+ if (isExtractionFailure(outcome) && outcome.reason === "no-text-layer") {
206
+ const fallback = fallbackByMediaType.get(mediaType);
207
+ if (fallback !== undefined && fallback.id !== parser.id) {
208
+ try {
209
+ outcome = await withTimeout(fallback.parse({ bytes, mediaType, limits }), limits.timeoutMs);
210
+ }
211
+ catch (error) {
212
+ log("fallback parser threw", { tenantId, fileId, parser: fallback.id, error });
213
+ }
214
+ }
215
+ }
216
+ if (isExtractionFailure(outcome))
217
+ return record(tenantId, fileId, {
218
+ state: "failed",
219
+ failureReason: outcome.reason,
220
+ failureMessage: outcome.message,
221
+ at: clock(),
222
+ });
223
+ // Stored by reference (AC-6). The blob is written *before* the ref is recorded: the reverse would leave
224
+ // a file pointing at a blob that does not exist, and a dangling ref reads as corruption while an
225
+ // unreferenced blob is merely waste.
226
+ // AC-4. Reported before the blob is written, and *stripped* from what is stored: a priced operation
227
+ // already happened, so a crash between here and the record must still have billed it — and a stored
228
+ // document has no business carrying billing data.
229
+ const { usage, ...document } = outcome;
230
+ for (const entry of usage ?? []) {
231
+ try {
232
+ await deps.onPricedOperation?.({ tenantId, fileId, usage: entry });
233
+ }
234
+ catch (error) {
235
+ // Logged, not thrown. A ledger write that fails must not discard an extraction that succeeded, and
236
+ // an unbilled call is a smaller problem than a document the user paid for and cannot read.
237
+ log("failed to record extraction usage", { tenantId, fileId, kind: entry.kind, error });
238
+ }
239
+ }
240
+ const ref = await deps.blobs.put({ tenantId, value: document });
241
+ return record(tenantId, fileId, {
242
+ state: "extracted",
243
+ ref,
244
+ pageCount: document.pageCount ?? 0,
245
+ blockCount: document.blocks.length,
246
+ truncated: document.truncated,
247
+ // On the record as well as in the document, so a listing can flag a low-confidence extraction
248
+ // without fetching the blob to find out.
249
+ ...(document.confidence === undefined ? {} : { confidence: document.confidence }),
250
+ at: clock(),
251
+ });
252
+ },
253
+ /** The extracted document, or null when there is none to read. */
254
+ async getExtracted(context, id) {
255
+ const file = await deps.metadata.get({ tenantId: context.tenantId, id });
256
+ if (file === null || file.extraction === undefined)
257
+ return { document: null };
258
+ if (file.extraction.state !== "extracted" || file.extraction.ref === undefined)
259
+ // The record comes back even with no document, because *why* there is nothing is the useful part —
260
+ // AC-4's whole point is that the assistant says "that PDF is a scan" rather than nothing.
261
+ return { document: null, extraction: file.extraction };
262
+ const value = await deps.blobs.get({ tenantId: context.tenantId, ref: file.extraction.ref });
263
+ return { document: value ?? null, extraction: file.extraction };
264
+ },
265
+ /**
266
+ * Re-enqueue extractions that never finished.
267
+ *
268
+ * Two shapes, and both are silent without this: a `pending` file whose enqueue was lost, and a `running`
269
+ * file whose worker died. Both are re-requested rather than repaired — the pipeline is idempotent, so
270
+ * running it again is the repair.
271
+ */
272
+ async sweepStuckExtractions(context, input) {
273
+ const states = {};
274
+ let requeued = 0;
275
+ for (const state of ["pending", "running"]) {
276
+ const page = await deps.metadata.listByExtractionState({
277
+ tenantId: context.tenantId,
278
+ state,
279
+ olderThan: input.olderThan,
280
+ limit: input.limit,
281
+ });
282
+ states[state] = page.items.length;
283
+ for (const file of page.items) {
284
+ const result = await this.requestExtraction(context, file.id, file.mediaType);
285
+ if (result.enqueued)
286
+ requeued += 1;
287
+ }
288
+ }
289
+ return { requeued, states };
290
+ },
291
+ };
292
+ };
293
+ //# sourceMappingURL=extraction.js.map
@@ -0,0 +1,199 @@
1
+ /**
2
+ * Document extraction — `docs/05-knowledge-and-documents.md`, REQ-027 (#131).
3
+ *
4
+ * Extraction turns an opaque attachment into something the model can reason about. The shape it produces is
5
+ * the design decision here, and it is deliberately **not a flat string**.
6
+ *
7
+ * A flat string is what every quick extractor produces and it destroys exactly the information a question is
8
+ * usually about. "What was Q3 revenue?" is answerable from a table and unanswerable from that table flattened
9
+ * into prose — the row and column that gave a number its meaning are gone, and the model has no way to know
10
+ * they were ever there. So the intermediate is a block list: headings keep their level, tables keep their
11
+ * cells, lists keep their items.
12
+ *
13
+ * The second decision is that **failure is a value, not an exception**. A document that cannot be read is an
14
+ * ordinary outcome — a scan with no text layer, an encrypted file, something past a limit — and the
15
+ * assistant has to be able to say which. An extractor that threw would leave the caller with a stack trace
16
+ * and the user with a file that silently behaves as if it were empty, which is the failure mode AC-4 names.
17
+ */
18
+ import type { BlobRef, FileId, TenantId } from "../core/ids.js";
19
+ /** Heading depth, as in HTML. Beyond six the source is not using headings for structure. */
20
+ export type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
21
+ /**
22
+ * One piece of a document.
23
+ *
24
+ * A closed union rather than a bag with optional fields: a `table` with no `rows` and a `heading` with no
25
+ * `level` are both unrepresentable, so a renderer never has to decide what to do about one.
26
+ */
27
+ export type DocumentBlock = {
28
+ readonly kind: "heading";
29
+ readonly level: HeadingLevel;
30
+ readonly text: string;
31
+ readonly page?: number;
32
+ } | {
33
+ readonly kind: "paragraph";
34
+ readonly text: string;
35
+ readonly page?: number;
36
+ } | {
37
+ readonly kind: "table";
38
+ /** Rows of cells. The first row is the header when `hasHeader` is true. */
39
+ readonly rows: readonly (readonly string[])[];
40
+ readonly hasHeader: boolean;
41
+ readonly page?: number;
42
+ } | {
43
+ readonly kind: "list";
44
+ readonly items: readonly string[];
45
+ readonly ordered: boolean;
46
+ readonly page?: number;
47
+ };
48
+ /**
49
+ * Why a document could not be read.
50
+ *
51
+ * Each value exists because the user-facing sentence differs. `no-text-layer` is not `malformed`: the first
52
+ * means "this is a scan, OCR would help" and the second means "this file is broken". Collapsing them would
53
+ * send someone to re-export a file that needed a different pipeline.
54
+ */
55
+ export declare const EXTRACTION_FAILURES: readonly ["unsupported-type", "too-large", "too-many-pages", "timed-out", "encrypted", "no-text-layer", "malformed"];
56
+ export type ExtractionFailureReason = (typeof EXTRACTION_FAILURES)[number];
57
+ export type ExtractionFailure = {
58
+ readonly reason: ExtractionFailureReason;
59
+ /** What the user is told. Names the limit when there is one, because "too large" sends someone to guess. */
60
+ readonly message: string;
61
+ };
62
+ /** What a parser produces when it succeeds. */
63
+ export type ExtractedDocument = {
64
+ readonly blocks: readonly DocumentBlock[];
65
+ /** Pages seen. Absent for formats with no pagination — a CSV does not have pages. */
66
+ readonly pageCount?: number;
67
+ /** True when a limit stopped extraction early. The content is usable; it is just not all of it. */
68
+ readonly truncated: boolean;
69
+ /**
70
+ * Things the caller should know that are not failures.
71
+ *
72
+ * Surfaced rather than logged: "three pages had no text layer" changes how much a user should trust an
73
+ * answer, and a log line does not reach them.
74
+ */
75
+ readonly warnings: readonly string[];
76
+ /**
77
+ * How confident the extraction is, 0–1 (#132).
78
+ *
79
+ * Present only for extraction that *has* a confidence — OCR and vision. A PDF's text layer is not
80
+ * recognised, it is read, so a confidence there would be a number with nothing behind it. Absent therefore
81
+ * means "not a probabilistic extraction", which is a different fact from "confidence unknown".
82
+ */
83
+ readonly confidence?: number;
84
+ /**
85
+ * Priced operations this extraction performed (#132, AC-4).
86
+ *
87
+ * **Transport only.** The pipeline reports each entry to `onPricedOperation` and then strips the field
88
+ * before storing the blob, so a stored document never carries billing data. It lives on the result rather
89
+ * than in a callback because only the pipeline knows which file and tenant a parse belonged to — a callback
90
+ * handed to the parser at construction is shared across every file it ever parses, and correlating one
91
+ * would mean trusting that parses never interleave.
92
+ */
93
+ readonly usage?: readonly {
94
+ readonly kind: "vision" | "ocr";
95
+ readonly modelId: string;
96
+ readonly inputTokens: number;
97
+ readonly outputTokens: number;
98
+ readonly cachedInputTokens: number;
99
+ }[];
100
+ };
101
+ /**
102
+ * The bounds a parser runs inside — AC-3.
103
+ *
104
+ * Every one of these exists because a document is attacker-controlled input. A page limit alone is not
105
+ * enough: a single-page PDF can carry a content stream that decompresses to gigabytes, which is why
106
+ * `maxTextBytes` is separate from `maxBytes`, and why `maxBlocks` is separate again — a million empty
107
+ * paragraphs cost little text and a great deal of everything else.
108
+ */
109
+ export type ExtractionLimits = {
110
+ /** Bytes read from storage. Past this the document is refused rather than truncated: a half-read PDF is not a shorter PDF. */
111
+ readonly maxBytes: number;
112
+ readonly maxPages: number;
113
+ /** Extracted text kept, across all blocks. Truncates rather than refuses — half a document is still useful. */
114
+ readonly maxTextBytes: number;
115
+ readonly maxBlocks: number;
116
+ /** Wall-clock ceiling. A parser that ignores it is stopped by the pipeline's own race. */
117
+ readonly timeoutMs: number;
118
+ };
119
+ /**
120
+ * Conservative defaults.
121
+ *
122
+ * `maxPages: 500` is a long report, not a book; `maxTextBytes: 2 MiB` is roughly 500k tokens of source text,
123
+ * far more than any context window, so the ceiling never binds before the read bounds do. `timeoutMs` is
124
+ * short on purpose — a worker occupied for a minute by one document is a worker not serving anyone else, and
125
+ * a document that slow is a document to reject rather than wait for.
126
+ */
127
+ export declare const DEFAULT_EXTRACTION_LIMITS: ExtractionLimits;
128
+ /**
129
+ * A parser for one family of media types.
130
+ *
131
+ * `mediaTypes` is data rather than a `canParse(type)` method so the pipeline can build its dispatch table
132
+ * once and answer "is this type supported at all" without running anything — which is what makes
133
+ * `unsupported-type` a decision taken before a byte is read.
134
+ */
135
+ export interface DocumentParser {
136
+ readonly id: string;
137
+ readonly mediaTypes: readonly string[];
138
+ /**
139
+ * Parse, or return a typed failure.
140
+ *
141
+ * Returns rather than throws for document problems; a thrown error means the parser itself is broken, and
142
+ * the pipeline records that separately so the two are never confused in a report.
143
+ */
144
+ parse(input: {
145
+ readonly bytes: Uint8Array;
146
+ /**
147
+ * The normalised media type, without parameters.
148
+ *
149
+ * Passed in rather than re-derived: a parser serving five types has to know which one it was handed, and
150
+ * a parser that guessed from the bytes would disagree with the dispatch table that chose it.
151
+ */
152
+ readonly mediaType: string;
153
+ readonly limits: ExtractionLimits;
154
+ }): Promise<ExtractedDocument | ExtractionFailure>;
155
+ }
156
+ /** Discriminates a parser's two return shapes without a tag on the success side. */
157
+ export declare const isExtractionFailure: (value: ExtractedDocument | ExtractionFailure) => value is ExtractionFailure;
158
+ /**
159
+ * Extraction state on a file.
160
+ *
161
+ * `pending` and `failed` are both terminal-ish states a user can see, and they are separate from the file's
162
+ * own `state`: a file is perfectly `stored` while its extraction is `failed`, and conflating the two would
163
+ * make an unreadable document look like a lost upload.
164
+ */
165
+ export declare const EXTRACTION_STATES: readonly ["pending", "running", "extracted", "failed", "skipped"];
166
+ export type ExtractionState = (typeof EXTRACTION_STATES)[number];
167
+ /** What the file metadata records about its derived text. */
168
+ export type ExtractionRecord = {
169
+ readonly state: ExtractionState;
170
+ /** Where the `ExtractedDocument` lives. `BlobStore` holds JSON, which is exactly what this is. */
171
+ readonly ref?: BlobRef;
172
+ readonly failure?: ExtractionFailure;
173
+ readonly pageCount?: number;
174
+ readonly blockCount?: number;
175
+ readonly truncated?: boolean;
176
+ readonly at?: string;
177
+ };
178
+ /** A unit of extraction work. Mirrors `RunJob`'s shape so the two queues read the same way. */
179
+ export type ExtractionJob = {
180
+ readonly tenantId: TenantId;
181
+ readonly fileId: FileId;
182
+ };
183
+ /**
184
+ * The queue's producing side.
185
+ *
186
+ * Separate from `JobDispatcher` rather than a method added to it: the run path depends on that interface, and
187
+ * widening it would make every existing implementation — including test doubles — incomplete for a job type
188
+ * they have nothing to do with.
189
+ */
190
+ export interface ExtractionDispatcher {
191
+ enqueueExtraction(job: ExtractionJob): Promise<void>;
192
+ }
193
+ export * from "./parsers/text.js";
194
+ export * from "./parsers/pdf.js";
195
+ export * from "./extraction.js";
196
+ export * from "./render.js";
197
+ export * from "./read-tool.js";
198
+ export * from "./vision.js";
199
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Document extraction — `docs/05-knowledge-and-documents.md`, REQ-027 (#131).
3
+ *
4
+ * Extraction turns an opaque attachment into something the model can reason about. The shape it produces is
5
+ * the design decision here, and it is deliberately **not a flat string**.
6
+ *
7
+ * A flat string is what every quick extractor produces and it destroys exactly the information a question is
8
+ * usually about. "What was Q3 revenue?" is answerable from a table and unanswerable from that table flattened
9
+ * into prose — the row and column that gave a number its meaning are gone, and the model has no way to know
10
+ * they were ever there. So the intermediate is a block list: headings keep their level, tables keep their
11
+ * cells, lists keep their items.
12
+ *
13
+ * The second decision is that **failure is a value, not an exception**. A document that cannot be read is an
14
+ * ordinary outcome — a scan with no text layer, an encrypted file, something past a limit — and the
15
+ * assistant has to be able to say which. An extractor that threw would leave the caller with a stack trace
16
+ * and the user with a file that silently behaves as if it were empty, which is the failure mode AC-4 names.
17
+ */
18
+ /**
19
+ * Why a document could not be read.
20
+ *
21
+ * Each value exists because the user-facing sentence differs. `no-text-layer` is not `malformed`: the first
22
+ * means "this is a scan, OCR would help" and the second means "this file is broken". Collapsing them would
23
+ * send someone to re-export a file that needed a different pipeline.
24
+ */
25
+ export const EXTRACTION_FAILURES = [
26
+ "unsupported-type",
27
+ "too-large",
28
+ "too-many-pages",
29
+ "timed-out",
30
+ "encrypted",
31
+ "no-text-layer",
32
+ "malformed",
33
+ ];
34
+ /**
35
+ * Conservative defaults.
36
+ *
37
+ * `maxPages: 500` is a long report, not a book; `maxTextBytes: 2 MiB` is roughly 500k tokens of source text,
38
+ * far more than any context window, so the ceiling never binds before the read bounds do. `timeoutMs` is
39
+ * short on purpose — a worker occupied for a minute by one document is a worker not serving anyone else, and
40
+ * a document that slow is a document to reject rather than wait for.
41
+ */
42
+ export const DEFAULT_EXTRACTION_LIMITS = {
43
+ maxBytes: 25 * 1024 * 1024,
44
+ maxPages: 500,
45
+ maxTextBytes: 2 * 1024 * 1024,
46
+ maxBlocks: 20_000,
47
+ timeoutMs: 20_000,
48
+ };
49
+ /** Discriminates a parser's two return shapes without a tag on the success side. */
50
+ export const isExtractionFailure = (value) => "reason" in value;
51
+ /**
52
+ * Extraction state on a file.
53
+ *
54
+ * `pending` and `failed` are both terminal-ish states a user can see, and they are separate from the file's
55
+ * own `state`: a file is perfectly `stored` while its extraction is `failed`, and conflating the two would
56
+ * make an unreadable document look like a lost upload.
57
+ */
58
+ export const EXTRACTION_STATES = ["pending", "running", "extracted", "failed", "skipped"];
59
+ export * from "./parsers/text.js";
60
+ export * from "./parsers/pdf.js";
61
+ export * from "./extraction.js";
62
+ export * from "./render.js";
63
+ export * from "./read-tool.js";
64
+ export * from "./vision.js";
65
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,47 @@
1
+ /**
2
+ * PDF text extraction (#131).
3
+ *
4
+ * Over the raw PDF syntax, with `node:zlib` for the compressed streams and no dependency. That is a real
5
+ * decision with real limits, so they are stated here rather than discovered later.
6
+ *
7
+ * **What it handles.** PDFs produced by the tools people actually use — Word, LaTeX, Chrome's print-to-PDF,
8
+ * Google Docs, most report generators. It walks the content streams, follows the text operators, and uses the
9
+ * positioning operators to reconstruct lines and paragraphs. Font size changes become heading levels, because
10
+ * a PDF has no headings: it has text that happens to be bigger, and inferring from that is the only signal
11
+ * available.
12
+ *
13
+ * **What it does not handle**, each of which is reported as a typed failure rather than as empty text:
14
+ *
15
+ * - **Encrypted documents.** Detected by `/Encrypt` in the trailer and refused. Extracting from one would
16
+ * mean implementing the standard security handler, which is a decryption tool wearing a parser's clothes.
17
+ * - **Scans with no text layer.** A photograph of a page contains no text operators at all. Reported as
18
+ * `no-text-layer`, which is the answer that sends someone to OCR instead of to a bug report.
19
+ * - **Custom-encoded embedded fonts.** A Type0/CID font with an embedded `ToUnicode` map needs that map
20
+ * applied; without it the bytes are glyph indices, not characters. Detected as mojibake and warned about
21
+ * rather than silently returned as text — a garbled answer is worse than a refusal.
22
+ *
23
+ * Tables are the honest weak spot and the comment on `groupIntoBlocks` says how far it gets: a PDF does not
24
+ * contain tables, it contains text at coordinates, and column inference from coordinates is a heuristic. It
25
+ * recovers regular grids and says so when it is unsure.
26
+ */
27
+ import type { DocumentParser, ExtractedDocument, ExtractionFailure, ExtractionLimits } from "../index.js";
28
+ /**
29
+ * Emitted when the inflated total is cut off.
30
+ *
31
+ * A named constant because a test asserting on it is asserting on the bound itself. The alternative — checking
32
+ * that *something* was truncated — passes when the block or text ceiling fires instead, which is a different
33
+ * bound and does nothing to stop the decompression work.
34
+ */
35
+ export declare const DECOMPRESSION_LIMIT_WARNING = "Stopped decompressing: the document's streams expand past the allowed size.";
36
+ /**
37
+ * `\` escapes inside a PDF literal string.
38
+ *
39
+ * Including the line continuation, which is the one people forget: a backslash at end of line means "no
40
+ * character here", and treating it as a literal backslash inserts one into every wrapped string.
41
+ */
42
+ export declare const unescapePdfString: (raw: string) => string;
43
+ /** A hex string, `<48656C6C6F>`. An odd final digit is padded with zero, as the spec requires. */
44
+ export declare const decodeHexString: (raw: string) => string;
45
+ export declare const parsePdf: (bytes: Uint8Array, limits: ExtractionLimits) => ExtractedDocument | ExtractionFailure;
46
+ export declare const createPdfDocumentParser: () => DocumentParser;
47
+ //# sourceMappingURL=pdf.d.ts.map