@cosmicdrift/kumiko-framework 0.385.0 → 0.387.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (298) hide show
  1. package/LICENSE +21 -57
  2. package/README.md +2 -2
  3. package/agents/widgets.md +39 -39
  4. package/dist/api/api-constants.js +2 -2
  5. package/dist/api/auth-middleware.js +7 -7
  6. package/dist/api/auth-rate-limiters.js +7 -3
  7. package/dist/api/auth-routes-mfa.js +1 -1
  8. package/dist/api/auth-routes-session.js +1 -1
  9. package/dist/api/auth-routes-shared.js +7 -3
  10. package/dist/api/auth-routes-signup.js +1 -1
  11. package/dist/api/client-ip.js +3 -1
  12. package/dist/api/extra-route.js +1 -1
  13. package/dist/api/pii-leak-guard.js +4 -2
  14. package/dist/api/readiness.js +5 -5
  15. package/dist/api/redis-sse-broker.js +8 -6
  16. package/dist/api/request-context.d.ts +2 -0
  17. package/dist/api/request-context.js +16 -7
  18. package/dist/api/routes.js +2 -2
  19. package/dist/api/server.js +34 -33
  20. package/dist/api/sse-broker.js +1 -1
  21. package/dist/api/sse-route.d.ts +13 -13
  22. package/dist/api/sse-route.js +15 -15
  23. package/dist/auth/index.js +4 -4
  24. package/dist/auth/roles.d.ts +4 -4
  25. package/dist/auth/roles.js +24 -24
  26. package/dist/compliance/duration-spec.js +18 -20
  27. package/dist/compliance/index.js +2 -3
  28. package/dist/compliance/override-schema.d.ts +4 -4
  29. package/dist/compliance/override-schema.js +19 -22
  30. package/dist/compliance/profiles.d.ts +26 -29
  31. package/dist/compliance/profiles.js +62 -69
  32. package/dist/compliance/sub-processors.d.ts +28 -30
  33. package/dist/compliance/sub-processors.js +17 -21
  34. package/dist/consumer-cli.js +4 -4
  35. package/dist/crypto/blind-index.js +18 -17
  36. package/dist/crypto/ciphertext-pattern.js +1 -1
  37. package/dist/crypto/event-pii.js +3 -3
  38. package/dist/crypto/kek-source.d.ts +1 -1
  39. package/dist/crypto/kek-source.js +3 -2
  40. package/dist/crypto/kms-adapter.d.ts +1 -1
  41. package/dist/crypto/kms-adapter.js +4 -6
  42. package/dist/crypto/kms-wiring.d.ts +1 -2
  43. package/dist/crypto/kms-wiring.js +2 -2
  44. package/dist/crypto/pg-kms-adapter.js +2 -6
  45. package/dist/crypto/pii-field-encryption.js +4 -4
  46. package/dist/crypto/subject-resolver.d.ts +1 -1
  47. package/dist/crypto/subject-resolver.js +2 -2
  48. package/dist/db/apply-entity-event.d.ts +6 -6
  49. package/dist/db/apply-entity-event.js +55 -55
  50. package/dist/db/blind-index-cleanup.js +7 -7
  51. package/dist/db/collect-table-metas.js +10 -11
  52. package/dist/db/compound-types.d.ts +1 -1
  53. package/dist/db/compound-types.js +12 -12
  54. package/dist/db/cross-tenant-reads.d.ts +8 -0
  55. package/dist/db/cross-tenant-reads.js +23 -0
  56. package/dist/db/cursor.js +3 -4
  57. package/dist/db/dialect.d.ts +2 -2
  58. package/dist/db/dialect.js +2 -2
  59. package/dist/db/eagerload.d.ts +3 -3
  60. package/dist/db/eagerload.js +21 -12
  61. package/dist/db/entity-table-meta.d.ts +2 -2
  62. package/dist/db/entity-table-meta.js +4 -4
  63. package/dist/db/event-store-executor-context.d.ts +1 -1
  64. package/dist/db/event-store-executor-context.js +13 -13
  65. package/dist/db/event-store-executor-read.js +27 -28
  66. package/dist/db/event-store-executor-write.js +65 -70
  67. package/dist/db/located-timestamp.d.ts +11 -11
  68. package/dist/db/located-timestamp.js +29 -30
  69. package/dist/db/migrate-generator.d.ts +1 -1
  70. package/dist/db/migrate-generator.js +26 -19
  71. package/dist/db/migrate-runner.js +25 -25
  72. package/dist/db/money.d.ts +6 -6
  73. package/dist/db/money.js +29 -27
  74. package/dist/db/queries/backfill-pii.js +5 -5
  75. package/dist/db/queries/ddl.js +1 -1
  76. package/dist/db/queries/es-ops.d.ts +1 -1
  77. package/dist/db/queries/es-ops.js +1 -1
  78. package/dist/db/queries/event-consumer.js +3 -3
  79. package/dist/db/queries/event-store.js +4 -2
  80. package/dist/db/queries/projection-rebuild.js +3 -3
  81. package/dist/db/queries/raw-sql.d.ts +2 -2
  82. package/dist/db/queries/raw-sql.js +2 -2
  83. package/dist/db/queries/shadow-swap.js +19 -19
  84. package/dist/db/queries/test-stack.d.ts +4 -1
  85. package/dist/db/queries/test-stack.js +15 -2
  86. package/dist/db/query.d.ts +1 -1
  87. package/dist/db/query.js +6 -6
  88. package/dist/db/rebuild-marker.js +20 -21
  89. package/dist/db/replay-migration-sql.js +2 -2
  90. package/dist/db/schema-inspection.js +4 -5
  91. package/dist/db/table-builder.d.ts +1 -2
  92. package/dist/db/table-builder.js +82 -82
  93. package/dist/db/tenant-db.d.ts +2 -2
  94. package/dist/db/tenant-db.js +9 -2
  95. package/dist/derivatives/delete-stored-file.js +5 -2
  96. package/dist/derivatives/derivatives-context.js +1 -1
  97. package/dist/engine/boot-validator/access-roles.js +4 -3
  98. package/dist/engine/boot-validator/api-ext.js +14 -14
  99. package/dist/engine/boot-validator/config-deps.js +7 -6
  100. package/dist/engine/boot-validator/detail-screens.js +3 -3
  101. package/dist/engine/boot-validator/entity-handler.js +60 -61
  102. package/dist/engine/boot-validator/gdpr-storage.js +4 -4
  103. package/dist/engine/boot-validator/i18n-keys.js +1 -1
  104. package/dist/engine/boot-validator/index.d.ts +2 -2
  105. package/dist/engine/boot-validator/index.js +20 -20
  106. package/dist/engine/boot-validator/nav.js +5 -5
  107. package/dist/engine/boot-validator/ownership.js +12 -9
  108. package/dist/engine/boot-validator/parent-ref.js +1 -1
  109. package/dist/engine/boot-validator/pii-retention.js +50 -57
  110. package/dist/engine/boot-validator/projection-list-screens.js +7 -7
  111. package/dist/engine/boot-validator/query-refs.js +1 -1
  112. package/dist/engine/boot-validator/screens-actions.js +15 -16
  113. package/dist/engine/boot-validator/screens-forms.js +10 -11
  114. package/dist/engine/boot-validator/screens-lists.js +14 -15
  115. package/dist/engine/boot-validator/screens.js +34 -40
  116. package/dist/engine/boot-validator/security-baseline.js +3 -2
  117. package/dist/engine/boot-validator/transfer-graph.js +2 -2
  118. package/dist/engine/boot-validator/workspaces.js +3 -3
  119. package/dist/engine/bridge-stub.js +3 -4
  120. package/dist/engine/build-app-schema.js +75 -75
  121. package/dist/engine/build-config-feature-schema.js +50 -50
  122. package/dist/engine/build-target.js +20 -23
  123. package/dist/engine/codemod/pipeline-codemod.js +20 -13
  124. package/dist/engine/config-helpers.js +4 -4
  125. package/dist/engine/constants.js +2 -2
  126. package/dist/engine/create-app.js +2 -2
  127. package/dist/engine/define-feature.js +12 -11
  128. package/dist/engine/define-handler.js +12 -11
  129. package/dist/engine/define-step.js +1 -1
  130. package/dist/engine/define-workflow.d.ts +5 -5
  131. package/dist/engine/define-workflow.js +6 -6
  132. package/dist/engine/entity-handlers.js +13 -14
  133. package/dist/engine/event-def.js +2 -2
  134. package/dist/engine/extension-names.d.ts +16 -17
  135. package/dist/engine/extension-names.js +37 -38
  136. package/dist/engine/extensions/escape-hatch-usage.js +1 -1
  137. package/dist/engine/extensions/tenant-data.js +2 -2
  138. package/dist/engine/extensions/user-data.d.ts +23 -24
  139. package/dist/engine/factories.d.ts +14 -13
  140. package/dist/engine/factories.js +38 -37
  141. package/dist/engine/feature-ast/entity-field-types.js +1 -1
  142. package/dist/engine/feature-ast/extractors/handlers.js +1 -1
  143. package/dist/engine/feature-ast/extractors/hooks.js +1 -1
  144. package/dist/engine/feature-ast/extractors/shared.d.ts +2 -3
  145. package/dist/engine/feature-ast/extractors/shared.js +2 -3
  146. package/dist/engine/feature-ast/parse.js +13 -13
  147. package/dist/engine/feature-ast/patch.js +1 -1
  148. package/dist/engine/feature-ast/pattern-change-schema.d.ts +8 -0
  149. package/dist/engine/feature-ast/pattern-change-schema.js +1 -1
  150. package/dist/engine/feature-ast/patterns.d.ts +2 -2
  151. package/dist/engine/feature-ast/render.js +2 -2
  152. package/dist/engine/feature-config-events-jobs.js +15 -12
  153. package/dist/engine/feature-entity-handlers.js +9 -5
  154. package/dist/engine/feature-manifest.d.ts +9 -9
  155. package/dist/engine/feature-manifest.js +6 -7
  156. package/dist/engine/feature-ui-extensions.d.ts +3 -3
  157. package/dist/engine/feature-ui-extensions.js +30 -22
  158. package/dist/engine/field-access.js +5 -6
  159. package/dist/engine/index.d.ts +1 -0
  160. package/dist/engine/index.js +4 -4
  161. package/dist/engine/object-form.d.ts +1 -0
  162. package/dist/engine/object-form.js +14 -4
  163. package/dist/engine/ownership.js +3 -3
  164. package/dist/engine/parse-ref-target.js +5 -7
  165. package/dist/engine/pattern-library/index.js +2 -2
  166. package/dist/engine/pattern-library/library.js +2 -2
  167. package/dist/engine/pattern-library/types.js +3 -3
  168. package/dist/engine/pipeline.js +2 -2
  169. package/dist/engine/project-app-schema-for-roles.js +1 -1
  170. package/dist/engine/projection-helpers.js +9 -9
  171. package/dist/engine/qualified-name.js +2 -2
  172. package/dist/engine/registry-facade.js +1 -1
  173. package/dist/engine/registry-ingest.js +12 -12
  174. package/dist/engine/registry-state.js +17 -19
  175. package/dist/engine/registry-validate.js +12 -12
  176. package/dist/engine/run-in.js +2 -2
  177. package/dist/engine/run-pipeline.js +7 -0
  178. package/dist/engine/schema-builder.js +40 -38
  179. package/dist/engine/screen-filter-ops.js +17 -20
  180. package/dist/engine/screen-helpers.js +4 -3
  181. package/dist/engine/state-machine.d.ts +12 -12
  182. package/dist/engine/state-machine.js +7 -7
  183. package/dist/engine/steps/_resolver-utils.js +4 -3
  184. package/dist/engine/steps/for-each.js +4 -4
  185. package/dist/engine/steps/unsafe-projection-delete.js +1 -1
  186. package/dist/engine/steps/unsafe-projection-upsert.js +4 -4
  187. package/dist/engine/steps/wait-for-event.js +4 -4
  188. package/dist/engine/steps/workflow-system-events.js +3 -3
  189. package/dist/engine/tier-resolver-extension.d.ts +29 -27
  190. package/dist/engine/tier-resolver-extension.js +26 -26
  191. package/dist/engine/transfer-adjacency.js +2 -2
  192. package/dist/engine/validate-projection-allowlist.js +7 -27
  193. package/dist/engine/walk-step-instances.d.ts +2 -0
  194. package/dist/engine/walk-step-instances.js +38 -0
  195. package/dist/engine/where-rule-lint.js +1 -1
  196. package/dist/engine/write-handler-qn-extract.js +1 -1
  197. package/dist/entrypoint/index.js +3 -3
  198. package/dist/env/index.d.ts +7 -6
  199. package/dist/env/index.js +10 -9
  200. package/dist/errors/transition-details.js +7 -7
  201. package/dist/errors/write-error-info.d.ts +6 -6
  202. package/dist/errors/write-error-info.js +6 -6
  203. package/dist/es-ops/context.d.ts +9 -9
  204. package/dist/es-ops/context.js +28 -28
  205. package/dist/es-ops/index.js +7 -9
  206. package/dist/es-ops/operations-schema.js +4 -10
  207. package/dist/es-ops/runner.d.ts +11 -11
  208. package/dist/es-ops/runner.js +49 -53
  209. package/dist/es-ops/types.d.ts +55 -55
  210. package/dist/es-ops/types.js +11 -11
  211. package/dist/event-store/admin-api.js +6 -7
  212. package/dist/event-store/errors.js +1 -1
  213. package/dist/event-store/event-store.js +2 -2
  214. package/dist/event-store/rebuild-dead-letter.js +1 -1
  215. package/dist/files/feature.js +9 -10
  216. package/dist/files/file-handle.js +5 -6
  217. package/dist/files/file-ref-entity.js +15 -15
  218. package/dist/files/file-ref-table.js +7 -8
  219. package/dist/files/file-routes.d.ts +7 -0
  220. package/dist/files/file-routes.js +11 -5
  221. package/dist/files/in-memory-provider.js +8 -9
  222. package/dist/files/local-provider.js +18 -25
  223. package/dist/files/provider-resolver.d.ts +7 -7
  224. package/dist/files/storage-tracking.js +2 -2
  225. package/dist/files/zip-stream.d.ts +12 -19
  226. package/dist/files/zip-stream.js +44 -63
  227. package/dist/i18n/required-surface-keys.d.ts +1 -1
  228. package/dist/i18n/required-surface-keys.js +8 -8
  229. package/dist/internal/stack-support.d.ts +1 -1
  230. package/dist/internal/stack-support.js +1 -1
  231. package/dist/jobs/job-runner-helpers.d.ts +1 -2
  232. package/dist/jobs/job-runner-helpers.js +8 -18
  233. package/dist/jobs/job-runner-schedulers.d.ts +1 -1
  234. package/dist/jobs/job-runner-schedulers.js +3 -4
  235. package/dist/jobs/job-runner.js +17 -18
  236. package/dist/lifecycle/lifecycle.js +3 -5
  237. package/dist/lifecycle/signal-handlers.js +5 -4
  238. package/dist/logging/utils.d.ts +2 -1
  239. package/dist/logging/utils.js +38 -10
  240. package/dist/migrations/index.js +1 -1
  241. package/dist/migrations/kumiko-drift.js +14 -13
  242. package/dist/migrations/pending-rebuilds.d.ts +21 -20
  243. package/dist/migrations/pending-rebuilds.js +27 -27
  244. package/dist/migrations/projection-table-index.d.ts +3 -3
  245. package/dist/migrations/projection-table-index.js +8 -8
  246. package/dist/observability/metric-validator.js +1 -1
  247. package/dist/observability/metrics-handle.js +3 -3
  248. package/dist/pipeline/append-event-core.js +11 -2
  249. package/dist/pipeline/cascade-handler.d.ts +1 -1
  250. package/dist/pipeline/dispatch-batch.js +2 -2
  251. package/dist/pipeline/dispatch-query.js +10 -10
  252. package/dist/pipeline/dispatch-shared.d.ts +1 -0
  253. package/dist/pipeline/dispatch-shared.js +42 -19
  254. package/dist/pipeline/dispatch-stream.js +1 -1
  255. package/dist/pipeline/dispatch-write.js +3 -3
  256. package/dist/pipeline/dispatcher-utils.d.ts +1 -1
  257. package/dist/pipeline/dispatcher-utils.js +1 -1
  258. package/dist/pipeline/event-consumer-state.js +2 -2
  259. package/dist/pipeline/event-dispatcher-delivery.js +1 -1
  260. package/dist/pipeline/event-dispatcher.js +17 -28
  261. package/dist/pipeline/irreversible-operation-gate.js +1 -1
  262. package/dist/pipeline/lifecycle-pipeline.js +9 -9
  263. package/dist/pipeline/member-read-only-transaction.js +1 -3
  264. package/dist/pipeline/msp-rebuild.js +1 -1
  265. package/dist/pipeline/projection-rebuild.js +13 -12
  266. package/dist/pipeline/system-hooks.js +39 -41
  267. package/dist/pipeline/system-identity-switch.js +1 -3
  268. package/dist/pipeline/tenant-timezone-cache.js +1 -1
  269. package/dist/random/generate.d.ts +29 -29
  270. package/dist/random/generate.js +30 -30
  271. package/dist/random/words.js +20 -20
  272. package/dist/rate-limit/middleware.js +9 -5
  273. package/dist/redis/client.js +2 -2
  274. package/dist/redis/pubsub-signal.js +14 -17
  275. package/dist/schema-cli.js +116 -22
  276. package/dist/search/index.js +4 -4
  277. package/dist/search/purge-subject.js +4 -5
  278. package/dist/search/reindex-entity.js +6 -9
  279. package/dist/time/boot-tz-warning.d.ts +2 -2
  280. package/dist/time/boot-tz-warning.js +8 -8
  281. package/dist/time/iana.js +10 -10
  282. package/dist/time/index.js +9 -9
  283. package/dist/time/legacy-date.d.ts +2 -2
  284. package/dist/time/legacy-date.js +6 -8
  285. package/dist/time/polyfill.js +1 -1
  286. package/dist/time/tz-context.d.ts +1 -1
  287. package/dist/time/tz-context.js +1 -1
  288. package/dist/ui-types/app-schema.js +7 -7
  289. package/dist/ui-types/list-row-meta.js +4 -4
  290. package/dist/upgrade-cli.js +10 -9
  291. package/dist/utils/compare.js +1 -1
  292. package/package.json +4 -4
  293. package/src/changes.json +54 -0
  294. package/src/scripts/codemod/crypto-shredding-testing-move.ts +2 -2
  295. package/src/scripts/codemod/migrate-cross-tenant.ts +1 -1
  296. package/src/scripts/codemod/migrate-open-to-all.ts +1 -1
  297. package/src/scripts/codemod/pii-personal-migration.ts +17 -8
  298. package/src/scripts/codemod/unprocessable-error-details-reason.ts +2 -2
