apps 0.0.1-beta.11 → 0.0.1-beta.110

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 (197) hide show
  1. package/README.md +60 -42
  2. package/framework-reference.json +1 -1
  3. package/js/chunk-2RGJMA7N.js +5186 -0
  4. package/js/chunk-3BI34I5S.js +449 -0
  5. package/js/chunk-3BOEMXAJ.js +713 -0
  6. package/js/{chunk-JU7VHODU.js → chunk-4PIQKACY.js} +65 -10
  7. package/js/{chunk-VFZKSM3T.js → chunk-4X2UI4R2.js} +3 -3
  8. package/js/{chunk-KUMBKVJD.js → chunk-62NVUKED.js} +2 -2
  9. package/js/chunk-74H25C4X.js +95 -0
  10. package/js/chunk-7HG422GB.js +90 -0
  11. package/js/{chunk-UK573G7W.js → chunk-ACQXVKVQ.js} +87 -51
  12. package/js/chunk-BKIQCV72.js +994 -0
  13. package/js/{chunk-IJX4CYZO.js → chunk-DILSCQPJ.js} +228 -264
  14. package/js/chunk-HLMJBE4T.js +49 -0
  15. package/js/{chunk-RELFXUOO.js → chunk-JCX2K2SI.js} +931 -289
  16. package/js/{chunk-M3B3DE4K.js → chunk-KLSEU35Y.js} +463 -218
  17. package/js/chunk-OD3KFKZD.js +1617 -0
  18. package/js/chunk-OIH73VFM.js +175 -0
  19. package/js/chunk-OWN3DP2W.js +88 -0
  20. package/js/{chunk-IPI4MFSN.js → chunk-PBGPES67.js} +2 -2
  21. package/js/{chunk-WVVV4TX2.js → chunk-RIV62DRM.js} +4 -3
  22. package/js/{chunk-RFVIONFX.js → chunk-SXQVVXJE.js} +123 -44
  23. package/js/chunk-UPTAFF6L.js +335 -0
  24. package/js/{chunk-ODEIDRGW.js → chunk-URKOTNDA.js} +795 -176
  25. package/js/{chunk-5T57N25Q.js → chunk-VAI3EVCS.js} +643 -69
  26. package/js/{chunk-57RKNWL2.js → chunk-YDW7DX3A.js} +4 -4
  27. package/js/{chunk-CDL5PM2Q.js → chunk-ZKDXML77.js} +19239 -18404
  28. package/js/{chunk-GPFVWWDZ.js → chunk-ZXG7RMCB.js} +113 -58
  29. package/js/client.js +148 -84
  30. package/js/contracts.js +150 -21
  31. package/js/effect.js +18 -19
  32. package/js/{es-APDZKREY.js → es-DIULA3ZF.js} +62 -1659
  33. package/js/graphql.js +231 -188
  34. package/js/host.js +116 -78
  35. package/js/index.js +213 -98
  36. package/js/mcp/effect.js +21 -21
  37. package/js/mcp.js +91 -57
  38. package/js/openapi.js +4349 -1415
  39. package/js/react.js +2 -3
  40. package/js/skills/effect.js +6 -3
  41. package/js/skills.js +208 -81
  42. package/js/storage/facet.js +1 -540
  43. package/js/ui/auth/contracts.js +2 -2
  44. package/js/ui/auth.js +5 -5
  45. package/js/ui/contracts.js +9 -8
  46. package/js/ui/serving.js +8609 -228
  47. package/package.json +3 -8
  48. package/runtime.json +1 -1
  49. package/types/app-cache/src/contracts/cache.d.ts +18 -0
  50. package/types/app-cache/src/index.d.ts +34 -2
  51. package/types/apps/src/contracts/api-response-error.d.ts +4 -0
  52. package/types/apps/src/contracts/app.d.ts +56 -11
  53. package/types/apps/src/contracts/cache.d.ts +20 -0
  54. package/types/apps/src/contracts/context.d.ts +60 -17
  55. package/types/apps/src/contracts/elicitation.d.ts +23 -7
  56. package/types/apps/src/contracts/events.d.ts +94 -0
  57. package/types/apps/src/contracts/failure.d.ts +30 -1
  58. package/types/apps/src/contracts/graphql.d.ts +40 -2
  59. package/types/apps/src/contracts/host.d.ts +1456 -20
  60. package/types/apps/src/contracts/icons.d.ts +28 -0
  61. package/types/apps/src/contracts/live.d.ts +2 -0
  62. package/types/apps/src/contracts/mcp.d.ts +81 -20
  63. package/types/apps/src/contracts/network.d.ts +120 -0
  64. package/types/apps/src/contracts/openapi-compile.d.ts +82 -3
  65. package/types/apps/src/contracts/openapi-document.d.ts +1 -1
  66. package/types/apps/src/contracts/openapi.d.ts +41 -9
  67. package/types/apps/src/contracts/operations.d.ts +14 -7
  68. package/types/apps/src/contracts/placement.d.ts +102 -0
  69. package/types/apps/src/contracts/protocol-version.d.ts +2 -2
  70. package/types/apps/src/contracts/protocols/1.d.ts +733 -81
  71. package/types/apps/src/contracts/protocols/10.d.ts +1450 -0
  72. package/types/apps/src/contracts/protocols/11.d.ts +1149 -0
  73. package/types/apps/src/contracts/protocols/12.d.ts +3310 -0
  74. package/types/apps/src/contracts/protocols/2.d.ts +69 -26
  75. package/types/apps/src/contracts/protocols/3.d.ts +133 -33
  76. package/types/apps/src/contracts/protocols/4.d.ts +122 -45
  77. package/types/apps/src/contracts/protocols/5.d.ts +157 -47
  78. package/types/apps/src/contracts/protocols/6.d.ts +1629 -0
  79. package/types/apps/src/contracts/protocols/7.d.ts +1785 -0
  80. package/types/apps/src/contracts/protocols/8.d.ts +1182 -0
  81. package/types/apps/src/contracts/protocols/9.d.ts +1104 -0
  82. package/types/apps/src/contracts/protocols/current.d.ts +6869 -0
  83. package/types/apps/src/contracts/protocols/oauth-6.d.ts +74 -0
  84. package/types/apps/src/contracts/protocols/oauth.d.ts +134 -0
  85. package/types/apps/src/contracts/provider-error.d.ts +9 -0
  86. package/types/apps/src/contracts/provider.d.ts +85 -6
  87. package/types/apps/src/contracts/router.d.ts +2 -1
  88. package/types/apps/src/contracts/schedules.d.ts +7 -1
  89. package/types/apps/src/contracts/schema.d.ts +21 -0
  90. package/types/apps/src/contracts/shared-catalog.d.ts +11 -0
  91. package/types/apps/src/contracts/skills.d.ts +50 -13
  92. package/types/apps/src/contracts/sql.d.ts +63 -0
  93. package/types/apps/src/contracts/ui.d.ts +70 -30
  94. package/types/apps/src/contracts/webhooks.d.ts +24 -7
  95. package/types/apps/src/contracts/workflows.d.ts +56 -34
  96. package/types/apps/src/facet.d.ts +4 -5
  97. package/types/apps/src/host.d.ts +5 -0
  98. package/types/apps/src/implementation/account-router.d.ts +4 -3
  99. package/types/apps/src/implementation/app.d.ts +41 -14
  100. package/types/apps/src/implementation/authoring.d.ts +25 -5
  101. package/types/apps/src/implementation/browser-scripts.gen.d.ts +6 -0
  102. package/types/apps/src/implementation/cache.d.ts +29 -2
  103. package/types/apps/src/implementation/catalog-cache.d.ts +35 -4
  104. package/types/apps/src/implementation/elicitation.d.ts +18 -15
  105. package/types/apps/src/implementation/events.d.ts +64 -0
  106. package/types/apps/src/implementation/failure-detail.d.ts +28 -5
  107. package/types/apps/src/implementation/graphql-catalog.d.ts +3 -3
  108. package/types/apps/src/implementation/graphql.d.ts +4 -3
  109. package/types/apps/src/implementation/input-problems.d.ts +6 -2
  110. package/types/apps/src/implementation/invocation-timing.d.ts +18 -0
  111. package/types/apps/src/implementation/live.d.ts +2 -2
  112. package/types/apps/src/implementation/mcp-call.d.ts +6 -5
  113. package/types/apps/src/implementation/mcp-catalog.d.ts +6 -4
  114. package/types/apps/src/implementation/mcp-client.d.ts +17 -9
  115. package/types/apps/src/implementation/mcp-tools.d.ts +2 -1
  116. package/types/apps/src/implementation/mcp.d.ts +20 -9
  117. package/types/apps/src/implementation/network.d.ts +16 -0
  118. package/types/apps/src/implementation/openapi-compile.d.ts +38 -7
  119. package/types/apps/src/implementation/openapi-document.d.ts +50 -20
  120. package/types/apps/src/implementation/openapi-names.d.ts +9 -4
  121. package/types/apps/src/implementation/openapi-request.d.ts +10 -2
  122. package/types/apps/src/implementation/openapi-source.d.ts +23 -1
  123. package/types/apps/src/implementation/openapi-upgrade.d.ts +4 -0
  124. package/types/apps/src/implementation/operations.d.ts +58 -7
  125. package/types/apps/src/implementation/optimistic.d.ts +1 -1
  126. package/types/apps/src/implementation/placement.d.ts +60 -0
  127. package/types/apps/src/implementation/protocol-operations.d.ts +3 -0
  128. package/types/apps/src/implementation/provider-error.d.ts +28 -1
  129. package/types/apps/src/implementation/provider.d.ts +19 -4
  130. package/types/apps/src/implementation/query-commit.d.ts +1 -1
  131. package/types/apps/src/implementation/router-catalog.d.ts +24 -18
  132. package/types/apps/src/implementation/router.d.ts +26 -6
  133. package/types/apps/src/implementation/runtime-yield.d.ts +7 -0
  134. package/types/apps/src/implementation/schedules.d.ts +39 -8
  135. package/types/apps/src/implementation/schema.d.ts +108 -11
  136. package/types/apps/src/implementation/skills.d.ts +18 -24
  137. package/types/apps/src/implementation/sql.d.ts +71 -0
  138. package/types/apps/src/implementation/ui-account-notice.d.ts +9 -0
  139. package/types/apps/src/implementation/ui-auth.d.ts +1 -1
  140. package/types/apps/src/implementation/ui-client.d.ts +3 -3
  141. package/types/apps/src/implementation/ui-document.d.ts +5 -0
  142. package/types/apps/src/implementation/ui-errors.d.ts +1 -1
  143. package/types/apps/src/implementation/ui-serving.d.ts +28 -11
  144. package/types/apps/src/implementation/ui-watch.d.ts +1 -1
  145. package/types/apps/src/implementation/upstream-error.d.ts +17 -0
  146. package/types/apps/src/implementation/webhooks.d.ts +15 -6
  147. package/types/apps/src/implementation/workflow-context.d.ts +6 -2
  148. package/types/apps/src/implementation/workflows.d.ts +11 -4
  149. package/types/apps/src/index.d.ts +51 -16
  150. package/types/apps/src/mcp.d.ts +20 -1
  151. package/types/apps/src/openapi.d.ts +22 -1
  152. package/types/apps/src/react.d.ts +1 -1
  153. package/types/apps/src/skills.d.ts +10 -8
  154. package/types/apps/src/ui-serving.d.ts +1 -1
  155. package/types/telemetry/src/app-records.d.ts +42 -0
  156. package/types/telemetry/src/browser-operations.d.ts +2 -2
  157. package/types/telemetry/src/browser.d.ts +8 -3
  158. package/types/telemetry/src/config.d.ts +2 -2
  159. package/types/telemetry/src/index.d.ts +2 -1
  160. package/types/telemetry/src/layer.d.ts +10 -4
  161. package/types/telemetry/src/measurements.d.ts +1 -1
  162. package/types/telemetry/src/ownership.d.ts +60 -0
  163. package/types/telemetry/src/recorded-failure.d.ts +29 -0
  164. package/types/telemetry/src/relay.d.ts +16 -6
  165. package/types/telemetry/src/span-attributes.d.ts +17 -1
  166. package/types/telemetry/src/transport.d.ts +8 -1
  167. package/types/utils/src/api-error.d.ts +55 -0
  168. package/types/utils/src/connection-failure.d.ts +33 -0
  169. package/types/utils/src/legacy-keys.d.ts +20 -0
  170. package/types/utils/src/recorded-message.d.ts +18 -0
  171. package/types/utils/src/schema-definitions.d.ts +13 -0
  172. package/types/utils/src/user-facing-error.d.ts +83 -0
  173. package/js/chunk-27F7W2LG.js +0 -34
  174. package/js/chunk-EFAPIRVB.js +0 -299
  175. package/js/chunk-KRSCJDTW.js +0 -2668
  176. package/js/chunk-MKWZPDR6.js +0 -63
  177. package/js/chunk-N7YDVXYJ.js +0 -186
  178. package/js/chunk-NKLWZJLE.js +0 -172
  179. package/js/chunk-PJJ24GU7.js +0 -877
  180. package/js/chunk-PZB6T6KU.js +0 -19
  181. package/js/chunk-R3EV5FTO.js +0 -97
  182. package/js/chunk-TYVDBQSI.js +0 -242
  183. package/js/chunk-YBRC463S.js +0 -224
  184. package/js/chunk-Z2QY3YTF.js +0 -507
  185. package/js/chunk-ZQINWV4W.js +0 -630
  186. package/js/mcp/stdio.js +0 -128
  187. package/types/app-data/src/implementation/cursor.d.ts +0 -10
  188. package/types/app-data/src/implementation/index-key.d.ts +0 -7
  189. package/types/app-data/src/implementation/promise.d.ts +0 -68
  190. package/types/app-data/src/implementation/range.d.ts +0 -8
  191. package/types/app-data/src/implementation/schema.d.ts +0 -18
  192. package/types/app-data/src/implementation/sqlite.d.ts +0 -11
  193. package/types/app-data/src/index.d.ts +0 -4
  194. package/types/apps/src/contracts/storage.d.ts +0 -68
  195. package/types/apps/src/implementation/mcp-stdio.d.ts +0 -4
  196. package/types/apps/src/implementation/storage.d.ts +0 -15
  197. package/types/apps/src/mcp-stdio.d.ts +0 -5
