@plumbus/core 0.8.0-beta.1 → 0.8.0-beta.10

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 (272) hide show
  1. package/CHANGELOG.md +187 -0
  2. package/README.md +8 -1
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/ai/ai/cost-tracker.d.ts +22 -2
  5. package/dist/ai/ai/model-pricing.d.ts +8 -2
  6. package/dist/ai/ai/openai-strict-schema.d.ts +84 -0
  7. package/dist/ai/ai/provider.d.ts +90 -6
  8. package/dist/ai/ai-service.d.ts +53 -1
  9. package/dist/ai/ai-service.d.ts.map +1 -1
  10. package/dist/ai/ai-service.js +550 -119
  11. package/dist/ai/ai-service.js.map +1 -1
  12. package/dist/ai/cost-tracker.d.ts +22 -2
  13. package/dist/ai/cost-tracker.d.ts.map +1 -1
  14. package/dist/ai/cost-tracker.js +59 -20
  15. package/dist/ai/cost-tracker.js.map +1 -1
  16. package/dist/ai/explainability.d.ts +2 -1
  17. package/dist/ai/explainability.d.ts.map +1 -1
  18. package/dist/ai/explainability.js +1 -0
  19. package/dist/ai/explainability.js.map +1 -1
  20. package/dist/ai/index.d.ts +2 -2
  21. package/dist/ai/index.d.ts.map +1 -1
  22. package/dist/ai/index.js +1 -1
  23. package/dist/ai/index.js.map +1 -1
  24. package/dist/ai/model-pricing.d.ts +8 -2
  25. package/dist/ai/model-pricing.d.ts.map +1 -1
  26. package/dist/ai/model-pricing.js +113 -15
  27. package/dist/ai/model-pricing.js.map +1 -1
  28. package/dist/ai/openai-strict-schema.d.ts +85 -0
  29. package/dist/ai/openai-strict-schema.d.ts.map +1 -0
  30. package/dist/ai/openai-strict-schema.js +730 -0
  31. package/dist/ai/openai-strict-schema.js.map +1 -0
  32. package/dist/ai/prompt-overrides.d.ts +19 -0
  33. package/dist/ai/prompt-overrides.d.ts.map +1 -0
  34. package/dist/ai/prompt-overrides.js +60 -0
  35. package/dist/ai/prompt-overrides.js.map +1 -0
  36. package/dist/ai/provider-failure.d.ts +9 -0
  37. package/dist/ai/provider-failure.d.ts.map +1 -0
  38. package/dist/ai/provider-failure.js +112 -0
  39. package/dist/ai/provider-failure.js.map +1 -0
  40. package/dist/ai/provider.d.ts +90 -6
  41. package/dist/ai/provider.d.ts.map +1 -1
  42. package/dist/ai/provider.js +546 -97
  43. package/dist/ai/provider.js.map +1 -1
  44. package/dist/ai/rag/pipeline.d.ts +21 -1
  45. package/dist/ai/rag/pipeline.d.ts.map +1 -1
  46. package/dist/ai/rag/pipeline.js +17 -1
  47. package/dist/ai/rag/pipeline.js.map +1 -1
  48. package/dist/ai/tool-loop.d.ts +8 -1
  49. package/dist/ai/tool-loop.d.ts.map +1 -1
  50. package/dist/ai/tool-loop.js +7 -0
  51. package/dist/ai/tool-loop.js.map +1 -1
  52. package/dist/ai/types/config.d.ts +13 -0
  53. package/dist/ai/types/fields.d.ts +6 -0
  54. package/dist/ai/types/prompt.d.ts +9 -0
  55. package/dist/ai/usage-validation.d.ts.map +1 -1
  56. package/dist/ai/usage-validation.js +6 -1
  57. package/dist/ai/usage-validation.js.map +1 -1
  58. package/dist/ai/validation.d.ts +7 -3
  59. package/dist/ai/validation.d.ts.map +1 -1
  60. package/dist/ai/validation.js +38 -25
  61. package/dist/ai/validation.js.map +1 -1
  62. package/dist/ai/zod-to-provider-schema.d.ts.map +1 -1
  63. package/dist/ai/zod-to-provider-schema.js +246 -16
  64. package/dist/ai/zod-to-provider-schema.js.map +1 -1
  65. package/dist/api/route-generator.d.ts +2 -12
  66. package/dist/api/route-generator.d.ts.map +1 -1
  67. package/dist/api/route-generator.js +33 -41
  68. package/dist/api/route-generator.js.map +1 -1
  69. package/dist/approvals/index.d.ts +1 -1
  70. package/dist/approvals/index.d.ts.map +1 -1
  71. package/dist/approvals/index.js.map +1 -1
  72. package/dist/approvals/memory-store.d.ts.map +1 -1
  73. package/dist/approvals/memory-store.js +22 -0
  74. package/dist/approvals/memory-store.js.map +1 -1
  75. package/dist/approvals/schema.d.ts +34 -0
  76. package/dist/approvals/schema.d.ts.map +1 -1
  77. package/dist/approvals/schema.js +2 -0
  78. package/dist/approvals/schema.js.map +1 -1
  79. package/dist/approvals/service.d.ts.map +1 -1
  80. package/dist/approvals/service.js +36 -0
  81. package/dist/approvals/service.js.map +1 -1
  82. package/dist/approvals/sql-store.d.ts.map +1 -1
  83. package/dist/approvals/sql-store.js +42 -1
  84. package/dist/approvals/sql-store.js.map +1 -1
  85. package/dist/approvals/types.d.ts +11 -0
  86. package/dist/approvals/types.d.ts.map +1 -1
  87. package/dist/approvals/types.js +1 -0
  88. package/dist/approvals/types.js.map +1 -1
  89. package/dist/audit/service.d.ts.map +1 -1
  90. package/dist/audit/service.js +16 -1
  91. package/dist/audit/service.js.map +1 -1
  92. package/dist/cli/cli.d.ts.map +1 -1
  93. package/dist/cli/cli.js +2 -1
  94. package/dist/cli/cli.js.map +1 -1
  95. package/dist/cli/commands/browser-extension.d.ts +16 -0
  96. package/dist/cli/commands/browser-extension.d.ts.map +1 -1
  97. package/dist/cli/commands/browser-extension.js +16 -5
  98. package/dist/cli/commands/browser-extension.js.map +1 -1
  99. package/dist/cli/commands/dev.d.ts.map +1 -1
  100. package/dist/cli/commands/dev.js +7 -1
  101. package/dist/cli/commands/dev.js.map +1 -1
  102. package/dist/cli/commands/e2e.d.ts +12 -0
  103. package/dist/cli/commands/e2e.d.ts.map +1 -1
  104. package/dist/cli/commands/e2e.js +11 -0
  105. package/dist/cli/commands/e2e.js.map +1 -1
  106. package/dist/cli/commands/generate-openapi-from-zod.d.ts.map +1 -1
  107. package/dist/cli/commands/generate-openapi-from-zod.js +9 -1
  108. package/dist/cli/commands/generate-openapi-from-zod.js.map +1 -1
  109. package/dist/cli/commands/index.d.ts +1 -0
  110. package/dist/cli/commands/index.d.ts.map +1 -1
  111. package/dist/cli/commands/index.js +1 -0
  112. package/dist/cli/commands/index.js.map +1 -1
  113. package/dist/cli/commands/init.d.ts +1 -1
  114. package/dist/cli/commands/init.d.ts.map +1 -1
  115. package/dist/cli/commands/init.js +63 -3
  116. package/dist/cli/commands/init.js.map +1 -1
  117. package/dist/cli/commands/migrate.d.ts.map +1 -1
  118. package/dist/cli/commands/migrate.js +8 -14
  119. package/dist/cli/commands/migrate.js.map +1 -1
  120. package/dist/cli/commands/payments.d.ts +56 -0
  121. package/dist/cli/commands/payments.d.ts.map +1 -0
  122. package/dist/cli/commands/payments.js +184 -0
  123. package/dist/cli/commands/payments.js.map +1 -0
  124. package/dist/cli/commands/run.d.ts.map +1 -1
  125. package/dist/cli/commands/run.js +2 -7
  126. package/dist/cli/commands/run.js.map +1 -1
  127. package/dist/cli/commands/seed.d.ts.map +1 -1
  128. package/dist/cli/commands/seed.js +2 -7
  129. package/dist/cli/commands/seed.js.map +1 -1
  130. package/dist/cli/commands/start.d.ts.map +1 -1
  131. package/dist/cli/commands/start.js +9 -1
  132. package/dist/cli/commands/start.js.map +1 -1
  133. package/dist/cli/commands/worker.js +1 -1
  134. package/dist/cli/commands/worker.js.map +1 -1
  135. package/dist/cli/discover.d.ts +2 -0
  136. package/dist/cli/discover.d.ts.map +1 -1
  137. package/dist/cli/discover.js +39 -5
  138. package/dist/cli/discover.js.map +1 -1
  139. package/dist/config/loader.d.ts +4 -1
  140. package/dist/config/loader.d.ts.map +1 -1
  141. package/dist/config/loader.js +112 -24
  142. package/dist/config/loader.js.map +1 -1
  143. package/dist/data/connection.d.ts +23 -0
  144. package/dist/data/connection.d.ts.map +1 -1
  145. package/dist/data/connection.js +23 -6
  146. package/dist/data/connection.js.map +1 -1
  147. package/dist/data/schema-generator.d.ts.map +1 -1
  148. package/dist/data/schema-generator.js +2 -2
  149. package/dist/data/schema-generator.js.map +1 -1
  150. package/dist/define/definePrompt.d.ts.map +1 -1
  151. package/dist/define/definePrompt.js +6 -0
  152. package/dist/define/definePrompt.js.map +1 -1
  153. package/dist/durable/index.d.ts +2 -2
  154. package/dist/durable/index.d.ts.map +1 -1
  155. package/dist/durable/index.js +2 -2
  156. package/dist/durable/index.js.map +1 -1
  157. package/dist/durable/postgres-persist.d.ts +47 -2
  158. package/dist/durable/postgres-persist.d.ts.map +1 -1
  159. package/dist/durable/postgres-persist.js +152 -6
  160. package/dist/durable/postgres-persist.js.map +1 -1
  161. package/dist/durable/spine-claim.d.ts +22 -1
  162. package/dist/durable/spine-claim.d.ts.map +1 -1
  163. package/dist/durable/spine-claim.js +54 -3
  164. package/dist/durable/spine-claim.js.map +1 -1
  165. package/dist/events/dispatcher.d.ts +20 -0
  166. package/dist/events/dispatcher.d.ts.map +1 -1
  167. package/dist/events/dispatcher.js +61 -26
  168. package/dist/events/dispatcher.js.map +1 -1
  169. package/dist/events/pipeline-audit.d.ts +14 -0
  170. package/dist/events/pipeline-audit.d.ts.map +1 -0
  171. package/dist/events/pipeline-audit.js +29 -0
  172. package/dist/events/pipeline-audit.js.map +1 -0
  173. package/dist/events/worker.d.ts +3 -0
  174. package/dist/events/worker.d.ts.map +1 -1
  175. package/dist/events/worker.js +14 -14
  176. package/dist/events/worker.js.map +1 -1
  177. package/dist/execution/capability-error-hook.d.ts +24 -0
  178. package/dist/execution/capability-error-hook.d.ts.map +1 -0
  179. package/dist/execution/capability-error-hook.js +17 -0
  180. package/dist/execution/capability-error-hook.js.map +1 -0
  181. package/dist/execution/capability-executor.d.ts +2 -2
  182. package/dist/execution/capability-executor.d.ts.map +1 -1
  183. package/dist/execution/capability-executor.js +45 -7
  184. package/dist/execution/capability-executor.js.map +1 -1
  185. package/dist/execution/context-factory.d.ts.map +1 -1
  186. package/dist/execution/context-factory.js +9 -1
  187. package/dist/execution/context-factory.js.map +1 -1
  188. package/dist/execution/deferred-flow-service.d.ts.map +1 -1
  189. package/dist/execution/deferred-flow-service.js +7 -0
  190. package/dist/execution/deferred-flow-service.js.map +1 -1
  191. package/dist/execution/index.d.ts +1 -0
  192. package/dist/execution/index.d.ts.map +1 -1
  193. package/dist/execution/index.js.map +1 -1
  194. package/dist/fields/index.d.ts +7 -0
  195. package/dist/fields/index.d.ts.map +1 -1
  196. package/dist/fields/index.js +10 -1
  197. package/dist/fields/index.js.map +1 -1
  198. package/dist/flows/dead-letter.d.ts +18 -1
  199. package/dist/flows/dead-letter.d.ts.map +1 -1
  200. package/dist/flows/dead-letter.js +17 -1
  201. package/dist/flows/dead-letter.js.map +1 -1
  202. package/dist/flows/engine.d.ts +16 -1
  203. package/dist/flows/engine.d.ts.map +1 -1
  204. package/dist/flows/engine.js +218 -29
  205. package/dist/flows/engine.js.map +1 -1
  206. package/dist/flows/flow-service.d.ts.map +1 -1
  207. package/dist/flows/flow-service.js +3 -0
  208. package/dist/flows/flow-service.js.map +1 -1
  209. package/dist/flows/step-executor.d.ts +8 -0
  210. package/dist/flows/step-executor.d.ts.map +1 -1
  211. package/dist/flows/step-executor.js.map +1 -1
  212. package/dist/index.d.ts +4 -4
  213. package/dist/index.d.ts.map +1 -1
  214. package/dist/index.js +1 -1
  215. package/dist/index.js.map +1 -1
  216. package/dist/runtime/bootstrap.d.ts +18 -1
  217. package/dist/runtime/bootstrap.d.ts.map +1 -1
  218. package/dist/runtime/bootstrap.js +36 -5
  219. package/dist/runtime/bootstrap.js.map +1 -1
  220. package/dist/runtime/load-extensions.d.ts +2 -1
  221. package/dist/runtime/load-extensions.d.ts.map +1 -1
  222. package/dist/runtime/load-extensions.js +49 -1
  223. package/dist/runtime/load-extensions.js.map +1 -1
  224. package/dist/runtime/register-consumers.d.ts +3 -0
  225. package/dist/runtime/register-consumers.d.ts.map +1 -1
  226. package/dist/runtime/register-consumers.js +27 -6
  227. package/dist/runtime/register-consumers.js.map +1 -1
  228. package/dist/runtime/start-worker-pool.d.ts.map +1 -1
  229. package/dist/runtime/start-worker-pool.js +36 -15
  230. package/dist/runtime/start-worker-pool.js.map +1 -1
  231. package/dist/server/bootstrap.d.ts +42 -16
  232. package/dist/server/bootstrap.d.ts.map +1 -1
  233. package/dist/server/bootstrap.js +91 -21
  234. package/dist/server/bootstrap.js.map +1 -1
  235. package/dist/testing/context.d.ts +2 -0
  236. package/dist/testing/context.d.ts.map +1 -1
  237. package/dist/testing/context.js +8 -3
  238. package/dist/testing/context.js.map +1 -1
  239. package/dist/testing/field-validation.d.ts.map +1 -1
  240. package/dist/testing/field-validation.js +3 -0
  241. package/dist/testing/field-validation.js.map +1 -1
  242. package/dist/types/config.d.ts +13 -0
  243. package/dist/types/config.d.ts.map +1 -1
  244. package/dist/types/context.d.ts +70 -4
  245. package/dist/types/context.d.ts.map +1 -1
  246. package/dist/types/fields.d.ts +6 -0
  247. package/dist/types/fields.d.ts.map +1 -1
  248. package/dist/types/index.d.ts +1 -1
  249. package/dist/types/index.d.ts.map +1 -1
  250. package/dist/types/index.js.map +1 -1
  251. package/dist/types/prompt.d.ts +9 -0
  252. package/dist/types/prompt.d.ts.map +1 -1
  253. package/dist/worker/bootstrap.d.ts +19 -0
  254. package/dist/worker/bootstrap.d.ts.map +1 -1
  255. package/dist/worker/bootstrap.js +156 -84
  256. package/dist/worker/bootstrap.js.map +1 -1
  257. package/instructions/README.md +4 -1
  258. package/instructions/ai-classification.md +132 -0
  259. package/instructions/ai.md +94 -15
  260. package/instructions/cli.md +19 -1
  261. package/instructions/entities.md +3 -1
  262. package/instructions/flows.md +6 -0
  263. package/instructions/framework.md +13 -3
  264. package/instructions/peer-dependencies.md +2 -1
  265. package/instructions/prompts.md +2 -1
  266. package/migrations/durable-tenant/0002_approval_cancellation.sql +3 -0
  267. package/migrations/durable-tenant/meta/_journal.json +7 -0
  268. package/package.json +6 -5
  269. package/dist/ai/__typecheck-fixtures__/legacy-cost-api.d.ts +0 -5
  270. package/dist/ai/__typecheck-fixtures__/legacy-cost-api.d.ts.map +0 -1
  271. package/dist/ai/__typecheck-fixtures__/legacy-cost-api.js +0 -15
  272. package/dist/ai/__typecheck-fixtures__/legacy-cost-api.js.map +0 -1