package/LICENSE CHANGED
@@ -1,57 +1,21 @@
1
- Business Source License 1.1
2
-
3
- Parameters
4
-
5
- Licensor: Marc Frost
6
-
7
- Licensed Work: @cosmicdrift/kumiko-framework
8
- The Licensed Work is © 2026 Marc Frost.
9
-
10
- Additional Use Grant:
11
- You may use the Licensed Work in production for any purpose, including
12
- commercially, EXCEPT for the Restricted Use.
13
-
14
- "Restricted Use" is defined as using the Licensed Work to provide a platform
15
- or service to third parties that allows them to host, deploy, or run their
16
- own applications built with the Licensed Work. This includes, but is not
17
- limited to: managed hosting services, software-as-a-service (SaaS) platforms,
18
- platform-as-a-service (PaaS), developer platforms, or any multi-tenant
19
- managed offering of the Licensed Work.
20
-
21
- This restriction does not apply to the Licensor, any entity controlled by,
22
- controlling, or under common control with the Licensor ("Affiliates"), or
23
- contractors acting on their behalf. The Licensor remains free to use the
24
- Licensed Work for any purpose, including for the operation of kumiko.rocks.
25
-
26
- Change Date: 2030-05-05
27
- Change License: Apache License, Version 2.0
28
-
29
-
30
- Terms
31
-
32
- The Licensor hereby grants you the right to copy, modify, create derivative works,
33
- redistribute, and make non-production use of the Licensed Work. The Licensor may
34
- make an Additional Use Grant, above, permitting limited production use.
35
-
36
- Effective on the Change Date, or the fourth anniversary of the first publicly
37
- available distribution of the Licensed Work under this License, whichever comes
38
- first, this License will convert to the Change License.
39
-
40
- This Business Source License governs use of the Licensed Work in all cases, except
41
- as to any use that is explicitly granted in the Additional Use Grant above or
42
- under the Change License after the Change Date.
43
-
44
- If your use of the Licensed Work does not comply with the requirements of this
45
- License, you must cease use of the Licensed Work immediately.
46
-
47
- All copies of the Licensed Work, and all derivative works thereof, must include
48
- this License.
49
-
50
- This License does not grant you any right, title, or interest in any trademark,
51
- logo, or branding of the Licensor, except as required to comply with this License.
52
-
53
- TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON AN
54
- “AS IS” BASIS. LICENSOR DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
55
- WITHOUT LIMITATION WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
56
- TITLE, AND NON-INFRINGEMENT. IN NO EVENT WILL LICENSOR BE LIABLE FOR ANY DAMAGES
57
- ARISING OUT OF OR RELATED TO THIS LICENSE OR THE USE OF THE LICENSED WORK.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Marc Frost
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @cosmicdrift/kumiko-framework
2
2
 