@@ -1,39 +1,45 @@
1
1
  import {
2
- makeCache
3
- } from "./chunk-NKLWZJLE.js";
2
+ authorCache,
3
+ unavailableCache
4
+ } from "./chunk-UPTAFF6L.js";
4
5
  import {
5
6
  accountSecrets,
6
7
  boundFailureMessage,
7
8
  describeFailure,
8
9
  failureDetail,
10
+ leavingProviderError,
11
+ parseMcpError,
12
+ parseSkillLoadFailed,
9
13
  readCatalog,
10
14
  routerSkills
11
- } from "./chunk-Z2QY3YTF.js";
15
+ } from "./chunk-BKIQCV72.js";
16
+ import {
17
+ appInvocationFetch
18
+ } from "./chunk-HLMJBE4T.js";
12
19
  import {
13
20
  folderSkillsEffect
14
- } from "./chunk-IPI4MFSN.js";
21
+ } from "./chunk-PBGPES67.js";
15
22
  import {
16
23
  ResponseStatusError,
17
- authorDatabase,
24
+ declaredEvents,
18
25
  isApp,
26
+ makeEmitter,
19
27
  toEffectApp,
20
- unavailableStorage
21
- } from "./chunk-TYVDBQSI.js";
22
- import {
23
- parseDatabaseSchema
24
- } from "./chunk-ZQINWV4W.js";
28
+ withPlacements
29
+ } from "./chunk-3BI34I5S.js";
25
30
  import {
26
31
  declaredOperations,
27
- fromPromise,
28
32
  locate,
29
- nativeOperation,
30
- toPromise
31
- } from "./chunk-GPFVWWDZ.js";
33
+ nativeOperation
34
+ } from "./chunk-ZXG7RMCB.js";
32
35
  import {
33
36
  accountProviderError,
37
+ appCode,
38
+ fromPromise,
34
39
  httpProviderError,
35
- parseProviderError
36
- } from "./chunk-MKWZPDR6.js";
40
+ parseProviderError,
41
+ toPromise
42
+ } from "./chunk-OIH73VFM.js";
37
43
  import {
38
44
  AccountCheckResult,
39
45
  DeclaredProvider,
@@ -57,14 +63,15 @@ import {
57
63
  HostToolNotFound,
58
64
  HostToolPolicyFailed,
59
65
  HostedRouter,
66
+ HostedSharedCatalog,
60
67
  HostedTool,
61
68
  HostedToolSummary,
62
69
  HostedWebhook,
63
70
  HostedWorkflow,
64
71
  InvocationDeadline,
72
+ InvocationTimingSink,
65
73
  ManualWebhookSetup,
66
74
  ManyAccounts,
67
- McpError,
68
75
  NonRetryableError,
69
76
  OpenapiResponseError,
70
77
  ResolvedAccounts,
@@ -80,42 +87,85 @@ import {
80
87
  approvalElicitation,
81
88
  defaultWebhookTransportLimits,
82
89
  maxInputProblems,
90
+ migrationsDirectory,
91
+ normalizePlacements,
92
+ reservedTablePrefix,
93
+ shareCatalog,
94
+ sqlBindingsLimit,
83
95
  workflowDurationMillis
84
- } from "./chunk-KRSCJDTW.js";
85
- import {
86
- CacheError
87
- } from "./chunk-R3EV5FTO.js";
96
+ } from "./chunk-2RGJMA7N.js";
88
97
  import {
89
98
  AppSkills,
90
- SkillFile,
91
- SkillLoadFailed
92
- } from "./chunk-JU7VHODU.js";
93
- import {
94
- captureTelemetry,
95
- invocationFetch
96
- } from "./chunk-ODEIDRGW.js";
99
+ SkillFile
100
+ } from "./chunk-4PIQKACY.js";
97
101
  import {
98
- decoderOf,
102
+ allowedValues,
103
+ echoableKey,
99
104
  importedJsonSchema,
105
+ importedSchemaKeys,
100
106
  jsonSchemaDecoder,
101
- jsonSchemaDocument
102
- } from "./chunk-5T57N25Q.js";
107
+ jsonSchemaDocument,
108
+ maxAlternativeKeys,
109
+ missingKey,
110
+ missingKeyText,
111
+ objectShape,
112
+ unionShape
113
+ } from "./chunk-VAI3EVCS.js";
114
+ import {
115
+ captureTelemetry,
116
+ makeOwnershipAccounting,
117
+ measuredSpan,
118
+ owned
119
+ } from "./chunk-URKOTNDA.js";
103
120
  import {
104
121
  JsonObject,
105
122
  JsonValue
106
- } from "./chunk-27F7W2LG.js";
123
+ } from "./chunk-7HG422GB.js";
107
124
  import {
125
+ Base64_exports,
108
126
  Cause_exports,
109
127
  Clock_exports,
110
128
  Effect_exports,
111
- Encoding_exports,
112
129
  Match_exports,
113
130
  Option_exports,
114
131
  Redacted_exports,
132
+ SchemaAST_exports,
115
133
  SchemaIssue_exports,
116
134
  Schema_exports,
117
135
  Stream_exports
118
- } from "./chunk-CDL5PM2Q.js";
136
+ } from "./chunk-ZKDXML77.js";
137
+
138
+ // src/implementation/invocation-timing.ts
139
+ var elicitationWaitSpan = "app.elicitation.wait";
140
+ var workflowControlSpan = "app.workflow.control";
141
+ var timedInvocation = (operation) => (effect) => Effect_exports.gen(function* () {
142
+ const report = yield* InvocationTimingSink;
143
+ return yield* effect.pipe(
144
+ measuredSpan(`app.${operation}`, {}, (start) => {
145
+ const accounting = makeOwnershipAccounting(start, "executor");
146
+ return {
147
+ around: accounting.observe,
148
+ end: (end, span) => {
149
+ const spent = accounting.read(end);
150
+ report({
151
+ elapsedMs: Number(end - start) / 1e6,
152
+ upstreamMs: spent.upstream,
153
+ elicitationMs: spent.person,
154
+ authoredMs: spent.app,
155
+ leaseMs: spent.lease,
156
+ backgroundMs: spent.background
157
+ });
158
+ span.attribute("executor.upstream.wait_ms", spent.upstream);
159
+ span.attribute("executor.elicitation.wait_ms", spent.person);
160
+ span.attribute("executor.authored_ms", spent.app);
161
+ span.attribute("executor.lease.wait_ms", spent.lease);
162
+ span.attribute("executor.background_ms", spent.background);
163
+ span.attribute("executor.overhead_ms", spent.executor);
164
+ }
165
+ };
166
+ })
167
+ );
168
+ });
119
169
 
120
170
  // src/implementation/elicitation.ts
121
171
  var prepareElicitation = (input) => Effect_exports.gen(function* () {
@@ -143,13 +193,17 @@ var prepareElicitation = (input) => Effect_exports.gen(function* () {
143
193
  });
144
194
  return { request, respond };
145
195
  });
146
- var makeElicit = (handler, signal) => toPromise(
196
+ var makeElicit = (handler, signal, telemetry) => toPromise(
147
197
  (input) => Effect_exports.gen(function* () {
148
198
  if (handler === void 0) return yield* new ElicitationFailed({ reason: "unavailable" });
149
199
  const form = yield* prepareElicitation(input);
150
- return yield* handler(form.request, signal).pipe(Effect_exports.flatMap(form.respond));
200
+ return yield* handler(form.request, signal).pipe(
201
+ owned("person", elicitationWaitSpan),
202
+ Effect_exports.flatMap(form.respond)
203
+ );
151
204
  }),
152
- signal
205
+ signal,
206
+ telemetry
153
207
  );
154
208
 
155
209
  // src/contracts/approval.ts
@@ -235,10 +289,7 @@ var makeWorkflowContext = (execution, definition, fresh, signal) => Effect_expor
235
289
  );
236
290
  const step2 = yield* fresh(stepId, AbortSignal.any([signal, controller.signal]));
237
291
  const result = yield* workflowSafe(
238
- Effect_exports.tryPromise({
239
- try: () => callback(step2.context),
240
- catch: (error) => error
241
- }),
292
+ fromPromise(callback, "step")(step2.context),
242
293
  step2.secrets
243
294
  );
244
295
  return yield* Schema_exports.decodeUnknownEffect(WorkflowValue)(result).pipe(
@@ -319,15 +370,144 @@ var makeWorkflowContext = (execution, definition, fresh, signal) => Effect_expor
319
370
  });
320
371
 
321
372
  // src/implementation/input-problems.ts