@@ -31,6 +31,20 @@ Prompts should NOT hardcode `provider` or model `name` unless you have a deliber
31
31
 
32
32
  ## `ctx.ai` Operations
33
33
 
34
+ ## Provider admission and trace propagation
35
+
36
+ Hosts can export `aiProviderConcurrency`, `resolveAIProviderHeaders`, and `onAIProviderSpan`
37
+ from `app/server.ts`. The concurrency ceiling is shared by every identity-bound AI service and
38
+ defaults to a scope of provider + tenant + `costContext.serviceArea`; saturation throws the
39
+ structured `ai-provider-concurrency-exhausted` refusal immediately rather than waiting. Override
40
+ `resolveScope` only when the host has another non-private package/workload key.
41
+
42
+ `resolveAIProviderHeaders` is called for every real provider attempt, including validation retries
43
+ and streams. Use it for W3C `traceparent`/`tracestate`; credential and content-type headers are
44
+ reserved and refused. `onAIProviderSpan` receives the completed client span and is best effort:
45
+ exporter failures never change the model-call result. Flow workers receive the persisted flow
46
+ correlation id in the same hook, so a host can preserve one trace across request → flow → provider.
47
+
34
48
  ### Generate
35
49
 
36
50
  Invoke a named prompt with typed input, get validated output:
