@armadra/agent 0.0.0-stage → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (407) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/LICENSE +21 -0
  3. package/README.md +426 -2
  4. package/dist/agent/agent.d.ts +56 -0
  5. package/dist/agent/agent.js +129 -0
  6. package/dist/agent/loop.d.ts +51 -0
  7. package/dist/agent/loop.js +158 -0
  8. package/dist/agent/queue.d.ts +25 -0
  9. package/dist/agent/queue.js +55 -0
  10. package/dist/agent/retry.d.ts +22 -0
  11. package/dist/agent/retry.js +92 -0
  12. package/dist/agent/schema.d.ts +27 -0
  13. package/dist/agent/schema.js +186 -0
  14. package/dist/agent/session-cache.d.ts +100 -0
  15. package/dist/agent/session-cache.js +511 -0
  16. package/dist/agent/session-compaction.d.ts +49 -0
  17. package/dist/agent/session-compaction.js +251 -0
  18. package/dist/agent/session-core.d.ts +103 -0
  19. package/dist/agent/session-core.js +7 -0
  20. package/dist/agent/session-run.d.ts +55 -0
  21. package/dist/agent/session-run.js +246 -0
  22. package/dist/agent/session-state.d.ts +39 -0
  23. package/dist/agent/session-state.js +118 -0
  24. package/dist/agent/session-subagent.d.ts +54 -0
  25. package/dist/agent/session-subagent.js +157 -0
  26. package/dist/agent/session-sync.d.ts +20 -0
  27. package/dist/agent/session-sync.js +104 -0
  28. package/dist/agent/session-tools.d.ts +24 -0
  29. package/dist/agent/session-tools.js +277 -0
  30. package/dist/agent/session.d.ts +117 -0
  31. package/dist/agent/session.js +484 -0
  32. package/dist/agent/system-prompt.d.ts +49 -0
  33. package/dist/agent/system-prompt.js +121 -0
  34. package/dist/agent/tool-runner.d.ts +64 -0
  35. package/dist/agent/tool-runner.js +309 -0
  36. package/dist/agent/transform.d.ts +26 -0
  37. package/dist/agent/transform.js +171 -0
  38. package/dist/agent/types.d.ts +378 -0
  39. package/dist/agent/types.js +21 -0
  40. package/dist/ai/apis/anthropic-messages.d.ts +26 -0
  41. package/dist/ai/apis/anthropic-messages.js +214 -0
  42. package/dist/ai/apis/anthropic-request.d.ts +42 -0
  43. package/dist/ai/apis/anthropic-request.js +282 -0
  44. package/dist/ai/apis/api.d.ts +28 -0
  45. package/dist/ai/apis/api.js +102 -0
  46. package/dist/ai/apis/cache-params.d.ts +48 -0
  47. package/dist/ai/apis/cache-params.js +163 -0
  48. package/dist/ai/apis/google-generative-ai.d.ts +32 -0
  49. package/dist/ai/apis/google-generative-ai.js +249 -0
  50. package/dist/ai/apis/google-request.d.ts +40 -0
  51. package/dist/ai/apis/google-request.js +255 -0
  52. package/dist/ai/apis/openai-compat.d.ts +35 -0
  53. package/dist/ai/apis/openai-compat.js +142 -0
  54. package/dist/ai/apis/openai-completions.d.ts +28 -0
  55. package/dist/ai/apis/openai-completions.js +246 -0
  56. package/dist/ai/apis/openai-request.d.ts +35 -0
  57. package/dist/ai/apis/openai-request.js +303 -0
  58. package/dist/ai/apis/openai-responses-request.d.ts +52 -0
  59. package/dist/ai/apis/openai-responses-request.js +266 -0
  60. package/dist/ai/apis/openai-responses.d.ts +28 -0
  61. package/dist/ai/apis/openai-responses.js +303 -0
  62. package/dist/ai/apis/shared.d.ts +56 -0
  63. package/dist/ai/apis/shared.js +179 -0
  64. package/dist/ai/cache/economics.d.ts +24 -0
  65. package/dist/ai/cache/economics.js +69 -0
  66. package/dist/ai/cache/fingerprint.d.ts +17 -0
  67. package/dist/ai/cache/fingerprint.js +39 -0
  68. package/dist/ai/cache/miss.d.ts +49 -0
  69. package/dist/ai/cache/miss.js +96 -0
  70. package/dist/ai/cache/reporting.d.ts +39 -0
  71. package/dist/ai/cache/reporting.js +102 -0
  72. package/dist/ai/cache/types.d.ts +94 -0
  73. package/dist/ai/cache/types.js +19 -0
  74. package/dist/ai/cache/warmer.d.ts +94 -0
  75. package/dist/ai/cache/warmer.js +224 -0
  76. package/dist/ai/context.d.ts +35 -0
  77. package/dist/ai/context.js +114 -0
  78. package/dist/ai/cost.d.ts +21 -0
  79. package/dist/ai/cost.js +60 -0
  80. package/dist/ai/event-stream.d.ts +33 -0
  81. package/dist/ai/event-stream.js +101 -0
  82. package/dist/ai/fake/fake-provider.d.ts +68 -0
  83. package/dist/ai/fake/fake-provider.js +278 -0
  84. package/dist/ai/fake/fake-script.d.ts +69 -0
  85. package/dist/ai/fake/fake-script.js +142 -0
  86. package/dist/ai/http.d.ts +54 -0
  87. package/dist/ai/http.js +212 -0
  88. package/dist/ai/json-partial.d.ts +17 -0
  89. package/dist/ai/json-partial.js +243 -0
  90. package/dist/ai/overflow.d.ts +22 -0
  91. package/dist/ai/overflow.js +61 -0
  92. package/dist/ai/providers/auth.d.ts +71 -0
  93. package/dist/ai/providers/auth.js +202 -0
  94. package/dist/ai/providers/builtin.d.ts +23 -0
  95. package/dist/ai/providers/builtin.js +148 -0
  96. package/dist/ai/providers/catalog-data.d.ts +5 -0
  97. package/dist/ai/providers/catalog-data.js +19 -0
  98. package/dist/ai/providers/catalog.d.ts +28 -0
  99. package/dist/ai/providers/catalog.js +166 -0
  100. package/dist/ai/providers/registry.d.ts +83 -0
  101. package/dist/ai/providers/registry.js +305 -0
  102. package/dist/ai/sse.d.ts +32 -0
  103. package/dist/ai/sse.js +140 -0
  104. package/dist/ai/thinking.d.ts +42 -0
  105. package/dist/ai/thinking.js +101 -0
  106. package/dist/ai/types.d.ts +428 -0
  107. package/dist/ai/types.js +19 -0
  108. package/dist/bundle/ama-sandbox.cjs +324 -0
  109. package/dist/bundle/ama.cjs +25770 -0
  110. package/dist/bundle.d.ts +9 -0
  111. package/dist/bundle.js +15 -0
  112. package/dist/cli/args.d.ts +82 -0
  113. package/dist/cli/args.js +381 -0
  114. package/dist/cli/bootstrap.d.ts +18 -0
  115. package/dist/cli/bootstrap.js +398 -0
  116. package/dist/cli/compose-providers.d.ts +37 -0
  117. package/dist/cli/compose-providers.js +104 -0
  118. package/dist/cli/compose-session.d.ts +69 -0
  119. package/dist/cli/compose-session.js +370 -0
  120. package/dist/cli/compose-store.d.ts +24 -0
  121. package/dist/cli/compose-store.js +125 -0
  122. package/dist/cli/compose.d.ts +106 -0
  123. package/dist/cli/compose.js +206 -0
  124. package/dist/cli/default-model.d.ts +16 -0
  125. package/dist/cli/default-model.js +24 -0
  126. package/dist/cli/deps.d.ts +205 -0
  127. package/dist/cli/deps.js +12 -0
  128. package/dist/cli/exit-codes.d.ts +29 -0
  129. package/dist/cli/exit-codes.js +40 -0
  130. package/dist/cli/main.d.ts +26 -0
  131. package/dist/cli/main.js +165 -0
  132. package/dist/cli/runtime.d.ts +90 -0
  133. package/dist/cli/runtime.js +8 -0
  134. package/dist/cli/startup-screen.d.ts +15 -0
  135. package/dist/cli/startup-screen.js +56 -0
  136. package/dist/cli/startup-steps.d.ts +35 -0
  137. package/dist/cli/startup-steps.js +197 -0
  138. package/dist/cli/subcommands/auth.d.ts +12 -0
  139. package/dist/cli/subcommands/auth.js +91 -0
  140. package/dist/cli/subcommands/config.d.ts +44 -0
  141. package/dist/cli/subcommands/config.js +201 -0
  142. package/dist/cli/subcommands/context.d.ts +21 -0
  143. package/dist/cli/subcommands/context.js +41 -0
  144. package/dist/cli/subcommands/doctor.d.ts +11 -0
  145. package/dist/cli/subcommands/doctor.js +246 -0
  146. package/dist/cli/subcommands/models-cache-probe.d.ts +34 -0
  147. package/dist/cli/subcommands/models-cache-probe.js +190 -0
  148. package/dist/cli/subcommands/models-discover.d.ts +40 -0
  149. package/dist/cli/subcommands/models-discover.js +204 -0
  150. package/dist/cli/subcommands/models.d.ts +40 -0
  151. package/dist/cli/subcommands/models.js +144 -0
  152. package/dist/cli/subcommands/sessions.d.ts +11 -0
  153. package/dist/cli/subcommands/sessions.js +100 -0
  154. package/dist/codemode/capability.d.ts +37 -0
  155. package/dist/codemode/capability.js +55 -0
  156. package/dist/codemode/declarations.d.ts +44 -0
  157. package/dist/codemode/declarations.js +111 -0
  158. package/dist/codemode/host-side.d.ts +59 -0
  159. package/dist/codemode/host-side.js +253 -0
  160. package/dist/codemode/modes.d.ts +23 -0
  161. package/dist/codemode/modes.js +40 -0
  162. package/dist/codemode/protocol.d.ts +91 -0
  163. package/dist/codemode/protocol.js +114 -0
  164. package/dist/codemode/sandbox-entry.d.ts +25 -0
  165. package/dist/codemode/sandbox-entry.js +323 -0
  166. package/dist/codemode/store.d.ts +26 -0
  167. package/dist/codemode/store.js +53 -0
  168. package/dist/codemode/tool.d.ts +105 -0
  169. package/dist/codemode/tool.js +283 -0
  170. package/dist/compaction/branch-summary.d.ts +30 -0
  171. package/dist/compaction/branch-summary.js +79 -0
  172. package/dist/compaction/breaker.d.ts +32 -0
  173. package/dist/compaction/breaker.js +65 -0
  174. package/dist/compaction/cut-point.d.ts +23 -0
  175. package/dist/compaction/cut-point.js +70 -0
  176. package/dist/compaction/estimate.d.ts +30 -0
  177. package/dist/compaction/estimate.js +114 -0
  178. package/dist/compaction/prune-tier.d.ts +31 -0
  179. package/dist/compaction/prune-tier.js +83 -0
  180. package/dist/compaction/serialize.d.ts +20 -0
  181. package/dist/compaction/serialize.js +115 -0
  182. package/dist/compaction/summarize-tier.d.ts +111 -0
  183. package/dist/compaction/summarize-tier.js +305 -0
  184. package/dist/config/auth-file.d.ts +39 -0
  185. package/dist/config/auth-file.js +92 -0
  186. package/dist/config/context-files.d.ts +30 -0
  187. package/dist/config/context-files.js +77 -0
  188. package/dist/config/load.d.ts +45 -0
  189. package/dist/config/load.js +97 -0
  190. package/dist/config/merge.d.ts +73 -0
  191. package/dist/config/merge.js +248 -0
  192. package/dist/config/paths.d.ts +38 -0
  193. package/dist/config/paths.js +108 -0
  194. package/dist/config/profile.d.ts +27 -0
  195. package/dist/config/profile.js +65 -0
  196. package/dist/config/schema.d.ts +34 -0
  197. package/dist/config/schema.js +413 -0
  198. package/dist/config/trust.d.ts +42 -0
  199. package/dist/config/trust.js +122 -0
  200. package/dist/config/types.d.ts +172 -0
  201. package/dist/config/types.js +27 -0
  202. package/dist/config/write.d.ts +10 -0
  203. package/dist/config/write.js +25 -0
  204. package/dist/errors.d.ts +28 -0
  205. package/dist/errors.js +30 -0
  206. package/dist/hooks/config.d.ts +49 -0
  207. package/dist/hooks/config.js +102 -0
  208. package/dist/hooks/dispatcher.d.ts +41 -0
  209. package/dist/hooks/dispatcher.js +104 -0
  210. package/dist/hooks/matcher.d.ts +22 -0
  211. package/dist/hooks/matcher.js +108 -0
  212. package/dist/hooks/protocol.d.ts +33 -0
  213. package/dist/hooks/protocol.js +240 -0
  214. package/dist/hooks/runner.d.ts +30 -0
  215. package/dist/hooks/runner.js +143 -0
  216. package/dist/hooks/types.d.ts +147 -0
  217. package/dist/hooks/types.js +20 -0
  218. package/dist/host/api-impl.d.ts +75 -0
  219. package/dist/host/api-impl.js +173 -0
  220. package/dist/host/loader.d.ts +31 -0
  221. package/dist/host/loader.js +147 -0
  222. package/dist/host/types.d.ts +169 -0
  223. package/dist/host/types.js +14 -0
  224. package/dist/host.d.ts +5 -0
  225. package/dist/host.js +4 -0
  226. package/dist/index.d.ts +41 -0
  227. package/dist/index.js +28 -0
  228. package/dist/modes/commands-core.d.ts +44 -0
  229. package/dist/modes/commands-core.js +171 -0
  230. package/dist/modes/interactive/approval-dialog.d.ts +48 -0
  231. package/dist/modes/interactive/approval-dialog.js +202 -0
  232. package/dist/modes/interactive/commands.d.ts +41 -0
  233. package/dist/modes/interactive/commands.js +202 -0
  234. package/dist/modes/interactive/completion.d.ts +53 -0
  235. package/dist/modes/interactive/completion.js +157 -0
  236. package/dist/modes/interactive/interactive-mode.d.ts +46 -0
  237. package/dist/modes/interactive/interactive-mode.js +487 -0
  238. package/dist/modes/interactive/key-dispatch.d.ts +29 -0
  239. package/dist/modes/interactive/key-dispatch.js +107 -0
  240. package/dist/modes/interactive/line/line-editor.d.ts +50 -0
  241. package/dist/modes/interactive/line/line-editor.js +232 -0
  242. package/dist/modes/interactive/line/line-mode.d.ts +20 -0
  243. package/dist/modes/interactive/line/line-mode.js +216 -0
  244. package/dist/modes/interactive/line/line-render.d.ts +35 -0
  245. package/dist/modes/interactive/line/line-render.js +171 -0
  246. package/dist/modes/interactive/line/paste-state.d.ts +26 -0
  247. package/dist/modes/interactive/line/paste-state.js +72 -0
  248. package/dist/modes/interactive/message-view.d.ts +77 -0
  249. package/dist/modes/interactive/message-view.js +269 -0
  250. package/dist/modes/interactive/pickers.d.ts +42 -0
  251. package/dist/modes/interactive/pickers.js +115 -0
  252. package/dist/modes/interactive/startup-ui.d.ts +28 -0
  253. package/dist/modes/interactive/startup-ui.js +238 -0
  254. package/dist/modes/interactive/status-bar.d.ts +48 -0
  255. package/dist/modes/interactive/status-bar.js +139 -0
  256. package/dist/modes/interactive/tool-view.d.ts +95 -0
  257. package/dist/modes/interactive/tool-view.js +344 -0
  258. package/dist/modes/print/json-event.d.ts +15 -0
  259. package/dist/modes/print/json-event.js +39 -0
  260. package/dist/modes/print/print-mode.d.ts +15 -0
  261. package/dist/modes/print/print-mode.js +96 -0
  262. package/dist/modes/rpc/commands.d.ts +40 -0
  263. package/dist/modes/rpc/commands.js +258 -0
  264. package/dist/modes/rpc/jsonl.d.ts +16 -0
  265. package/dist/modes/rpc/jsonl.js +55 -0
  266. package/dist/modes/rpc/rpc-mode.d.ts +22 -0
  267. package/dist/modes/rpc/rpc-mode.js +153 -0
  268. package/dist/modes/session-report.d.ts +55 -0
  269. package/dist/modes/session-report.js +278 -0
  270. package/dist/modes/shared.d.ts +13 -0
  271. package/dist/modes/shared.js +52 -0
  272. package/dist/modes/startup-ui-text.d.ts +16 -0
  273. package/dist/modes/startup-ui-text.js +103 -0
  274. package/dist/permissions/broker.d.ts +47 -0
  275. package/dist/permissions/broker.js +107 -0
  276. package/dist/permissions/dangerous.d.ts +47 -0
  277. package/dist/permissions/dangerous.js +370 -0
  278. package/dist/permissions/pipeline.d.ts +51 -0
  279. package/dist/permissions/pipeline.js +181 -0
  280. package/dist/permissions/preview.d.ts +43 -0
  281. package/dist/permissions/preview.js +463 -0
  282. package/dist/permissions/rules.d.ts +58 -0
  283. package/dist/permissions/rules.js +249 -0
  284. package/dist/permissions/types.d.ts +100 -0
  285. package/dist/permissions/types.js +18 -0
  286. package/dist/rpc.d.ts +171 -0
  287. package/dist/rpc.js +13 -0
  288. package/dist/sdk.d.ts +98 -0
  289. package/dist/sdk.js +195 -0
  290. package/dist/session/manager.d.ts +70 -0
  291. package/dist/session/manager.js +278 -0
  292. package/dist/session/migrate.d.ts +19 -0
  293. package/dist/session/migrate.js +53 -0
  294. package/dist/session/projection.d.ts +50 -0
  295. package/dist/session/projection.js +180 -0
  296. package/dist/session/store.d.ts +47 -0
  297. package/dist/session/store.js +210 -0
  298. package/dist/session/tree.d.ts +21 -0
  299. package/dist/session/tree.js +88 -0
  300. package/dist/session/types.d.ts +199 -0
  301. package/dist/session/types.js +18 -0
  302. package/dist/skills/discover.d.ts +60 -0
  303. package/dist/skills/discover.js +160 -0
  304. package/dist/skills/expand.d.ts +35 -0
  305. package/dist/skills/expand.js +46 -0
  306. package/dist/skills/frontmatter.d.ts +27 -0
  307. package/dist/skills/frontmatter.js +157 -0
  308. package/dist/skills/index-prompt.d.ts +13 -0
  309. package/dist/skills/index-prompt.js +42 -0
  310. package/dist/skills/templates.d.ts +52 -0
  311. package/dist/skills/templates.js +177 -0
  312. package/dist/tools/bash.d.ts +49 -0
  313. package/dist/tools/bash.js +222 -0
  314. package/dist/tools/edit-fuzzy.d.ts +36 -0
  315. package/dist/tools/edit-fuzzy.js +129 -0
  316. package/dist/tools/edit.d.ts +38 -0
  317. package/dist/tools/edit.js +241 -0
  318. package/dist/tools/file-mutex.d.ts +8 -0
  319. package/dist/tools/file-mutex.js +29 -0
  320. package/dist/tools/glob.d.ts +44 -0
  321. package/dist/tools/glob.js +217 -0
  322. package/dist/tools/grep.d.ts +39 -0
  323. package/dist/tools/grep.js +179 -0
  324. package/dist/tools/ignore.d.ts +40 -0
  325. package/dist/tools/ignore.js +174 -0
  326. package/dist/tools/ls.d.ts +21 -0
  327. package/dist/tools/ls.js +96 -0
  328. package/dist/tools/output-accumulator.d.ts +48 -0
  329. package/dist/tools/output-accumulator.js +106 -0
  330. package/dist/tools/paths.d.ts +20 -0
  331. package/dist/tools/paths.js +58 -0
  332. package/dist/tools/presets.d.ts +71 -0
  333. package/dist/tools/presets.js +128 -0
  334. package/dist/tools/process-tree.d.ts +35 -0
  335. package/dist/tools/process-tree.js +125 -0
  336. package/dist/tools/read.d.ts +28 -0
  337. package/dist/tools/read.js +181 -0
  338. package/dist/tools/registry.d.ts +43 -0
  339. package/dist/tools/registry.js +106 -0
  340. package/dist/tools/shell.d.ts +38 -0
  341. package/dist/tools/shell.js +91 -0
  342. package/dist/tools/task.d.ts +29 -0
  343. package/dist/tools/task.js +107 -0
  344. package/dist/tools/todo.d.ts +28 -0
  345. package/dist/tools/todo.js +107 -0
  346. package/dist/tools/truncate.d.ts +44 -0
  347. package/dist/tools/truncate.js +175 -0
  348. package/dist/tools/types.d.ts +135 -0
  349. package/dist/tools/types.js +19 -0
  350. package/dist/tools/write.d.ts +20 -0
  351. package/dist/tools/write.js +84 -0
  352. package/dist/tui/ansi.d.ts +29 -0
  353. package/dist/tui/ansi.js +420 -0
  354. package/dist/tui/component.d.ts +38 -0
  355. package/dist/tui/component.js +13 -0
  356. package/dist/tui/components/box.d.ts +26 -0
  357. package/dist/tui/components/box.js +65 -0
  358. package/dist/tui/components/container.d.ts +14 -0
  359. package/dist/tui/components/container.js +39 -0
  360. package/dist/tui/components/editor-buffer.d.ts +67 -0
  361. package/dist/tui/components/editor-buffer.js +300 -0
  362. package/dist/tui/components/editor-paste.d.ts +29 -0
  363. package/dist/tui/components/editor-paste.js +64 -0
  364. package/dist/tui/components/editor.d.ts +114 -0
  365. package/dist/tui/components/editor.js +530 -0
  366. package/dist/tui/components/key-value.d.ts +29 -0
  367. package/dist/tui/components/key-value.js +48 -0
  368. package/dist/tui/components/loader.d.ts +35 -0
  369. package/dist/tui/components/loader.js +71 -0
  370. package/dist/tui/components/markdown.d.ts +90 -0
  371. package/dist/tui/components/markdown.js +462 -0
  372. package/dist/tui/components/meter.d.ts +29 -0
  373. package/dist/tui/components/meter.js +47 -0
  374. package/dist/tui/components/overlay.d.ts +24 -0
  375. package/dist/tui/components/overlay.js +52 -0
  376. package/dist/tui/components/select-list.d.ts +57 -0
  377. package/dist/tui/components/select-list.js +179 -0
  378. package/dist/tui/components/spacer.d.ts +11 -0
  379. package/dist/tui/components/spacer.js +16 -0
  380. package/dist/tui/components/text.d.ts +30 -0
  381. package/dist/tui/components/text.js +65 -0
  382. package/dist/tui/keybindings.d.ts +66 -0
  383. package/dist/tui/keybindings.js +120 -0
  384. package/dist/tui/keys.d.ts +38 -0
  385. package/dist/tui/keys.js +275 -0
  386. package/dist/tui/stdin-buffer.d.ts +41 -0
  387. package/dist/tui/stdin-buffer.js +206 -0
  388. package/dist/tui/terminal.d.ts +79 -0
  389. package/dist/tui/terminal.js +153 -0
  390. package/dist/tui/theme.d.ts +31 -0
  391. package/dist/tui/theme.js +199 -0
  392. package/dist/tui/tui.d.ts +103 -0
  393. package/dist/tui/tui.js +363 -0
  394. package/dist/tui/vt-screen.d.ts +46 -0
  395. package/dist/tui/vt-screen.js +251 -0
  396. package/dist/tui.d.ts +30 -0
  397. package/dist/tui.js +31 -0
  398. package/dist/version.d.ts +8 -0
  399. package/dist/version.js +24 -0
  400. package/docs/codemode.md +102 -0
  401. package/docs/hooks.md +196 -0
  402. package/docs/host-api.md +161 -0
  403. package/docs/providers.md +316 -0
  404. package/docs/rpc.md +253 -0
  405. package/docs/session-format.md +119 -0
  406. package/docs/tui.md +168 -0
  407. package/package.json +101 -4
