@exponential_app/engine 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +54 -0
  3. package/README.md +35 -2
  4. package/dist/capabilities.js +29 -0
  5. package/dist/capabilities.js.map +9 -0
  6. package/dist/chunks/index-4ntgtkmb.js +6783 -0
  7. package/dist/chunks/index-4ntgtkmb.js.map +13 -0
  8. package/dist/chunks/index-ans80t68.js +382 -0
  9. package/dist/chunks/index-ans80t68.js.map +9 -0
  10. package/dist/chunks/index-ax4aay30.js +315 -0
  11. package/dist/chunks/index-ax4aay30.js.map +10 -0
  12. package/dist/chunks/index-e49v2qpf.js +2177 -0
  13. package/dist/chunks/index-e49v2qpf.js.map +9 -0
  14. package/dist/chunks/index-m2845dn6.js +72 -0
  15. package/dist/chunks/index-m2845dn6.js.map +10 -0
  16. package/dist/chunks/index-md3hz6r2.js +3015 -0
  17. package/dist/chunks/index-md3hz6r2.js.map +19 -0
  18. package/dist/chunks/index-n0acffvs.js +366 -0
  19. package/dist/chunks/index-n0acffvs.js.map +10 -0
  20. package/dist/chunks/index-nrr8x2kq.js +259 -0
  21. package/dist/chunks/index-nrr8x2kq.js.map +10 -0
  22. package/dist/chunks/index-s3xjr701.js +170 -0
  23. package/dist/chunks/index-s3xjr701.js.map +10 -0
  24. package/dist/chunks/index-t89cvabm.js +2301 -0
  25. package/dist/chunks/index-t89cvabm.js.map +10 -0
  26. package/dist/chunks/index-zw0575tv.js +126 -0
  27. package/dist/chunks/index-zw0575tv.js.map +10 -0
  28. package/dist/consent.js +24 -0
  29. package/dist/consent.js.map +9 -0
  30. package/dist/index.js +526 -0
  31. package/dist/index.js.map +11 -0
  32. package/dist/lifecycle.js +11 -0
  33. package/dist/lifecycle.js.map +9 -0
  34. package/dist/outbox.js +27 -0
  35. package/dist/outbox.js.map +9 -0
  36. package/dist/pdf.js +15 -0
  37. package/dist/pdf.js.map +9 -0
  38. package/dist/providers.js +26 -0
  39. package/dist/providers.js.map +9 -0
  40. package/dist/runner.js +24 -0
  41. package/dist/runner.js.map +9 -0
  42. package/dist/tools.js +38 -0
  43. package/dist/tools.js.map +9 -0
  44. package/dist/types/domain-contract/src/app-records.d.ts +3148 -0
  45. package/dist/types/domain-contract/src/app-share.d.ts +222 -0
  46. package/dist/types/domain-contract/src/apps.d.ts +594 -0
  47. package/dist/types/eengine/fixtures/providers.json +55 -0
  48. package/dist/types/eengine/src/broker.d.ts +80 -0
  49. package/dist/types/eengine/src/capabilities.d.ts +86 -0
  50. package/dist/types/eengine/src/consent.d.ts +50 -0
  51. package/dist/types/eengine/src/index.d.ts +11 -0
  52. package/dist/types/eengine/src/instance.d.ts +122 -0
  53. package/dist/types/eengine/src/lifecycle.d.ts +64 -0
  54. package/dist/types/eengine/src/outbox.d.ts +3 -0
  55. package/dist/types/eengine/src/pdf.d.ts +36 -0
  56. package/dist/types/eengine/src/providers.d.ts +38 -0
  57. package/dist/types/eengine/src/runner.d.ts +82 -0
  58. package/dist/types/eengine/src/stores.d.ts +125 -0
  59. package/dist/types/eengine/src/tools.d.ts +79 -0
  60. package/dist/types/eengine/src/web/index.d.ts +20 -0
  61. package/dist/types/eengine/src/web/mcp.d.ts +27 -0
  62. package/dist/types/eengine/src/web/platform.d.ts +91 -0
  63. package/dist/types/eengine/src/web/stores.d.ts +125 -0
  64. package/dist/types/emcp/fixtures/automations.json +521 -0
  65. package/dist/types/emcp/fixtures/hub.json +498 -0
  66. package/dist/types/emcp/src/access.d.ts +55 -0
  67. package/dist/types/emcp/src/audit.d.ts +22 -0
  68. package/dist/types/emcp/src/automations.d.ts +111 -0
  69. package/dist/types/emcp/src/exposes.d.ts +242 -0
  70. package/dist/types/emcp/src/hub.d.ts +63 -0
  71. package/dist/types/emcp/src/index.d.ts +7 -0
  72. package/dist/types/emcp/src/pure.d.ts +11 -0
  73. package/dist/types/emcp/src/upstream.d.ts +288 -0
  74. package/dist/types/emcp/src/vault.d.ts +34 -0
  75. package/dist/web/index.js +791 -0
  76. package/dist/web/index.js.map +13 -0
  77. package/fixtures/bundle.json +1165 -0
  78. package/fixtures/capabilities.json +373 -0
  79. package/fixtures/consent.json +146 -0
  80. package/fixtures/lifecycle.json +125 -0
  81. package/fixtures/outbox.json +8 -0
  82. package/fixtures/providers.json +55 -0
  83. package/fixtures/runner.json +2167 -0
  84. package/package.json +92 -4
