pyric 0.1.0-alpha.19 → 0.1.0-alpha.21

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 (281) hide show
  1. package/README.md +64 -89
  2. package/dist/ai/blocked.d.ts +52 -0
  3. package/dist/ai/blocked.d.ts.map +1 -0
  4. package/dist/ai/blocked.js +83 -0
  5. package/dist/ai/blocked.js.map +1 -0
  6. package/dist/ai/broker/broker.d.ts +50 -0
  7. package/dist/ai/broker/broker.d.ts.map +1 -1
  8. package/dist/ai/broker/broker.js +157 -21
  9. package/dist/ai/broker/broker.js.map +1 -1
  10. package/dist/ai/broker/gemini-engine.d.ts +56 -0
  11. package/dist/ai/broker/gemini-engine.d.ts.map +1 -0
  12. package/dist/ai/broker/gemini-engine.js +235 -0
  13. package/dist/ai/broker/gemini-engine.js.map +1 -0
  14. package/dist/ai/broker/index.d.ts +2 -1
  15. package/dist/ai/broker/index.d.ts.map +1 -1
  16. package/dist/ai/broker/index.js +2 -1
  17. package/dist/ai/broker/index.js.map +1 -1
  18. package/dist/ai/broker/openai-engine.d.ts +10 -1
  19. package/dist/ai/broker/openai-engine.d.ts.map +1 -1
  20. package/dist/ai/broker/openai-engine.js +26 -4
  21. package/dist/ai/broker/openai-engine.js.map +1 -1
  22. package/dist/ai/broker/synthesizer.d.ts +14 -0
  23. package/dist/ai/broker/synthesizer.d.ts.map +1 -1
  24. package/dist/ai/broker/synthesizer.js +31 -0
  25. package/dist/ai/broker/synthesizer.js.map +1 -1
  26. package/dist/ai/broker/types.d.ts +29 -0
  27. package/dist/ai/broker/types.d.ts.map +1 -1
  28. package/dist/ai/instances.js +1 -1
  29. package/dist/ai/instances.js.map +1 -1
  30. package/dist/ai/internal.d.ts +5 -0
  31. package/dist/ai/internal.d.ts.map +1 -1
  32. package/dist/ai/internal.js +5 -0
  33. package/dist/ai/internal.js.map +1 -1
  34. package/dist/ai/response-helpers.d.ts.map +1 -1
  35. package/dist/ai/response-helpers.js +6 -24
  36. package/dist/ai/response-helpers.js.map +1 -1
  37. package/dist/analytics/index.d.ts +40 -0
  38. package/dist/analytics/index.d.ts.map +1 -0
  39. package/dist/analytics/index.js +25 -0
  40. package/dist/analytics/index.js.map +1 -0
  41. package/dist/app-check/index.d.ts +31 -0
  42. package/dist/app-check/index.d.ts.map +1 -0
  43. package/dist/app-check/index.js +20 -0
  44. package/dist/app-check/index.js.map +1 -0
  45. package/dist/auth/instances.js +1 -1
  46. package/dist/auth/instances.js.map +1 -1
  47. package/dist/database/controls.d.ts +1 -1
  48. package/dist/database/controls.js +1 -1
  49. package/dist/database/instances.js +1 -1
  50. package/dist/database/instances.js.map +1 -1
  51. package/dist/database/sandbox/backend.d.ts +1 -0
  52. package/dist/database/sandbox/backend.d.ts.map +1 -1
  53. package/dist/database/sandbox/backend.js +1 -0
  54. package/dist/database/sandbox/backend.js.map +1 -1
  55. package/dist/database/sandbox/rules-eval.d.ts +6 -2
  56. package/dist/database/sandbox/rules-eval.d.ts.map +1 -1
  57. package/dist/database/sandbox/rules-eval.js +15 -2
  58. package/dist/database/sandbox/rules-eval.js.map +1 -1
  59. package/dist/database/sandbox/write-plane.d.ts +1 -0
  60. package/dist/database/sandbox/write-plane.d.ts.map +1 -1
  61. package/dist/database/sandbox/write-plane.js +7 -2
  62. package/dist/database/sandbox/write-plane.js.map +1 -1
  63. package/dist/database/sandbox-controls.d.ts +12 -5
  64. package/dist/database/sandbox-controls.d.ts.map +1 -1
  65. package/dist/database/sandbox-controls.js +20 -9
  66. package/dist/database/sandbox-controls.js.map +1 -1
  67. package/dist/database/sandbox-namespace.d.ts +19 -1
  68. package/dist/database/sandbox-namespace.d.ts.map +1 -1
  69. package/dist/database/sandbox-namespace.js +24 -1
  70. package/dist/database/sandbox-namespace.js.map +1 -1
  71. package/dist/deferred/entry.d.ts +75 -0
  72. package/dist/deferred/entry.d.ts.map +1 -0
  73. package/dist/deferred/entry.js +143 -0
  74. package/dist/deferred/entry.js.map +1 -0
  75. package/dist/firestore/instances.js +1 -1
  76. package/dist/firestore/instances.js.map +1 -1
  77. package/dist/firestore/lite.d.ts +72 -0
  78. package/dist/firestore/lite.d.ts.map +1 -0
  79. package/dist/firestore/lite.js +22 -0
  80. package/dist/firestore/lite.js.map +1 -0
  81. package/dist/firestore/persistence.d.ts +2 -2
  82. package/dist/firestore/persistence.js +1 -1
  83. package/dist/firestore/sandbox-controls.d.ts +2 -0
  84. package/dist/firestore/sandbox-controls.d.ts.map +1 -1
  85. package/dist/firestore/sandbox-controls.js +8 -0
  86. package/dist/firestore/sandbox-controls.js.map +1 -1
  87. package/dist/functions/index.d.ts +29 -0
  88. package/dist/functions/index.d.ts.map +1 -0
  89. package/dist/functions/index.js +20 -0
  90. package/dist/functions/index.js.map +1 -0
  91. package/dist/messaging/client.d.ts +2 -1
  92. package/dist/messaging/client.d.ts.map +1 -1
  93. package/dist/messaging/client.js +2 -2
  94. package/dist/messaging/client.js.map +1 -1
  95. package/dist/messaging/instance.d.ts +2 -2
  96. package/dist/messaging/instance.d.ts.map +1 -1
  97. package/dist/messaging/instance.js +40 -6
  98. package/dist/messaging/instance.js.map +1 -1
  99. package/dist/messaging/sw.d.ts +2 -1
  100. package/dist/messaging/sw.d.ts.map +1 -1
  101. package/dist/messaging/sw.js +2 -2
  102. package/dist/messaging/sw.js.map +1 -1
  103. package/dist/performance/index.d.ts +22 -0
  104. package/dist/performance/index.d.ts.map +1 -0
  105. package/dist/performance/index.js +19 -0
  106. package/dist/performance/index.js.map +1 -0
  107. package/dist/remote-config/index.d.ts +40 -0
  108. package/dist/remote-config/index.d.ts.map +1 -0
  109. package/dist/remote-config/index.js +25 -0
  110. package/dist/remote-config/index.js.map +1 -0
  111. package/dist/rules/grammar/FirestoreValidator.d.ts.map +1 -1
  112. package/dist/rules/grammar/FirestoreValidator.js +43 -27
  113. package/dist/rules/grammar/FirestoreValidator.js.map +1 -1
  114. package/dist/rules/grammar/document-access-count.d.ts +24 -0
  115. package/dist/rules/grammar/document-access-count.d.ts.map +1 -0
  116. package/dist/rules/grammar/document-access-count.js +100 -0
  117. package/dist/rules/grammar/document-access-count.js.map +1 -0
  118. package/dist/rules/linter/ast-utils.d.ts +2 -6
  119. package/dist/rules/linter/ast-utils.d.ts.map +1 -1
  120. package/dist/rules/linter/ast-utils.js +2 -63
  121. package/dist/rules/linter/ast-utils.js.map +1 -1
  122. package/dist/rules/linter/hallucinations.d.ts +8 -0
  123. package/dist/rules/linter/hallucinations.d.ts.map +1 -1
  124. package/dist/rules/linter/hallucinations.js +121 -31
  125. package/dist/rules/linter/hallucinations.js.map +1 -1
  126. package/dist/rules/linter/linter.d.ts +10 -1
  127. package/dist/rules/linter/linter.d.ts.map +1 -1
  128. package/dist/rules/linter/linter.js +23 -9
  129. package/dist/rules/linter/linter.js.map +1 -1
  130. package/dist/rules/rtdb/grammar/RtdbExpr.ohm +5 -2
  131. package/dist/rules/rtdb/grammar/RtdbExpr.ohm.generated.d.ts +1 -1
  132. package/dist/rules/rtdb/grammar/RtdbExpr.ohm.generated.d.ts.map +1 -1
  133. package/dist/rules/rtdb/grammar/RtdbExpr.ohm.generated.js +5 -2
  134. package/dist/rules/rtdb/grammar/RtdbExpr.ohm.generated.js.map +1 -1
  135. package/dist/rules/rtdb/grammar/simulator.d.ts.map +1 -1
  136. package/dist/rules/rtdb/grammar/simulator.js +61 -15
  137. package/dist/rules/rtdb/grammar/simulator.js.map +1 -1
  138. package/dist/rules/rtdb/simulation/handler.d.ts.map +1 -1
  139. package/dist/rules/rtdb/simulation/handler.js +96 -35
  140. package/dist/rules/rtdb/simulation/handler.js.map +1 -1
  141. package/dist/rules/simulator/document-lookups.d.ts +4 -0
  142. package/dist/rules/simulator/document-lookups.d.ts.map +1 -1
  143. package/dist/rules/simulator/document-lookups.js +42 -3
  144. package/dist/rules/simulator/document-lookups.js.map +1 -1
  145. package/dist/rules/simulator/eval-error.d.ts +11 -0
  146. package/dist/rules/simulator/eval-error.d.ts.map +1 -1
  147. package/dist/rules/simulator/eval-error.js +14 -0
  148. package/dist/rules/simulator/eval-error.js.map +1 -1
  149. package/dist/rules/simulator/evaluation-builtins.d.ts.map +1 -1
  150. package/dist/rules/simulator/evaluation-builtins.js +33 -8
  151. package/dist/rules/simulator/evaluation-builtins.js.map +1 -1
  152. package/dist/rules/simulator/evaluation-context.d.ts +12 -0
  153. package/dist/rules/simulator/evaluation-context.d.ts.map +1 -1
  154. package/dist/rules/simulator/evaluator.d.ts +1 -1
  155. package/dist/rules/simulator/evaluator.d.ts.map +1 -1
  156. package/dist/rules/simulator/evaluator.js +16 -4
  157. package/dist/rules/simulator/evaluator.js.map +1 -1
  158. package/dist/rules/simulator/handler.d.ts.map +1 -1
  159. package/dist/rules/simulator/handler.js +52 -5
  160. package/dist/rules/simulator/handler.js.map +1 -1
  161. package/dist/rules/simulator/lookup-budget.d.ts +37 -0
  162. package/dist/rules/simulator/lookup-budget.d.ts.map +1 -0
  163. package/dist/rules/simulator/lookup-budget.js +96 -0
  164. package/dist/rules/simulator/lookup-budget.js.map +1 -0
  165. package/dist/rules/stdlib-modules.d.ts +7 -0
  166. package/dist/rules/stdlib-modules.d.ts.map +1 -1
  167. package/dist/rules/stdlib-modules.js +11 -11
  168. package/dist/rules/stdlib-modules.js.map +1 -1
  169. package/dist/sandbox/admin-firestore/get-firestore.d.ts +6 -0
  170. package/dist/sandbox/admin-firestore/get-firestore.d.ts.map +1 -1
  171. package/dist/sandbox/admin-firestore/get-firestore.js +22 -3
  172. package/dist/sandbox/admin-firestore/get-firestore.js.map +1 -1
  173. package/dist/sandbox/index.d.ts +1 -1
  174. package/dist/sandbox/index.d.ts.map +1 -1
  175. package/dist/sandbox/index.js +1 -1
  176. package/dist/sandbox/index.js.map +1 -1
  177. package/dist/sandbox/persistence/types.d.ts +1 -1
  178. package/dist/sandbox/remote.d.ts +2 -2
  179. package/dist/sandbox/sandbox-context.d.ts +8 -1
  180. package/dist/sandbox/sandbox-context.d.ts.map +1 -1
  181. package/dist/sandbox/sandbox-context.js +59 -8
  182. package/dist/sandbox/sandbox-context.js.map +1 -1
  183. package/dist/sandbox/types/auth-state.d.ts +1 -0
  184. package/dist/sandbox/types/auth-state.d.ts.map +1 -1
  185. package/dist/sandbox/types/events.d.ts +2 -1
  186. package/dist/sandbox/types/events.d.ts.map +1 -1
  187. package/dist/sandbox/types/operation.d.ts +1 -0
  188. package/dist/sandbox/types/operation.d.ts.map +1 -1
  189. package/dist/storage/enforce.d.ts +20 -1
  190. package/dist/storage/enforce.d.ts.map +1 -1
  191. package/dist/storage/enforce.js +47 -14
  192. package/dist/storage/enforce.js.map +1 -1
  193. package/dist/storage/index.d.ts +1 -1
  194. package/dist/storage/index.d.ts.map +1 -1
  195. package/dist/storage/list.js +1 -1
  196. package/dist/storage/list.js.map +1 -1
  197. package/dist/storage/persistence.d.ts.map +1 -1
  198. package/dist/storage/persistence.js +66 -14
  199. package/dist/storage/persistence.js.map +1 -1
  200. package/dist/storage/sandbox/rules-evaluation-error.d.ts +25 -0
  201. package/dist/storage/sandbox/rules-evaluation-error.d.ts.map +1 -1
  202. package/dist/storage/sandbox/rules-evaluation-error.js +29 -0
  203. package/dist/storage/sandbox/rules-evaluation-error.js.map +1 -1
  204. package/dist/storage/sandbox/rules-evaluator.d.ts +10 -5
  205. package/dist/storage/sandbox/rules-evaluator.d.ts.map +1 -1
  206. package/dist/storage/sandbox/rules-evaluator.js +105 -33
  207. package/dist/storage/sandbox/rules-evaluator.js.map +1 -1
  208. package/dist/storage/sandbox/rules-methods.d.ts.map +1 -1
  209. package/dist/storage/sandbox/rules-methods.js +17 -7
  210. package/dist/storage/sandbox/rules-methods.js.map +1 -1
  211. package/dist/storage/service.d.ts +23 -2
  212. package/dist/storage/service.d.ts.map +1 -1
  213. package/dist/storage/service.js +37 -26
  214. package/dist/storage/service.js.map +1 -1
  215. package/package.json +42 -2
  216. package/src/ai/blocked.ts +97 -0
  217. package/src/ai/broker/broker.ts +153 -22
  218. package/src/ai/broker/gemini-engine.ts +311 -0
  219. package/src/ai/broker/index.ts +2 -0
  220. package/src/ai/broker/openai-engine.ts +31 -3
  221. package/src/ai/broker/synthesizer.ts +34 -0
  222. package/src/ai/broker/types.ts +27 -1
  223. package/src/ai/instances.ts +1 -1
  224. package/src/ai/internal.ts +5 -0
  225. package/src/ai/response-helpers.ts +6 -25
  226. package/src/analytics/index.ts +53 -0
  227. package/src/app/dispatch.test.ts +3 -1
  228. package/src/app-check/index.ts +41 -0
  229. package/src/auth/instances.ts +1 -1
  230. package/src/database/controls.ts +1 -1
  231. package/src/database/instances.ts +1 -1
  232. package/src/database/sandbox/backend.ts +1 -0
  233. package/src/database/sandbox/rules-eval.ts +18 -2
  234. package/src/database/sandbox/write-plane.ts +8 -2
  235. package/src/database/sandbox-controls.ts +29 -9
  236. package/src/database/sandbox-namespace.ts +28 -1
  237. package/src/deferred/entry.ts +167 -0
  238. package/src/firestore/instances.ts +1 -1
  239. package/src/firestore/lite.ts +91 -0
  240. package/src/firestore/persistence.ts +2 -2
  241. package/src/firestore/sandbox-controls.ts +10 -0
  242. package/src/functions/index.ts +38 -0
  243. package/src/messaging/client.ts +3 -2
  244. package/src/messaging/instance.ts +44 -7
  245. package/src/messaging/sw.ts +3 -2
  246. package/src/performance/index.ts +31 -0
  247. package/src/remote-config/index.ts +53 -0
  248. package/src/rules/grammar/FirestoreValidator.ts +44 -28
  249. package/src/rules/grammar/document-access-count.ts +75 -0
  250. package/src/rules/linter/ast-utils.ts +2 -36
  251. package/src/rules/linter/hallucinations.ts +123 -25
  252. package/src/rules/linter/linter.ts +32 -10
  253. package/src/rules/rtdb/grammar/RtdbExpr.ohm +5 -2
  254. package/src/rules/rtdb/grammar/RtdbExpr.ohm.generated.ts +5 -2
  255. package/src/rules/rtdb/grammar/simulator.ts +41 -15
  256. package/src/rules/rtdb/simulation/handler.ts +149 -46
  257. package/src/rules/simulator/document-lookups.ts +46 -3
  258. package/src/rules/simulator/eval-error.ts +16 -0
  259. package/src/rules/simulator/evaluation-builtins.ts +35 -8
  260. package/src/rules/simulator/evaluation-context.ts +12 -0
  261. package/src/rules/simulator/evaluator.ts +14 -3
  262. package/src/rules/simulator/handler.ts +68 -4
  263. package/src/rules/simulator/lookup-budget.ts +101 -0
  264. package/src/rules/stdlib-modules.ts +19 -11
  265. package/src/sandbox/admin-firestore/get-firestore.ts +19 -3
  266. package/src/sandbox/index.ts +5 -1
  267. package/src/sandbox/persistence/types.ts +1 -1
  268. package/src/sandbox/remote.ts +2 -2
  269. package/src/sandbox/sandbox-context.ts +69 -9
  270. package/src/sandbox/types/auth-state.ts +1 -1
  271. package/src/sandbox/types/events.ts +2 -1
  272. package/src/sandbox/types/operation.ts +1 -1
  273. package/src/storage/enforce.ts +53 -14
  274. package/src/storage/index.ts +1 -1
  275. package/src/storage/list.ts +1 -1
  276. package/src/storage/persistence.ts +74 -16
  277. package/src/storage/sandbox/rules-evaluation-error.ts +32 -0
  278. package/src/storage/sandbox/rules-evaluator.ts +109 -32
  279. package/src/storage/sandbox/rules-methods.ts +21 -7
  280. package/src/storage/service.ts +93 -27
  281. package/README.md.orig +0 -53