3
- [![License: BUSL-1.1](https://img.shields.io/badge/License-BUSL--1.1-blue.svg)](../../LICENSE)
3
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](../../LICENSE)
4
4
  [![TypeScript](https://img.shields.io/badge/TypeScript-strict-blue.svg)](https://www.typescriptlang.org/)
5
5
 
6
6
  Framework core for Kumiko — engine, pipeline, API, DB, event-store, and
@@ -131,4 +131,4 @@ sample under `samples/`; the roadmap lives in [docs/plans/uebersicht.md](../../d
131
131
 
132
132
  ## License
133
133
 
134
- BUSL-1.1 — see [LICENSE](../../LICENSE).
134
+ MIT, see [LICENSE](../../LICENSE).
package/agents/widgets.md CHANGED
@@ -16,11 +16,11 @@ Value exports of `@cosmicdrift/kumiko-renderer/web`.
16
16
  | `BareFormProvider` | |
17
17
  | `BooleanField` | Checkbox/Switch = Field(layout:"inline") + Input(kind:"boolean"). |
18
18
  | `Card` | |
19
- | `CollapsibleSection` | Aufklappbare Sektion (native <details>, aber React-kontrolliert: sonst springt das open-Attribut bei jedem Parent-Re-Render auf den Initialwert zurück). |
20
- | `ComparisonTable` | Transponierte Vergleichstabelle: Zeile = Kennzahl, Spalte = Variante, je Kennzahl optional die beste Spalte hervorgehoben. |
21
- | `DateField` | Datumsfeld = Field + Input(kind:"date"), ISO yyyy-mm-dd. |
19
+ | `CollapsibleSection` | Collapsible section using native <details> with React-controlled state; otherwise the open attribute resets to its initial value on every parent re-render. |
20
+ | `ComparisonTable` | Transposed comparison table: row = metric, column = variant, with the best column optionally highlighted for each metric. |
21
+ | `DateField` | Date field = Field + Input(kind:"date"), ISO yyyy-mm-dd. |
22
22
  | `DefaultAppShell` | |
23
- | `DetailList` | Read-only Schlüssel-Wert-Liste für Detail-Masken (Label links gedimmt, Wert rechts). |
23
+ | `DetailList` | Read-only key-value list for detail views (dimmed label on the left, value on the right). |
24
24
  | `DispatcherProvider` | |
25
25
  | `Drawer` | Slide-in panel beside a list (e.g. mail reader next to the inbox) — thin wrapper over the Sheet primitive with header/body/footer slots so screens skip per-route Radix boilerplate. |
26
26
  | `DropdownMenu` | |
@@ -32,10 +32,10 @@ Value exports of `@cosmicdrift/kumiko-renderer/web`.
32
32
  | `DropdownMenuSeparator` | |
33
33
  | `DropdownMenuTrigger` | |
34
34
  | `EditorPanel` | |
35
- | `EmptyState` | Leerer-Zustand mit optionalem Icon + CTA — die dashed-Box-Optik der entityList-Empty-States, als Standalone-Widget für Custom-Screens. |
36
- | `ErrorState` | Fehler-Zustand auf Basis des Banner-Primitives, mit Retry-Button. |
37
- | `FeedList` | Nicht-tabellarische Kurzliste (z.B. |
38
- | `FileField` | Datei-Upload = Field + Input(kind:"file"\|"image") — FileRef-basiert. |
35
+ | `EmptyState` | Empty state with an optional icon and CTA, using the dashed-box style of entityList empty states as a standalone widget for custom screens. |
36
+ | `ErrorState` | Error state based on the Banner primitive, with a retry button. |
37
+ | `FeedList` | Non-tabular compact list (e.g. "upcoming appointments") — primary text plus an optional right-aligned trailing value for each row. |
38
+ | `FileField` | File upload = Field + Input(kind:"file"\|"image"), based on FileRef. |
39
39
  | `FloatingPanel` | Non-modal panel floating above the page: draggable by its header, resizable from all edges and corners, clamped to the viewport, geometry optionally remembered. |
40
40
  | `FormScreenShell` | |
41
41
  | `HeaderActionGroup` | |
@@ -46,14 +46,14 @@ Value exports of `@cosmicdrift/kumiko-renderer/web`.
46
46
  | `LanguageMenuItems` | Locale choices as dropdown entries, for a user menu that carries the shell controls. |
47
47
  | `LanguageSwitcher` | |
48
48
  | `LiveEventsProvider` | |
49
- | `LoadingState` | Lade-Zustand: Skeleton-Zeilen in Card-Optik. |
49
+ | `LoadingState` | Loading state with skeleton rows styled like cards; `rows` controls the height. |
50
50
  | `LocaleProvider` | |
51
- | `MiniStat` | Kompakte Kennzahl-Kachel (ohne Icon) — für dichte KPI-Raster. |
52
- | `ModeSwitch` | Segmented-Control für sich ausschließende Modi — die prominente Alternative zum vergrabenen <select>. |
51
+ | `MiniStat` | Compact metric tile (without an icon) — for dense KPI grids. |
52
+ | `ModeSwitch` | Segmented control for mutually exclusive modes—the prominent alternative to the buried <select>. |
53
53
  | `MoneyField` | |
54
54
  | `NavProvider` | |
55
55
  | `NavTree` | |
56
- | `NumberField` | Zahlenfeld = Field + Input(kind:"number") in einem — nimmt der Screen die wiederholte id/name/value/onChange-Verdrahtung ab. |
56
+ | `NumberField` | Number field = Field + Input(kind:"number"); avoids repeating the id/name/value/onChange wiring at each screen. |
57
57
  | `PageSection` | |
58
58
  | `PdfPreview` | |
59
59
  | `PercentField` | |
@@ -64,22 +64,22 @@ Value exports of `@cosmicdrift/kumiko-renderer/web`.
64
64
  | `PrimitivesProvider` | |
65
65
  | `ProfileMenu` | |
66
66
  | `ProgressBar` | Progress bar, `value` 0..1 (clamped). |
67
- | `ProgressList` | Liste aus Label/Wert-Kopfzeile + Fortschrittsbalken pro Eintrag (z.B. |
67
+ | `ProgressList` | Label/value header and progress bar for each entry (for example, loan repayment progress). |
68
68
  | `PublicShell` | One header and footer for anonymous pages; `focus` narrows to a task column, `card` centers a single card (sign-in). |
69
69
  | `QueryTable` | |
70
- | `RangeField` | Schieberegler = Field + Input(kind:"range"). |
70
+ | `RangeField` | Slider = Field + Input(kind:"range"). |
71
71
  | `RenderEdit` | |
72
72
  | `RenderField` | |
73
73
  | `RenderList` | |
74
74
  | `ResizableHandle` | |
75
75
  | `ResizablePanel` | |
76
76
  | `ResizablePanelGroup` | |
77
- | `ResultPanel` | Ergebnis-Sektion eines Rechners: SectionCard mit Empty-Zustand (Banner) oder Kennzahl-Liste (DetailList) + optionalen Extras (Tabelle, Hinweise). |
78
- | `ResultTable` | Statische, prop-getriebene Ergebnistabelle für berechnete Zeilen (Tranchen, Szenarien). |
77
+ | `ResultPanel` | Result section for a calculator: SectionCard with an empty state (Banner) or a metric list (DetailList) plus optional extras (table, notes). |
78
+ | `ResultTable` | Static, prop-driven result table for calculated rows (tranches, scenarios). |
79
79
  | `RichContentEditor` | |
80
80
  | `ScreenWidthProvider` | |
81
- | `SectionCard` | Sektion mit Titel + optionalem Action-Slot (Range-Umschalter, Filter) — ersetzt die wiederholten `<section className={CARD}>…<h2>`-Blöcke in Custom-Screens. |
82
- | `SelectField` | Auswahlfeld = Field + Input(kind:"select"). |
81
+ | `SectionCard` | Section with a title and optional action slot (range switcher, filter) — replaces repeated `<section className={CARD}>…<h2>` blocks in custom screens. |
82
+ | `SelectField` | Select field = Field + Input(kind:"select"). |
83
83
  | `SideBySideTable` | Small read-only grid with real table semantics (`th scope`) whose cells are arbitrary React nodes — for side-by-side comparisons like "from the document" vs. |
84
84
  | `Sidebar` | |
85
85
  | `SidebarBrand` | |
@@ -91,15 +91,15 @@ Value exports of `@cosmicdrift/kumiko-renderer/web`.
91
91
  | `SidebarUser` | |
92
92
  | `Sparkline` | |
93
93
  | `Stack` | |
94
- | `StatCard` | Icon-tragende Kennzahl-Kachel mit optionalem Delta-Chip, Trend-Zeile und Sparkline. |
94
+ | `StatCard` | Icon-bearing metric tile with an optional delta chip, trend line, and sparkline. |
95
95
  | `StatusBadge` | Pill badge with a status dot. |
96
96
  | `StatusBarChart` | Status bar strip (e.g. 90-day uptime): variable-height bars with gradient fade + tick line on top; the last entry gets a "now" accent stripe. |
97
97
  | `StepBar` | Wizard step overview — numbered chips with a connector line between them. |
98
- | `TextField` | Textfeld = Field + Input(kind:"text"). |
99
- | `TextareaField` | Mehrzeiliges Textfeld = Field + Input(kind:"textarea"). |
98
+ | `TextField` | Text field = Field + Input(kind:"text"). |
99
+ | `TextareaField` | Multi-line text field = Field + Input(kind:"textarea"). |
100
100
  | `ThemeMenuItem` | Theme toggle as a dropdown entry, for a user menu that carries the shell controls. |
101
101
  | `ThemeToggle` | |
102
- | `TimeseriesChart` | Zeitreihen-Linien-Chart (geglättete Linie + Flächen-Verlauf). |
102
+ | `TimeseriesChart` | Time-series line chart (smoothed line with area fill). |
103
103
  | `ToastProvider` | |
104
104
  | `TokensProvider` | |
105
105
  | `Topbar` | |
@@ -112,16 +112,16 @@ Value exports of `@cosmicdrift/kumiko-renderer/web`.
112
112
 
113
113
  | Name | Description |
114
114
  | --- | --- |
115
- | `useBrowserNavApi` | React-Hook der eine NavApi aus der Browser-History baut. |
116
- | `useBrowserTokensApi` | Hook der eine TokensApi für den Browser baut. |
115
+ | `useBrowserNavApi` | React hook that builds a NavApi from browser history. |
116
+ | `useBrowserTokensApi` | Builds a TokensApi for the browser. |
117
117
  | `useDispatcher` | |
118
118
  | `useDispatcherStatus` | |
119
- | `useDraft` | Formular-State für Rechner-Screens: ein Draft-Objekt + `patch` für Teil-Updates + `field(name)` das die `{ id, name, value, onChange }`-Props fertig für die Feld-Widgets (NumberField/MoneyField/PercentField) liefert. |
119
+ | `useDraft` | Form state for calculator screens: a draft object plus `patch` for partial updates, and `field(name)` that provides `{ id, name, value, onChange }` props ready for field widgets (NumberField/MoneyField/PercentField). |
120
120
  | `useForm` | |
121
121
  | `useIsNarrowViewport` | |
122
122
  | `useIsViewportBelow` | |
123
- | `useLiveEvents` | Hook für useQuery-like Consumer. |
124
- | `useLocale` | Liefert den aktuellen LocaleResolver und abonniert automatisch Locale-Änderungen — der aufrufende Component re-rendert sobald die Sprache gewechselt wird. |
123
+ | `useLiveEvents` | Hook for a useQuery-like consumer. |
124
+ | `useLocale` | Provides the current LocaleResolver and automatically subscribes to locale changes — the calling component re-renders when the language changes. |
125
125
  | `useNav` | |
126
126
  | `usePrimitives` | |
127
127
  | `useQuery` | |
@@ -129,8 +129,8 @@ Value exports of `@cosmicdrift/kumiko-renderer/web`.
129
129
  | `useStore` | |
130
130
  | `useStoreSelector` | |
131
131
  | `useToast` | |
132
- | `useTokenController` | Volle API inkl. |
133
- | `useTokens` | Read-only Tokens-Hook. |
132
+ | `useTokenController` | Full API including theme toggling, for a dark/light switch button. |
133
+ | `useTokens` | Read-only tokens hook. |
134
134
  | `useTranslation` | `t("key", params)` resolves in this order: 1. |
135
135
  | `useUserRoles` | |
136
136
 
@@ -138,17 +138,17 @@ Value exports of `@cosmicdrift/kumiko-renderer/web`.
138
138
 
139
139
  | Name | Description |
140
140
  | --- | --- |
141
- | `applyTokensToCssVars` | Historisch: schrieb Tokens auf :root als CSS-vars. |
141
+ | `applyTokensToCssVars` | Historically wrote tokens to :root as CSS variables. |
142
142
  | `buildFileUploadFormData` | |
143
143
  | `buildNavRegistrySlice` | |
144
144
  | `buildNavRegistrySliceForApp` | |
145
- | `cn` | shadcn-Standard-Helper: clsx für konditionales Zusammenstecken, tailwind-merge für Konfliktauflösung (z.B. |
145
+ | `cn` | Standard shadcn helper: `clsx` combines conditional classes, while `tailwind-merge` resolves conflicts (for example, `px-2 px-4` → `px-4`). |
146
146
  | `createBrowserDraftStorage` | Default DraftStorage when createKumikoApp boots without one. |
147
- | `createBrowserLocaleResolver` | Default-Resolver wenn createKumikoApp ohne `locale`-Option gebootet wird. |
148
- | `createEventSourceLiveEvents` | Liefert einen `LiveEventSubscriber` der EventSource-backed ist. |
147
+ | `createBrowserLocaleResolver` | Default resolver when createKumikoApp is booted without a `locale` option. |
148
+ | `createEventSourceLiveEvents` | Returns an EventSource-backed `LiveEventSubscriber`. |
149
149
  | `createKumikoApp` | |
150
150
  | `createPublicSurface` | |
151
- | `createStaticLocaleResolver` | Default-Resolver für Apps ohne eigene i18n-Schicht. |
151
+ | `createStaticLocaleResolver` | Default resolver for apps without their own i18n layer. |
152
152
  | `downloadFileRef` | |
153
153
  | `fetchFileRefBlob` | |
154
154
  | `fileNameFromContentDisposition` | |
@@ -157,16 +157,16 @@ Value exports of `@cosmicdrift/kumiko-renderer/web`.
157
157
  | `formatMoney` | |
158
158
  | `formatPath` | |
159
159
  | `parsePath` | |
160
- | `parseTargetFromSearchParams` | Inverse — parse the current search-params back into a TargetRef. |
160
+ | `parseTargetFromSearchParams` | Parses the current search parameters back into a TargetRef. |
161
161
  | `postWithDownload` | |
162
162
  | `qualifyScreenId` | |
163
163
  | `readLoadedBuild` | |
164
164
  | `reparentAppSchemaNavs` | |
165
165
  | `resolveDefaultId` | |
166
166
  | `saveBlobAsFile` | |
167
- | `smoothPath` | Quadratic-durch-Mittelpunkte-Trick: glättet die Zick-Zack-Linie ohne Overshoot (~5 Zeilen statt Catmull-Rom/Bezier-Fit). |
168
- | `synthesizeActionFormEntity` | Baut eine minimale EntityDefinition aus den Inline-Fields eines ActionForm- oder SecretMint-Screens. |
169
- | `synthesizeActionFormScreen` | Wandelt ein ActionFormScreenDefinition oder SecretMintScreenDefinition in die EntityEditScreen-Shape die RenderEdit erwartet. |
167
+ | `smoothPath` | Quadratic midpoint technique: smooths the zigzag line without overshoot (~5 lines instead of a Catmull-Rom/Bezier fit). |
168
+ | `synthesizeActionFormEntity` | Builds a minimal EntityDefinition from the inline fields of an ActionForm or SecretMint screen. |
169
+ | `synthesizeActionFormScreen` | Converts an ActionFormScreenDefinition or SecretMintScreenDefinition to the EntityEditScreen shape expected by RenderEdit. |
170
170
  | `uploadFileRef` | Posts one file to the stack's file endpoint (double-submit CSRF) and returns the new FileRef id. |
171
171
 
172
172
  ## Constants
@@ -177,7 +177,7 @@ Value exports of `@cosmicdrift/kumiko-renderer/web`.
177
177
  | `STATUS_TONE_TEXT` | |
178
178
  | `cssVarTokens` | Tokens with var-string values. |
179
179
  | `defaultPrimitives` | |
180
- | `defaultTokens` | Default-Tokens — identisch zu `cssVarTokens` (var-string-refs). |
180
+ | `defaultTokens` | Default tokens—identical to `cssVarTokens` (variable-string references). |
181
181
  | `lightTokens` | |
182
182
 
183
183
  ## Classes
@@ -137,8 +137,8 @@ export const TENANT_HEADER_NAME = "X-Tenant";
137
137
  export const LOCALE_HEADER_NAME = "X-Locale";
138
138
  // Internal-only signal set by buildServer's app.notFound() to mark "no
139
139
  // route matched" 404s so tryHonoFirst (server-runtime) can tell them apart
140
- // from a matched route's own deliberate 404 (e.g. default-deny reads) —
141
- // see kumiko-framework#2435. Never reaches a client: tryHonoFirst and every
140
+ // from a matched route's own deliberate 404 (e.g. default-deny reads).
141
+ // Never reaches a client: tryHonoFirst and every
142
142
  // direct app.fetch() passthrough strip it before the response leaves the
143
143
  // process, since its presence would otherwise let a caller distinguish
144
144
  // "unregistered path" from "registered path, access denied" — a leak the
@@ -126,13 +126,13 @@ export function authMiddleware(jwt, options = {}) {
126
126
  // No JWT → either fall through as anonymous (when the server opts in)
127
127
  // or reject with 401 (preserving the pre-anonymous default).
128
128
  //
129
- // Auth-routes-Sonderfall: `/api/auth/*`-Pfade die NICHT in
130
- // PUBLIC_API_PATHS sind (tenants, switch-tenant, logout) brauchen
131
- // einen JWT — aber keinen Tenant-Resolve. Würden sie durch
132
- // handleAnonymous laufen, wirft resolveTenant 400 tenant_required
133
- // wenn kein Tenant declared ist. Falsche Diagnose: das Problem ist
134
- // missing authentication, nicht missing tenant. Daher hier direkt
135
- // 401, ohne den anonymous-Tenant-Flow zu durchlaufen.
129
+ // Special case for auth routes: `/api/auth/*` paths that are NOT in
130
+ // PUBLIC_API_PATHS (tenants, switch-tenant, logout) require a JWT —
131
+ // but not tenant resolution. If they went through handleAnonymous,
132
+ // resolveTenant would throw 400 tenant_required when no tenant is
133
+ // declared. That would be the wrong diagnosis: the problem is missing
134
+ // authentication, not a missing tenant. Therefore, return 401 here
135
+ // without entering the anonymous-tenant flow.
136
136
  if (anonymousAccess) {
137
137
  if (isSessionRequiredApiPath(c.req.path)) {
138
138
  return middlewareReject(c, {
@@ -93,10 +93,14 @@ export function createRedisLoginRateLimiter(redis, maxAttempts = 10, windowMs =
93
93
  // shipped (PTTL returns -1 for "no expiry set, key exists") — without
94
94
  // it, those pre-existing keys would grow forever with no way back to
95
95
  // a normal window.
96
- const count = (await redis.eval(`local c = redis.call('INCR', KEYS[1])
96
+ const count = await redis.eval(`local c = redis.call('INCR', KEYS[1])
97
97
  if c == 1 or redis.call('PTTL', KEYS[1]) < 0 then redis.call('PEXPIRE', KEYS[1], ARGV[1]) end
98
- return c`, 1, redisKey, windowMs));
99
- return count <= maxAttempts;
98
+ return c`, 1, redisKey, windowMs);
99
+ const attempts = typeof count === "string" && /^\d+$/.test(count) ? Number(count) : count;
100
+ if (typeof attempts !== "number" || !Number.isInteger(attempts)) {
101
+ throw new TypeError(`redis INCR script returned ${String(count)}, expected an integer`);
102
+ }
103
+ return attempts <= maxAttempts;
100
104
  },
101
105
  async reset(key) {
102
106
  await redis.del(`${prefix}${key}`);
@@ -130,7 +130,7 @@ export function registerMfaPreauthEnableStartRoute(ctx) {
130
130
  // Second axis, independent of the IP-derived bucket above: the
131
131
  // preauthSetupToken is minted by login.write.ts and not freely
132
132
  // choosable, so this cap survives an attacker rotating
133
- // x-forwarded-for to bypass the IP bucket (kumiko-framework#1522).
133
+ // x-forwarded-for to bypass the IP bucket.
134
134
  const tokenAllowed = await rateLimiter.check(preauthTokenKeyOf(body.preauthSetupToken));
135
135
  if (!tokenAllowed) {
136
136
  return c.json({ isSuccess: false, error: "rate_limited" }, 429);
@@ -121,7 +121,7 @@ export function registerSessionRoutes(ctx) {
121
121
  id: user.id,
122
122
  tenantId: targetTenantId,
123
123
  roles: sessionRoles,
124
- // Tenant-independent prefs must survive the switch (fw#2343).
124
+ // Tenant-independent prefs must survive the switch.
125
125
  ...tenantIndependentPrefs(user),
126
126
  };
127
127
  const claims = await dispatcher.resolveAuthClaims(targetSession);
@@ -2,8 +2,9 @@ import * as z from "zod";
2
2
  import { createAnonymousUser } from "../engine/system-user.js";
3
3
  import { SYSTEM_TENANT_ID } from "../engine/types/index.js";
4
4
  import { NotFoundError } from "../errors/index.js";
5
- // Cross-device try-first handover (kumiko-framework#3035 follow-up,
6
- // offlot-app#454): an optional tenant-handover grant the signup-request
5
+ import { createFallbackLogger } from "../logging/utils.js";
6
+ const log = createFallbackLogger("kumiko");
7
+ // Cross-device try-first handover: an optional tenant-handover grant the signup-request
7
8
  // route forwards to the handler for server-side verification and binding —
8
9
  // the grant itself never appears anywhere else (URL, mail, log, response).
9
10
  export const SignupRequestBody = z.object({
@@ -68,7 +69,10 @@ export function registerTokenRequestRoute(opts) {
68
69
  // rate_limited is an expected, attacker-triggerable outcome; logging it
69
70
  // would flood the sink and bury real misconfiguration errors.
70
71
  if (!result.isSuccess && result.error.code !== "rate_limited") {
71
- console.error(`[kumiko] token request handler "${opts.requestHandler}" failed: ${result.error.code}`);
72
+ log.error("token request handler failed", {
73
+ handler: opts.requestHandler,
74
+ code: result.error.code,
75
+ });
72
76
  }
73
77
  return c.json({ isSuccess: true });
74
78
  });
@@ -63,7 +63,7 @@ export function registerSignupRoutes(ctx) {
63
63
  // switcher), signup knows exactly ONE membership — the frontend UI uses
64
64
  // it directly as the redirect target.
65
65
  tenantKey: data.tenantKey,
66
- // Cross-device try-first handover (kumiko-framework#3035 follow-up):
66
+ // Cross-device try-first handover:
67
67
  // present only when signup-confirm actually claimed a bound grant —
68
68
  // absent, not null, so a client that doesn't know the field sees
69
69
  // nothing unusual.
@@ -1,3 +1,5 @@
1
+ import { createFallbackLogger } from "../logging/utils.js";
2
+ const log = createFallbackLogger("kumiko");
1
3
  // Single source for client-IP extraction, shared by every IP-keyed rate
2
4
  // limit and audit field. Deriving the caller IP independently per call site
3
5
  // makes it easy for one to silently trust the first XFF entry, which is
@@ -70,7 +72,7 @@ export function createClientIpResolver(trustedProxyHops, context = "resolveClien
70
72
  if (trustedProxyHops === 0) {
71
73
  if (xff !== undefined && xff.trim().length > 0 && !warnedUnexpectedForwardedFor) {
72
74
  warnedUnexpectedForwardedFor = true;
73
- console.warn("[kumiko] received X-Forwarded-For but trustedProxyHops is 0 (no proxy trusted) — " +
75
+ log.warn("received X-Forwarded-For but trustedProxyHops is 0 (no proxy trusted) — " +
74
76
  "ignoring it. Running behind a proxy? Set trustedProxyHops, otherwise all clients " +
75
77
  "share one rate-limit bucket.");
76
78
  }
@@ -1,5 +1,5 @@
1
1
  // ExtraRoute — declarative replacement for the old `extraRoutes: (app, deps)
2
- // => void` closure (kumiko-framework#3050). Every entry declares its access
2
+ // => void` closure. Every entry declares its access
3
3
  // tier (`entry`) up front; buildServer wires the matching guard + deps
4
4
  // instead of handing every route a raw db/redis escape hatch.
5
5
  export const ExtraRouteEntries = {
@@ -1,5 +1,7 @@
1
1
  import { PII_CIPHERTEXT_PREFIX } from "../crypto/index.js";
2
+ import { createFallbackLogger } from "../logging/utils.js";
2
3
  import { resolveRouteLabel } from "./route-label.js";
4
+ const log = createFallbackLogger("kumiko:pii-leak-guard");
3
5
  const isProductionEnv = () => process.env["NODE_ENV"] === "production";
4
6
  // Version-agnostic: catches both the current PII_CIPHERTEXT_PREFIX and any
5
7
  // older/decrypt-only format version still present in unmigrated rows.
@@ -28,7 +30,7 @@ export function piiCiphertextResponseGuard() {
28
30
  // skip: clean response — the common case
29
31
  if (!CIPHERTEXT_RE.test(text))
30
32
  return;
31
- const detail = `[api] JSON response for ${c.req.method} ${resolveRouteLabel(c)} contains a PII ciphertext ` +
33
+ const detail = `JSON response for ${c.req.method} ${resolveRouteLabel(c)} contains a PII ciphertext ` +
32
34
  `("${PII_CIPHERTEXT_PREFIX}…") — a raw DB read leaked to the API surface. ` +
33
35
  `Decrypt before returning (decryptStoredPii / executor read path).`;
34
36
  if (!isProductionEnv()) {
@@ -36,7 +38,7 @@ export function piiCiphertextResponseGuard() {
36
38
  // skip: response replaced with the loud 500 above — nothing left to do
37
39
  return;
38
40
  }
39
- console.error(detail);
41
+ log.error(detail);
40
42
  const headers = new Headers(c.res.headers);
41
43
  headers.delete("content-length");
42
44
  c.res = new Response(text.replace(CIPHERTEXT_REDACT_RE, "[pii-redacted]"), {
@@ -74,11 +74,11 @@ export function redisPingCheck(redis) {
74
74
  },
75
75
  };
76
76
  }
77
- // Lag-Check für den Async-Event-Dispatcher. Liest HWM + Consumer-Cursor aus
78
- // der DB — kein extra Redis/Runtime-State. Fail-Schwelle ist in EventIds
79
- // (bigint), weil das die natürliche Einheit ist (events sind monoton id'd).
80
- // Ein Tuning-Beispiel: maxLagEvents = 1_000 bedeutet "wenn die Projection
81
- // mehr als 1k Events hinter HWM ist, stoppe neue Traffic-Zuweisung".
77
+ // Lag check for the asynchronous event dispatcher. Reads the HWM and consumer cursor from
78
+ // the database, without relying on extra Redis or runtime state. The failure threshold is
79
+ // measured in event IDs (`bigint`), the natural unit because events have monotonically
80
+ // increasing IDs. For example, `maxLagEvents = 1_000` stops routing new traffic when the
81
+ // projection falls more than 1,000 events behind the HWM.
82
82
  export function dispatcherLagCheck(db, consumerNames, maxLagEvents) {
83
83
  return {
84
84
  name: "dispatcher_lag",
@@ -1,8 +1,10 @@
1
1
  import { isPlainObject } from "@cosmicdrift/kumiko-types/type-guards";
2
+ import { createFallbackLogger } from "../logging/utils.js";
2
3
  import { redisChannelPrefixFromEnv, redisClientOptionsFromEnv, } from "../redis/client.js";
3
4
  import { createRedisPubSubSignal } from "../redis/pubsub-signal.js";
4
5
  import { createSseBroker, readNonEmptyStringArray, } from "./sse-broker.js";
5
- // Channel namespace for cross-replica fanout (fw#2625). Every pod publishes
6
+ const log = createFallbackLogger("kumiko:sse-broker");
7
+ // Channel namespace for cross-replica fanout. Every pod publishes
6
8
  // here and every pod's psubscribe listens here, so a push on one instance
7
9
  // reaches SSE clients connected to any other instance without a shared
8
10
  // cursor — Pub/Sub for connected-client fanout, the event-store's shared
@@ -21,9 +23,9 @@ export function isRedisSseBroker(broker) {
21
23
  // runProdApp must funnel through this rather than each deciding on their
22
24
  // own — runProdApp doing exactly that (building an in-memory broker
23
25
  // unconditionally, then passing it in as ServerOptions.sseBroker) is why
24
- // fw#2625's fanout fix (fw#2630) shipped without ever taking effect in
25
- // production: an explicit ServerOptions.sseBroker always wins in
26
- // buildServer, so its own REDIS_URL-based default never ran.
26
+ // the fanout fix shipped without ever taking effect in production: an
27
+ // explicit ServerOptions.sseBroker always wins in buildServer, so its own
28
+ // REDIS_URL-based default never ran.
27
29
  export function createDefaultSseBroker(env = process.env) {
28
30
  const redisUrl = env["REDIS_URL"];
29
31
  const ownedRedisSseBroker = redisUrl
@@ -109,7 +111,7 @@ export function createRedisSseBroker(opts) {
109
111
  signal.onMessage((channel, payload) => {
110
112
  if (channel.startsWith(channelPrefix)) {
111
113
  if (!isSseEvent(payload)) {
112
- console.error(`[kumiko:sse-broker] dropping malformed event on "${channel}"`);
114
+ log.error(`dropping malformed event on "${channel}"`);
113
115
  // skip: malformed payload already logged above, nothing to deliver
114
116
  return;
115
117
  }
@@ -132,7 +134,7 @@ export function createRedisSseBroker(opts) {
132
134
  pushToChannel(channel, event) {
133
135
  return signal.publishConfirmed(`${channelPrefix}${channel}`, event);
134
136
  },
135
- // fw#1601: this is the security-critical call — a revoked session's
137
+ // This is the security-critical call — a revoked session's
136
138
  // stream must close on every replica, not just the one that observed
137
139
  // the revocation event. Publishing (rather than calling inner directly,
138
140
  // like the in-memory broker does) is what makes that true here.
@@ -21,6 +21,7 @@ export type RequestContextData = {
21
21
  readonly risk: AgentRisk;
22
22
  };
23
23
  readonly chargedRateLimitBuckets?: Set<string>;
24
+ readonly agentToolCall?: true;
24
25
  };
25
26
  export declare const requestContext: {
26
27
  run<T>(data: RequestContextData, fn: () => T): T;
@@ -41,4 +42,5 @@ export declare function runWithOrigin<T>(origin: {
41
42
  };
42
43
  }, fn: () => T): T;
43
44
  export declare function runAsDirectCallEntry<T>(fn: () => T): T;
45
+ export declare function isAgentToolCallScope(): boolean;
44
46
  export declare function runWithWriteOrigin<T>(origin: WriteOrigin, fn: () => T): T;
@@ -48,15 +48,24 @@ export function runWithOrigin(origin, fn) {
48
48
  : new Set(),
49
49
  }, fn);
50
50
  }
51
- // Clears entryHandler so the next dispatch inside fn becomes the new entry —
52
- // used by dispatchToolCall so the invoked tool handler, not the surrounding
53
- // turn/approve handler, is what the irreversible-operation gate sees.
51
+ // Clears entryHandler so the next dispatch inside fn becomes the new entry, letting
52
+ // the irreversible-operation gate see the invoked tool handler instead of the
53
+ // surrounding turn/approve handler. Reserved for dispatchToolCall, so it also marks
54
+ // the scope as an agent tool call; it always mints a scope, because an agent loop
55
+ // running outside any request must still carry that marker.
54
56
  export function runAsDirectCallEntry(fn) {
55
57
  const current = requestContext.get();
56
- if (!current)
57
- return fn();
58
- const { entryHandler: _entryHandler, ...rest } = current;
59
- return requestContext.run(rest, fn);
58
+ const requestId = current?.requestId ?? requestContext.generateId();
59
+ const { entryHandler: _entryHandler, ...rest } = current ?? {};
60
+ return requestContext.run({
61
+ ...rest,
62
+ requestId,
63
+ correlationId: current?.correlationId ?? requestId,
64
+ agentToolCall: true,
65
+ }, fn);
66
+ }
67
+ export function isAgentToolCallScope() {
68
+ return requestContext.get()?.agentToolCall === true;
60
69
  }
61
70
  // An ungated origin never mints a scope: seeds and boot writes keep their missing context.
62
71
  export function runWithWriteOrigin(origin, fn) {
@@ -129,7 +129,7 @@ export function createApiRoutes(dispatcher, options = {}) {
129
129
  // dispatch gates (feature/rate-limit/access/validation) plus the handler's
130
130
  // first yield — is raced against a heartbeat timeout BEFORE streamSSE, so
131
131
  // a gate failure that settles in time maps to its real HTTP status via
132
- // queryErrorResponse instead of a flushed-200 error frame (framework#1517).
132
+ // queryErrorResponse instead of a flushed-200 error frame.
133
133
  api.post(Routes.stream, async (c) => {
134
134
  const user = getUser(c);
135
135
  const body = await readDispatchBody(c);
@@ -149,7 +149,7 @@ export function createApiRoutes(dispatcher, options = {}) {
149
149
  // Hook the raw request signal directly so a client disconnect during the
150
150
  // pre-pull still reclaims the generator instead of leaking a Redis
151
151
  // subscription/DB cursor held open inside its still-unresolved first
152
- // `.next()` (framework#1528).
152
+ // `.next()`.
153
153
  const signal = c.req.raw.signal;
154
154
  const onPrePullAbort = () => void generator.return(undefined);
155
155
  signal.addEventListener("abort", onPrePullAbort);