@@ -0,0 +1,498 @@
1
+ {
2
+ "$comment": "EXP-1507 (eMCP local hub, epic EXP-1503, 2026-10-31): ONE LOOPBACK ENDPOINT OVER SEVERAL MCP SERVERS, the device-side twin of `/api/mcp/apps`. A hub MOUNTS servers under a name (`serverNamePattern`: lowercase letters, digits and `-`, starting with a letter or digit, at most `serverNameMax`; never `__`, the separator), each an upstream MCP server (Streamable HTTP, the run's headers resolved) or an in-process tool provider; mounting a name twice replaces it. NAMING: the hub lists a mounted tool as exposes.json's qualified name with the SERVER NAME in place of the app slug (`qualifiedToolName(server, tool)`, `<server>__<tool>`, cut + hashed past 64) and resolves a call with `splitToolName` against the run's own list, so a tool whose own name holds `__` is never listed; servers are listed sorted by name and a qualified name an earlier server holds is dropped (`listToolNames`; within a server its own order). GRANTS (`listCases`): the hub admits a RUN with its own bearer token (an earlier token of the run dies; a revoke kills it) and a GRANT: `servers` = server name -> level (`read` | `write`; a server not named = `none`), plus the `teamId` and `userId` the run acts for (null on a device with no team). A tool's access is exposes.json's `upstreamAccess(annotations)` (read iff `readOnlyHint: true`, else write: destructive tools need write like every other); a tool is LISTED and CALLABLE iff access.json's `toolVisible(level, access)`. A CALL (`callCases`) to a name not on the run's list answers MCP's unknown-tool error (JSON-RPC -32602), never a hint that it exists, and is audited `denied` with error `unknownTool`; a listed call reaches its server once, as `tool` with the arguments as received. AUDIT: EVERY tools/call, denied ones included, writes ONE `mcp_calls` row (audit.json's columns, handed to the host's sink after the call, never blocking it): `id` a fresh uuid, `team_id` / `user_id` = the grant's, `client_id` = the run's session id (on a device the run is the client), `app_id` null, `tool` = the name as called, `status` = audit.json's `callStatus` of the outcome, `error` = `errors[outcome]` (null for `ok` and `isError`), `latency_ms` = the hub's wall time rounded down, `args_hash` = audit.json's `argsHash` of the arguments as received, `created_at` = ISO-8601 UTC with milliseconds. Outcomes (`outcomes`): `ok` = a result; `isError` = a result with `isError: true` -> failed; `invalidParams` = the server answered JSON-RPC -32602 -> invalid; `rpcError` = any other JSON-RPC error, `unreachable` = no connection or an HTTP error, `timeout` = no answer in time -> upstream; `unknown` = not listed for the run -> denied. Retention is the host's (audit.json `retentionDays`).",
3
+ "serverNamePattern": "^[a-z0-9][a-z0-9-]*$",
4
+ "serverNameMax": 32,
5
+ "outcomes": {
6
+ "ok": "ok",
7
+ "isError": "failed",
8
+ "invalidParams": "invalid",
9
+ "rpcError": "upstream",
10
+ "unreachable": "upstream",
11
+ "timeout": "upstream",
12
+ "unknown": "denied"
13
+ },
14
+ "errors": {
15
+ "ok": null,
16
+ "isError": null,
17
+ "invalidParams": "invalidArguments",
18
+ "rpcError": "upstreamFailed",
19
+ "unreachable": "upstreamFailed",
20
+ "timeout": "upstreamTimeout",
21
+ "unknown": "unknownTool"
22
+ },
23
+ "unknownToolCode": -32602,
24
+ "serverNameCases": [
25
+ {
26
+ "name": "files",
27
+ "expected": true
28
+ },
29
+ {
30
+ "name": "my-server-2",
31
+ "expected": true
32
+ },
33
+ {
34
+ "name": "2fa",
35
+ "expected": true
36
+ },
37
+ {
38
+ "name": "",
39
+ "expected": false
40
+ },
41
+ {
42
+ "name": "Files",
43
+ "expected": false
44
+ },
45
+ {
46
+ "name": "my_server",
47
+ "expected": false
48
+ },
49
+ {
50
+ "name": "a__b",
51
+ "expected": false
52
+ },
53
+ {
54
+ "name": "-lead",
55
+ "expected": false
56
+ },
57
+ {
58
+ "name": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
59
+ "expected": false
60
+ },
61
+ {
62
+ "name": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
63
+ "expected": true
64
+ }
65
+ ],
66
+ "servers": [
67
+ {
68
+ "name": "files",
69
+ "tools": [
70
+ {
71
+ "name": "read_file",
72
+ "annotations": {
73
+ "readOnlyHint": true
74
+ }
75
+ },
76
+ {
77
+ "name": "write_file"
78
+ },
79
+ {
80
+ "name": "delete_file",
81
+ "annotations": {
82
+ "destructiveHint": true
83
+ }
84
+ }
85
+ ]
86
+ },
87
+ {
88
+ "name": "computer",
89
+ "tools": [
90
+ {
91
+ "name": "screenshot",
92
+ "annotations": {
93
+ "readOnlyHint": true
94
+ }
95
+ },
96
+ {
97
+ "name": "click",
98
+ "annotations": {
99
+ "readOnlyHint": false
100
+ }
101
+ },
102
+ {
103
+ "name": "bad__name",
104
+ "annotations": {
105
+ "readOnlyHint": true
106
+ }
107
+ }
108
+ ]
109
+ },
110
+ {
111
+ "name": "linear",
112
+ "tools": [
113
+ {
114
+ "name": "list_issues",
115
+ "annotations": {
116
+ "readOnlyHint": true
117
+ }
118
+ }
119
+ ]
120
+ }
121
+ ],
122
+ "listCases": [
123
+ {
124
+ "name": "write on files, read on computer, nothing on linear",
125
+ "grant": {
126
+ "servers": {
127
+ "files": "write",
128
+ "computer": "read"
129
+ },
130
+ "teamId": "t1",
131
+ "userId": "u1"
132
+ },
133
+ "expected": [
134
+ "computer__screenshot",
135
+ "files__read_file",
136
+ "files__write_file",
137
+ "files__delete_file"
138
+ ]
139
+ },
140
+ {
141
+ "name": "no grant lists nothing",
142
+ "grant": {
143
+ "servers": {},
144
+ "teamId": null,
145
+ "userId": null
146
+ },
147
+ "expected": []
148
+ },
149
+ {
150
+ "name": "read everywhere lists only read-only tools",
151
+ "grant": {
152
+ "servers": {
153
+ "files": "read",
154
+ "computer": "read",
155
+ "linear": "read"
156
+ },
157
+ "teamId": null,
158
+ "userId": null
159
+ },
160
+ "expected": [
161
+ "computer__screenshot",
162
+ "files__read_file",
163
+ "linear__list_issues"
164
+ ]
165
+ },
166
+ {
167
+ "name": "an unknown server in the grant lists nothing extra",
168
+ "grant": {
169
+ "servers": {
170
+ "ghost": "write"
171
+ },
172
+ "teamId": null,
173
+ "userId": null
174
+ },
175
+ "expected": []
176
+ }
177
+ ],
178
+ "callCases": [
179
+ {
180
+ "name": "a granted read tool answers",
181
+ "grant": {
182
+ "servers": {
183
+ "files": "write",
184
+ "computer": "read"
185
+ },
186
+ "teamId": "t1",
187
+ "userId": "u1"
188
+ },
189
+ "run": "run-1",
190
+ "call": {
191
+ "name": "files__read_file",
192
+ "arguments": {
193
+ "path": "/tmp/a"
194
+ }
195
+ },
196
+ "upstream": "ok",
197
+ "expected": {
198
+ "routed": {
199
+ "server": "files",
200
+ "tool": "read_file"
201
+ },
202
+ "row": {
203
+ "teamId": "t1",
204
+ "userId": "u1",
205
+ "clientId": "run-1",
206
+ "appId": null,
207
+ "tool": "files__read_file",
208
+ "status": "ok",
209
+ "error": null,
210
+ "argsHash": "sha256:15df809ad5fadb66f0b31bafc206dcfe620d8da8767fb76e41d3603a45dc870d"
211
+ }
212
+ }
213
+ },
214
+ {
215
+ "name": "a granted write tool that errors is failed",
216
+ "grant": {
217
+ "servers": {
218
+ "files": "write",
219
+ "computer": "read"
220
+ },
221
+ "teamId": "t1",
222
+ "userId": "u1"
223
+ },
224
+ "run": "run-1",
225
+ "call": {
226
+ "name": "files__write_file",
227
+ "arguments": {
228
+ "path": "/tmp/a",
229
+ "text": "x"
230
+ }
231
+ },
232
+ "upstream": "isError",
233
+ "expected": {
234
+ "routed": {
235
+ "server": "files",
236
+ "tool": "write_file"
237
+ },
238
+ "row": {
239
+ "teamId": "t1",
240
+ "userId": "u1",
241
+ "clientId": "run-1",
242
+ "appId": null,
243
+ "tool": "files__write_file",
244
+ "status": "failed",
245
+ "error": null,
246
+ "argsHash": "sha256:a835aef067e005a76bff09fcbb9b3706400153a45f58557f82d0a2ed10d24357"
247
+ }
248
+ }
249
+ },
250
+ {
251
+ "name": "a write tool on a read grant is unknown",
252
+ "grant": {
253
+ "servers": {
254
+ "files": "write",
255
+ "computer": "read"
256
+ },
257
+ "teamId": "t1",
258
+ "userId": "u1"
259
+ },
260
+ "run": "run-1",
261
+ "call": {
262
+ "name": "computer__click",
263
+ "arguments": {
264
+ "x": 1,
265
+ "y": 2
266
+ }
267
+ },
268
+ "upstream": null,
269
+ "expected": {
270
+ "routed": null,
271
+ "row": {
272
+ "teamId": "t1",
273
+ "userId": "u1",
274
+ "clientId": "run-1",
275
+ "appId": null,
276
+ "tool": "computer__click",
277
+ "status": "denied",
278
+ "error": "unknownTool",
279
+ "argsHash": "sha256:689a8f1db95402580476e38c264278ce7b1e664320cfb4e9ae8d3a908cf09964"
280
+ }
281
+ }
282
+ },
283
+ {
284
+ "name": "a server outside the grant is unknown",
285
+ "grant": {
286
+ "servers": {
287
+ "files": "write",
288
+ "computer": "read"
289
+ },
290
+ "teamId": "t1",
291
+ "userId": "u1"
292
+ },
293
+ "run": "run-1",
294
+ "call": {
295
+ "name": "linear__list_issues",
296
+ "arguments": null
297
+ },
298
+ "upstream": null,
299
+ "expected": {
300
+ "routed": null,
301
+ "row": {
302
+ "teamId": "t1",
303
+ "userId": "u1",
304
+ "clientId": "run-1",
305
+ "appId": null,
306
+ "tool": "linear__list_issues",
307
+ "status": "denied",
308
+ "error": "unknownTool",
309
+ "argsHash": "sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a"
310
+ }
311
+ }
312
+ },
313
+ {
314
+ "name": "a name that is no tool is unknown",
315
+ "grant": {
316
+ "servers": {
317
+ "files": "write",
318
+ "computer": "read"
319
+ },
320
+ "teamId": "t1",
321
+ "userId": "u1"
322
+ },
323
+ "run": "run-1",
324
+ "call": {
325
+ "name": "exponential_issues",
326
+ "arguments": {
327
+ "op": "get"
328
+ }
329
+ },
330
+ "upstream": null,
331
+ "expected": {
332
+ "routed": null,
333
+ "row": {
334
+ "teamId": "t1",
335
+ "userId": "u1",
336
+ "clientId": "run-1",
337
+ "appId": null,
338
+ "tool": "exponential_issues",
339
+ "status": "denied",
340
+ "error": "unknownTool",
341
+ "argsHash": "sha256:9947f251d5afe097024f2fda1a3628ca26263332cc01f991c32fbee174509b5e"
342
+ }
343
+ }
344
+ },
345
+ {
346
+ "name": "arguments the server refuses are invalid",
347
+ "grant": {
348
+ "servers": {
349
+ "files": "write",
350
+ "computer": "read"
351
+ },
352
+ "teamId": "t1",
353
+ "userId": "u1"
354
+ },
355
+ "run": "run-1",
356
+ "call": {
357
+ "name": "files__read_file",
358
+ "arguments": {
359
+ "path": 3
360
+ }
361
+ },
362
+ "upstream": "invalidParams",
363
+ "expected": {
364
+ "routed": {
365
+ "server": "files",
366
+ "tool": "read_file"
367
+ },
368
+ "row": {
369
+ "teamId": "t1",
370
+ "userId": "u1",
371
+ "clientId": "run-1",
372
+ "appId": null,
373
+ "tool": "files__read_file",
374
+ "status": "invalid",
375
+ "error": "invalidArguments",
376
+ "argsHash": "sha256:e68a0cd06893b799b492d46c161213bb510a98800fa5ae27ee9ee86893a67a2c"
377
+ }
378
+ }
379
+ },
380
+ {
381
+ "name": "an unreachable server is upstream",
382
+ "grant": {
383
+ "servers": {
384
+ "files": "write",
385
+ "computer": "read"
386
+ },
387
+ "teamId": "t1",
388
+ "userId": "u1"
389
+ },
390
+ "run": "run-1",
391
+ "call": {
392
+ "name": "computer__screenshot",
393
+ "arguments": {}
394
+ },
395
+ "upstream": "unreachable",
396
+ "expected": {
397
+ "routed": {
398
+ "server": "computer",
399
+ "tool": "screenshot"
400
+ },
401
+ "row": {
402
+ "teamId": "t1",
403
+ "userId": "u1",
404
+ "clientId": "run-1",
405
+ "appId": null,
406
+ "tool": "computer__screenshot",
407
+ "status": "upstream",
408
+ "error": "upstreamFailed",
409
+ "argsHash": "sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a"
410
+ }
411
+ }
412
+ },
413
+ {
414
+ "name": "a server that never answers is upstream",
415
+ "grant": {
416
+ "servers": {
417
+ "files": "write",
418
+ "computer": "read"
419
+ },
420
+ "teamId": "t1",
421
+ "userId": "u1"
422
+ },
423
+ "run": "run-1",
424
+ "call": {
425
+ "name": "files__delete_file",
426
+ "arguments": {
427
+ "path": "/tmp/a"
428
+ }
429
+ },
430
+ "upstream": "timeout",
431
+ "expected": {
432
+ "routed": {
433
+ "server": "files",
434
+ "tool": "delete_file"
435
+ },
436
+ "row": {
437
+ "teamId": "t1",
438
+ "userId": "u1",
439
+ "clientId": "run-1",
440
+ "appId": null,
441
+ "tool": "files__delete_file",
442
+ "status": "upstream",
443
+ "error": "upstreamTimeout",
444
+ "argsHash": "sha256:15df809ad5fadb66f0b31bafc206dcfe620d8da8767fb76e41d3603a45dc870d"
445
+ }
446
+ }
447
+ },
448
+ {
449
+ "name": "another rpc error is upstream",
450
+ "grant": {
451
+ "servers": {
452
+ "files": "write",
453
+ "computer": "read"
454
+ },
455
+ "teamId": "t1",
456
+ "userId": "u1"
457
+ },
458
+ "run": "run-1",
459
+ "call": {
460
+ "name": "files__read_file",
461
+ "arguments": {
462
+ "path": "/tmp/b"
463
+ }
464
+ },
465
+ "upstream": "rpcError",
466
+ "expected": {
467
+ "routed": {
468
+ "server": "files",
469
+ "tool": "read_file"
470
+ },
471
+ "row": {
472
+ "teamId": "t1",
473
+ "userId": "u1",
474
+ "clientId": "run-1",
475
+ "appId": null,
476
+ "tool": "files__read_file",
477
+ "status": "upstream",
478
+ "error": "upstreamFailed",
479
+ "argsHash": "sha256:e877f6ab58c441e61056d01fc02073f833cbf957e2849be594f33996e10be718"
480
+ }
481
+ }
482
+ }
483
+ ],
484
+ "createdAtCases": [
485
+ {
486
+ "ms": 0,
487
+ "expected": "1970-01-01T00:00:00.000Z"
488
+ },
489
+ {
490
+ "ms": 1793491200123,
491
+ "expected": "2026-11-01T00:00:00.123Z"
492
+ },
493
+ {
494
+ "ms": 951782400000,
495
+ "expected": "2000-02-29T00:00:00.000Z"
496
+ }
497
+ ]
498
+ }
@@ -0,0 +1,55 @@
1
+ import type { ToolAccess } from "./exposes.js";
2
+ export declare const GRANT_KINDS: readonly GrantKind[];
3
+ export declare const GRANT_ACCESS: readonly GrantAccess[];
4
+ export declare const POLICY_LEVELS: readonly Level[];
5
+ export type GrantKind = `builtin` | `apps`;
6
+ /** The consent page's Read only | Full. */
7
+ export type GrantAccess = `read` | `full`;
8
+ export type Level = `none` | `read` | `write`;
9
+ /** The `mcp_grants` columns this contract reads. */
10
+ export interface Grant {
11
+ kind: GrantKind;
12
+ allTeams: boolean;
13
+ teamIds: readonly string[];
14
+ boardIds: readonly string[];
15
+ allApps: boolean;
16
+ appIds: readonly string[];
17
+ access: GrantAccess;
18
+ }
19
+ /** A grant's access as a level (`full` = `write`). */
20
+ export declare function grantLevel(access: GrantAccess): Level;
21
+ /** Every reason a grant row is malformed (empty = valid). */
22
+ export declare function grantErrors(grant: Grant): string[];
23
+ /** The member's policy for an app (the Settings › MCP Access table); no
24
+ * row = `write`. */
25
+ export declare function memberPolicy(row: Level | null | undefined): Level;
26
+ export interface AccessInput {
27
+ grant: Grant;
28
+ /** The app's id and team. */
29
+ appId: string;
30
+ teamId: string;
31
+ /** The app is trashed. */
32
+ appDeleted: boolean;
33
+ /** The caller's role in the app's team, null = not a member. */
34
+ role: string | null;
35
+ /** The caller's policy row for this app, null = none. */
36
+ policy: Level | null;
37
+ }
38
+ /** Effective access to one app = grant ∩ the member's policy; `none` for
39
+ * an app the grant does not cover, a trashed app, a non-member or a
40
+ * malformed grant. */
41
+ export declare function effectiveAccess(input: AccessInput): Level;
42
+ /** May a caller with `level` see (and call) a tool of `access`? A read
43
+ * token never sees a write tool. */
44
+ export declare function toolVisible(level: Level, access: ToolAccess): boolean;
45
+ export interface AssistantScopeInput {
46
+ /** The member's hidden personal team (the Assistant's own conversation). */
47
+ personalTeamId: string;
48
+ /** The teams the member belongs to now, in their order. */
49
+ memberTeamIds: readonly string[];
50
+ /** The composer's Teams pick on the conversation root; null = every team. */
51
+ pickedTeamIds: readonly string[] | null;
52
+ }
53
+ /** #EXP-1720: the teams an Assistant tree may touch this turn: the
54
+ * personal team, then the picked teams ∩ the member's (null = all). */
55
+ export declare function assistantScope(input: AssistantScopeInput): string[];
@@ -0,0 +1,22 @@
1
+ export declare const CALL_STATUSES: readonly CallStatus[];
2
+ export declare const AUDIT_RETENTION_DAYS: number;
3
+ export type CallStatus = `ok` | `denied` | `invalid` | `failed` | `upstream`;
4
+ export { canonicalJson } from "./pure.js";
5
+ /** The row's `args_hash`: `sha256:` + the hex SHA-256 of the canonical
6
+ * JSON of the call's arguments (none = `{}`). Never the arguments. */
7
+ export declare function argsHash(args: unknown): string;
8
+ export interface CallOutcome {
9
+ /** The tool was not listed for the caller, or a call-time check refused. */
10
+ denied?: boolean;
11
+ /** The arguments failed the tool's input schema. */
12
+ invalid?: boolean;
13
+ /** The upstream answered an error or could not be reached. */
14
+ upstreamError?: boolean;
15
+ /** The run itself failed (the result is an error). */
16
+ error?: boolean;
17
+ }
18
+ /** The row's `status`, first match wins: denied, invalid, upstream,
19
+ * failed, ok. */
20
+ export declare function callStatus(outcome: CallOutcome): CallStatus;
21
+ /** Is a row created at `createdAt` past retention at `now` (ISO times)? */
22
+ export declare function auditExpired(createdAt: string, now: string): boolean;
@@ -0,0 +1,111 @@
1
+ import fixture from "../fixtures/automations.json" with { type: "json" };
2
+ import { type Expose, type ExposeRun } from "./exposes.js";
3
+ export declare const AUTOMATIONS_LIMITS: {
4
+ namePattern: string;
5
+ nameMax: number;
6
+ maxAutomations: number;
7
+ minScheduleGapMinutes: number;
8
+ eventPattern: string;
9
+ eventMax: number;
10
+ runsPerMinute: number;
11
+ clientPrefix: string;
12
+ };
13
+ export declare const AUTOMATION_ERRORS: readonly string[];
14
+ export type AutomationTrigger = {
15
+ schedule: {
16
+ cron: string;
17
+ tz?: string;
18
+ };
19
+ } | {
20
+ record: {
21
+ kind: string;
22
+ op: `create` | `update` | `delete`;
23
+ };
24
+ } | {
25
+ event: string;
26
+ } | {
27
+ webhook: {
28
+ name: string;
29
+ };
30
+ };
31
+ export interface Automation {
32
+ name: string;
33
+ on: AutomationTrigger;
34
+ run: ExposeRun;
35
+ /** A member's email; default = the publisher (`resolveSponsor`). */
36
+ sponsor?: string;
37
+ }
38
+ export type TriggerKind = `schedule` | `record` | `event` | `webhook`;
39
+ export interface AutomationError {
40
+ index: number | null;
41
+ code: string;
42
+ }
43
+ export interface AutomationContext {
44
+ kinds: readonly string[];
45
+ upstreams: readonly string[];
46
+ }
47
+ /** The kind of an automation's `on` (exactly one key), else null. */
48
+ export declare function triggerKind(on: unknown): TriggerKind | null;
49
+ /** One cron field's values (`*`, lists, ranges, steps; digits only), or null. */
50
+ export declare function cronFieldValues(raw: string, min: number, max: number): number[] | null;
51
+ /** A schedule's check: null = fine, else `schedule` | `scheduleTooOften`.
52
+ * The gap = the smallest distance between the minutes of a day it fires
53
+ * on (minute × hour, circular over the day). */
54
+ export declare function scheduleError(schedule: unknown): `schedule` | `scheduleTooOften` | null;
55
+ /** Every `${…}` of a value is a root the trigger carries (`templateRoots`):
56
+ * `record.<path>`, `event.<path>`, `firedAt`. */
57
+ export declare function templatesValid(value: unknown, kind: TriggerKind): boolean;
58
+ /** Rewrites `${record.…}`, `${event.…}`, `${firedAt}` to exposes'
59
+ * `${input.<root>…}`, so `resolveArgs` fills them. */
60
+ export declare function toInputTemplates<T>(value: T): T;
61
+ /** The run with its templates filled from the trigger's input
62
+ * `{record?, event?, firedAt}`. */
63
+ export declare function resolveAutomationRun(run: ExposeRun, input: Record<string, unknown>): ExposeRun;
64
+ /** The run as an exposes binding (the broker runs it like a tool). */
65
+ export declare function automationExpose(automation: Pick<Automation, `name` | `run`>): Expose;
66
+ /** Every error of an app's `automations`, in order (empty = valid). */
67
+ export declare function validateAutomations(list: unknown, ctx: AutomationContext): AutomationError[];
68
+ /** The trigger a record write, an event or a webhook delivery is, for
69
+ * `matchAutomations`; `byAgent` = the slug of the agent whose session
70
+ * tree caused it. */
71
+ export type FiredBy = ({
72
+ record: {
73
+ kind: string;
74
+ op: string;
75
+ };
76
+ } | {
77
+ event: string;
78
+ } | {
79
+ webhook: string;
80
+ }) & {
81
+ byAgent?: string | null;
82
+ };
83
+ /** The automations a record write or event fires, in package order. LOOP
84
+ * GUARD: a write or event caused by an automation run fires nothing; one
85
+ * caused from an agent's tree (`byAgent`) fires no hand-off to that agent. */
86
+ export declare function matchAutomations(list: unknown, firedBy: FiredBy, fromAutomation: boolean): string[];
87
+ export interface SponsorMember {
88
+ id: string;
89
+ email: string;
90
+ role: string;
91
+ /** The member's app access (`none` | `read` | `write`). */
92
+ level: string;
93
+ }
94
+ export type SponsorState = (typeof fixture.sponsorStates)[number];
95
+ export type SponsorResult = {
96
+ userId: string;
97
+ } | {
98
+ paused: SponsorState;
99
+ };
100
+ /** C23: who an automation acts as, or why it is paused. */
101
+ export declare function resolveSponsor(input: {
102
+ declared: string | null | undefined;
103
+ publishedBy: string | null | undefined;
104
+ members: readonly SponsorMember[];
105
+ }): SponsorResult;
106
+ /** Whether another run may start (the app's runs of the last 60 s). */
107
+ export declare function rateAllows(runsLastMinute: number): boolean;
108
+ /** The audit row's `client_id`. */
109
+ export declare function automationClientId(name: string): string;
110
+ /** The audit row's `tool`. */
111
+ export declare function automationTool(run: ExposeRun): string;