322
- var leafHook = (issue) => SchemaIssue_exports.hasInput(issue) ? "Invalid value" : SchemaIssue_exports.defaultLeafHook(issue);
373
+ var nativeObjectShape = (ast, limit) => objectShape(
374
+ ast.propertySignatures.flatMap(
375
+ ({ name, type }) => typeof name === "string" ? [{ name, optional: SchemaAST_exports.isOptional(SchemaAST_exports.toEncoded(type)) }] : []
376
+ ),
377
+ ast.indexSignatures.length > 0,
378
+ limit
379
+ );
380
+ var unionMembers = (ast) => SchemaAST_exports.isUnion(ast) ? ast.types.flatMap(unionMembers) : SchemaAST_exports.isUndefined(ast) || SchemaAST_exports.isVoid(ast) ? [] : [ast];
381
+ var fixedValues = (ast) => {
382
+ if (SchemaAST_exports.isLiteral(ast)) return [ast.literal];
383
+ if (SchemaAST_exports.isEnum(ast)) return ast.enums.map(([, value]) => value);
384
+ if (!SchemaAST_exports.isUnion(ast)) return void 0;
385
+ const members = unionMembers(ast).map(fixedValues);
386
+ return members.length > 0 && members.every((values) => values !== void 0) ? members.flat() : void 0;
387
+ };
388
+ var describeMember = (ast) => Match_exports.value(ast).pipe(
389
+ Match_exports.when(
390
+ SchemaAST_exports.isObjects,
391
+ (objects) => objects.propertySignatures.length > 0 ? nativeObjectShape(objects, maxAlternativeKeys) : "object"
392
+ ),
393
+ Match_exports.when(SchemaAST_exports.isArrays, () => "array"),
394
+ Match_exports.when(SchemaAST_exports.isTemplateLiteral, () => "a string matching a template"),
395
+ Match_exports.when(SchemaAST_exports.isString, () => "string"),
396
+ Match_exports.when(SchemaAST_exports.isNumber, () => "number"),
397
+ Match_exports.when(SchemaAST_exports.isBoolean, () => "boolean"),
398
+ Match_exports.when(SchemaAST_exports.isNull, () => "null"),
399
+ Match_exports.orElse(() => "a value with other constraints")
400
+ );
401
+ var memberSelector = (members) => {
402
+ const [first] = members;
403
+ if (members.length < 2 || first === void 0 || !SchemaAST_exports.isObjects(first)) return void 0;
404
+ return first.propertySignatures.map(({ name }) => name).find(
405
+ (name) => typeof name === "string" && members.every(
406
+ (member) => SchemaAST_exports.isObjects(member) && member.propertySignatures.some(
407
+ (property) => property.name === name && fixedValues(SchemaAST_exports.toEncoded(property.type)) !== void 0
408
+ )
409
+ )
410
+ );
411
+ };
412
+ var unionExpectation = (ast) => {
413
+ const members = unionMembers(ast);
414
+ const fixed = members.map(fixedValues);
415
+ const values = fixed.flatMap((memberValues) => memberValues === void 0 ? [] : memberValues);
416
+ const others = members.filter((_, index) => fixed[index] === void 0);
417
+ if (others.length === 0) return allowedValues(values);
418
+ return unionShape(
419
+ [
420
+ ...values.length === 0 ? [] : [allowedValues(values)],
421
+ ...new Set(others.map(describeMember))
422
+ ],
423
+ memberSelector(members)
424
+ );
425
+ };
426
+ var expectedUnion = (ast) => `Expected ${unionExpectation(ast)}`;
427
+ var expectedValue = (ast) => {
428
+ const encoded = SchemaAST_exports.toEncoded(ast);
429
+ const values = fixedValues(encoded);
430
+ if (values !== void 0) return allowedValues(values);
431
+ const members = unionMembers(encoded);
432
+ const [only] = members;
433
+ if (only === void 0) return void 0;
434
+ if (members.length > 1 && SchemaAST_exports.isUnion(encoded)) return unionExpectation(encoded);
435
+ if (SchemaAST_exports.isObjects(only) && only.propertySignatures.length > 0)
436
+ return nativeObjectShape(only);
437
+ const described = describeMember(only);
438
+ return described === "a value with other constraints" ? void 0 : described;
439
+ };
440
+ var leafHook = (issue) => SchemaIssue_exports.hasInput(issue) ? "Invalid value" : Match_exports.value(issue).pipe(
441
+ Match_exports.tag("InvalidType", ({ ast }) => {
442
+ const values = fixedValues(ast);
443
+ return values !== void 0 ? `Expected ${allowedValues(values)}` : SchemaAST_exports.isObjects(ast) && ast.propertySignatures.length > 0 ? `Expected ${nativeObjectShape(ast)}` : SchemaIssue_exports.defaultLeafHook(issue);
444
+ }),
445
+ Match_exports.orElse(SchemaIssue_exports.defaultLeafHook)
446
+ );
323
447
  var checkHook = (issue) => SchemaIssue_exports.hasInput(issue) || SchemaIssue_exports.hasInput(issue.issue) ? SchemaIssue_exports.defaultCheckHook(issue) ?? "Invalid value" : SchemaIssue_exports.defaultCheckHook(issue);
324
448
  var format = SchemaIssue_exports.makeFormatterStandardSchemaV1({ leafHook, checkHook });