@@ -45,14 +45,15 @@ export function storageAuth(target) {
45
45
  export class StorageService {
46
46
  backend;
47
47
  rules;
48
- constructor(backend, rules = null) {
48
+ crossServiceIam;
49
+ constructor(backend, rules = null, crossServiceIam = 'granted') {
49
50
  this.backend = backend;
50
51
  this.rules = rules;
52
+ this.crossServiceIam = crossServiceIam;
51
53
  }
52
54
  }
53
- // ─── Caches ─────────────────────────────────────────────────────────
54
- /** One `StorageService` per `Sandbox`. */
55
- const SERVICES = new WeakMap();
55
+ /** One open storage service, with its opening configuration, per `Sandbox`. */
56
+ const OPEN_SERVICES = new WeakMap();
56
57
  /** One sandbox `FirebaseStorage` handle per `SandboxContext`. */
57
58
  const SANDBOX_HANDLES = new WeakMap();
58
59
  /**
@@ -65,15 +66,25 @@ const SANDBOX_HANDLES = new WeakMap();
65
66
  */
66
67
  const BARE_SANDBOX_HANDLES = new WeakMap();
67
68
  /**
68
- * The rules SOURCE each sandbox's service was opened with (`null` when
69
- * opened without rules). Late-config detection: rules are honored only on
70
- * the FIRST storage call per `Sandbox`, so silently discarding a LATER,
71
- * DIFFERENT `rules` option would be a silent rules wipe — {@link
72
- * ensureService} throws instead. Re-supplying the IDENTICAL source stays
73
- * fine (idempotent multi-handle construction, e.g. per-user contexts).
69
+ * Reject a late option that differs from the one the service is already
70
+ * open with. Both `rules` and `crossServiceIam` are honored only on the
71
+ * FIRST storage call per `Sandbox`. Silently discarding a later, differing
72
+ * value would wipe the rules, or reopen the false-ALLOW surface the IAM
73
+ * mode models, without a word. Re-supplying the IDENTICAL value stays fine
74
+ * (idempotent multi-handle construction, for example per-user contexts),
75
+ * and so does omitting it.
74
76
  */
75
- const SERVICE_RULES_SOURCE = new WeakMap();
76
- const SERVICE_RULES_RESOLUTION = new WeakMap();
77
+ function rejectDifferingLateConfig(option, descriptor, supplied, openedWith, caller) {
78
+ if (supplied === undefined || supplied === openedWith)
79
+ return;
80
+ const openedDescription = openedWith === null
81
+ ? `without ${option}`
82
+ : `with a different ${descriptor}`;
83
+ throw new Error(`pyric/storage: ${caller} received a ${descriptor}, but this sandbox's storage service ` +
84
+ `is already open ${openedDescription}. ${option} is honored only on the FIRST storage ` +
85
+ 'call per Sandbox, so this value would be silently discarded. Configure it on the first ' +
86
+ 'storage call for this sandbox.');
87
+ }
77
88
  /**
78
89
  * Get (or open) the ONE per-sandbox `StorageService`. Loud on the
79
90
  * silent-rules-wipe hazard: when the service is already open and the
@@ -82,16 +93,11 @@ const SERVICE_RULES_RESOLUTION = new WeakMap();
82
93
  * on the first storage call for the sandbox instead.
83
94
  */
84
95
  function ensureService(sandbox, options, caller) {
85
- const existing = SERVICES.get(sandbox);
96
+ const existing = OPEN_SERVICES.get(sandbox);
86
97
  if (existing) {
87
- const openedWith = SERVICE_RULES_SOURCE.get(sandbox) ?? null;
88
- if (options.rules !== undefined && options.rules !== openedWith) {
89
- throw new Error(`pyric/storage: ${caller} received a rules source, but this sandbox's storage ` +
90
- `service is already open ${openedWith === null ? 'without rules' : 'with a different rules source'} ` +
91
- '— rules are honored only on the FIRST storage call per Sandbox, so these rules ' +
92
- 'would be silently discarded. Configure rules on the first storage call for this sandbox.');
93
- }
94
- return existing;
98
+ rejectDifferingLateConfig('rules', 'rules source', options.rules, existing.rulesSource, caller);
99
+ rejectDifferingLateConfig('crossServiceIam', 'crossServiceIam mode', options.crossServiceIam, existing.crossServiceIam, caller);
100
+ return existing.service;
95
101
  }
96
102
  let rules = null;
97
103
  let resolution = null;
@@ -116,10 +122,14 @@ function ensureService(sandbox, options, caller) {
116
122
  }
117
123
  // Explicit dbName wins; otherwise scope the default by project identity so
118
124
  // two projects on one origin never share a storage database (issue #359).
119
- const servicePromise = openStorageBackend(options.dbName ?? storageDbName(options.projectId)).then((backend) => new StorageService(backend, rules));
120
- SERVICES.set(sandbox, servicePromise);
121
- SERVICE_RULES_SOURCE.set(sandbox, options.rules ?? null);
122
- SERVICE_RULES_RESOLUTION.set(sandbox, resolution);
125
+ const crossServiceIam = options.crossServiceIam ?? 'granted';
126
+ const servicePromise = openStorageBackend(options.dbName ?? storageDbName(options.projectId)).then((backend) => new StorageService(backend, rules, crossServiceIam));
127
+ OPEN_SERVICES.set(sandbox, {
128
+ service: servicePromise,
129
+ rulesSource: options.rules ?? null,
130
+ rulesResolution: resolution,
131
+ crossServiceIam,
132
+ });
123
133
  // Join the sandbox's persistable-service REGISTRY so `sandbox.resetAll()`
124
134
  // reaches storage (issue #359: Studio's reset cleared Firestore + auth but
125
135
  // never storage — storage was invisible to the sandbox). Storage does NOT
@@ -176,6 +186,7 @@ export function getStorageSandbox(target, options = {}) {
176
186
  kind: 'sandbox',
177
187
  sandbox,
178
188
  context: ctx,
189
+ currentAuth: fromBareSandbox ? () => sandbox.currentUser : undefined,
179
190
  bucket,
180
191
  servicePromise,
181
192
  };
@@ -286,6 +297,6 @@ export function getStorageService(storage) {
286
297
  }
287
298
  /** Internal assurance seam for the exact rules source retained by Storage setup. */
288
299
  export function getStorageRulesResolution(storage) {
289
- return SERVICE_RULES_RESOLUTION.get(targetOf(storage).sandbox) ?? null;
300
+ return OPEN_SERVICES.get(targetOf(storage).sandbox)?.rulesResolution ?? null;
290
301
  }
291
302
  //# sourceMappingURL=service.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/storage/service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/storage/service.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pyric",
3
- "version": "0.1.0-alpha.19",
3
+ "version": "0.1.0-alpha.21",
4
4
  "license": "Apache-2.0",
5
5
  "homepage": "https://pyric.dev",
6
6
  "repository": {
@@ -13,6 +13,22 @@
13
13
  },
14
14
  "description": "Firebase for agents \u2014 modular Web SDK adapters (firestore, auth, database, storage) with swappable sandbox/prod backends, plus rules tooling and in-process sandbox. Mirrors firebase.",
15
15
  "type": "module",
16
+ "sideEffects": [
17
+ "./dist/app/register.js",
18
+ "./dist/app/registry.js",
19
+ "./dist/app/runtime.js",
20
+ "./dist/firestore/sandbox/write-runtime.js",
21
+ "./dist/rules/grammar/FirestoreParser.js",
22
+ "./dist/sandbox/admin-firestore/listeners.js",
23
+ "./dist/sandbox/internal/firebase-error.js",
24
+ "./src/app/register.ts",
25
+ "./src/app/registry.ts",
26
+ "./src/app/runtime.ts",
27
+ "./src/firestore/sandbox/write-runtime.ts",
28
+ "./src/rules/grammar/FirestoreParser.ts",
29
+ "./src/sandbox/admin-firestore/listeners.ts",
30
+ "./src/sandbox/internal/firebase-error.ts"
31
+ ],
16
32
  "exports": {
17
33
  "./app": {
18
34
  "types": "./dist/app/index.d.ts",
@@ -38,6 +54,10 @@
38
54
  "types": "./dist/firestore/internal/value-codec.d.ts",
39
55
  "import": "./dist/firestore/internal/value-codec.js"
40
56
  },
57
+ "./firestore/lite": {
58
+ "types": "./dist/firestore/lite.d.ts",
59
+ "import": "./dist/firestore/lite.js"
60
+ },
41
61
  "./auth": {
42
62
  "types": "./dist/auth/index.d.ts",
43
63
  "import": "./dist/auth/index.js"
@@ -125,6 +145,26 @@
125
145
  "./sandbox/admin-firestore": {
126
146
  "types": "./dist/sandbox/admin-firestore/index.d.ts",
127
147
  "import": "./dist/sandbox/admin-firestore/index.js"
148
+ },
149
+ "./app-check": {
150
+ "types": "./dist/app-check/index.d.ts",
151
+ "import": "./dist/app-check/index.js"
152
+ },
153
+ "./functions": {
154
+ "types": "./dist/functions/index.d.ts",
155
+ "import": "./dist/functions/index.js"
156
+ },
157
+ "./analytics": {
158
+ "types": "./dist/analytics/index.d.ts",
159
+ "import": "./dist/analytics/index.js"
160
+ },
161
+ "./performance": {
162
+ "types": "./dist/performance/index.d.ts",
163
+ "import": "./dist/performance/index.js"
164
+ },
165
+ "./remote-config": {
166
+ "types": "./dist/remote-config/index.d.ts",
167
+ "import": "./dist/remote-config/index.js"
128
168
  }
129
169
  },
130
170
  "files": [
@@ -150,7 +190,7 @@
150
190
  "zod": "^3.23.0"
151
191
  },
152
192
  "devDependencies": {
153
- "@types/bun": "latest",
193
+ "@types/bun": "1.3.14",
154
194
  "firebase": "^12.12.0",
155
195
  "firebase-admin": "^13.0.0",
156
196
  "typescript": "^5.7.0"
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Blocked-response detection: the ONE definition of "this envelope carries
3
+ * no usable content because a filter fired", shared by the two places that
4
+ * need it:
5
+ *
6
+ * - `response-helpers.ts`, which throws upstream's exact
7
+ * `Response error: Candidate was blocked due to <reason>` out of
8
+ * `text()` / `functionCalls()` / `inlineDataParts()` when the caller
9
+ * reaches for content that isn't there;
10
+ * - `broker/broker.ts`, which lands a `response_blocked` event on the
11
+ * sandbox's unified stream at RESPONSE time, so the block is visible
12
+ * even when nobody ever calls `text()` (an app that renders
13
+ * `response.text ?? ''` used to show an empty bubble and nothing else).
14
+ *
15
+ * A block is not a rejection: production answers HTTP 200 with an empty
16
+ * candidate and the reason in `finishReason` (SAFETY, RECITATION,
17
+ * BLOCKLIST, PROHIBITED_CONTENT, SPII, the IMAGE_* variants), or, when the
18
+ * PROMPT itself was refused, with no candidates at all and a
19
+ * `promptFeedback.blockReason`. Nothing throws on the wire path.
20
+ *
21
+ * Leaf module by construction: it imports only `./enums.js` (which imports
22
+ * nothing) and a type. `broker/` must NOT reach into `response-helpers.ts`
23
+ * for this: that file pulls `./errors.js`, which imports `./broker/index.js`
24
+ * as a value, and the round trip would be a real runtime cycle.
25
+ */
26
+
27
+ import type { WireResponse } from './broker/types.js';
28
+ import { FinishReason } from './enums.js';
29
+
30
+ /**
31
+ * Finish reasons that mean "no usable content": the installed
32
+ * `@firebase/ai@2.12.0` `badFinishReasons` set, verbatim and in upstream's
33
+ * order. Every one of these makes the response helpers throw instead of
34
+ * returning text, which is exactly the set worth announcing to a developer.
35
+ */
36
+ const BLOCKING_FINISH_REASONS: readonly string[] = [
37
+ FinishReason.RECITATION,
38
+ FinishReason.SAFETY,
39
+ FinishReason.BLOCKLIST,
40
+ FinishReason.PROHIBITED_CONTENT,
41
+ FinishReason.SPII,
42
+ FinishReason.MALFORMED_FUNCTION_CALL,
43
+ FinishReason.IMAGE_SAFETY,
44
+ FinishReason.IMAGE_PROHIBITED_CONTENT,
45
+ FinishReason.IMAGE_OTHER,
46
+ FinishReason.NO_IMAGE,
47
+ FinishReason.IMAGE_RECITATION,
48
+ FinishReason.LANGUAGE,
49
+ FinishReason.UNEXPECTED_TOOL_CALL,
50
+ FinishReason.TOO_MANY_TOOL_CALLS,
51
+ FinishReason.MISSING_THOUGHT_SIGNATURE,
52
+ FinishReason.MALFORMED_RESPONSE,
53
+ ];
54
+
55
+ /** Does this candidate's finish reason mean the content was withheld? */
56
+ export function isBlockingFinishReason(finishReason: string | undefined): boolean {
57
+ if (!finishReason) return false;
58
+ return BLOCKING_FINISH_REASONS.some((reason) => reason === finishReason);
59
+ }
60
+
61
+ /** What a block was, in the wire's own vocabulary. Exactly one of
62
+ * `finishReason` (a candidate was withheld) / `blockReason` (the prompt was
63
+ * refused) is normally set; both may be absent when production sent
64
+ * `promptFeedback` with no reason at all. */
65
+ export interface ResponseBlock {
66
+ /** Candidate-level: the blocking `finishReason` off candidate 0. */
67
+ finishReason?: string;
68
+ /** Candidate-level: production's own explanatory `finishMessage`. */
69
+ finishMessage?: string;
70
+ /** Prompt-level: `promptFeedback.blockReason`. */
71
+ blockReason?: string;
72
+ }
73
+
74
+ /**
75
+ * Classify an envelope, mirroring `hasValidCandidates` in
76
+ * `response-helpers.ts` EXACTLY so the event stream and the thrown error can
77
+ * never disagree: candidate 0 with a blocking finish reason is a block; no
78
+ * candidate 0 at all plus any `promptFeedback` is a block; everything else
79
+ * (including a plain `STOP` or `MAX_TOKENS`) is not.
80
+ *
81
+ * Returns `null` when the response is fine, so a caller reads as
82
+ * `const block = describeResponseBlock(res); if (block !== null) …`.
83
+ */
84
+ export function describeResponseBlock(response: WireResponse): ResponseBlock | null {
85
+ const firstCandidate = response.candidates?.[0];
86
+ if (firstCandidate !== undefined) {
87
+ if (!isBlockingFinishReason(firstCandidate.finishReason)) return null;
88
+ const candidateBlock: ResponseBlock = {};
89
+ candidateBlock.finishReason = firstCandidate.finishReason;
90
+ candidateBlock.finishMessage = firstCandidate.finishMessage;
91
+ return candidateBlock;
92
+ }
93
+ if (response.promptFeedback === undefined) return null;
94
+ const promptBlock: ResponseBlock = {};
95
+ promptBlock.blockReason = response.promptFeedback.blockReason;
96
+ return promptBlock;
97
+ }
@@ -18,6 +18,17 @@
18
18
  * - a model-turn functionCall part with no thoughtSignature
19
19
  * → `ai-error-fncall-missing-thought-signature`
20
20
  *
21
+ * It also announces the refusals production does NOT throw for: a safety /
22
+ * recitation / blocklist block arrives as a 200 with an empty candidate, so
23
+ * the broker lands a `response_blocked` event ({@link emitBlockIfBlocked})
24
+ * alongside the normal op event. Without it a filtered answer is
25
+ * indistinguishable from a model that simply had nothing to say.
26
+ *
27
+ * The same goes for a model SUBSTITUTION ({@link emitModelSubstitution}): an
28
+ * engine that redirects the requested model (an openai `modelMap` entry or
29
+ * catch-all `model`, a gemini experimental alias) answers normally, so
30
+ * nothing anywhere says the developer tested a model they never named.
31
+ *
21
32
  * Event emission is best-effort behind one small choke-point ({@link emit}):
22
33
  * a throw from the emit path (or any event consumer downstream) must never
23
34
  * poison the AI operation the caller just completed — handler errors are the
@@ -28,15 +39,18 @@
28
39
  import type { Sandbox } from 'pyric/sandbox';
29
40
  import { emitSandboxEvent, makeServiceMutationEvent } from 'pyric/sandbox/internal';
30
41
 
42
+ import { describeResponseBlock } from '../blocked.js';
31
43
  import { AiBrokerError, Synthesizer, badRole, emptyContents, missingThoughtSignature } from './synthesizer.js';
32
44
  import { ScriptedEngine } from './scripted-engine.js';
33
45
  import { OpenAiEngine } from './openai-engine.js';
46
+ import { GeminiEngine } from './gemini-engine.js';
34
47
  import type {
35
48
  AnswerEngine,
36
49
  CountTokensRequest,
37
50
  CountTokensResponse,
38
51
  EngineConfig,
39
52
  GenerateContentRequest,
53
+ ModelResolution,
40
54
  RawEnvelope,
41
55
  WireChunk,
42
56
  WireResponse,
@@ -75,7 +89,7 @@ function isAnswerEngine(value: EngineConfig | AnswerEngine): value is AnswerEngi
75
89
  }
76
90
 
77
91
  /** What Studio's stream (and the construction log line) name the engine. */
78
- type EngineKind = 'scripted' | 'openai' | 'custom';
92
+ type EngineKind = 'scripted' | 'openai' | 'gemini' | 'custom';
79
93
 
80
94
  export class AiBroker {
81
95
  readonly engine: AnswerEngine;
@@ -93,6 +107,10 @@ export class AiBroker {
93
107
  } else if (engine.kind === 'scripted') {
94
108
  this.engine = new ScriptedEngine(engine.script ?? [], new Synthesizer());
95
109
  this.engineKind = 'scripted';
110
+ } else if (engine.kind === 'gemini') {
111
+ this.engine = new GeminiEngine(engine);
112
+ this.engineKind = 'gemini';
113
+ this.engineBaseUrl = engine.baseUrl;
96
114
  } else {
97
115
  this.engine = new OpenAiEngine(engine);
98
116
  this.engineKind = 'openai';
@@ -104,10 +122,16 @@ export class AiBroker {
104
122
 
105
123
  /** One-line construction-time summary of what this broker resolved to. */
106
124
  private describeEngine(): string {
107
- if (this.engineKind === 'openai') {
125
+ const isOpenAiEngine = this.engineKind === 'openai';
126
+ if (isOpenAiEngine) {
108
127
  return `[pyric/ai] engine resolved: openai (model=${this.engineModel ?? 'passthrough'}, upstream=${this.engineBaseUrl})`;
109
128
  }
110
- if (this.engineKind === 'custom') {
129
+ const isGeminiEngine = this.engineKind === 'gemini';
130
+ if (isGeminiEngine) {
131
+ return `[pyric/ai] engine resolved: gemini (upstream=${this.engineBaseUrl ?? 'https://generativelanguage.googleapis.com'})`;
132
+ }
133
+ const isCustomEngine = this.engineKind === 'custom';
134
+ if (isCustomEngine) {
111
135
  return '[pyric/ai] engine resolved: custom AnswerEngine';
112
136
  }
113
137
  return '[pyric/ai] engine resolved: scripted (zero-config unless a script is queued)';
@@ -115,24 +139,109 @@ export class AiBroker {
115
139
 
116
140
  /** Additive detail fields every emitted event carries so Studio can show the engine. */
117
141
  private engineDetail(): Record<string, unknown> {
118
- return {
142
+ const detail: Record<string, unknown> = {
119
143
  engine: this.engineKind,
120
- ...(this.engineKind === 'openai'
121
- ? { model: this.engineModel, baseUrl: this.engineBaseUrl }
122
- : {}),
123
144
  };
145
+ const isOpenAiEngine = this.engineKind === 'openai';
146
+ if (isOpenAiEngine) {
147
+ detail.model = this.engineModel;
148
+ detail.baseUrl = this.engineBaseUrl;
149
+ }
150
+ const isGeminiEngine = this.engineKind === 'gemini';
151
+ if (isGeminiEngine) {
152
+ detail.baseUrl = this.engineBaseUrl ?? 'https://generativelanguage.googleapis.com';
153
+ }
154
+ return detail;
155
+ }
156
+
157
+ /**
158
+ * Announce a SAFETY / RECITATION / BLOCKLIST / … block on the event stream.
159
+ *
160
+ * A block is the broker's SILENT refusal: unlike `request_rejected` nothing
161
+ * throws (production answers 200 with an empty candidate), so an app that
162
+ * renders whatever `text()` gave it just shows nothing, and the developer
163
+ * gets no signal anywhere. Emitting at RESPONSE time (not from the response
164
+ * helpers, which only fire if someone reaches for content) means the event
165
+ * lands whether or not the caller ever asks for the text.
166
+ *
167
+ * Detection is `blocked.ts`'s, shared verbatim with the response helpers'
168
+ * throw path, so the stream and the thrown error can never disagree.
169
+ *
170
+ * Returns whether the envelope WAS blocked, so the stream wrapper can
171
+ * announce at most once across its chunks.
172
+ */
173
+ private emitBlockIfBlocked(model: string, response: WireResponse): boolean {
174
+ const block = describeResponseBlock(response);
175
+ if (block === null) return false;
176
+ this.emit('response_blocked', model, { ...block });
177
+ return true;
178
+ }
179
+
180
+ /**
181
+ * Announce that the engine will answer as a DIFFERENT model than the one
182
+ * requested.
183
+ *
184
+ * A substitution is the quietest failure the broker has: nothing throws,
185
+ * content comes back, and the developer concludes they tested model X when
186
+ * model Y answered (an openai `modelMap` entry or catch-all `model`, a
187
+ * gemini experimental alias). Only engines that actually redirect implement
188
+ * {@link AnswerEngine.resolveEffectiveModel}; the rest are silent by
189
+ * construction.
190
+ *
191
+ * Fired at REQUEST time, before delegating: the swap is a property of the
192
+ * call about to go out, so it lands even when that call then fails.
193
+ * Comparison is bare-vs-bare, since `models/x` to `x` is the wire's prefix
194
+ * convention rather than a substitution, and an exact match emits nothing.
195
+ *
196
+ * `countTokens` is deliberately not announced: the openai engine never
197
+ * sends a model there at all (the count is synthesized locally), so an
198
+ * announcement would name an upstream call that never happens.
199
+ */
200
+ private emitModelSubstitution(model: string): void {
201
+ const resolve = this.engine.resolveEffectiveModel;
202
+ if (typeof resolve !== 'function') return;
203
+ let effective: ModelResolution;
204
+ try {
205
+ effective = resolve.call(this.engine, model);
206
+ } catch {
207
+ // A misbehaving engine's self-report must never fail the op.
208
+ return;
209
+ }
210
+ const requestedBare = model.startsWith('models/') ? model.slice('models/'.length) : model;
211
+ if (effective.model === requestedBare) return;
212
+ this.emit('model_substituted', model, {
213
+ requestedModel: model,
214
+ effectiveModel: effective.model,
215
+ reason: effective.reason,
216
+ });
217
+ }
218
+
219
+ private emitRejectionIfBrokerError(model: string, err: unknown): void {
220
+ const code = err instanceof AiBrokerError ? err.envelope.error.code : 500;
221
+ const status = err instanceof AiBrokerError ? err.envelope.error.status : 'INTERNAL';
222
+ const message = err instanceof AiBrokerError
223
+ ? err.envelope.error.message
224
+ : err instanceof Error ? err.message : String(err);
225
+ this.emit('request_rejected', model, { code, status, message });
124
226
  }
125
227
 
126
228
  async generateContent(req: GenerateContentRequest, model: string): Promise<WireResponse> {
127
229
  this.validate(req, model);
128
- const response = await this.engine.generateContent(req, model);
129
- this.emit('generate_content', model, {
130
- contentCount: req.contents.length,
131
- finishReason: response.candidates?.[0]?.finishReason,
132
- totalTokenCount: response.usageMetadata?.totalTokenCount,
133
- responseId: response.responseId,
134
- });
135
- return response;
230
+ this.emitModelSubstitution(model);
231
+ try {
232
+ const response = await this.engine.generateContent(req, model);
233
+ this.emit('generate_content', model, {
234
+ contentCount: req.contents.length,
235
+ finishReason: response.candidates?.[0]?.finishReason,
236
+ totalTokenCount: response.usageMetadata?.totalTokenCount,
237
+ responseId: response.responseId,
238
+ });
239
+ this.emitBlockIfBlocked(model, response);
240
+ return response;
241
+ } catch (err) {
242
+ this.emitRejectionIfBrokerError(model, err);
243
+ throw err;
244
+ }
136
245
  }
137
246
 
138
247
  /**
@@ -143,27 +252,49 @@ export class AiBroker {
143
252
  */
144
253
  streamGenerateContent(req: GenerateContentRequest, model: string): AsyncIterable<WireChunk> {
145
254
  this.validate(req, model);
255
+ this.emitModelSubstitution(model);
146
256
  const inner = this.engine.streamGenerateContent(req, model);
147
257
  const emit = (chunkCount: number) =>
148
258
  this.emit('stream_generate_content', model, {
149
259
  contentCount: req.contents.length,
150
260
  chunkCount,
151
261
  });
262
+ const emitRejection = (err: unknown) => this.emitRejectionIfBrokerError(model, err);
263
+ // A block surfaces on whichever chunk carries the finish reason (the last
264
+ // one, per the captured framing), so it is announced DURING iteration:
265
+ // an abandoned stream still reports why it produced nothing. At most one
266
+ // per stream: the aggregate has a single blocking reason, not one per chunk.
267
+ let announcedBlock = false;
268
+ const emitBlock = (chunk: WireChunk) => {
269
+ if (announcedBlock) return;
270
+ announcedBlock = this.emitBlockIfBlocked(model, chunk);
271
+ };
152
272
  return (async function* wrapped(): AsyncGenerator<WireChunk> {
153
273
  let chunkCount = 0;
154
- for await (const chunk of inner) {
155
- chunkCount++;
156
- yield chunk;
274
+ try {
275
+ for await (const chunk of inner) {
276
+ chunkCount++;
277
+ emitBlock(chunk);
278
+ yield chunk;
279
+ }
280
+ emit(chunkCount);
281
+ } catch (err) {
282
+ emitRejection(err);
283
+ throw err;
157
284
  }
158
- emit(chunkCount);
159
285
  })();
160
286
  }
161
287
 
162
288
  async countTokens(req: CountTokensRequest, model: string): Promise<CountTokensResponse> {
163
289
  this.validateContents(req, model);
164
- const response = await this.engine.countTokens(req, model);
165
- this.emit('count_tokens', model, { totalTokens: response.totalTokens });
166
- return response;
290
+ try {
291
+ const response = await this.engine.countTokens(req, model);
292
+ this.emit('count_tokens', model, { totalTokens: response.totalTokens });
293
+ return response;
294
+ } catch (err) {
295
+ this.emitRejectionIfBrokerError(model, err);
296
+ throw err;
297
+ }
167
298
  }
168
299
 
169
300
  // ── Production-shaped validation ──────────────────────────────────────────