@@ -0,0 +1,303 @@
1
+ /**
2
+ * openai-responses 协议(设计 §3.1):POST `{baseUrl}/responses`(stream),`event: response.*` →
3
+ * 事件;没有 `[DONE]`,以 `response.completed / incomplete / failed` 结束。
4
+ *
5
+ * - 每个 output item 一个内容块(按 `output_index` 对应,缺省时按 item id):reasoning → 思考、
6
+ * message → 文本、function_call → 工具调用;`output_item.done` 关块并记签名:reasoning 记完整
7
+ * item(含 `encrypted_content`,供无状态回放),message 记 id / phase,function_call 记 `fc_` id;
8
+ * - 思考文本来自 `reasoning_summary_text.delta`(多段 summary 之间空一行)或
9
+ * `reasoning_text.delta`;增量缺失而 done 里有全文时补齐;
10
+ * - `function_call_arguments.done` 与已拼接的增量不一致时以前缀补齐;
11
+ * - 停止:completed → stop(有工具调用则 toolUse);incomplete + max_output_tokens → length,其它
12
+ * incomplete 原因 → error;failed / `error` 事件 → error(`code: message`);无终止事件 → 断流;
13
+ * - usage:input = input_tokens − input_tokens_details.cached_tokens,reasoning =
14
+ * output_tokens_details.reasoning_tokens(已含在 output_tokens 里);cached_tokens 出现(含 0)即
15
+ * `cacheReported: true`。
16
+ */
17
+ import { AssistantEventStreamImpl } from "../event-stream.js";
18
+ import { USER_AGENT, authHeaders, describeErrorJson, joinUrl, mergeHeaders } from "../http.js";
19
+ import { readSseEvents } from "../sse.js";
20
+ import { affinityHeaders, postWithCacheFallback, resolveCacheRetention } from "./cache-params.js";
21
+ import { buildResponsesRequest, detectResponsesCompat, encodeMessageSignature, } from "./openai-responses-request.js";
22
+ import { BlockTracker, ProviderStopError, STREAM_ENDED_MESSAGE, createOutput, finishDone, finishError, requireApiKey, } from "./shared.js";
23
+ function num(value) {
24
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
25
+ }
26
+ function obj(value) {
27
+ return typeof value === "object" && value !== null && !Array.isArray(value)
28
+ ? value
29
+ : undefined;
30
+ }
31
+ function str(value) {
32
+ return typeof value === "string" ? value : undefined;
33
+ }
34
+ export function parseResponsesUsage(raw) {
35
+ const input = num(raw["input_tokens"]) ?? 0;
36
+ const cached = num(obj(raw["input_tokens_details"])?.["cached_tokens"]);
37
+ const cacheRead = cached ?? 0;
38
+ const usage = {
39
+ input: Math.max(0, input - cacheRead),
40
+ output: num(raw["output_tokens"]) ?? 0,
41
+ cacheRead,
42
+ cacheWrite: 0,
43
+ totalTokens: 0,
44
+ cacheReported: cached !== undefined,
45
+ };
46
+ const reasoning = num(obj(raw["output_tokens_details"])?.["reasoning_tokens"]);
47
+ if (reasoning !== undefined)
48
+ usage.reasoning = reasoning;
49
+ return usage;
50
+ }
51
+ export function mapResponsesStatus(status, incomplete) {
52
+ if (status === "incomplete") {
53
+ if (incomplete === "max_output_tokens")
54
+ return { reason: "length" };
55
+ return {
56
+ reason: "error",
57
+ message: incomplete ? `Response incomplete: ${incomplete}` : "Response incomplete",
58
+ };
59
+ }
60
+ if (status === "failed" || status === "cancelled") {
61
+ return { reason: "error", message: `Response ${status}` };
62
+ }
63
+ return { reason: "stop" };
64
+ }
65
+ function appendArgs(state, tracker, index, delta) {
66
+ if (delta.length === 0)
67
+ return;
68
+ state.args.set(index, (state.args.get(index) ?? "") + delta);
69
+ tracker.appendToolArgs(index, delta);
70
+ }
71
+ /** done 给出的全文与已拼接的增量对齐:是前缀则补尾巴,尚无增量则整段补上。 */
72
+ function reconcileArgs(state, tracker, index, full) {
73
+ const sofar = state.args.get(index) ?? "";
74
+ if (full.startsWith(sofar))
75
+ appendArgs(state, tracker, index, full.slice(sofar.length));
76
+ }
77
+ function slotOf(state, data, kind) {
78
+ const outputIndex = num(data["output_index"]);
79
+ const itemId = str(data["item_id"]) ?? str(obj(data["item"])?.["id"]);
80
+ const slot = (outputIndex !== undefined ? state.byOutput.get(outputIndex) : undefined) ??
81
+ (itemId !== undefined ? state.byItem.get(itemId) : undefined);
82
+ return slot?.kind === kind ? slot : undefined;
83
+ }
84
+ function openSlot(state, data, tracker) {
85
+ const item = obj(data["item"]);
86
+ const type = str(item?.["type"]);
87
+ let slot;
88
+ if (type === "reasoning")
89
+ slot = { index: tracker.startThinking(), kind: "thinking" };
90
+ else if (type === "message")
91
+ slot = { index: tracker.startText(), kind: "text" };
92
+ else if (type === "function_call") {
93
+ const callId = str(item?.["call_id"]) || str(item?.["id"]) || `call_${state.byOutput.size}`;
94
+ const index = tracker.startToolCall(callId, str(item?.["name"]) ?? "");
95
+ const block = tracker.toolCallBlock(index);
96
+ const itemId = str(item?.["id"]);
97
+ if (block && itemId)
98
+ block.thoughtSignature = itemId;
99
+ slot = { index, kind: "toolCall" };
100
+ appendArgs(state, tracker, index, str(item?.["arguments"]) ?? "");
101
+ }
102
+ if (!slot)
103
+ return undefined;
104
+ const outputIndex = num(data["output_index"]);
105
+ if (outputIndex !== undefined)
106
+ state.byOutput.set(outputIndex, slot);
107
+ const itemId = str(item?.["id"]);
108
+ if (itemId)
109
+ state.byItem.set(itemId, slot);
110
+ return slot;
111
+ }
112
+ function texts(value, field) {
113
+ if (!Array.isArray(value))
114
+ return [];
115
+ return value.map((part) => str(obj(part)?.[field]) ?? "").filter((t) => t.length > 0);
116
+ }
117
+ function closeSlot(state, data, tracker) {
118
+ const item = obj(data["item"]) ?? {};
119
+ const type = str(item["type"]);
120
+ const kind = type === "reasoning" ? "thinking" : type === "message" ? "text" : "toolCall";
121
+ if (type !== "reasoning" && type !== "message" && type !== "function_call")
122
+ return;
123
+ const slot = slotOf(state, data, kind) ?? openSlot(state, data, tracker);
124
+ if (!slot || !tracker.isOpen(slot.index))
125
+ return;
126
+ const block = tracker.output.content[slot.index];
127
+ if (type === "reasoning" && block?.type === "thinking") {
128
+ if (block.thinking.length === 0) {
129
+ const full = texts(item["summary"], "text");
130
+ const raw = full.length > 0 ? full : texts(item["content"], "text");
131
+ tracker.appendThinking(slot.index, raw.join("\n\n"));
132
+ }
133
+ block.thinkingSignature = JSON.stringify(item);
134
+ }
135
+ else if (type === "message" && block?.type === "text") {
136
+ if (block.text.length === 0) {
137
+ const parts = Array.isArray(item["content"]) ? item["content"] : [];
138
+ const full = parts.map((p) => str(obj(p)?.["text"]) ?? str(obj(p)?.["refusal"]) ?? "");
139
+ tracker.appendText(slot.index, full.join(""));
140
+ }
141
+ const id = str(item["id"]);
142
+ if (id)
143
+ block.textSignature = encodeMessageSignature(id, str(item["phase"]));
144
+ }
145
+ else if (type === "function_call" && block?.type === "toolCall") {
146
+ const args = str(item["arguments"]);
147
+ if (args !== undefined)
148
+ reconcileArgs(state, tracker, slot.index, args);
149
+ }
150
+ tracker.end(slot.index);
151
+ }
152
+ /** 处理一个事件;返回 true 表示已到终止事件。 */
153
+ function handleEvent(data, state, tracker) {
154
+ const output = tracker.output;
155
+ const type = str(data["type"]);
156
+ switch (type) {
157
+ case "response.created":
158
+ case "response.in_progress": {
159
+ const id = str(obj(data["response"])?.["id"]);
160
+ if (id && !output.responseId)
161
+ output.responseId = id;
162
+ return false;
163
+ }
164
+ case "response.output_item.added":
165
+ openSlot(state, data, tracker);
166
+ return false;
167
+ case "response.reasoning_summary_part.added": {
168
+ const slot = slotOf(state, data, "thinking");
169
+ const block = slot ? output.content[slot.index] : undefined;
170
+ if (slot && (num(data["summary_index"]) ?? 0) > 0 && block?.type === "thinking") {
171
+ if (block.thinking.length > 0)
172
+ tracker.appendThinking(slot.index, "\n\n");
173
+ }
174
+ return false;
175
+ }
176
+ case "response.reasoning_summary_text.delta":
177
+ case "response.reasoning_text.delta": {
178
+ const slot = slotOf(state, data, "thinking");
179
+ if (slot)
180
+ tracker.appendThinking(slot.index, str(data["delta"]) ?? "");
181
+ return false;
182
+ }
183
+ case "response.output_text.delta":
184
+ case "response.refusal.delta": {
185
+ const slot = slotOf(state, data, "text");
186
+ if (slot)
187
+ tracker.appendText(slot.index, str(data["delta"]) ?? "");
188
+ return false;
189
+ }
190
+ case "response.function_call_arguments.delta": {
191
+ const slot = slotOf(state, data, "toolCall");
192
+ if (slot)
193
+ appendArgs(state, tracker, slot.index, str(data["delta"]) ?? "");
194
+ return false;
195
+ }
196
+ case "response.function_call_arguments.done": {
197
+ const slot = slotOf(state, data, "toolCall");
198
+ const full = str(data["arguments"]);
199
+ if (slot && full !== undefined)
200
+ reconcileArgs(state, tracker, slot.index, full);
201
+ return false;
202
+ }
203
+ case "response.output_item.done":
204
+ closeSlot(state, data, tracker);
205
+ return false;
206
+ case "response.completed":
207
+ case "response.incomplete":
208
+ case "response.failed": {
209
+ const response = obj(data["response"]) ?? {};
210
+ const id = str(response["id"]);
211
+ if (id)
212
+ output.responseId = id;
213
+ const usage = obj(response["usage"]);
214
+ if (usage)
215
+ output.usage = parseResponsesUsage(usage);
216
+ const status = str(response["status"]) ?? type.slice("response.".length);
217
+ const reason = str(obj(response["incomplete_details"])?.["reason"]);
218
+ output.rawStopReason = reason ?? status;
219
+ if (type === "response.failed") {
220
+ const error = obj(response["error"]);
221
+ throw new Error((error ? describeErrorJson({ error }) : undefined) ??
222
+ (reason ? `Response failed: ${reason}` : "Response failed"));
223
+ }
224
+ state.stop = mapResponsesStatus(status, reason);
225
+ return true;
226
+ }
227
+ case "error":
228
+ throw new Error(describeErrorJson(data) ?? JSON.stringify(data));
229
+ default:
230
+ return false;
231
+ }
232
+ }
233
+ function buildHeaders(model, options) {
234
+ return mergeHeaders({ "content-type": "application/json", accept: "text/event-stream", "user-agent": USER_AGENT }, authHeaders(options.apiKey, model.authHeader, "authorization-bearer"), affinityHeaders(model, "openai-responses", options.sessionId, resolveCacheRetention(options.cacheRetention)), model.headers, options.headers);
235
+ }
236
+ async function run(stream, model, context, options) {
237
+ const tracker = new BlockTracker(stream, createOutput(model));
238
+ try {
239
+ const request = buildResponsesRequest(model, context, options);
240
+ tracker.output.thinkingLevel = request.thinkingLevel;
241
+ if (request.providerThinkingLevel !== undefined) {
242
+ tracker.output.providerThinkingLevel = request.providerThinkingLevel;
243
+ }
244
+ const replaced = options.onPayload?.(request.body);
245
+ const baseUrl = model.baseUrl ?? "https://api.openai.com/v1";
246
+ const response = await postWithCacheFallback(model, joinUrl(baseUrl, "/responses"), {
247
+ headers: buildHeaders(model, options),
248
+ body: replaced === undefined ? request.body : replaced,
249
+ signal: options.signal,
250
+ timeoutMs: options.timeoutMs,
251
+ onResponse: options.onResponse,
252
+ });
253
+ stream.push({ type: "start", partial: tracker.output });
254
+ const state = {
255
+ byOutput: new Map(),
256
+ byItem: new Map(),
257
+ args: new Map(),
258
+ stop: undefined,
259
+ };
260
+ let finished = false;
261
+ const body = response.body;
262
+ for await (const sse of readSseEvents(body, options.signal)) {
263
+ const raw = sse.data.trim();
264
+ if (raw === "" || raw === "[DONE]")
265
+ continue;
266
+ let data;
267
+ try {
268
+ data = obj(JSON.parse(raw)) ?? {};
269
+ }
270
+ catch {
271
+ throw new Error(`Malformed SSE data (${sse.event ?? "message"}): ${raw.slice(0, 200)}`);
272
+ }
273
+ if (data["type"] === undefined && sse.event)
274
+ data["type"] = sse.event;
275
+ if (handleEvent(data, state, tracker)) {
276
+ finished = true;
277
+ break;
278
+ }
279
+ }
280
+ if (options.signal.aborted)
281
+ throw new Error("aborted");
282
+ if (!finished || !state.stop)
283
+ throw new Error(STREAM_ENDED_MESSAGE);
284
+ if (state.stop.reason === "error")
285
+ throw new ProviderStopError(state.stop.message);
286
+ const reason = state.stop.reason === "stop" && tracker.hasToolCalls ? "toolUse" : state.stop.reason;
287
+ finishDone(stream, tracker, model, reason);
288
+ }
289
+ catch (error) {
290
+ finishError(stream, tracker, model, error, options.signal);
291
+ }
292
+ }
293
+ function stream(model, context, options) {
294
+ requireApiKey(model, options);
295
+ const events = new AssistantEventStreamImpl();
296
+ void run(events, model, context, options).finally(() => events.end());
297
+ return events;
298
+ }
299
+ export const openAIResponsesApi = {
300
+ id: "openai-responses",
301
+ stream,
302
+ detectCompat: detectResponsesCompat,
303
+ };
@@ -0,0 +1,56 @@
1
+ /**
2
+ * 协议实现共用的流辅助:输出消息骨架、key 预检、内容块跟踪(保证块事件配对)、终止处理。
3
+ *
4
+ * 块事件规则(两条协议与 fake 一致,契约测试逐条断言):
5
+ * - 每个 `*_start` 恰好对应一个 `*_end`;终止事件之前所有打开的块都会被关闭(出错 / 中止时也
6
+ * 先关块再发 error,消费者不必处理悬空块);
7
+ * - `toolcall_end` 时 `toolCall.arguments` 一定是对象(容错解析,坏 JSON 退化为 `{}`);
8
+ * - 流式期间 `arguments` 随增量更新为「到目前为止」的部分对象。
9
+ */
10
+ import type { AssistantEventStreamImpl } from "../event-stream.js";
11
+ import type { AssistantMessage, Model, StreamOptions, TextBlock, ThinkingBlock, ToolCallBlock } from "../types.js";
12
+ export declare function createOutput(model: Model): AssistantMessage;
13
+ /** 缺 key 时同步抛 `AmaError{code:"no_api_key"}`(§3.1 流契约唯一的同步异常)。 */
14
+ export declare function requireApiKey(model: Model, options: StreamOptions): void;
15
+ type OpenBlock = {
16
+ kind: "text";
17
+ block: TextBlock;
18
+ } | {
19
+ kind: "thinking";
20
+ block: ThinkingBlock;
21
+ } | {
22
+ kind: "toolCall";
23
+ block: ToolCallBlock;
24
+ json: string;
25
+ };
26
+ export declare class BlockTracker {
27
+ private readonly stream;
28
+ readonly output: AssistantMessage;
29
+ private readonly open;
30
+ constructor(stream: AssistantEventStreamImpl, output: AssistantMessage);
31
+ isOpen(index: number): boolean;
32
+ kindOf(index: number): OpenBlock["kind"] | undefined;
33
+ startText(text?: string): number;
34
+ appendText(index: number, delta: string): void;
35
+ startThinking(thinking?: string, signature?: string, redacted?: boolean): number;
36
+ appendThinking(index: number, delta: string): void;
37
+ appendSignature(index: number, delta: string): void;
38
+ startToolCall(id: string, name: string, initialArgs?: Record<string, unknown>): number;
39
+ appendToolArgs(index: number, delta: string): void;
40
+ toolCallBlock(index: number): ToolCallBlock | undefined;
41
+ end(index: number): void;
42
+ endAll(): void;
43
+ get hasToolCalls(): boolean;
44
+ private push;
45
+ }
46
+ export declare const ABORTED_MESSAGE = "Request was aborted";
47
+ export declare const STREAM_ENDED_MESSAGE = "Stream ended before completion";
48
+ /** 正常结束:关块、算 usage / cost、发 done。 */
49
+ export declare function finishDone(stream: AssistantEventStreamImpl, tracker: BlockTracker, model: Model, reason: "stop" | "length" | "toolUse"): void;
50
+ /** 出错或中止:关块、写 errorMessage、发 error(signal 已中止 → aborted)。 */
51
+ export declare function finishError(stream: AssistantEventStreamImpl, tracker: BlockTracker, model: Model, error: unknown, signal: AbortSignal): void;
52
+ /** 内部用:可携带供应商原始 stop reason 的错误(content_filter、refusal 等)。 */
53
+ export declare class ProviderStopError extends Error {
54
+ constructor(message: string);
55
+ }
56
+ export {};
@@ -0,0 +1,179 @@
1
+ /**
2
+ * 协议实现共用的流辅助:输出消息骨架、key 预检、内容块跟踪(保证块事件配对)、终止处理。
3
+ *
4
+ * 块事件规则(两条协议与 fake 一致,契约测试逐条断言):
5
+ * - 每个 `*_start` 恰好对应一个 `*_end`;终止事件之前所有打开的块都会被关闭(出错 / 中止时也
6
+ * 先关块再发 error,消费者不必处理悬空块);
7
+ * - `toolcall_end` 时 `toolCall.arguments` 一定是对象(容错解析,坏 JSON 退化为 `{}`);
8
+ * - 流式期间 `arguments` 随增量更新为「到目前为止」的部分对象。
9
+ */
10
+ import { AmaError } from "../../errors.js";
11
+ import { finalizeUsage, emptyUsage } from "../cost.js";
12
+ import { errorText, hasAuthHeader } from "../http.js";
13
+ import { parseToolArguments } from "../json-partial.js";
14
+ export function createOutput(model) {
15
+ return {
16
+ role: "assistant",
17
+ content: [],
18
+ api: model.api,
19
+ provider: model.provider,
20
+ model: model.id,
21
+ usage: emptyUsage(),
22
+ stopReason: "stop",
23
+ timestamp: Date.now(),
24
+ };
25
+ }
26
+ /** 缺 key 时同步抛 `AmaError{code:"no_api_key"}`(§3.1 流契约唯一的同步异常)。 */
27
+ export function requireApiKey(model, options) {
28
+ if (options.apiKey)
29
+ return;
30
+ if (model.requiresApiKey === false)
31
+ return;
32
+ if (hasAuthHeader(model.headers) || hasAuthHeader(options.headers))
33
+ return;
34
+ throw new AmaError("no_api_key", `No API key for provider "${model.provider}"`, {
35
+ detail: { provider: model.provider, model: model.id },
36
+ });
37
+ }
38
+ export class BlockTracker {
39
+ stream;
40
+ output;
41
+ open = new Map();
42
+ constructor(stream, output) {
43
+ this.stream = stream;
44
+ this.output = output;
45
+ }
46
+ isOpen(index) {
47
+ return this.open.has(index);
48
+ }
49
+ kindOf(index) {
50
+ return this.open.get(index)?.kind;
51
+ }
52
+ startText(text = "") {
53
+ const block = { type: "text", text: "" };
54
+ const index = this.push(block);
55
+ this.open.set(index, { kind: "text", block });
56
+ this.stream.push({ type: "text_start", contentIndex: index, partial: this.output });
57
+ if (text)
58
+ this.appendText(index, text);
59
+ return index;
60
+ }
61
+ appendText(index, delta) {
62
+ const entry = this.open.get(index);
63
+ if (entry?.kind !== "text" || delta.length === 0)
64
+ return;
65
+ entry.block.text += delta;
66
+ this.stream.push({ type: "text_delta", contentIndex: index, delta, partial: this.output });
67
+ }
68
+ startThinking(thinking = "", signature, redacted = false) {
69
+ const block = { type: "thinking", thinking: "" };
70
+ if (signature !== undefined)
71
+ block.thinkingSignature = signature;
72
+ if (redacted)
73
+ block.redacted = true;
74
+ const index = this.push(block);
75
+ this.open.set(index, { kind: "thinking", block });
76
+ this.stream.push({ type: "thinking_start", contentIndex: index, partial: this.output });
77
+ if (thinking)
78
+ this.appendThinking(index, thinking);
79
+ return index;
80
+ }
81
+ appendThinking(index, delta) {
82
+ const entry = this.open.get(index);
83
+ if (entry?.kind !== "thinking" || delta.length === 0)
84
+ return;
85
+ entry.block.thinking += delta;
86
+ this.stream.push({ type: "thinking_delta", contentIndex: index, delta, partial: this.output });
87
+ }
88
+ appendSignature(index, delta) {
89
+ const entry = this.open.get(index);
90
+ if (entry?.kind !== "thinking")
91
+ return;
92
+ entry.block.thinkingSignature = (entry.block.thinkingSignature ?? "") + delta;
93
+ }
94
+ startToolCall(id, name, initialArgs) {
95
+ const block = { type: "toolCall", id, name, arguments: initialArgs ?? {} };
96
+ const index = this.push(block);
97
+ this.open.set(index, { kind: "toolCall", block, json: "" });
98
+ this.stream.push({
99
+ type: "toolcall_start",
100
+ contentIndex: index,
101
+ id,
102
+ name,
103
+ partial: this.output,
104
+ });
105
+ return index;
106
+ }
107
+ appendToolArgs(index, delta) {
108
+ const entry = this.open.get(index);
109
+ if (entry?.kind !== "toolCall" || delta.length === 0)
110
+ return;
111
+ entry.json += delta;
112
+ entry.block.arguments = parseToolArguments(entry.json);
113
+ this.stream.push({ type: "toolcall_delta", contentIndex: index, delta, partial: this.output });
114
+ }
115
+ toolCallBlock(index) {
116
+ const entry = this.open.get(index);
117
+ return entry?.kind === "toolCall" ? entry.block : undefined;
118
+ }
119
+ end(index) {
120
+ const entry = this.open.get(index);
121
+ if (!entry)
122
+ return;
123
+ this.open.delete(index);
124
+ if (entry.kind === "text") {
125
+ this.stream.push({ type: "text_end", contentIndex: index, partial: this.output });
126
+ }
127
+ else if (entry.kind === "thinking") {
128
+ this.stream.push({ type: "thinking_end", contentIndex: index, partial: this.output });
129
+ }
130
+ else {
131
+ if (entry.json.length > 0)
132
+ entry.block.arguments = parseToolArguments(entry.json);
133
+ this.stream.push({
134
+ type: "toolcall_end",
135
+ contentIndex: index,
136
+ toolCall: entry.block,
137
+ partial: this.output,
138
+ });
139
+ }
140
+ }
141
+ endAll() {
142
+ for (const index of [...this.open.keys()].sort((a, b) => a - b))
143
+ this.end(index);
144
+ }
145
+ get hasToolCalls() {
146
+ return this.output.content.some((block) => block.type === "toolCall");
147
+ }
148
+ push(block) {
149
+ this.output.content.push(block);
150
+ return this.output.content.length - 1;
151
+ }
152
+ }
153
+ export const ABORTED_MESSAGE = "Request was aborted";
154
+ export const STREAM_ENDED_MESSAGE = "Stream ended before completion";
155
+ /** 正常结束:关块、算 usage / cost、发 done。 */
156
+ export function finishDone(stream, tracker, model, reason) {
157
+ tracker.endAll();
158
+ const output = tracker.output;
159
+ output.stopReason = reason;
160
+ finalizeUsage(model, output.usage);
161
+ stream.push({ type: "done", reason, message: output });
162
+ }
163
+ /** 出错或中止:关块、写 errorMessage、发 error(signal 已中止 → aborted)。 */
164
+ export function finishError(stream, tracker, model, error, signal) {
165
+ tracker.endAll();
166
+ const output = tracker.output;
167
+ const aborted = signal.aborted;
168
+ output.stopReason = aborted ? "aborted" : "error";
169
+ output.errorMessage = aborted ? ABORTED_MESSAGE : errorText(error);
170
+ finalizeUsage(model, output.usage);
171
+ stream.push({ type: "error", reason: aborted ? "aborted" : "error", message: output });
172
+ }
173
+ /** 内部用:可携带供应商原始 stop reason 的错误(content_filter、refusal 等)。 */
174
+ export class ProviderStopError extends Error {
175
+ constructor(message) {
176
+ super(message);
177
+ this.name = "ProviderStopError";
178
+ }
179
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * 保温经济性(第三波 §1.7「经济性」)。[W3-C1b]
3
+ *
4
+ * P = 上一次真实请求的 input + cacheRead + cacheWrite:
5
+ * - `hitCost = price({cacheRead: P})`
6
+ * - `missCost = price(cacheWrite 价 > 0 ? {cacheWrite: P} : {input: P}) − hitCost`
7
+ * - `warmCost = price({cacheRead: P, output: 1})`
8
+ * - 概率 p:streaming 1、idle 0.15;`p·missCost − warmCost ≥ minSavingsUsd` 才发。
9
+ * 无价格或价格全为 0 → 「经济性不可算」,不发(reason `no_price`)。
10
+ *
11
+ * 计价用 `src/ai/cost.ts` 的 `priceTokens`(与 calculateCost 同一套阶梯与 1h 写入规则)。
12
+ */
13
+ import { priceTokens } from "../cost.js";
14
+ import type { Model } from "../types.js";
15
+ import type { WarmDecision, WarmingPhase } from "./types.js";
16
+ export declare const WARM_PROBABILITY: Readonly<Record<WarmingPhase, number>>;
17
+ export { priceTokens };
18
+ export declare function evaluateWarm(model: Pick<Model, "cost">, promptTokens: number, phase: WarmingPhase, minSavingsUsd: number): WarmDecision;
19
+ /** 期望节省(美元);不可算时 undefined。 */
20
+ export declare function expectedSavings(decision: WarmDecision): number | undefined;
21
+ /**
22
+ * 盈亏点:前缀至少多少 token 保温才划算(不计阶梯价)。永不划算或超过上下文窗口 → undefined。
23
+ */
24
+ export declare function warmBreakEven(model: Pick<Model, "cost" | "contextWindow">, phase: WarmingPhase, minSavingsUsd: number): number | undefined;
@@ -0,0 +1,69 @@
1
+ /**
2
+ * 保温经济性(第三波 §1.7「经济性」)。[W3-C1b]
3
+ *
4
+ * P = 上一次真实请求的 input + cacheRead + cacheWrite:
5
+ * - `hitCost = price({cacheRead: P})`
6
+ * - `missCost = price(cacheWrite 价 > 0 ? {cacheWrite: P} : {input: P}) − hitCost`
7
+ * - `warmCost = price({cacheRead: P, output: 1})`
8
+ * - 概率 p:streaming 1、idle 0.15;`p·missCost − warmCost ≥ minSavingsUsd` 才发。
9
+ * 无价格或价格全为 0 → 「经济性不可算」,不发(reason `no_price`)。
10
+ *
11
+ * 计价用 `src/ai/cost.ts` 的 `priceTokens`(与 calculateCost 同一套阶梯与 1h 写入规则)。
12
+ */
13
+ import { priceTokens } from "../cost.js";
14
+ export const WARM_PROBABILITY = {
15
+ streaming: 1,
16
+ idle: 0.15,
17
+ };
18
+ export { priceTokens };
19
+ function priced(model) {
20
+ const cost = model.cost;
21
+ return cost !== undefined && (cost.input > 0 || cost.cacheWrite > 0);
22
+ }
23
+ export function evaluateWarm(model, promptTokens, phase, minSavingsUsd) {
24
+ const probability = WARM_PROBABILITY[phase];
25
+ const decision = {
26
+ action: "stop",
27
+ phase,
28
+ promptTokens,
29
+ warmCost: undefined,
30
+ missCost: undefined,
31
+ probability,
32
+ };
33
+ if (!priced(model)) {
34
+ decision.reason = "no_price";
35
+ return decision;
36
+ }
37
+ const P = promptTokens;
38
+ const writes = (model.cost?.cacheWrite ?? 0) > 0;
39
+ const hit = priceTokens(model, { cacheRead: P }) ?? 0;
40
+ const full = priceTokens(model, writes ? { cacheWrite: P } : { input: P }) ?? 0;
41
+ decision.missCost = full - hit;
42
+ decision.warmCost = priceTokens(model, { cacheRead: P, output: 1 }) ?? 0;
43
+ if (probability * decision.missCost - decision.warmCost >= minSavingsUsd)
44
+ decision.action = "warm";
45
+ else
46
+ decision.reason = "below_min_savings";
47
+ return decision;
48
+ }
49
+ /** 期望节省(美元);不可算时 undefined。 */
50
+ export function expectedSavings(decision) {
51
+ if (decision.missCost === undefined || decision.warmCost === undefined)
52
+ return undefined;
53
+ return decision.probability * decision.missCost - decision.warmCost;
54
+ }
55
+ /**
56
+ * 盈亏点:前缀至少多少 token 保温才划算(不计阶梯价)。永不划算或超过上下文窗口 → undefined。
57
+ */
58
+ export function warmBreakEven(model, phase, minSavingsUsd) {
59
+ const cost = model.cost;
60
+ if (cost === undefined || !priced(model))
61
+ return undefined;
62
+ const p = WARM_PROBABILITY[phase];
63
+ const full = cost.cacheWrite > 0 ? cost.cacheWrite : cost.input;
64
+ const perToken = (p * (full - cost.cacheRead) - cost.cacheRead) / 1e6;
65
+ if (perToken <= 0)
66
+ return undefined;
67
+ const tokens = Math.ceil((minSavingsUsd + cost.output / 1e6) / perToken);
68
+ return model.contextWindow !== undefined && tokens > model.contextWindow ? undefined : tokens;
69
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * 前缀指纹(第三波 §1.2):一次请求的「可缓存前缀」= 折叠后的系统提示 + 工具表 + 模型。[W3-C1b]
3
+ *
4
+ * - `system` = sha256(normalizeContext().systemPrompt) 前 64 bit;
5
+ * - `tools` = sha256(JSON.stringify(工具表按名排序)) 前 64 bit;
6
+ * - `model` = `${provider}/${id}` 原文。
7
+ * 纯函数、零请求;每次真实请求算一次(system + tools 通常 < 20 KB)。未命中归因时与上一条
8
+ * 记录比对,说出「变了什么」。
9
+ */
10
+ import type { Model, ModelRef, TranscriptContext } from "../types.js";
11
+ import type { PrefixFingerprint } from "./types.js";
12
+ /** sha256 的前 16 位 hex(64 bit)。 */
13
+ export declare function hash16(text: string): string;
14
+ export declare function modelKey(model: Pick<Model, "provider" | "id"> | ModelRef): string;
15
+ export declare function fingerprintContext(context: TranscriptContext, model: Pick<Model, "provider" | "id">): PrefixFingerprint;
16
+ /** 两个指纹的差异(按归因顺序:system → tools → model);相同返回 undefined。 */
17
+ export declare function fingerprintChange(prev: PrefixFingerprint, cur: PrefixFingerprint): "system" | "tools" | "model" | undefined;