325
- var segment = (key) => typeof key === "number" ? `[${key}]` : typeof key === "string" && /^[A-Za-z_$][\w$-]{0,63}$/.test(key) ? `.${key}` : "[key]";
326
- var inputInvalid = (error) => {
449
+ var declaredKey = (parent, key) => parent !== void 0 && SchemaAST_exports.isObjects(parent) ? parent.propertySignatures.find(({ name }) => name === key)?.type : void 0;
450
+ var nativeKeys = (parent) => (key) => parent.indexSignatures.length > 0 || parent.propertySignatures.some(({ name }) => name === key);
451
+ var located = (issue, path, parent) => Match_exports.value(issue).pipe(
452
+ Match_exports.tag("Pointer", (pointer) => {
453
+ const declared = pointer.issue._tag === "MissingKey" && pointer.path.length === 1 ? declaredKey(parent, pointer.path[0]) : void 0;
454
+ return declared === void 0 || parent === void 0 || !SchemaAST_exports.isObjects(parent) ? located(pointer.issue, [...path, ...pointer.path]) : [
455
+ {
456
+ path: [...path, ...pointer.path],
457
+ message: missingKey(expectedValue(declared)),
458
+ accepts: nativeKeys(parent)
459
+ }
460
+ ];
461
+ }),
462
+ Match_exports.tag(
463
+ "Composite",
464
+ ({ ast, issues }) => issues.flatMap((issue2) => located(issue2, path, ast))
465
+ ),
466
+ Match_exports.tag("Encoding", (encoding) => located(encoding.issue, path)),
467
+ Match_exports.tag(
468
+ "AnyOf",
469
+ ({ ast, issues }) => issues.length === 0 ? [{ path, message: expectedUnion(ast) }] : issues.flatMap((issue2) => located(issue2, path))
470
+ ),
471
+ Match_exports.orElse((issue2) => {
472
+ const accepts = issue2._tag === "Filter" ? importedSchemaKeys(issue2.filter) : void 0;
473
+ return format(issue2).issues.map((problem) => ({
474
+ path: [
475
+ ...path,
476
+ ...(problem.path ?? []).map((key) => typeof key === "object" ? key.key : key)
477
+ ],
478
+ message: problem.message,
479
+ ...accepts === void 0 ? {} : { accepts }
480
+ }));
481
+ })
482
+ );
483
+ var segment = (key) => typeof key === "number" ? `[${key}]` : typeof key === "string" && echoableKey(key) ? `.${key}` : "[key]";
484
+ var rendered = (path) => `input${path.map(segment).join("")}`;
485
+ var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
486
+ var valueAt = (input, path) => path.reduce(
487
+ (value, key) => Array.isArray(value) && typeof key === "number" ? value[key] : isRecord(value) && typeof key === "string" && Object.hasOwn(value, key) ? value[key] : void 0,
488
+ input
489
+ );
490
+ var spelling = (key) => key.toLowerCase().replace(/[-_]/g, "");
491
+ var suppliedElsewhere = (input, path, accepts) => {
492
+ const key = path.at(-1);
493
+ if (typeof key !== "string" || !echoableKey(key)) return void 0;
494
+ const at = path.slice(0, -1);
495
+ const holder = valueAt(input, at);
496
+ if (!isRecord(holder)) return void 0;
497
+ const candidates = Object.keys(holder).filter((name) => echoableKey(name) && !accepts(name));
498
+ const respelled = candidates.find((name) => name !== key && spelling(name) === spelling(key));
499
+ const deeper = candidates.find((name) => {
500
+ const child = holder[name];
501
+ return isRecord(child) && Object.hasOwn(child, key);
502
+ });
503
+ const found = respelled !== void 0 ? [...at, respelled] : deeper !== void 0 ? [...at, deeper, key] : void 0;
504
+ return found === void 0 ? void 0 : `The input has ${String(found.at(-1))} at ${rendered(found)}; did you mean ${rendered(path)}?`;
505
+ };
506
+ var inputInvalid = (error, input) => {
327
507
  if (!Schema_exports.isSchemaError(error)) return new HostInputInvalid();
328
- const problems = format(error.issue).issues.slice(0, maxInputProblems).map(({ path, message }) => {
329
- const location = (path ?? []).map((key) => segment(typeof key === "object" ? key.key : key));
330
- return `${location.length === 0 ? "input" : `input${location.join("")}`}: ${message}`.slice(
508
+ const problems = located(error.issue, []).slice(0, maxInputProblems).map(({ path, message, accepts }) => {
509
+ const elsewhere = accepts !== void 0 && message.startsWith(missingKeyText) ? suppliedElsewhere(input, path, accepts) : void 0;
510
+ return `${rendered(path)}: ${message}${elsewhere === void 0 ? "" : `. ${elsewhere}`}`.slice(
331
511
  0,
332
512
  512
333
513
  );
@@ -336,19 +516,25 @@ var inputInvalid = (error) => {
336
516
  };
337
517
 
338
518
  // src/implementation/webhooks.ts
519
+ var withoutUser = ({
520
+ user: _user,
521
+ ...context
522
+ }) => context;
339
523
  var safe = (work, error) => Effect_exports.suspend(work).pipe(
340
524
  Effect_exports.catchCause(
341
525
  (cause) => Cause_exports.hasInterrupts(cause) ? Effect_exports.interrupt : Effect_exports.fail(error)
342
526
  )
343
527
  );
344
- var dispatchWebhook = (definition, command, context, accounts) => Effect_exports.gen(function* () {
528
+ var sourceRequirement = (account) => "app" in account ? { scope: "app", name: account.app } : { scope: "user", name: account.user };
529
+ var dispatchWebhook = (definition, command, context, slots) => Effect_exports.gen(function* () {
345
530
  const hooks = definition.webhooks ?? {};
346
531
  if (command.operation === "webhooks") {
347
532
  return yield* Effect_exports.forEach(
348
533
  Object.entries(hooks),
349
534
  ([name, hook2]) => safe(
350
535
  () => Effect_exports.gen(function* () {
351
- if (!Object.hasOwn(accounts, hook2.account))
536
+ const source2 = sourceRequirement(hook2.account);
537
+ if (!Object.hasOwn(slots[source2.scope], source2.name))
352
538
  return yield* new HostDeclarationInvalid();
353
539
  const describe = (schema) => {
354
540
  const imported = importedJsonSchema(schema);
@@ -368,7 +554,7 @@ var dispatchWebhook = (definition, command, context, accounts) => Effect_exports
368
554
  };
369
555
  return yield* Schema_exports.decodeUnknownEffect(HostedWebhook)({
370
556
  name,
371
- account: hook2.account,
557
+ account: source2.name,
372
558
  configSchema,
373
559
  ...setup === void 0 ? {} : { setup }
374
560
  });
@@ -379,14 +565,15 @@ var dispatchWebhook = (definition, command, context, accounts) => Effect_exports
379
565
  }
380
566
  const hook = Object.hasOwn(hooks, command.name) ? hooks[command.name] : void 0;
381
567
  if (hook === void 0) return yield* new HostOperationNotFound();
382
- const selected = accounts[hook.account];
383
- const source = Array.isArray(selected) ? selected.find((account2) => account2.id === command.sourceAccount) : selected;
384
- if (source === void 0 || source.id !== command.sourceAccount)
385
- return yield* new HostInputInvalid();
386
- const bound = context.accounts[hook.account];
387
- const account = Array.isArray(bound) ? bound.find(
388
- (value) => typeof value === "object" && value !== null && "id" in value && value.id === source.id
389
- ) : bound;
568
+ const source = sourceRequirement(hook.account);
569
+ const scope = source.scope === "app" ? context.app : context.user;
570
+ if (scope === void 0) return yield* new HostAccountsInvalid();
571
+ const bound = Object.hasOwn(scope.accounts, source.name) ? scope.accounts[source.name] : void 0;
572
+ const account = (Array.isArray(bound) ? bound : [bound]).find(
573
+ (value) => typeof value === "object" && value !== null && "id" in value && value.id === command.sourceAccount
574
+ );
575
+ if (account === void 0) return yield* new HostInputInvalid();
576
+ const hookContext = source.scope === "app" ? withoutUser(context) : context;
390
577
  const config = yield* safe(
391
578
  () => Schema_exports.decodeUnknownEffect(hook.config)(command.config),
392
579
  new HostInputInvalid()
@@ -403,7 +590,7 @@ var dispatchWebhook = (definition, command, context, accounts) => Effect_exports
403
590
  callbackUrl: command.callbackUrl,
404
591
  secret: command.secret
405
592
  };
406
- const invoke = (callback, value) => safe(() => callback(context, value), new HostOperationFailed());
593
+ const invoke = (callback, value) => safe(() => callback(hookContext, value), new HostOperationFailed());
407
594
  if (command.operation === "webhook-complete") {
408
595
  if (hook.setup === void 0) return yield* new HostInputInvalid();
409
596
  return yield* safe(
@@ -434,7 +621,7 @@ var dispatchWebhook = (definition, command, context, accounts) => Effect_exports
434
621
  yield* invoke(hook.unregister, { ...input, state });
435
622
  return null;
436
623
  }
437
- const body = yield* Effect_exports.fromResult(Encoding_exports.decodeBase64(command.request.body)).pipe(
624
+ const body = yield* Effect_exports.fromResult(Base64_exports.decode(command.request.body)).pipe(
438
625
  Effect_exports.mapError(() => new HostInputInvalid())
439
626
  );
440
627
  if (body.byteLength > defaultWebhookTransportLimits.maxBodyBytes)
@@ -467,7 +654,8 @@ var dispatchWebhook = (definition, command, context, accounts) => Effect_exports
467
654
  return yield* new HostOutputInvalid();
468
655
  chunks.push(chunk);
469
656
  })
470
- )
657
+ ),
658
+ appCode("webhook")
471
659
  );
472
660
  const bytes = new Uint8Array(size);
473
661
  let offset = 0;
@@ -483,79 +671,414 @@ var dispatchWebhook = (definition, command, context, accounts) => Effect_exports
483
671
  () => Schema_exports.decodeUnknownEffect(WebhookResponseData)({
484
672
  status: response.status,
485
673
  headers,
486
- body: Encoding_exports.encodeBase64(new Uint8Array(bytes))
674
+ body: Base64_exports.encode(new Uint8Array(bytes))
487
675
  }),
488
676
  new HostOutputInvalid()
489
677
  );
490
678
  });
491
679
 
492
- // src/implementation/cache.ts
493
- var unavailableCache = {
494
- transport: () => Effect_exports.fail(new CacheError({ reason: "unavailable" })),
495
- background: () => Effect_exports.die(new Error("Cache background runner is unavailable"))
680
+ // src/implementation/sql.ts
681
+ var receipts = `${reservedTablePrefix}step_receipts`;
682
+ var stepEvents = `${reservedTablePrefix}step_events`;
683
+ var migrationsTable = `${reservedTablePrefix}migrations`;
684
+ var legacyRows = `${reservedTablePrefix}legacy_rows`;
685
+ var materialize = (cursor) => {
686
+ const rows = cursor.toArray();
687
+ return {
688
+ columnNames: cursor.columnNames,
689
+ rowsRead: cursor.rowsRead,
690
+ rowsWritten: cursor.rowsWritten,
691
+ toArray: () => [...rows],
692
+ one: () => {
693
+ if (rows.length !== 1)
694
+ throw new Error(`Expected exactly one row, but the statement returned ${rows.length}.`);
695
+ return rows[0];
696
+ },
697
+ [Symbol.iterator]: () => rows[Symbol.iterator]()
698
+ };
496
699
  };
497
- var authorCache = (host, accounts, signal, deadline) => {
498
- const scoped = (scope, callerSignal = signal) => {
499
- const cache = makeCache(host.transport, host.background, scope, deadline);
500
- const load = (method, options) => cache[method]({
501
- key: options.key,
502
- schema: decoderOf(options.schema),
503
- freshFor: options.freshFor,
504
- ...options.staleFor === void 0 ? {} : { staleFor: options.staleFor },
505
- load: Effect_exports.acquireUseRelease(
506
- Effect_exports.sync(() => new AbortController()),
507
- (controller) => invocationFetch(controller.signal).pipe(
508
- Effect_exports.flatMap(
509
- (fetch) => fromPromise(options.load)({
510
- fetch,
511
- signal: controller.signal,
512
- cache: scoped(scope, controller.signal)
513
- })
514
- )
515
- ),
516
- (controller) => Effect_exports.sync(() => controller.abort())
517
- )
518
- });
700
+ var isThenable = (value) => (typeof value === "object" || typeof value === "function") && value !== null && "then" in value && typeof value.then === "function";
701
+ var missingDatabase = () => {
702
+ throw new Error(
703
+ `This app has no database. Add its first migration, such as ${migrationsDirectory}/0001_create_notes.sql with a CREATE TABLE statement, and deploy again.`
704
+ );
705
+ };
706
+ var noDatabase = { exec: missingDatabase, transaction: missingDatabase };
707
+ var settings = [
708
+ "foreign_keys",
709
+ "defer_foreign_keys",
710
+ "ignore_check_constraints",
711
+ "legacy_alter_table",
712
+ "recursive_triggers",
713
+ "reverse_unordered_selects"
714
+ ];
715
+ var readSettings = (storage) => new Map(
716
+ settings.map((name) => [name, Number(storage.sql.exec(`PRAGMA ${name}`).toArray()[0]?.[name])])
717
+ );
718
+ var changedSetting = (storage, baseline) => {
719
+ const current = readSettings(storage);
720
+ return settings.find((name) => current.get(name) !== baseline.get(name));
721
+ };
722
+ var restoreSettings = (storage, baseline) => {
723
+ const current = readSettings(storage);
724
+ for (const [name, value] of baseline)
725
+ if (current.get(name) !== value) storage.sql.exec(`PRAGMA ${name} = ${value}`);
726
+ storage.sql.exec("PRAGMA case_sensitive_like = OFF");
727
+ };
728
+ var ReadRolledBack = class extends Error {
729
+ };
730
+ var authorSql = (storage, options) => {
731
+ let closed = false;
732
+ const live = () => {
733
+ if (closed || options.signal.aborted)
734
+ throw new Error("This call has ended, so its ctx.sql no longer runs statements.");
735
+ };
736
+ let open = false;
737
+ let transactions = 0;
738
+ const baseline = readSettings(storage);
739
+ const checkSettings = () => {
740
+ const changed = changedSetting(storage, baseline);
741
+ if (changed !== void 0)
742
+ throw refuse(
743
+ new Error(
744
+ `SQLite connection settings belong to Executor, so app code cannot change PRAGMA ${changed}; the statement was rolled back. Migrations may change settings while they run.`
745
+ )
746
+ );
747
+ };
748
+ const changes = () => Number(storage.sql.exec("SELECT total_changes() AS n").toArray()[0]?.n);
749
+ let violation;
750
+ const refuse = (error) => {
751
+ if (open) violation ??= error;
752
+ return error;
753
+ };
754
+ const run = (query, bindings, mode) => {
755
+ try {
756
+ return runStatement(query, bindings, mode);
757
+ } finally {
758
+ restoreSettings(storage, baseline);
759
+ }
760
+ };
761
+ const runStatement = (query, bindings, mode) => {
762
+ live();
763
+ if (bindings.length > sqlBindingsLimit)
764
+ throw refuse(
765
+ new Error(
766
+ `A statement can bind at most ${sqlBindingsLimit} values, but this one binds ${bindings.length}. Pass a long list as one JSON array and read it with json_each(?), or split the work into batches.`
767
+ )
768
+ );
769
+ if (mode === "write") {
770
+ const statement = () => {
771
+ const cursor = materialize(storage.sql.exec(query, ...bindings));
772
+ checkSettings();
773
+ return cursor;
774
+ };
775
+ if (!open) return storage.transactionSync(statement);
776
+ try {
777
+ return statement();
778
+ } catch (error) {
779
+ refuse(error instanceof Error ? error : new Error(String(error)));
780
+ throw error;
781
+ }
782
+ }
783
+ if (open) {
784
+ return runStatement(query, bindings, "write");
785
+ }
786
+ const before = changes();
787
+ let read;
788
+ let changed = false;
789
+ try {
790
+ storage.transactionSync(() => {
791
+ read = materialize(storage.sql.exec(query, ...bindings));
792
+ changed = changes() !== before;
793
+ checkSettings();
794
+ throw new ReadRolledBack();
795
+ });
796
+ } catch (error) {
797
+ if (!(error instanceof ReadRolledBack)) throw error;
798
+ }
799
+ if (changed || read === void 0)
800
+ throw new Error(
801
+ options.step !== void 0 ? "A workflow step mutation writes only inside ctx.sql.transaction(...), so a retried step applies its writes once." : "Queries are read-only. Change data from a mutation."
802
+ );
803
+ return read;
804
+ };
805
+ const reader = {
806
+ exec: (query, ...bindings) => run(query, bindings, "read")
807
+ };
808
+ const transactionHandle = () => {
809
+ let active = true;
810
+ const tx = {
811
+ exec: (query, ...bindings) => {
812
+ if (!active)
813
+ throw new Error("This transaction has ended, so its tx no longer runs statements.");
814
+ return run(query, bindings, "write");
815
+ }
816
+ };
519
817
  return {
520
- get: toPromise((options) => load("get", options), callerSignal),
521
- revalidate: toPromise(
522
- (options) => load("revalidate", options),
523
- callerSignal
524
- ),
525
- read: toPromise(
526
- (key, schema) => cache.read([key]).pipe(
527
- Effect_exports.flatMap((entries) => {
528
- const entry = entries[0];
529
- return entry === void 0 || entry === null ? Effect_exports.succeed(void 0) : Schema_exports.decodeUnknownEffect(decoderOf(schema))(entry.value);
530
- })
531
- ),
532
- callerSignal
533
- ),
534
- readMany: toPromise(
535
- (keys, schema) => cache.read(keys).pipe(
536
- Effect_exports.flatMap(
537
- (entries) => Effect_exports.forEach(
538
- entries,
539
- (entry) => entry === null ? Effect_exports.succeed(void 0) : Schema_exports.decodeUnknownEffect(decoderOf(schema))(entry.value)
540
- )
541
- )
542
- ),
543
- callerSignal
544
- ),
545
- write: toPromise(cache.write, callerSignal),
546
- invalidate: toPromise(cache.invalidate, callerSignal),
547
- forAccount: (account) => {
548
- const bound = Object.values(accounts).flatMap((value) => Array.isArray(value) ? value : [value]).find((value) => value.id === account.id);
549
- if (bound === void 0) throw new CacheError({ reason: "invalid" });
550
- return scoped(
551
- { account: bound.id, method: bound.method, generation: bound.generation },
552
- callerSignal
818
+ tx,
819
+ end: () => {
820
+ active = false;
821
+ }
822
+ };
823
+ };
824
+ const transaction = (work) => {
825
+ live();
826
+ if (open) throw new Error("ctx.sql.transaction(...) cannot be nested.");
827
+ const step = options.step;
828
+ if (step !== void 0 && transactions > 0)
829
+ throw new Error(
830
+ "A workflow step mutation opens at most one ctx.sql.transaction(...), which records the step's result."
831
+ );
832
+ transactions += 1;
833
+ violation = void 0;
834
+ const handle = transactionHandle();
835
+ const emittedBefore = options.emitted?.length;
836
+ const discardEmitted = () => {
837
+ if (options.emitted !== void 0 && emittedBefore !== void 0)
838
+ options.emitted.length = emittedBefore;
839
+ };
840
+ const body = () => {
841
+ open = true;
842
+ try {
843
+ const complete = () => {
844
+ const value = work(handle.tx);
845
+ if (isThenable(value))
846
+ throw new Error(
847
+ "ctx.sql.transaction(...) callbacks are synchronous and cannot await. Call outside services before or after the transaction."
848
+ );
849
+ if (violation !== void 0) throw violation;
850
+ return value;
851
+ };
852
+ if (step === void 0) return complete();
853
+ storage.sql.exec(
854
+ `CREATE TABLE IF NOT EXISTS ${receipts} (id TEXT PRIMARY KEY, fingerprint TEXT NOT NULL, result TEXT NOT NULL) WITHOUT ROWID`
855
+ );
856
+ const saved = storage.sql.exec(`SELECT fingerprint, result FROM ${receipts} WHERE id = ?`, step.key).toArray()[0];
857
+ storage.sql.exec(
858
+ `CREATE TABLE IF NOT EXISTS ${stepEvents} (id TEXT PRIMARY KEY, events TEXT NOT NULL) WITHOUT ROWID`
553
859
  );
860
+ if (saved !== void 0) {
861
+ if (saved.fingerprint !== step.fingerprint)
862
+ throw new Error("This workflow step already ran with different input.");
863
+ const replayed = storage.sql.exec(`SELECT events FROM ${stepEvents} WHERE id = ?`, step.key).toArray()[0];
864
+ if (replayed !== void 0 && options.emitted !== void 0)
865
+ options.emitted.push(...JSON.parse(String(replayed.events)));
866
+ return JSON.parse(String(saved.result));
867
+ }
868
+ const encoded = JSON.stringify(complete() ?? null);
869
+ const emitted = options.emitted?.slice(emittedBefore ?? 0) ?? [];
870
+ if (emitted.length > 0)
871
+ storage.sql.exec(
872
+ `INSERT INTO ${stepEvents} (id, events) VALUES (?, ?)`,
873
+ step.key,
874
+ JSON.stringify(emitted)
875
+ );
876
+ storage.sql.exec(
877
+ `INSERT INTO ${receipts} (id, fingerprint, result) VALUES (?, ?, ?)`,
878
+ step.key,
879
+ step.fingerprint,
880
+ encoded
881
+ );
882
+ return JSON.parse(encoded);
883
+ } finally {
884
+ open = false;
885
+ handle.end();
554
886
  }
555
887
  };
888
+ try {
889
+ return storage.transactionSync(body);
890
+ } catch (error) {
891
+ discardEmitted();
892
+ throw error;
893
+ }
556
894
  };
557
- return scoped("shared");
895
+ const writer = {
896
+ // A step's writes go through its one transaction, which also records its receipt.
897
+ exec: (query, ...bindings) => run(query, bindings, options.step ? "read" : "write"),
898
+ transaction
899
+ };
900
+ return {
901
+ reader,
902
+ writer,
903
+ close: () => {
904
+ closed = true;
905
+ }
906
+ };
907
+ };
908
+ var MigrationFileInvalid = class extends Schema_exports.TaggedError()(
909
+ "MigrationFileInvalid",
910
+ { path: Schema_exports.String, reason: Schema_exports.String }
911
+ ) {
912
+ get message() {
913
+ return `${this.path}: ${this.reason}`;
914
+ }
915
+ };
916
+ var MigrationHistoryChanged = class extends Schema_exports.TaggedError()(
917
+ "MigrationHistoryChanged",
918
+ { reason: Schema_exports.String }
919
+ ) {
920
+ get message() {
921
+ return `${this.reason} Applied migrations cannot change: restore the original file and add a new migration instead. Nothing was applied; the previous version is still active.`;
922
+ }
558
923
  };
924
+ var MigrationFailed = class extends Schema_exports.TaggedError()("MigrationFailed", {
925
+ path: Schema_exports.String,
926
+ reason: Schema_exports.String
927
+ }) {
928
+ get message() {
929
+ return `${this.path} failed: ${this.reason}. No migrations were applied; the previous version is still active.`;
930
+ }
931
+ };
932
+ var migrationName = /^(\d+)_[A-Za-z0-9_-]+\.sql$/;
933
+ var sha256 = (text) => (
934
+ // oxlint-disable-next-line executor/authored-code-through-adapter -- Web Crypto
935
+ Effect_exports.promise(() => crypto.subtle.digest("SHA-256", new TextEncoder().encode(text))).pipe(
936
+ Effect_exports.map(
937
+ (digest) => Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("")
938
+ )
939
+ )
940
+ );
941
+ var statementsOf = (script) => {
942
+ let end = 0;
943
+ let index = 0;
944
+ while (index < script.length) {
945
+ const char = script[index];
946
+ const next = script[index + 1];
947
+ if (char === "-" && next === "-") {
948
+ const newline = script.indexOf("\n", index);
949
+ index = newline === -1 ? script.length : newline + 1;
950
+ continue;
951
+ }
952
+ if (char === "/" && next === "*") {
953
+ const close = script.indexOf("*/", index + 2);
954
+ index = close === -1 ? script.length : close + 2;
955
+ continue;
956
+ }
957
+ const closing = char === "'" || char === '"' || char === "`" ? char : char === "[" ? "]" : void 0;
958
+ if (closing !== void 0) {
959
+ let after = index + 1;
960
+ while (true) {
961
+ const found = script.indexOf(closing, after);
962
+ if (found === -1) {
963
+ after = script.length;
964
+ break;
965
+ }
966
+ if (closing !== "]" && script[found + 1] === closing) {
967
+ after = found + 2;
968
+ continue;
969
+ }
970
+ after = found + 1;
971
+ break;
972
+ }
973
+ index = after;
974
+ end = index;
975
+ continue;
976
+ }
977
+ index += 1;
978
+ if (char !== void 0 && !/\s/u.test(char)) end = index;
979
+ }
980
+ return script.slice(0, end);
981
+ };
982
+ var hasMigrations = (files) => files.some(
983
+ (file) => file.path.startsWith(`${migrationsDirectory}/`) && file.path.endsWith(".sql")
984
+ );
985
+ var migrationsOf = (files) => Effect_exports.gen(function* () {
986
+ const found = [];
987
+ for (const file of files) {
988
+ if (!file.path.startsWith(`${migrationsDirectory}/`) || !file.path.endsWith(".sql")) continue;
989
+ const name = file.path.slice(migrationsDirectory.length + 1);
990
+ const match = migrationName.exec(name);
991
+ if (name.includes("/") || match === null)
992
+ return yield* new MigrationFileInvalid({
993
+ path: file.path,
994
+ reason: "migrations are files directly in migrations/ named like 0001_create_notes.sql: a number that orders them, an underscore, then a name."
995
+ });
996
+ const content = file.content.replaceAll("\r\n", "\n");
997
+ found.push({
998
+ path: file.path,
999
+ order: Number(match[1]),
1000
+ content,
1001
+ hash: yield* sha256(content)
1002
+ });
1003
+ }
1004
+ const ordered = [...found].sort((left, right) => left.order - right.order);
1005
+ for (const [index, migration] of ordered.entries()) {
1006
+ const previous = ordered[index - 1];
1007
+ if (previous !== void 0 && previous.order === migration.order)
1008
+ return yield* new MigrationFileInvalid({
1009
+ path: migration.path,
1010
+ reason: `it has the same number as ${previous.path}; give each migration its own number.`
1011
+ });
1012
+ }
1013
+ return ordered;
1014
+ });
1015
+ var History = Schema_exports.Array(
1016
+ Schema_exports.Struct({ id: Schema_exports.Number, path: Schema_exports.String, hash: Schema_exports.String })
1017
+ );
1018
+ var migrate = (storage, files) => Effect_exports.gen(function* () {
1019
+ const migrations = yield* migrationsOf(files);
1020
+ const baseline = readSettings(storage);
1021
+ return yield* Effect_exports.sync(
1022
+ () => storage.transactionSync(() => {
1023
+ storage.sql.exec(
1024
+ `CREATE TABLE IF NOT EXISTS ${migrationsTable} (id INTEGER PRIMARY KEY, path TEXT NOT NULL, hash TEXT NOT NULL, applied_at TEXT NOT NULL)`
1025
+ );
1026
+ const stored = storage.sql.exec("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'app_rows'").toArray().length > 0;
1027
+ storage.sql.exec(`DROP VIEW IF EXISTS ${legacyRows}`);
1028
+ storage.sql.exec(
1029
+ `CREATE VIEW ${legacyRows} (table_name, id, body) AS ${stored ? "SELECT table_name, id, body FROM app_rows" : "SELECT '', '', '' WHERE 0"}`
1030
+ );
1031
+ const history = Schema_exports.decodeUnknownSync(History)(
1032
+ storage.sql.exec(`SELECT id, path, hash FROM ${migrationsTable} ORDER BY id`).toArray()
1033
+ );
1034
+ for (const [index, applied] of history.entries()) {
1035
+ const current = migrations[index];
1036
+ if (current === void 0)
1037
+ throw new MigrationHistoryChanged({
1038
+ reason: `${applied.path} was applied to this app's database but is no longer in migrations/.`
1039
+ });
1040
+ if (current.path !== applied.path)
1041
+ throw new MigrationHistoryChanged({
1042
+ reason: `Migration ${index + 1} was applied as ${applied.path}, but the build has ${current.path} in its place.`
1043
+ });
1044
+ if (current.hash !== applied.hash)
1045
+ throw new MigrationHistoryChanged({
1046
+ reason: `${applied.path} changed after it was applied to this app's database.`
1047
+ });
1048
+ }
1049
+ const pending = migrations.slice(history.length);
1050
+ const appliedAt = (/* @__PURE__ */ new Date()).toISOString();
1051
+ for (const [offset, migration] of pending.entries()) {
1052
+ const statements = statementsOf(migration.content);
1053
+ try {
1054
+ if (statements !== "") storage.sql.exec(statements);
1055
+ } catch (error) {
1056
+ throw new MigrationFailed({
1057
+ path: migration.path,
1058
+ reason: error instanceof Error ? error.message : String(error)
1059
+ });
1060
+ }
1061
+ storage.sql.exec(
1062
+ `INSERT INTO ${migrationsTable} (id, path, hash, applied_at) VALUES (?, ?, ?, ?)`,
1063
+ history.length + offset + 1,
1064
+ migration.path,
1065
+ migration.hash,
1066
+ appliedAt
1067
+ );
1068
+ }
1069
+ return pending.map((migration, offset) => ({
1070
+ id: history.length + offset + 1,
1071
+ name: migration.path
1072
+ }));
1073
+ })
1074
+ ).pipe(
1075
+ // Both are thrown inside the transaction so that it rolls back; they are expected failures.
1076
+ Effect_exports.catchDefect(
1077
+ (defect) => Schema_exports.is(MigrationFailed)(defect) || Schema_exports.is(MigrationHistoryChanged)(defect) ? Effect_exports.fail(defect) : Effect_exports.die(defect)
1078
+ ),
1079
+ Effect_exports.ensuring(Effect_exports.sync(() => restoreSettings(storage, baseline)))
1080
+ );
1081
+ });
559
1082
 
560
1083
  // src/implementation/host.ts
561
1084
  var WireCatalog = Schema_exports.Struct({
@@ -575,9 +1098,9 @@ var evaluationSafe = (work, secrets) => work.pipe(
575
1098
  const error = Cause_exports.squash(cause);
576
1099
  const provider = parseProviderError(error);
577
1100
  const skills = parseSkillLoadFailed(error);
578
- const mcp = parseMcpError(error);
1101
+ const mcp = parseMcpError(error, secrets);
579
1102
  return Effect_exports.fail(
580
- Option_exports.isSome(provider) ? provider.value : Option_exports.isSome(skills) ? skills.value : Option_exports.isSome(mcp) ? mcp.value : new HostEvaluationFailed(failureDetail(error, secrets))
1103
+ Option_exports.isSome(provider) ? leavingProviderError(provider.value, secrets, "discover") : Option_exports.isSome(skills) ? skills.value : Option_exports.isSome(mcp) ? mcp.value : new HostEvaluationFailed(failureDetail(error, secrets))
581
1104
  );
582
1105
  })
583
1106
  );
@@ -594,6 +1117,15 @@ var declarationSafe = (work, summary) => Effect_exports.suspend(work).pipe(
594
1117
  (cause) => Cause_exports.hasInterrupts(cause) ? Effect_exports.interrupt : Effect_exports.fail(declarationInvalid(summary, Cause_exports.squash(cause)))
595
1118
  )
596
1119
  );
1120
+ function declaredExposure(exposure) {
1121
+ const named = (kind) => Object.entries(exposure ?? {}).filter(([, value]) => value === kind).map(([field]) => field).sort();
1122
+ const plain = named("plain");
1123
+ const raw = named("raw");
1124
+ return { ...plain.length === 0 ? {} : { plain }, ...raw.length === 0 ? {} : { raw } };
1125
+ }
1126
+ function declaredPlacements(request) {
1127
+ return request === void 0 || request.length === 0 ? {} : { request: normalizePlacements(request) };
1128
+ }
597
1129
  function providerDeclaration(provider) {
598
1130
  return declarationSafe(
599
1131
  () => Effect_exports.gen(function* () {
@@ -604,27 +1136,33 @@ function providerDeclaration(provider) {
604
1136
  auth.set(name, {
605
1137
  type: "secrets",
606
1138
  label: method.label,
607
- fields: yield* jsonSchemaDocument(method.fields)
1139
+ fields: yield* jsonSchemaDocument(method.fields),
1140
+ ...declaredExposure(method.exposure),
1141
+ ...declaredPlacements(method.request)
608
1142
  });
609
1143
  break;
610
1144
  case "oauth2":
611
1145
  auth.set(name, {
612
1146
  type: "oauth2",
613
1147
  ...method.config,
614
- response: yield* jsonSchemaDocument(method.response)
1148
+ response: yield* jsonSchemaDocument(method.response),
1149
+ ...declaredExposure(method.exposure),
1150
+ ...declaredPlacements(method.request)
615
1151
  });
616
1152
  break;
617
1153
  }
618
1154
  }
619
1155
  return yield* Schema_exports.decodeUnknownEffect(DeclaredProvider)({
620
1156
  name: provider.name,
621
- auth: Object.fromEntries(auth)
1157
+ auth: Object.fromEntries(auth),
1158
+ // Order and repetition carry no meaning, so neither changes the provider's identity.
1159
+ ...provider.hosts === void 0 ? {} : { hosts: [...new Set(provider.hosts)].sort() }
622
1160
  });
623
1161
  }),
624
1162
  `Account provider "${provider.name}" has an invalid declaration`
625
1163
  );
626
1164
  }
627
- function requirements(slots, database) {
1165
+ function declaredSlots(slots) {
628
1166
  return Effect_exports.gen(function* () {
629
1167
  const accounts = /* @__PURE__ */ new Map();
630
1168
  for (const [slot, selection] of Object.entries(slots)) {
@@ -635,13 +1173,31 @@ function requirements(slots, database) {
635
1173
  ...provider.health === void 0 ? {} : { health: true }
636
1174
  });
637
1175
  }
1176
+ return Object.fromEntries(accounts);
1177
+ });
1178
+ }
1179
+ function requirements(slots, sql, events, icons) {
1180
+ return Effect_exports.gen(function* () {
638
1181
  return yield* Schema_exports.decodeUnknownEffect(DeclaredRequirements)({
639
- accounts: Object.fromEntries(accounts),
640
- capabilities: { skills: true, toolIndex: true, skillSources: true, scheduledTools: true },
641
- ...database === void 0 ? {} : { database }
1182
+ accounts: { app: yield* declaredSlots(slots.app), user: yield* declaredSlots(slots.user) },
1183
+ capabilities: {
1184
+ skills: true,
1185
+ toolIndex: true,
1186
+ skillSources: true,
1187
+ scheduledTools: true,
1188
+ sharedDefinitions: true
1189
+ },
1190
+ ...sql ? { sql: true } : {},
1191
+ ...events === void 0 || Object.keys(events).length === 0 ? {} : {
1192
+ events: yield* declarationSafe(
1193
+ () => declaredEvents(events),
1194
+ "The app's event declarations are invalid"
1195
+ )
1196
+ },
1197
+ ...icons === void 0 ? {} : { icons }
642
1198
  }).pipe(
643
1199
  Effect_exports.mapError(
644
- (cause) => declarationInvalid("The app's account or database declarations are invalid", cause)
1200
+ (cause) => declarationInvalid("The app's account declarations are invalid", cause)
645
1201
  )
646
1202
  );
647
1203
  });
@@ -653,52 +1209,76 @@ function canonical(value) {
653
1209
  }
654
1210
  return JSON.stringify(value);
655
1211
  }
1212
+ function withoutGrants({ hosts: _hosts, ...provider }) {
1213
+ return {
1214
+ ...provider,
1215
+ auth: Object.fromEntries(
1216
+ Object.entries(provider.auth).map(([name, { request: _request, ...method }]) => [
1217
+ name,
1218
+ method
1219
+ ])
1220
+ )
1221
+ };
1222
+ }
1223
+ function bindScope(slots, declarations, selected) {
1224
+ return Effect_exports.gen(function* () {
1225
+ if (Object.keys(selected).some((slot) => !Object.hasOwn(slots, slot))) {
1226
+ return yield* Effect_exports.fail(new HostAccountsInvalid());
1227
+ }
1228
+ const bound = /* @__PURE__ */ new Map();
1229
+ for (const [slot, selection] of Object.entries(slots)) {
1230
+ const requirement = Object.hasOwn(declarations, slot) ? declarations[slot] : void 0;
1231
+ const supplied = Object.hasOwn(selected, slot) ? selected[slot] : void 0;
1232
+ if (requirement === void 0 || supplied === void 0)
1233
+ return yield* Effect_exports.fail(new HostAccountsInvalid());
1234
+ const provider = selection instanceof ManyAccounts ? selection.provider : selection;
1235
+ const many = selection instanceof ManyAccounts;
1236
+ if (many !== Array.isArray(supplied)) return yield* Effect_exports.fail(new HostAccountsInvalid());
1237
+ const accounts = Array.isArray(supplied) ? supplied : [supplied];
1238
+ const ids = /* @__PURE__ */ new Set();
1239
+ const values = [];
1240
+ for (const account of accounts) {
1241
+ if (ids.has(account.id)) return yield* Effect_exports.fail(new HostAccountsInvalid());
1242
+ ids.add(account.id);
1243
+ if (canonical(withoutGrants(account.provider)) !== canonical(withoutGrants(requirement.definition)) || !Object.hasOwn(provider.auth, account.method))
1244
+ return yield* Effect_exports.fail(new HostAccountsInvalid());
1245
+ const method = provider.auth[account.method];
1246
+ if (method === void 0) return yield* Effect_exports.fail(new HostAccountsInvalid());
1247
+ const fields = yield* Schema_exports.decodeUnknownEffect(
1248
+ method._tag === "secrets" ? method.fields : method.response
1249
+ )(account.fields);
1250
+ const granted = Object.hasOwn(account.provider.auth, account.method) ? account.provider.auth[account.method]?.request : void 0;
1251
+ values.push(withPlacements({ id: account.id, method: account.method, fields }, granted));
1252
+ }
1253
+ if (many) bound.set(slot, values);
1254
+ else {
1255
+ const account = values[0];
1256
+ if (account === void 0) return yield* Effect_exports.fail(new HostAccountsInvalid());
1257
+ bound.set(slot, account);
1258
+ }
1259
+ }
1260
+ return Object.fromEntries(bound);
1261
+ });
1262
+ }
656
1263
  function bindAccounts(slots, declarations, context) {
657
1264
  return safe2(
658
1265
  () => Effect_exports.gen(function* () {
659
1266
  const selected = yield* Schema_exports.decodeUnknownEffect(ResolvedAccounts)(
660
1267
  Redacted_exports.value(context.accounts)
661
1268
  );
662
- if (Object.keys(selected).some((slot) => !Object.hasOwn(slots, slot))) {
663
- return yield* Effect_exports.fail(new HostAccountsInvalid());
664
- }
665
- const bound = /* @__PURE__ */ new Map();
666
- for (const [slot, selection] of Object.entries(slots)) {
667
- const requirement = declarations.accounts[slot];
668
- const supplied = Object.hasOwn(selected, slot) ? selected[slot] : void 0;
669
- if (requirement === void 0 || supplied === void 0)
670
- return yield* Effect_exports.fail(new HostAccountsInvalid());
671
- const provider = selection instanceof ManyAccounts ? selection.provider : selection;
672
- const many = selection instanceof ManyAccounts;
673
- if (many !== Array.isArray(supplied))
674
- return yield* Effect_exports.fail(new HostAccountsInvalid());
675
- const accounts = Array.isArray(supplied) ? supplied : [supplied];
676
- const ids = /* @__PURE__ */ new Set();
677
- const values = [];
678
- for (const account of accounts) {
679
- if (ids.has(account.id)) return yield* Effect_exports.fail(new HostAccountsInvalid());
680
- ids.add(account.id);
681
- if (canonical(account.provider) !== canonical(requirement.definition) || !Object.hasOwn(provider.auth, account.method))
682
- return yield* Effect_exports.fail(new HostAccountsInvalid());
683
- const method = provider.auth[account.method];
684
- if (method === void 0) return yield* Effect_exports.fail(new HostAccountsInvalid());
685
- const fields = yield* Schema_exports.decodeUnknownEffect(
686
- method._tag === "secrets" ? method.fields : method.response
687
- )(account.fields);
688
- values.push({ id: account.id, method: account.method, fields });
689
- }
690
- if (many) bound.set(slot, values);
691
- else {
692
- const account = values[0];
693
- if (account === void 0) return yield* Effect_exports.fail(new HostAccountsInvalid());
694
- bound.set(slot, account);
695
- }
696
- }
697
- return { accounts: Object.fromEntries(bound) };
1269
+ const app = yield* bindScope(slots.app, declarations.accounts.app, selected.app);
1270
+ if (selected.user === void 0) return { app: { accounts: app } };
1271
+ const user = yield* bindScope(slots.user, declarations.accounts.user, selected.user);
1272
+ return { app: { accounts: app }, user: { accounts: user } };
698
1273
  }),
699
1274
  new HostAccountsInvalid()
700
1275
  );
701
1276
  }
1277
+ var userAccountIds = (bound) => Object.values(bound.user?.accounts ?? {}).flatMap(
1278
+ (selected) => (Array.isArray(selected) ? selected : [selected]).map(
1279
+ (account) => account.id
1280
+ )
1281
+ );
702
1282
  var fetchLifetime = (signal, background) => Effect_exports.acquireRelease(
703
1283
  Effect_exports.sync(() => {
704
1284
  const controller = new AbortController();
@@ -734,7 +1314,7 @@ var fetchLifetime = (signal, background) => Effect_exports.acquireRelease(
734
1314
  }),
735
1315
  (lifetime) => Effect_exports.sync(lifetime.close)
736
1316
  );
737
- function dispatch(app, request, context, signal) {
1317
+ function dispatch(app, request, context, signal, emitted) {
738
1318
  return Effect_exports.scoped(
739
1319
  Effect_exports.gen(function* () {
740
1320
  if (!isApp(app))
@@ -743,28 +1323,52 @@ function dispatch(app, request, context, signal) {
743
1323
  );
744
1324
  const native = toEffectApp(app);
745
1325
  const secrets = accountSecrets(context.accounts);
746
- const storageFailure = (error) => Effect_exports.fail(new HostOperationFailed(failureDetail(error, secrets)));
747
- const declared = yield* requirements(native.accounts, native.database?.schema);
748
- if (request.operation === "requirements") {
749
- if (native.database !== void 0)
750
- yield* parseDatabaseSchema(native.database.schema).pipe(
751
- Effect_exports.catchTag(
752
- "AppDatabaseError",
753
- (cause) => Effect_exports.fail(
754
- declarationInvalid("The app's account or database declarations are invalid", cause)
755
- )
756
- )
757
- );
1326
+ const ownsSql = hasMigrations(context.files ?? []);
1327
+ const declared = yield* requirements(native.accounts, ownsSql, native.events, native.icons);
1328
+ if (request.operation === "requirements")
758
1329
  return yield* safe2(
759
1330
  () => Schema_exports.decodeUnknownEffect(JsonValue)(declared),
760
1331
  new HostDeclarationInvalid()
761
1332
  );
1333
+ if (request.operation === "migrate") {
1334
+ if (!ownsSql) return yield* new HostOperationNotFound();
1335
+ const storage2 = context.storage;
1336
+ if (storage2 === void 0)
1337
+ return yield* new HostOperationFailed(
1338
+ failureDetail(new Error("This host gave the app no SQL storage."), secrets)
1339
+ );
1340
+ const sources = yield* Schema_exports.decodeUnknownEffect(Schema_exports.Array(SkillFile))(
1341
+ context.files ?? []
1342
+ ).pipe(Effect_exports.mapError(() => new HostDeclarationInvalid()));
1343
+ const applied = yield* migrate(storage2, sources).pipe(
1344
+ Effect_exports.mapError((error) => new HostOperationFailed(failureDetail(error, secrets))),
1345
+ Effect_exports.withSpan("app.sql.migrate")
1346
+ );
1347
+ return yield* safe2(
1348
+ () => Schema_exports.decodeUnknownEffect(JsonValue)(applied),
1349
+ new HostOutputInvalid()
1350
+ );
762
1351
  }
763
1352
  const lifetime = yield* Effect_exports.acquireRelease(
764
1353
  Effect_exports.sync(() => new AbortController()),
765
1354
  (controller) => Effect_exports.sync(() => controller.abort())
766
1355
  );
767
1356
  const invocationSignal = AbortSignal.any([signal, lifetime.signal]);
1357
+ const storage = context.storage;
1358
+ const sqlSession = (step) => !ownsSql ? Effect_exports.succeed({ reader: noDatabase, writer: noDatabase }) : storage === void 0 ? Effect_exports.fail(
1359
+ new HostOperationFailed(
1360
+ failureDetail(new Error("This host gave the app no SQL storage."), secrets)
1361
+ )
1362
+ ) : Effect_exports.acquireRelease(
1363
+ Effect_exports.sync(
1364
+ () => authorSql(storage, {
1365
+ signal: invocationSignal,
1366
+ emitted,
1367
+ ...step === void 0 ? {} : { step }
1368
+ })
1369
+ ),
1370
+ (session2) => Effect_exports.sync(session2.close)
1371
+ );
768
1372
  const deadline = context.deadline === void 0 ? void 0 : yield* safe2(
769
1373
  () => Schema_exports.decodeUnknownEffect(InvocationDeadline)(context.deadline),
770
1374
  new HostInputInvalid()
@@ -791,33 +1395,56 @@ function dispatch(app, request, context, signal) {
791
1395
  declared,
792
1396
  request.requirement,
793
1397
  context,
794
- invocationSignal
1398
+ invocationSignal,
1399
+ deadline
795
1400
  ).pipe(withinDeadline);
796
1401
  let running;
797
- let transactionOpen = false;
798
1402
  const delivery = (request2, signal2) => Effect_exports.suspend(
799
- () => transactionOpen ? Effect_exports.fail(new ElicitationFailed({ reason: "transaction" })) : running !== void 0 && context.elicitation !== void 0 ? context.elicitation(request2, signal2).pipe(
800
- Effect_exports.withSpan("app.tool.elicitation"),
1403
+ () => running !== void 0 && context.elicitation !== void 0 ? context.elicitation(request2, signal2).pipe(
1404
+ // Delivering the question and waiting for its answer is the person's time.
1405
+ owned("person", "app.tool.elicitation"),
801
1406
  Effect_exports.provideContext(running.context)
802
1407
  ) : Effect_exports.fail(new ElicitationFailed({ reason: "unavailable" }))
803
1408
  );
804
1409
  const unavailableWorkflow = () => Effect_exports.fail(new WorkflowFailure({ reason: "unavailable", retryable: false }));
1410
+ const telemetry = yield* captureTelemetry;
1411
+ const control = (operation, work) => work.pipe(
1412
+ owned("executor", workflowControlSpan, {
1413
+ attributes: { "executor.workflow.operation": operation }
1414
+ })
1415
+ );
805
1416
  const workflowControls = {
806
1417
  start: toPromise(
807
- (input2) => context.workflowControls === void 0 ? unavailableWorkflow() : context.workflowControls.start(input2),
808
- invocationSignal
1418
+ (input2) => control(
1419
+ "start",
1420
+ context.workflowControls === void 0 ? unavailableWorkflow() : context.workflowControls.start(input2)
1421
+ ),
1422
+ invocationSignal,
1423
+ telemetry.context
809
1424
  ),
810
1425
  get: toPromise(
811
- (input2) => context.workflowControls === void 0 ? unavailableWorkflow() : context.workflowControls.get(input2),
812
- invocationSignal
1426
+ (input2) => control(
1427
+ "get",
1428
+ context.workflowControls === void 0 ? unavailableWorkflow() : context.workflowControls.get(input2)
1429
+ ),
1430
+ invocationSignal,
1431
+ telemetry.context
813
1432
  ),
814
1433
  list: toPromise(
815
- (input2) => context.workflowControls === void 0 ? unavailableWorkflow() : context.workflowControls.list(input2),
816
- invocationSignal
1434
+ (input2) => control(
1435
+ "list",
1436
+ context.workflowControls === void 0 ? unavailableWorkflow() : context.workflowControls.list(input2)
1437
+ ),
1438
+ invocationSignal,
1439
+ telemetry.context
817
1440
  ),
818
1441
  terminate: toPromise(
819
- (input2) => context.workflowControls === void 0 ? unavailableWorkflow() : context.workflowControls.terminate(input2),
820
- invocationSignal
1442
+ (input2) => control(
1443
+ "terminate",
1444
+ context.workflowControls === void 0 ? unavailableWorkflow() : context.workflowControls.terminate(input2)
1445
+ ),
1446
+ invocationSignal,
1447
+ telemetry.context
821
1448
  )
822
1449
  };
823
1450
  const workflowReads = {
@@ -842,6 +1469,7 @@ function dispatch(app, request, context, signal) {
842
1469
  },
843
1470
  Redacted_exports.value(context.accounts),
844
1471
  invocationSignal,
1472
+ telemetry.context,
845
1473
  deadline
846
1474
  ),
847
1475
  files,
@@ -850,9 +1478,25 @@ function dispatch(app, request, context, signal) {
850
1478
  ),
851
1479
  workflows: workflowReads,
852
1480
  signal: invocationSignal,
853
- fetch: yield* invocationFetch(fetching.signal),
854
- elicit: makeElicit(delivery, invocationSignal)
1481
+ fetch: yield* appInvocationFetch(fetching.signal),
1482
+ elicit: makeElicit(delivery, invocationSignal, telemetry.context)
855
1483
  };
1484
+ const attributable = userAccountIds(bound);
1485
+ const accountIds = [
1486
+ ...Object.values(bound.app.accounts).flatMap(
1487
+ (selected) => (Array.isArray(selected) ? selected : [selected]).map(
1488
+ (account) => account.id
1489
+ )
1490
+ ),
1491
+ ...attributable
1492
+ ];
1493
+ const emitter = (sourceAccount) => makeEmitter({
1494
+ events: native.events ?? {},
1495
+ accounts: accountIds,
1496
+ attributable,
1497
+ ...sourceAccount === void 0 ? {} : { sourceAccount },
1498
+ emitted
1499
+ });
856
1500
  const definition = yield* evaluationSafe(native.evaluate(bound), secrets).pipe(
857
1501
  Effect_exports.withSpan("app.evaluate")
858
1502
  );
@@ -924,10 +1568,11 @@ function dispatch(app, request, context, signal) {
924
1568
  cache: authorCache(
925
1569
  context.cache ?? unavailableCache,
926
1570
  Redacted_exports.value(current.accounts),
927
- signal2
1571
+ signal2,
1572
+ (yield* captureTelemetry).context
928
1573
  ),
929
1574
  files,
930
- fetch: yield* invocationFetch(signal2),
1575
+ fetch: yield* appInvocationFetch(signal2),
931
1576
  signal: signal2,
932
1577
  runId: execution.runId,
933
1578
  stepId,
@@ -967,45 +1612,36 @@ function dispatch(app, request, context, signal) {
967
1612
  ...request.scheduled === true ? { scheduled: true } : {},
968
1613
  schedules: (name) => Object.entries(definition.schedules ?? {}).filter(([, schedule]) => schedule.tool === name).map(([name2, { tool: _tool, ...schedule }]) => ({ name: name2, ...schedule }))
969
1614
  }).pipe(Effect_exports.withSpan("app.catalog.read"));
1615
+ const { tools, routers } = catalog;
970
1616
  return yield* safe2(
971
- () => Schema_exports.encodeEffect(WireCatalog)(catalog).pipe(
972
- Effect_exports.flatMap(Schema_exports.decodeUnknownEffect(JsonValue))
973
- ),
1617
+ () => (request.definitions === "shared" && !summary && tools.every((tool2) => "inputSchema" in tool2) ? Schema_exports.encodeEffect(HostedSharedCatalog)(shareCatalog({ tools, routers })) : Schema_exports.encodeEffect(WireCatalog)(catalog)).pipe(Effect_exports.flatMap(Schema_exports.decodeUnknownEffect(JsonValue))),
974
1618
  new HostDeclarationInvalid()
975
1619
  );
976
1620
  }
977
1621
  if (request.operation === "webhook-complete" || request.operation === "webhook-validate" || request.operation === "webhooks" || request.operation === "webhook-register" || request.operation === "webhook-handle" || request.operation === "webhook-unregister") {
978
- const executeWebhook = (db) => dispatchWebhook(
1622
+ const session2 = yield* sqlSession();
1623
+ return yield* dispatchWebhook(
979
1624
  definition,
980
1625
  request,
981
1626
  {
982
1627
  files,
983
1628
  cache: bound.cache,
984
- accounts: bound.accounts,
1629
+ app: bound.app,
1630
+ ...bound.user === void 0 ? {} : { user: bound.user },
985
1631
  workflows: workflowControls,
986
1632
  signal: bound.signal,
987
1633
  fetch: bound.fetch,
988
- ...db === void 0 || native.database === void 0 ? {} : {
989
- db: authorDatabase(native.database.tables, db, invocationSignal, true)
990
- }
1634
+ sql: session2.writer,
1635
+ events: emitter("sourceAccount" in request ? request.sourceAccount : void 0)
991
1636
  },
992
- Redacted_exports.value(context.accounts)
993
- );
994
- if (native.database === void 0 || !["webhook-register", "webhook-handle", "webhook-unregister"].includes(request.operation))
995
- return yield* executeWebhook();
996
- const storage2 = context.storage ?? unavailableStorage;
997
- return yield* storage2.mutate(native.database.schema, executeWebhook).pipe(
998
- Effect_exports.catchTags({
999
- AppDatabaseError: storageFailure,
1000
- DatabaseLimitExceeded: (error) => Effect_exports.fail(error),
1001
- AppStorageUnavailable: storageFailure,
1002
- AppStorageError: storageFailure
1003
- })
1637
+ native.accounts
1004
1638
  );
1005
1639
  }
1006
1640
  const toolName = request.operation === "call" ? request.tool : request.name;
1007
1641
  const location = locate(definition.tools, toolName);
1008
- const tool = location === void 0 ? void 0 : location.kind === "operation" ? location.operation : yield* evaluationSafe(location.source.resolve(location.name), secrets);
1642
+ const tool = location === void 0 ? void 0 : location.kind === "operation" ? location.operation : yield* evaluationSafe(location.source.resolve(location.name), secrets).pipe(
1643
+ Effect_exports.withSpan("app.tool.resolve")
1644
+ );
1009
1645
  if (tool === void 0)
1010
1646
  return yield* request.operation === "call" ? new HostToolNotFound() : new HostOperationNotFound();
1011
1647
  const requested = request.operation === "call" ? request.kind : request.operation === "query" ? "query" : "mutation";
@@ -1016,7 +1652,7 @@ function dispatch(app, request, context, signal) {
1016
1652
  () => Schema_exports.decodeUnknownEffect(tool.input)(request.input)
1017
1653
  ).pipe(
1018
1654
  Effect_exports.catchCause(
1019
- (cause) => Cause_exports.hasInterrupts(cause) ? Effect_exports.interrupt : Effect_exports.fail(inputInvalid(Cause_exports.squash(cause)))
1655
+ (cause) => Cause_exports.hasInterrupts(cause) ? Effect_exports.interrupt : Effect_exports.fail(inputInvalid(Cause_exports.squash(cause), request.input))
1020
1656
  )
1021
1657
  );
1022
1658
  if (context.approval !== void 0) {
@@ -1061,24 +1697,23 @@ function dispatch(app, request, context, signal) {
1061
1697
  Match_exports.exhaustive
1062
1698
  );
1063
1699
  }
1064
- const execute = (db) => Effect_exports.gen(function* () {
1065
- transactionOpen = db !== void 0;
1700
+ const replay = context.replay === void 0 ? void 0 : yield* safe2(
1701
+ () => Schema_exports.decodeUnknownEffect(WorkflowReplay)(context.replay),
1702
+ new HostInputInvalid()
1703
+ );
1704
+ if (replay !== void 0 && kind !== "mutate") return yield* new HostInputInvalid();
1705
+ const session = yield* sqlSession(replay);
1706
+ const execute = () => Effect_exports.gen(function* () {
1066
1707
  const output = yield* Effect_exports.gen(function* () {
1067
1708
  running = yield* captureTelemetry;
1068
- const fetch = yield* invocationFetch(fetching.signal);
1709
+ const fetch = yield* appInvocationFetch(fetching.signal);
1069
1710
  return yield* tool.run(
1070
1711
  {
1071
1712
  ...bound,
1072
1713
  fetch,
1073
1714
  workflows: kind === "mutate" ? workflowControls : workflowReads,
1074
- ...db === void 0 || native.database === void 0 ? {} : {
1075
- db: authorDatabase(
1076
- native.database.tables,
1077
- db,
1078
- invocationSignal,
1079
- kind === "mutate"
1080
- )
1081
- }
1715
+ sql: kind === "mutate" ? session.writer : session.reader,
1716
+ ...kind === "mutate" ? { events: emitter() } : {}
1082
1717
  },
1083
1718
  input
1084
1719
  );
@@ -1093,14 +1728,16 @@ function dispatch(app, request, context, signal) {
1093
1728
  const error = Cause_exports.squash(cause);
1094
1729
  const provider = parseProviderError(error);
1095
1730
  const response = Schema_exports.decodeUnknownOption(OpenapiResponseError)(error);
1731
+ const mcp = parseMcpError(error, secrets);
1096
1732
  const failure = Schema_exports.decodeUnknownOption(ElicitationFailed)(error);
1733
+ const removed = Schema_exports.is(HostToolNotFound)(error) ? request.operation === "call" ? new HostToolNotFound() : new HostOperationNotFound() : void 0;
1097
1734
  return Effect_exports.fail(
1098
- Option_exports.isSome(provider) ? provider.value : Option_exports.isSome(response) ? new OpenapiResponseError({
1735
+ removed !== void 0 ? removed : Option_exports.isSome(provider) ? leavingProviderError(provider.value, secrets, "call") : Option_exports.isSome(response) ? new OpenapiResponseError({
1099
1736
  code: response.value.code,
1100
1737
  status: response.value.status,
1101
1738
  message: response.value.message,
1102
1739
  ...response.value.recovery === void 0 ? {} : { recovery: response.value.recovery }
1103
- }) : Option_exports.isSome(failure) ? failure.value : new HostOperationFailed(failureDetail(error, secrets))
1740
+ }) : Option_exports.isSome(mcp) ? mcp.value : Option_exports.isSome(failure) ? failure.value : new HostOperationFailed(failureDetail(error, secrets))
1104
1741
  );
1105
1742
  }),
1106
1743
  Effect_exports.withSpan("app.operation.execute", {
@@ -1117,47 +1754,45 @@ function dispatch(app, request, context, signal) {
1117
1754
  new HostOutputInvalid()
1118
1755
  );
1119
1756
  });
1120
- const replay = context.replay === void 0 ? void 0 : yield* safe2(
1121
- () => Schema_exports.decodeUnknownEffect(WorkflowReplay)(context.replay),
1122
- new HostInputInvalid()
1123
- );
1124
- if (replay !== void 0 && kind !== "mutate") return yield* new HostInputInvalid();
1125
- if (native.database === void 0) return yield* withinDeadline(execute());
1126
- const storage = context.storage ?? unavailableStorage;
1127
- return yield* storage[kind === "query" ? "read" : "mutate"](
1128
- native.database.schema,
1129
- (db) => withinDeadline(
1130
- replay === void 0 ? execute(db) : db.once(replay.key, replay.fingerprint, () => execute(db))
1131
- )
1132
- ).pipe(
1133
- Effect_exports.catchTags({
1134
- AppDatabaseError: storageFailure,
1135
- DatabaseLimitExceeded: (error) => Effect_exports.fail(error),
1136
- AppStorageUnavailable: storageFailure,
1137
- AppStorageError: storageFailure
1138
- })
1139
- );
1757
+ return yield* withinDeadline(execute());
1140
1758
  })
1141
1759
  );
1142
1760
  }
1143
- function checkAccount(slots, declared, requirement, context, signal) {
1761
+ function checkAccount(slots, declared, requirement, context, signal, deadline) {
1144
1762
  return Effect_exports.gen(function* () {
1145
- const selection = Object.hasOwn(slots, requirement) ? slots[requirement] : void 0;
1146
- const slot = Object.hasOwn(declared.accounts, requirement) ? declared.accounts[requirement] : void 0;
1763
+ const scope = Object.hasOwn(slots.app, requirement) ? "app" : Object.hasOwn(slots.user, requirement) ? "user" : void 0;
1764
+ if (scope === void 0) return yield* new HostAccountsInvalid();
1765
+ const selection = slots[scope][requirement];
1766
+ const slot = Object.hasOwn(declared.accounts[scope], requirement) ? declared.accounts[scope][requirement] : void 0;
1147
1767
  if (selection === void 0 || slot === void 0) return yield* new HostAccountsInvalid();
1148
1768
  const provider = selection instanceof ManyAccounts ? selection.provider : selection;
1149
1769
  const health = provider.health;
1150
1770
  if (health === void 0) return yield* new HostOperationNotFound();
1151
- const { accounts } = yield* bindAccounts(
1152
- { [requirement]: provider },
1153
- { accounts: { [requirement]: { ...slot, cardinality: "one" } } },
1154
- context
1771
+ const accounts = yield* safe2(
1772
+ () => Effect_exports.gen(function* () {
1773
+ const selected = yield* Schema_exports.decodeUnknownEffect(ResolvedAccounts)(
1774
+ Redacted_exports.value(context.accounts)
1775
+ );
1776
+ const supplied = selected[scope];
1777
+ if (supplied === void 0) return yield* new HostAccountsInvalid();
1778
+ return yield* bindScope(
1779
+ { [requirement]: provider },
1780
+ { [requirement]: { ...slot, cardinality: "one" } },
1781
+ supplied
1782
+ );
1783
+ }),
1784
+ new HostAccountsInvalid()
1155
1785
  ).pipe(Effect_exports.withSpan("app.accounts.bind"));
1156
1786
  const account = accounts[requirement];
1157
1787
  if (account === void 0 || !("id" in account)) return yield* new HostAccountsInvalid();
1158
1788
  const result = yield* Effect_exports.suspend(
1159
1789
  () => Effect_exports.gen(function* () {
1160
- return yield* health.run({ account, fetch: yield* invocationFetch(signal), signal });
1790
+ return yield* health.run({
1791
+ account,
1792
+ fetch: yield* appInvocationFetch(signal),
1793
+ signal,
1794
+ ...deadline === void 0 ? {} : { deadline }
1795
+ });
1161
1796
  })
1162
1797
  ).pipe(
1163
1798
  Effect_exports.catchCause((cause) => {
@@ -1165,8 +1800,9 @@ function checkAccount(slots, declared, requirement, context, signal) {
1165
1800
  const error = Cause_exports.squash(cause);
1166
1801
  const status = Schema_exports.decodeUnknownOption(ResponseStatusError)(error);
1167
1802
  const classified = Option_exports.isSome(status) ? Option_exports.fromNullishOr(httpProviderError(status.value.status)) : parseProviderError(error);
1803
+ const secrets = accountSecrets(context.accounts);
1168
1804
  return Effect_exports.fail(
1169
- Option_exports.isSome(classified) ? accountProviderError(classified.value, account.id) : new HostOperationFailed()
1805
+ Option_exports.isSome(classified) ? leavingProviderError(accountProviderError(classified.value, account.id), secrets) : new HostOperationFailed(failureDetail(error, secrets))
1170
1806
  );
1171
1807
  }),
1172
1808
  Effect_exports.withSpan("app.account.check")
@@ -1180,20 +1816,6 @@ function checkAccount(slots, declared, requirement, context, signal) {
1180
1816
  );
1181
1817
  });
1182
1818
  }
1183
- var parseSkillLoadFailed = (error) => Schema_exports.decodeUnknownOption(SkillLoadFailed)(error).pipe(
1184
- Option_exports.map(
1185
- ({ reason, message, status }) => new SkillLoadFailed({
1186
- reason,
1187
- ...message ? { message } : {},
1188
- ...status === void 0 ? {} : { status }
1189
- })
1190
- )
1191
- );
1192
- var parseMcpError = (error) => Schema_exports.decodeUnknownOption(McpError)(error).pipe(
1193
- Option_exports.map(
1194
- ({ phase, reason, status }) => new McpError({ phase, reason, ...status === void 0 ? {} : { status } })
1195
- )
1196
- );
1197
1819
  var errorStatus = Match_exports.type().pipe(
1198
1820
  Match_exports.tagsExhaustive({
1199
1821
  ProviderError: () => 502,
@@ -1233,20 +1855,37 @@ var createAppHandler = (app) => (request, context) => Effect_exports.gen(functio
1233
1855
  ...command.operation === "call" ? { "executor.tool.name": command.tool } : {}
1234
1856
  });
1235
1857
  let toolError = false;
1236
- const value = yield* dispatch(app, command, context, request.signal).pipe(
1858
+ const emitted = [];
1859
+ const value = yield* dispatch(app, command, context, request.signal, emitted).pipe(
1237
1860
  Effect_exports.provideService(ToolResultObservation, {
1238
1861
  failed: () => {
1239
1862
  toolError = true;
1240
1863
  }
1241
1864
  }),
1242
- Effect_exports.withSpan(`app.${command.operation}`)
1865
+ // Name what failed on the span; the bounded message stays in the reply only. The host
1866
+ // records an app's own error names and codes as `unrecognized` (telemetry `app-records`).
1867
+ Effect_exports.tapError(
1868
+ (error) => (error._tag === "HostEvaluationFailed" || error._tag === "HostOperationFailed" || error._tag === "HostDeclarationInvalid") && error.errorName !== void 0 ? Effect_exports.annotateCurrentSpan({
1869
+ "error.type": error.errorName,
1870
+ ...error.source === void 0 ? {} : { "executor.failure.source": error.source },
1871
+ ...error.code === void 0 ? {} : { "executor.failure.code": error.code }
1872
+ }) : Effect_exports.void
1873
+ ),
1874
+ // Tool operations time themselves, dividing their time by who owned the work in progress.
1875
+ // Other operations do not.
1876
+ command.operation === "call" || command.operation === "query" || command.operation === "mutate" ? timedInvocation(command.operation) : Effect_exports.withSpan(`app.${command.operation}`)
1243
1877
  );
1244
1878
  if (toolError)
1245
1879
  yield* Effect_exports.annotateCurrentSpan({
1246
1880
  "executor.outcome": "failed",
1247
1881
  "error.type": "McpToolError"
1248
1882
  });
1249
- return Response.json({ ok: true, value, ...toolError ? { toolError: true } : {} });
1883
+ return Response.json({
1884
+ ok: true,
1885
+ value,
1886
+ ...toolError ? { toolError: true } : {},
1887
+ ...emitted.length === 0 ? {} : { events: emitted }
1888
+ });
1250
1889
  }).pipe(
1251
1890
  Effect_exports.catch(
1252
1891
  (error) => Schema_exports.encodeEffect(HostError)(error).pipe(
@@ -1257,18 +1896,21 @@ var createAppHandler = (app) => (request, context) => Effect_exports.gen(functio
1257
1896
  )
1258
1897
  ),
1259
1898
  Effect_exports.catchCause(
1260
- (cause) => Cause_exports.hasInterrupts(cause) ? Effect_exports.interrupt : Effect_exports.logError(cause).pipe(
1261
- Effect_exports.andThen(
1262
- Schema_exports.encodeEffect(HostError)(
1263
- declarationInvalid(
1264
- "The app failed while it was loaded",
1265
- Cause_exports.squash(cause),
1266
- accountSecrets(context.accounts)
1899
+ (cause) => Cause_exports.hasInterrupts(cause) ? Effect_exports.interrupt : (
1900
+ // The cause can carry any text the app threw; its caller receives that, not the log.
1901
+ Effect_exports.logError("The app's request failed unexpectedly").pipe(
1902
+ Effect_exports.andThen(
1903
+ Schema_exports.encodeEffect(HostError)(
1904
+ declarationInvalid(
1905
+ "The app failed while it was loaded",
1906
+ Cause_exports.squash(cause),
1907
+ accountSecrets(context.accounts)
1908
+ )
1267
1909
  )
1268
- )
1269
- ),
1270
- Effect_exports.map((error) => Response.json({ ok: false, error }, { status: 500 })),
1271
- Effect_exports.orDie
1910
+ ),
1911
+ Effect_exports.map((error) => Response.json({ ok: false, error }, { status: 500 })),
1912
+ Effect_exports.orDie
1913
+ )
1272
1914
  )
1273
1915
  )
1274
1916
  );