@@ -56,6 +70,11 @@ const invoice = await ctx.ai.extract({
56
70
 
57
71
  ### Classify
58
72
 
73
+ **Start with [the classification recipe](ai-classification.md)** for provider/model
74
+ selection, TypeSafe/Jev, Laya, and OpenAI Decisions registration, defaults, thresholds,
75
+ and cost hooks (core **0.7.4+** / **0.8.0-beta.6+**). Use `classify()` for matching labels and `decide()`
76
+ for richer typed questions. Keep calls inside capabilities/flows.
77
+
59
78
  Classify text into provided categories:
60
79
 
61
80
  ```ts
@@ -74,6 +93,8 @@ const docs = await ctx.ai.retrieve({ query: "refund policy for enterprise custom
74
93
  // docs: Array<{ content: string, source: string, score: number, metadata?: Record<string, unknown> }>
75
94
  ```
76
95
 
96
+ `retrieve` passes its `signal` (inside flow steps `ctx.signal` by default) through the RAG pipeline to the adapter's `embed()` as `EmbeddingRequest.signal`: an abort ends the in-flight embedding request and skips the vector search. RAG `ingest`/`retrieve` take `signal` too. A custom `AIProviderAdapter.embed()` must pass `request.signal` to its transport.
97
+
77
98
  Ingest documents with `plumbus rag ingest <path>` (see `cli.md`).
78
99
 
79
100
  ### Generate With Usage
@@ -89,6 +110,12 @@ const { data, usage, model, provider, cost } = await ctx.ai.generateWithUsage({
89
110
 
90
111
  `generateWithUsage` is **overloaded**: called without `tools` (as above) it returns the flat `AIFinalGenerateResult` — `.data` is always present, plus a `finishReason`. Called **with** `tools` it returns the discriminated `AIToolEnabledGenerateResult` (see [Tool calling](#tool-calling-provider-native)).
91
112
 
113
+ On OpenAI reasoning models `usage.reasoningTokens` is the reasoning share of `outputTokens` (Chat Completions and the Responses API alike, summed over validation retries); it is already priced inside `outputTokens`, so never add it to a cost yourself.
114
+
115
+ With `enableStrictStructuredOutputs`, the OpenAI adapter sends an output whose root is not an object (`classify()`, a `z.array`/union/enum `extract()` schema or prompt `output`) inside a `{"value": …}` envelope and unwraps the answer itself, merges an `allOf` of several `.regex()`s into one schema, and names a `.default()` in the description (strict mode has no `default`); Zod still validates the original. For every provider the schema converter merges intersections of objects (`a.and(b)`, `a.and(b.optional())`, `a.and(b.nullable())`) into one object, sends `z.string().cidr()` as IPv4-or-IPv6 patterns, a tuple as an array of its item schemas (order in the description), and an optional output root as the value itself. Nothing to do in app code.
116
+
117
+ `usage.cacheWriteTokens` counts prompt tokens written to the provider cache (Anthropic, and OpenAI's `cache_write_tokens` on Chat Completions and the Responses API); like `cachedInputTokens` it is part of `inputTokens`. Cost charges them 1.25× input on Claude and OpenAI GPT-5.6+, and the input rate on earlier OpenAI models.
118
+
92
119
  `generateWithUsage` also accepts per-call `provider`, `model`, and provider-neutral `reasoning` overrides:
93
120
 
94
121
  ```ts
@@ -101,11 +128,27 @@ await ctx.ai.generateWithUsage({
101
128
  });
102
129
  ```
103
130
 
104
- Omit `reasoning` to inherit prompt/config; use `null` to restore the provider/model default for one call, clearing both inherited `reasoning` and inherited legacy `reasoningEffort`. Explicit modes are `{ mode:'disabled' }`, `{ mode:'effort', effort:'minimal'|'low'|'medium'|'high'|'xhigh'|'max' }`, and `{ mode:'budget', maxTokens }`. Built-in core adapters translate the intent: OpenAI uses Chat Completions normally and switches caller-tool requests that need active/default GPT-5.6 reasoning to the Responses API; Anthropic uses `thinking` + `output_config.effort`. Responses tool continuations are stateless (`store:false`) and preserve encrypted reasoning items through server-owned `providerState`. Custom providers translate the contract in their own `AIProviderAdapter`. Unsupported modes fail with `AIInvalidRequestError` before provider I/O when the adapter can identify the incompatibility. Deprecated `reasoningEffort` remains the unchanged OpenAI-only `'low' | 'medium' | 'high'` shorthand.
131
+ A per-call `timeoutMs` (on `generate`, `generateWithUsage`, `streamGenerate`, `extract`, `classify`, `retrieve` and `runToolLoop`; `timeout` on `EmbeddingRequest`, `timeoutMs` on RAG `ingest`/`retrieve`) overrides the prompt's `model.timeoutMs` and the provider's `requestTimeout` for that call; see `prompts.md`. A request its timeout ends throws a non-retryable `ProviderAPIError` carrying `timeoutMs`; detect it with `isProviderTimeoutError(error)` from `@plumbus/core` (`isProviderAPIError` for any provider HTTP failure) instead of checking the error's shape, and raise the timeout rather than retrying.
132
+
133
+ Omit `reasoning` to inherit prompt/config; use `null` to restore the provider/model default for one call, clearing both inherited `reasoning` and inherited legacy `reasoningEffort`. Explicit modes are `{ mode:'disabled' }`, `{ mode:'effort', effort:'minimal'|'low'|'medium'|'high'|'xhigh'|'max' }`, and `{ mode:'budget', maxTokens }`. Built-in core adapters translate the intent: OpenAI uses Chat Completions normally and switches caller-tool requests, and the `structuredOutputTransport:'tool'` function, that need active/default GPT-5.6 or GPT-6 reasoning to the Responses API; Anthropic uses `thinking` + `output_config.effort`. GPT-6 Chat Completions use `max_completion_tokens`; GPT-6 requests omit sampling temperature unless reasoning is explicitly disabled. Responses tool continuations are stateless (`store:false`) and preserve encrypted reasoning items through server-owned `providerState`. Custom providers translate the contract in their own `AIProviderAdapter`. Unsupported modes fail with `AIInvalidRequestError` before provider I/O when the adapter can identify the incompatibility. No GPT-6 model accepts effort `minimal`, and `gpt-6-astra` / `gpt-6.1-sol` have no off switch (`{ mode:'disabled' }` fails; use `low`). Claude Fable 5.1/5, Mythos 5.1/5/Preview, Opus 5.5/5/4.8/4.7, Sonnet 5.5/5, and Haiku 5.5 reject any `temperature` other than 1, so Plumbus omits both its `0.7` default and any explicitly configured temperature for them. On those models disabled is sent as `thinking: { type: 'between_tools' }` on `claude-sonnet-5-5` and fails on Opus 5.5, Fable, and Mythos (thinking cannot be turned off; lower the effort), and budgets fail on all but `claude-mythos-preview`. Deprecated `reasoningEffort` remains the unchanged OpenAI-only `'low' | 'medium' | 'high'` shorthand.
134
+
135
+ ### Prompt caching (Anthropic / Bedrock)
136
+
137
+ Included in this beta family (stable core **0.7.9+**). Pass `cache: true` (system + tools) or `{ system?, tools?, messages? }` on `generate` / `generateWithUsage` / `streamGenerate` / `runToolLoop`. To make it the default for every call, set `AI_PROMPT_CACHE=true` (or sections such as `system,messages`); a per-call `cache` overrides it. Anthropic gets `cache_control`; Bedrock Claude gets Converse `cachePoint` (`@plumbus/ai-bedrock` from this beta family, or stable 0.2.2+), except Claude 3 Haiku / Sonnet / Opus and Claude 3.5 Sonnet `20240620`, which reject it, and Claude 3.5 Sonnet v2 (Preview-only on Bedrock). OpenAI and non-Claude Bedrock models ignore the option. Shorter prompts may not meet provider minima — the call still succeeds.
138
+
139
+ `cache: true` marks only the system prompt and tools. In a single-turn call, a prompt's `description` and its input go into the user message, so keep long, stable instructions in `system`. In multi-turn mode (`messages` set), the description and `Input: {...}` are merged into the system prompt, so an input that changes between turns writes a new cache entry every turn.
140
+
141
+ ```ts
142
+ await ctx.ai.generateWithUsage({
143
+ prompt: 'classifyTicket',
144
+ input: { ticketText },
145
+ cache: true,
146
+ });
147
+ ```
105
148
 
106
149
  ### Tool calling (provider-native)
107
150
 
108
- Pass `tools` to `generate` / `generateWithUsage` to let the model call functions natively. Both built-in adapters implement caller tools on the wire (OpenAI `tools`/`tool_calls`; Anthropic `tool_use`/`tool_result` + `input_schema`). Build each `AITool.parameters` from `zodToProviderJsonSchema(schema).schema`:
151
+ Pass `tools` to `generate` / `generateWithUsage` to let the model call functions natively. Both built-in adapters implement caller tools on the wire (OpenAI `tools`/`tool_calls`; Anthropic `tool_use`/`tool_result` + `input_schema`). OpenAI caller tools are non-strict on both transports (the Responses API gets an explicit `strict: false`, since it would otherwise make every property required and the model would invent values for optional arguments), so optional arguments stay optional; still validate `call.arguments` with the tool's Zod input schema. Build each `AITool.parameters` from `zodToProviderJsonSchema(schema).schema`:
109
152
 
110
153
  ```ts
111
154
  import type { AITool } from "@plumbus/core";
@@ -155,7 +198,7 @@ const { final, messages, rounds } = await runToolLoop(ctx.ai, {
155
198
  // final is a flat AIFinalGenerateResult; final.data is the answer.
156
199
  ```
157
200
 
158
- `runToolLoop` defaults to `maxRounds: 8` (hard cap 20). On round exhaustion it makes ONE final request that **omits both `tools` and `toolChoice`** (never `toolChoice: 'none'`), so it always resolves to a non-tool answer. Invalid-argument tool calls are **never** executed — they surface to the model as a bounded `tool_arguments_invalid` observation. Observations are byte-bounded and wrapped in an `untrusted_tool_result` envelope.
201
+ `runToolLoop` defaults to `maxRounds: 8` (hard cap 20). On round exhaustion it makes ONE final request that **omits both `tools` and `toolChoice`** (never `toolChoice: 'none'`), so it always resolves to a non-tool answer. Invalid-argument tool calls are **never** executed — they surface to the model as a bounded `tool_arguments_invalid` observation. Observations are byte-bounded and wrapped in an `untrusted_tool_result` envelope. Pass `cache: true` to reuse the cached system prompt and tools across rounds (core 0.7.9+); it is forwarded to every request.
159
202
 
160
203
  An external `AIProviderAdapter` that omits the optional `capabilities` field is treated as declaring every capability `false` (no tool support).
161
204
 
@@ -251,9 +294,11 @@ AI_ANTHROPIC_BASE_URL=https://custom-anthropic.com # optional
251
294
  AI_ANTHROPIC_MODEL=claude-sonnet-4-20250514
252
295
  ```
253
296
 
297
+ Each provider slot (`AI_` single-provider, `AI_OPENAI_`, `AI_ANTHROPIC_`, `AI_BEDROCK_`) also reads `REQUEST_TIMEOUT` (a whole number of ms from 1 to 2147483647), `MAX_TOKENS` (a whole number of tokens, 0 or more) and `DAILY_COST_LIMIT` (US dollars, 0 or more, e.g. `10.5`); Bedrock also `AI_BEDROCK_PRICING_TTL_MS` (whole ms, 0 or more). Write plain numbers, never units: `15m`, `4k`, `$50` or `abc` make `loadConfig()` throw naming the variable (e.g. `Invalid AI_OPENAI_REQUEST_TIMEOUT="15m": expected a whole number of milliseconds from 1 to 2147483647`), so the process does not start; a blank value is unset.
298
+
254
299
  ### Amazon Bedrock
255
300
 
256
- Requires `@plumbus/core` **≥ 0.6.16** (provider slot, env discovery, adapter `cost`, wiring v13).
301
+ Requires `@plumbus/core` **≥ 0.7.0** (prompt caching needs core **≥ 0.7.9** and `@plumbus/ai-bedrock` **≥ 0.2.2**).
257
302
 
258
303
  ```bash
259
304
  pnpm add @plumbus/ai-bedrock
@@ -288,14 +333,22 @@ Set a global fallback model that all prompts use unless overridden:
288
333
  AI_DEFAULT_MODEL=gpt-4o
289
334
  ```
290
335
 
336
+ ### Default Prompt Caching
337
+
338
+ Turn on Anthropic / Bedrock prompt caching for every call (see [Prompt caching](#prompt-caching-anthropic--bedrock)):
339
+
340
+ ```bash
341
+ AI_PROMPT_CACHE=true # system + tools; or e.g. system,tools,messages
342
+ ```
343
+
291
344
  ### Per-Prompt Overrides
292
345
 
293
- Override model, provider, temperature, or maxTokens for any specific prompt via env vars:
346
+ Override model, provider, temperature, maxTokens, or the request timeout for any specific prompt via env vars:
294
347
 
295
348
  ```bash
296
349
  # Format: PROMPT_{NAME}_{FIELD}
297
350
  # Name = prompt name with dots → underscores, UPPERCASED
298
- # Fields: PROVIDER, MODEL, TEMPERATURE, MAX_TOKENS
351
+ # Fields: PROVIDER, MODEL, TEMPERATURE, MAX_TOKENS, TIMEOUT_MS
299
352
 
300
353
  # Route "writer.write_chapter" to Anthropic:
301
354
  PROMPT_WRITER_WRITE_CHAPTER_PROVIDER=anthropic
@@ -305,16 +358,19 @@ PROMPT_WRITER_WRITE_CHAPTER_MODEL=claude-sonnet-4-20250514
305
358
  PROMPT_INTERVIEW_EXTRACT_METADATA_MODEL=gpt-4o-mini
306
359
  ```
307
360
 
361
+ The HTTP server and the worker pool (flows, jobs, event handlers) both apply these. Numeric fields are checked when the config loads — `TIMEOUT_MS` a whole number of ms from 1 to 2147483647, `MAX_TOKENS` a whole number ≥ 1, `TEMPERATURE` 0–2 — and anything else (`abc`, `0`, `15m`) makes `loadConfig()` throw naming the variable, so the process does not start; a blank value is no override.
362
+
308
363
  ### Model Resolution Chain
309
364
 
310
- When a prompt is invoked, the model is resolved in this order:
365
+ When a prompt is invoked, each field (`provider`, `model`, `temperature`, `maxTokens`, `reasoning`, `timeoutMs`) is resolved on its own, first set wins:
311
366
 
312
- 1. **Dynamic overrides** (`resolveAiOverrides` hook) — highest priority
313
- 2. **Per-prompt env override** (`PROMPT_{NAME}_MODEL`)
314
- 3. **Prompt definition** (`model.name` in `definePrompt`) — if set
315
- 4. **Default model** (`AI_DEFAULT_MODEL`) — global fallback
367
+ 1. **Per-call value** (`provider`, `model`, `reasoning`, `timeoutMs` on `generate` / `generateWithUsage` / `streamGenerate`)
368
+ 2. **Dynamic overrides** (`resolveAiOverrides` hook's `promptOverrides`)
369
+ 3. **Per-prompt env override** (`PROMPT_{NAME}_*`)
370
+ 4. **Prompt definition** (`model` in `definePrompt`) — if set
371
+ 5. **Defaults** — the hook's `defaultModel` / `defaultProvider`, else `AI_DEFAULT_MODEL` / `AI_DEFAULT_PROVIDER`
316
372
 
317
- Provider resolution follows the same chain: dynamic override → per-prompt env → prompt definition → `AI_DEFAULT_PROVIDER`.
373
+ A layer decides only the fields it sets: a hook override `{ model: 'gpt-4.1-mini' }` keeps the prompt's `PROMPT_{NAME}_MAX_TOKENS` and `PROMPT_{NAME}_TIMEOUT_MS`. When an override changes `provider`, set `model` with it, or the model of a lower layer goes to the new provider. `reasoning` and the deprecated `reasoningEffort` count as one field: the highest layer that sets either decides both.
318
374
 
319
375
  ## Dynamic AI Config (DB Overrides)
320
376
 
@@ -330,14 +386,14 @@ export const resolveAiOverrides: NonNullable<ServerConfig['resolveAiOverrides']>
330
386
  return {
331
387
  defaultModel: 'gpt-4o', // override global default
332
388
  defaultProvider: 'openai', // override global provider
333
- promptOverrides: { // per-prompt overrides (key = lowercase, dots → underscores)
334
- interview_ask_next_question: { model: 'gpt-4o', temperature: 0.7 },
389
+ promptOverrides: { // per-prompt overrides, keyed by prompt name
390
+ 'interview.ask_next_question': { model: 'gpt-4o', temperature: 0.7 },
335
391
  },
336
392
  };
337
393
  };
338
394
  ```
339
395
 
340
- The framework calls this hook before each `generate()`, `generateWithUsage()`, and `streamGenerate()` call. Dynamic overrides merge with and take priority over env-based config.
396
+ The framework calls this hook before each `generate()`, `generateWithUsage()`, and `streamGenerate()` call, on the HTTP server and the worker pool alike. Its answer replaces the previous one (an override it stops returning no longer applies) and sits above the env config field by field, as in the chain above; unset fields keep the env and prompt values. Key `promptOverrides` by the prompt's name (`interview.ask_next_question`) or its env-style key (`interview_ask_next_question`, lower case, dots → underscores); when both are present, the entry under the name wins.
341
397
 
342
398
  **Important**: The resolver should implement caching (e.g. 60-second TTL) to avoid querying the database on every AI call. API credentials (API keys) always come from environment variables — only model selection, temperature, and maxTokens should be stored in the database.
343
399
 
@@ -368,3 +424,26 @@ See `docs/ai/ai-integration.md` and `docs/upgrading-contract-alignment.md` §12.
368
424
  - With security configured, generate/stream/extract/classify apply the field-classification policy. Extract/classify use the `text` field; arbitrary free-text PII is not automatically detected. Explainability stores redacted input.
369
425
  - Unknown prior spend rejects further requests when a dollar cap is configured. Local/unpriced providers still work without dollar caps; explicit free prices stay supported. Never supply fake zero costs to bypass budgets.
370
426
  - Read [the security release checklist](./upgrading-security-release.md) before upgrading custom cost hooks or AI wrappers.
427
+
428
+
429
+ ## Typed decisions (core 0.7.4+ / 0.8.0-beta.6+)
430
+
431
+ For choices, scores, and probabilities, use `ctx.ai.decide()` inside capabilities
432
+ and flows. Define reusable contracts with `defineDecision` from
433
+ `@plumbus/ai-decision`; CLI startup discovers `app/decisions/`. The shared contract
434
+ package is provided by core; install only the TypeSafe, Laya, or OpenAI Decisions
435
+ provider required by the app. Export `decisions = { providers, defaultProvider }` from
436
+ `app/server.ts` for both API and workers, using `createTypeSafeDecisionAdapter`,
437
+ `createLayaDecisionAdapter`, or `createOpenAIDecisionAdapter` at that server-owned
438
+ boundary. Do not register these adapters as text-completion providers or call them
439
+ directly for app business logic.
440
+
441
+ Core records `operation: 'decide'` with actual model, usage, cost, tenant/actor,
442
+ optional `decisionName`, and the caller's `costContext`. Existing
443
+ `onAICostRecorded(record, costContext, db)` hooks persist these rows; include the
444
+ new operation in any app-owned ledger enum/schema. Failed responses and cancelled
445
+ calls also produce rows. Unknown USD cost is null, never a free zero. Security
446
+ scans state and structured questions; budgets are checked before provider work.
447
+ Use `mockAI({ decide: result })` and test through `runCapability`/`simulateFlow`.
448
+
449
+ Guide: https://github.com/plumbus-framework/plumbus/blob/main/docs/ai/decision-providers.md
@@ -183,7 +183,7 @@ plumbus start --port $PLUMBUS_START_PORT
183
183
  plumbus start --port 8080 --host 127.0.0.1
184
184
  ```
185
185
 
186
- Behind a load balancer set `TRUST_PROXY=true` (or a specific IP/CIDR) so Fastify trusts `X-Forwarded-*` headers. `app/server.ts` extension hooks (`onRoutesRegistered`, `resolveAiOverrides`, `onCapabilityError`, `onProcessError`, `onAICostRecorded`, `onFlowError`, `enableStrictStructuredOutputs`, `credentials`) are loaded automatically.
186
+ Behind a load balancer set `TRUST_PROXY=true` (or a specific IP/CIDR) so Fastify trusts `X-Forwarded-*` headers. `app/server.ts` extension hooks (`onRoutesRegistered`, `resolveAiOverrides`, `onCapabilityError`, `onProcessError`, `onAICostRecorded`, `onFlowError`, `enableStrictStructuredOutputs`, `credentials`, `authenticationRuntime`) are loaded automatically.
187
187
 
188
188
  ## Background work
189
189
 
@@ -228,6 +228,24 @@ If `@plumbus/mcp` is not installed, `plumbus mcp serve` prints `Run: pnpm add @p
228
228
 
229
229
  Read `node_modules/@plumbus/core/instructions/mcp.md` and `node_modules/@plumbus/mcp/instructions/README.md` for MCP.
230
230
 
231
+ ## Payments
232
+
233
+ For apps with the optional `@plumbus/payments` add-on. The commands load the `payments` export of `app/payments/index.ts`; core does not depend on the add-on.
234
+
235
+ ```bash
236
+ # Check the payments config; --live also checks keys, webhook destinations, and the billing catalog at the provider
237
+ plumbus payments doctor --live --webhook-url https://api.example.com/payments/webhooks/stripe
238
+
239
+ # Create the provider's webhook destinations (prints each signing secret once; re-run after upgrading the add-on)
240
+ plumbus payments webhooks setup --url https://api.example.com/payments/webhooks/stripe
241
+
242
+ # Create or update the billing catalog (plans, prices, features, meters) from `billing` in the config
243
+ plumbus payments catalog sync
244
+ plumbus payments catalog check # read-only; exits 1 when the provider differs (CI)
245
+ ```
246
+
247
+ Read `node_modules/@plumbus/payments/instructions/README.md` for payments.
248
+
231
249
  ## App Commands
232
250
 
233
251
  Run custom command scripts defined in your project's `app/commands/` directory. Use this for setup scripts, data migration tasks, and other one-off operations that need framework infrastructure (DB, config, password hashing).
@@ -35,7 +35,9 @@ export const User = defineEntity({
35
35
  |-------------|-----------|-------|
36
36
  | `field.id()` | UUID string | Primary key |
37
37
  | `field.string(opts)` | Text | General text |
38
- | `field.number(opts)` | Numeric | Integer or decimal |
38
+ | `field.number(opts)` | 32-bit integer | Counts and small integers |
39
+ | `field.bigint(opts)` | 64-bit integer | Money in minor units (cents) and totals; JS number, safe integers only (core 0.7.7+) |
40
+ | `field.decimal(opts)` | Floating point | Measurements; never money |
39
41
  | `field.boolean(opts)` | Boolean | |
40
42
  | `field.timestamp(opts)` | Datetime | ISO timestamps |
41
43
  | `field.json(opts)` | JSONB | Arbitrary structured data |
@@ -114,6 +114,12 @@ retry: {
114
114
 
115
115
  Steps that fail with transient errors are retried. Permanent failures stop the flow and move it to the dead-letter queue.
116
116
 
117
+ Operators can stop an open execution through the framework service. `ctx.flows.cancel(id)`
118
+ aborts the active step and runs every declared compensation for completed steps;
119
+ `ctx.flows.terminate(id)` aborts and closes the execution without compensation. Both leave the
120
+ execution in the terminal `cancelled` state, never `completed`; applications should audit the
121
+ operator, reason, and which action was chosen in their recovery capability.
122
+
117
123
  ## Compiled definitions
118
124
 
119
125
  Scheduled flows use `createFlowScheduler` and `flow_schedules`. Set `schedule.catchUpPolicy` to `catch-up` for bounded missed-tick catch-up; default is `skip`. Do not add a second scheduler.
@@ -117,6 +117,10 @@ package.json # Private root — delegates scripts via pnpm -r
117
117
 
118
118
  In monorepo mode the `app/` and `config/` directories live under `backend/`. The CLI auto-detects the layout.
119
119
 
120
+ ### Discovery
121
+
122
+ The CLI registers every capability, entity, flow, event, prompt, and translation **exported** from a file under the matching `app/` directory (file names and nesting are free). An exported array, or a plain object without a `name` field, is expanded one level, so an add-on's collections register with one line each — for example `export const paymentCapabilities = payments.capabilities` and `export { paymentEntities } from '@plumbus/payments'`. A definition exported both alone and inside a collection is registered once.
123
+
120
124
  ## Framework-Provided Dependencies
121
125
 
122
126
  The framework provides common dependencies — **consumer apps must NOT install them separately**. Import through the framework's subpath exports:
@@ -141,20 +145,24 @@ The framework provides common dependencies — **consumer apps must NOT install
141
145
 
142
146
  ## Server Extensions (`app/server.ts`)
143
147
 
144
- The optional `app/server.ts` file exports hooks that customize server behavior. The framework auto-discovers this file on `plumbus dev` and `plumbus start`.
148
+ The optional `app/server.ts` file exports hooks that customize server behavior. The framework auto-discovers this file on `plumbus dev`, `plumbus start`, and `plumbus worker`.
145
149
 
146
150
  ### Available Hooks
147
151
 
148
152
  | Hook | When it fires | Use for |
149
153
  |------|--------------|---------|
150
154
  | `onRoutesRegistered` | After capability routes are registered | Adding custom routes (e.g. streaming endpoints) |
151
- | `onCapabilityError` | After a capability returns a non-success result | Logging capability failures to a system log table |
155
+ | `onCapabilityError` | After a capability returns a non-success result in an HTTP route, flow step, job, or event handler (`info.source`: `'http'`, `'flow'`, `'job'`, `'event'`) | Logging capability failures to a system log table |
152
156
  | `onFlowError` | After a flow fails permanently (retries exhausted) | Logging flow failures to a system log table |
153
157
  | `onProcessError` | On uncaught exceptions, unhandled rejections, and Fastify-level errors | Logging process-level crashes that bypass capability/flow hooks |
154
158
  | `resolveAiOverrides` | Before each AI call | Dynamic model/provider configuration from DB |
155
159
  | `onAICostRecorded` | After each AI cost record is written | Custom cost analytics or billing hooks |
160
+ | `aiProviderConcurrency` | Around each provider attempt | Immediate per-tenant/provider/package concurrency admission |
161
+ | `resolveAIProviderHeaders` | Before each provider attempt | Trusted W3C propagation headers |
162
+ | `onAIProviderSpan` | After each provider attempt | Export provider client spans |
156
163
  | `enableStrictStructuredOutputs` | Server bootstrap | Global toggle for strict structured-output mode |
157
164
  | `credentials` | Server bootstrap | Optional host credential catalog (`export const credentials`). Passed to `createServer({ credentials })`. Names, types, refs, and public labels only — secrets stay in the host resolver. |
165
+ | `authenticationRuntime` | Server bootstrap (included in the 0.8 beta family; stable core 0.7.8+) | Browser sign-in with `@plumbus/auth` (`createAuthRuntime(...)`): the same value `createServer({ authenticationRuntime })` takes |
158
166
 
159
167
  ### Error Capture Coverage
160
168
 
@@ -162,12 +170,14 @@ The framework provides hooks for **every error category**:
162
170
 
163
171
  | Error Type | Hook | Source Field |
164
172
  |------------|------|-------------|
165
- | Capability failure (handler throw, validation, access) | `onCapabilityError` | capability name, domain, error code |
173
+ | Capability failure (handler throw, validation, access) in HTTP routes, flow steps, jobs, and event handlers | `onCapabilityError` | capability name, domain, error code, `source` |
166
174
  | Flow permanent failure (retries exhausted) | `onFlowError` | flow name, step, execution ID |
167
175
  | Uncaught exception (process crash) | `onProcessError` | `source: 'uncaughtException'` |
168
176
  | Unhandled promise rejection | `onProcessError` | `source: 'unhandledRejection'` |
169
177
  | Fastify request error (malformed request, timeout) | `onProcessError` | `source: 'fastify'` |
170
178
 
179
+ `onCapabilityError` fires once per failed attempt: an event handler that fails on all N delivery attempts fires N times, and a retried flow step fires once per failed attempt. A failed job fires once. `sourceIp` and `userAgent` are set only for `source: 'http'`. Rejections before the capability runs (HTTP authentication, the worker's event/job payload and access checks) and nested `ctx.capabilities.invoke()` calls do not fire it.
180
+
171
181
  ### Example: Full Error Logging
172
182
 
173
183
  ```ts
@@ -22,6 +22,7 @@ The previous family remains on core 0.7.x, UI 0.8.x, MCP 0.6.x, voice 0.5.x, and
22
22
  | `@plumbus/chat-ui` | `@plumbus/chat` | `">=0.3.0-beta.0 <0.4.0"` (stable: `"0.3.x"`) |
23
23
  | `@plumbus/auth-cognito` | `@plumbus/auth` | `">=0.3.0-beta.0 <0.4.0"` (stable: `"0.3.x"`) |
24
24
  | Every `@plumbus/voice-*` provider | `@plumbus/voice` | `">=0.6.0-beta.0 <0.7.0"` (stable: `"0.6.x"`) |
25
+ | `@plumbus/payments-stripe` | `@plumbus/payments` | `">=0.3.0-beta.0 <0.4.0"` (stable: `"0.3.x"`) |
25
26
 
26
27
  UI 0.9.x keeps the required core peer (no direct core dependency) and uses `workspace:*` only for development. This prevents npm from accepting old application core plus a hidden new core nested inside UI. Install core and UI together. All Plumbus packages share the application runtime through peers.
27
28
 
@@ -43,4 +44,4 @@ Run lint, format checking, typechecking, tests, and packed npm install checks be
43
44
 
44
45
  ## Consumer upgrade
45
46
 
46
- Read [upgrading-security-release.md](./upgrading-security-release.md) and the 0.8 notes in the root docs, explicitly select the new package versions for every installed Plumbus add-on, and run `plumbus init --patch` for agent wiring **v16**. Keep application business logic in Plumbus primitives and `ctx.*`; do not bypass security checks to make a migration pass.
47
+ Read [upgrading-security-release.md](./upgrading-security-release.md) and the 0.8 notes in the root docs, explicitly select the new package versions for every installed Plumbus add-on, and run `plumbus init --patch --agent all` for current agent wiring (**v19** in this beta family and stable core 0.7.9+; v17 added the classification recipe). For classification/provider/model selection, read [ai-classification.md](./ai-classification.md). Keep application business logic in Plumbus primitives and `ctx.*`; do not bypass security checks to make a migration pass.
@@ -58,12 +58,13 @@ By default, any input keys not referenced as `{{key}}` in `system`/`description`
58
58
  | `maxTokens` | number? | Max output tokens |
59
59
  | `reasoning` | `{ mode:'disabled' } \| { mode:'effort', effort } \| { mode:'budget', maxTokens }?` | Provider-neutral reasoning intent; the selected adapter translates or explicitly rejects it |
60
60
  | `reasoningEffort` | `'low' \| 'medium' \| 'high'?` | Deprecated OpenAI-only shorthand; unchanged for source/runtime compatibility |
61
+ | `timeoutMs` | number? | How long one provider request may take, in ms (whole number, 1–2147483647), the whole answer included; unset keeps the provider's `requestTimeout` (default 120 000). Raise it for long answers (large `maxTokens`, reasoning); a request it ends fails with a non-retryable `ProviderAPIError` carrying `timeoutMs` |
61
62
 
62
63
  **Recommended convention:** omit `provider` and `name` in the prompt definition and resolve them via env (`AI_DEFAULT_PROVIDER`, `AI_DEFAULT_MODEL`, `PROMPT_{NAME}_*`) or the `resolveAiOverrides` hook in `app/server.ts`. Set only `temperature`, `maxTokens`, and (when needed) `reasoning` in the prompt when you want per-prompt tuning baked into the contract.
63
64
 
64
65
  Bare `z.string()` prompt `output` schemas run in plain text mode (same as a single-string-field object). Prefer that for free-text prompts; use an object schema when you need structured fields.
65
66
 
66
- Resolution order: `resolveAiOverrides` → per-prompt env vars → prompt `model` fields → `AI_DEFAULT_MODEL` / `AI_DEFAULT_PROVIDER`.
67
+ Resolution order, field by field (each of `provider`, `model`, `temperature`, `maxTokens`, `reasoning`, `timeoutMs` on its own): per-call value → `resolveAiOverrides` → per-prompt env vars → prompt `model` fields → defaults (the hook's `defaultModel` / `defaultProvider`, else `AI_DEFAULT_MODEL` / `AI_DEFAULT_PROVIDER`). A layer decides only the fields it sets, so a hook override of `model` keeps `PROMPT_{NAME}_TIMEOUT_MS` and `_MAX_TOKENS`; the hook's `promptOverrides` may be keyed by the prompt name or its env-style key (`report_summarize`), the name winning.
67
68
 
68
69
  ## Structured output and streaming flags
69
70
 
@@ -0,0 +1,3 @@
1
+ ALTER TABLE "core_plumbus"."approval_request"
2
+ ADD COLUMN IF NOT EXISTS cancelled_by_account_id text,
3
+ ADD COLUMN IF NOT EXISTS cancellation_reason text;
@@ -15,6 +15,13 @@
15
15
  "when": 1787184060000,
16
16
  "tag": "0001_human_task",
17
17
  "breakpoints": true
18
+ },
19
+ {
20
+ "idx": 2,
21
+ "version": "7",
22
+ "when": 1787184120000,
23
+ "tag": "0002_approval_cancellation",
24
+ "breakpoints": true
18
25
  }
19
26
  ]
20
27
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plumbus/core",
3
- "version": "0.8.0-beta.1",
3
+ "version": "0.8.0-beta.10",
4
4
  "description": "Plumbus framework core — types, SDK, runtime, CLI, test utilities",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -89,7 +89,8 @@
89
89
  "xml-crypto": "^6.1.2",
90
90
  "xpath": "^0.0.34",
91
91
  "zod": "^3.24.0",
92
- "zod-to-json-schema": "^3.25.2"
92
+ "zod-to-json-schema": "^3.25.2",
93
+ "@plumbus/ai-decision": "~0.3.0-beta.1"
93
94
  },
94
95
  "peerDependencies": {
95
96
  "@plumbus/api": ">=0.3.0-beta.0 <0.4.0",
@@ -121,10 +122,10 @@
121
122
  "@vitest/browser": "^3.0.0",
122
123
  "esbuild": "^0.25.12",
123
124
  "typescript": "^5.7.0",
124
- "@plumbus/ai-bedrock": "0.3.0-beta.0",
125
- "@plumbus/voice": "0.6.0-beta.0",
125
+ "@plumbus/api": "0.3.0-beta.0",
126
+ "@plumbus/voice": "0.6.0-beta.1",
126
127
  "@plumbus/mcp": "0.7.0-beta.0",
127
- "@plumbus/api": "0.3.0-beta.0"
128
+ "@plumbus/ai-bedrock": "0.3.0-beta.1"
128
129
  },
129
130
  "scripts": {
130
131
  "build": "tsc -b",
@@ -1,5 +0,0 @@
1
- import { type AIService, type RAGPipelineConfig, type RunToolLoopParams } from '@plumbus/core';
2
- export declare function legacyConsumer(ai: AIService, params: RunToolLoopParams): Promise<number>;
3
- export declare const legacyEmbeddingCallback: RAGPipelineConfig['onEmbeddingCost'];
4
- export declare const uncheckedEstimate: number;
5
- //# sourceMappingURL=legacy-cost-api.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"legacy-cost-api.d.ts","sourceRoot":"","sources":["../../../src/ai/__typecheck-fixtures__/legacy-cost-api.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,SAAS,EACd,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACvB,MAAM,eAAe,CAAC;AAEvB,wBAAsB,cAAc,CAAC,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,CAM9F;AAED,eAAO,MAAM,uBAAuB,EAAE,iBAAiB,CAAC,iBAAiB,CAGxE,CAAC;AAGF,eAAO,MAAM,iBAAiB,EAAE,MAAyC,CAAC"}
@@ -1,15 +0,0 @@
1
- import { calculateModelCost, estimateModelCost, runToolLoop, } from '@plumbus/core';
2
- export async function legacyConsumer(ai, params) {
3
- const result = await ai.generateWithUsage({ prompt: 'p', input: {} });
4
- const cost = result.cost;
5
- const loop = await runToolLoop(ai, params);
6
- const total = loop.aggregatedCost;
7
- return cost + total + calculateModelCost(1, 1, 'local');
8
- }
9
- export const legacyEmbeddingCallback = (info) => {
10
- const numericCost = info.cost;
11
- void numericCost;
12
- };
13
- // @ts-expect-error Unknown-aware estimates cannot be treated as definitely priced.
14
- export const uncheckedEstimate = estimateModelCost(1, 1, 'local');
15
- //# sourceMappingURL=legacy-cost-api.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"legacy-cost-api.js","sourceRoot":"","sources":["../../../src/ai/__typecheck-fixtures__/legacy-cost-api.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,WAAW,GAIZ,MAAM,eAAe,CAAC;AAEvB,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,EAAa,EAAE,MAAyB;IAC3E,MAAM,MAAM,GAAG,MAAM,EAAE,CAAC,iBAAiB,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;IACtE,MAAM,IAAI,GAAW,MAAM,CAAC,IAAI,CAAC;IACjC,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;IAC3C,MAAM,KAAK,GAAW,IAAI,CAAC,cAAc,CAAC;IAC1C,OAAO,IAAI,GAAG,KAAK,GAAG,kBAAkB,CAAC,CAAC,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC;AAC1D,CAAC;AAED,MAAM,CAAC,MAAM,uBAAuB,GAAyC,CAAC,IAAI,EAAE,EAAE;IACpF,MAAM,WAAW,GAAW,IAAI,CAAC,IAAI,CAAC;IACtC,KAAK,WAAW,CAAC;AACnB,CAAC,CAAC;AAEF,mFAAmF;AACnF,MAAM,CAAC,MAAM,iBAAiB,GAAW,iBAAiB,CAAC,CAAC,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC"}