@superpanel-sdk/runtime 0.5.0 → 0.7.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 (189) hide show
  1. package/README.md +3 -3
  2. package/dist/builder/codegen/emit.d.ts +5 -10
  3. package/dist/builder/codegen/emit.js +100 -106
  4. package/dist/builder/codegen/generate.d.ts +3 -5
  5. package/dist/builder/codegen/generate.js +13 -32
  6. package/dist/builder/company-imports.d.ts +5 -0
  7. package/dist/builder/company-imports.js +9 -1
  8. package/dist/builder/compile/compile-sequence.d.ts +13 -11
  9. package/dist/builder/compile/compile-sequence.js +135 -83
  10. package/dist/builder/compile/function-code.d.ts +15 -9
  11. package/dist/builder/compile/function-code.js +59 -29
  12. package/dist/builder/compile/registry.d.ts +4 -4
  13. package/dist/builder/compile/registry.js +14 -12
  14. package/dist/builder/errors.d.ts +9 -0
  15. package/dist/builder/errors.js +17 -0
  16. package/dist/builder/function-params.d.ts +10 -0
  17. package/dist/builder/function-params.js +203 -0
  18. package/dist/builder/load.d.ts +17 -17
  19. package/dist/builder/load.js +27 -25
  20. package/dist/builder/paths.d.ts +6 -1
  21. package/dist/builder/paths.js +16 -2
  22. package/dist/builder/schedules.d.ts +9 -7
  23. package/dist/builder/schedules.js +20 -21
  24. package/dist/builder/typecheck.d.ts +1 -1
  25. package/dist/builder/typecheck.js +3 -6
  26. package/dist/builder/types.d.ts +41 -5
  27. package/dist/builder/types.js +19 -0
  28. package/dist/cli/calls/index.d.ts +177 -0
  29. package/dist/cli/calls/index.js +96 -0
  30. package/dist/cli/debug/index.d.ts +31 -0
  31. package/dist/cli/debug/index.js +119 -0
  32. package/dist/cli/exec/index.d.ts +39 -0
  33. package/dist/cli/exec/index.js +138 -0
  34. package/dist/cli/exec/session.d.ts +48 -0
  35. package/dist/cli/exec/session.js +82 -0
  36. package/dist/cli/exec/trace.d.ts +18 -0
  37. package/dist/cli/exec/trace.js +52 -0
  38. package/dist/cli/index.js +84 -1
  39. package/dist/cli/logs/index.d.ts +47 -0
  40. package/dist/cli/logs/index.js +113 -0
  41. package/dist/cli/mobile/index.d.ts +12 -0
  42. package/dist/cli/mobile/index.js +405 -0
  43. package/dist/cli/serve/index.d.ts +40 -4
  44. package/dist/cli/serve/index.js +167 -28
  45. package/dist/cli/turns/index.d.ts +105 -0
  46. package/dist/cli/turns/index.js +128 -0
  47. package/dist/cli/turns/run.d.ts +13 -0
  48. package/dist/cli/turns/run.js +57 -0
  49. package/dist/cli/turns/shadow.d.ts +112 -0
  50. package/dist/cli/turns/shadow.js +54 -0
  51. package/dist/cli/type-gen/index.js +0 -1
  52. package/dist/compute-host/compute-host.d.ts +5 -6
  53. package/dist/compute-host/compute-host.js +4 -13
  54. package/dist/compute-host/source-compute-host.d.ts +5 -7
  55. package/dist/compute-host/source-compute-host.js +12 -33
  56. package/dist/core/case-orchestrator/contact-hours.d.ts +26 -6
  57. package/dist/core/case-orchestrator/contact-hours.js +27 -8
  58. package/dist/core/case-orchestrator/index.d.ts +1 -4
  59. package/dist/core/case-orchestrator/index.js +1 -4
  60. package/dist/core/case-orchestrator/render.d.ts +1 -1
  61. package/dist/core/case-orchestrator/render.js +15 -10
  62. package/dist/core/case-orchestrator/types.d.ts +83 -128
  63. package/dist/core/case-orchestrator/types.js +0 -4
  64. package/dist/core/context/email-message.d.ts +40 -1
  65. package/dist/core/context/email-message.js +27 -0
  66. package/dist/core/context/email.d.ts +13 -1
  67. package/dist/core/context/email.js +2 -3
  68. package/dist/core/context/exchanges.d.ts +25 -0
  69. package/dist/core/context/exchanges.js +25 -7
  70. package/dist/core/context/instances.d.ts +31 -2
  71. package/dist/core/context/instances.js +18 -4
  72. package/dist/core/context/platform-registry.d.ts +35 -0
  73. package/dist/core/context/sms.d.ts +30 -0
  74. package/dist/core/context/sms.js +20 -0
  75. package/dist/core/context/types.d.ts +29 -1
  76. package/dist/core/conversation/index.d.ts +58 -4
  77. package/dist/core/conversation/index.js +123 -4
  78. package/dist/core/functions/errors.d.ts +74 -0
  79. package/dist/core/functions/errors.js +26 -0
  80. package/dist/core/functions/events.d.ts +23 -78
  81. package/dist/core/functions/events.js +8 -17
  82. package/dist/core/functions/schedules.d.ts +9 -10
  83. package/dist/core/functions/schedules.js +2 -2
  84. package/dist/core/functions/types.d.ts +17 -72
  85. package/dist/core/functions/types.js +1 -9
  86. package/dist/core/page/define.d.ts +230 -160
  87. package/dist/core/page/define.js +145 -104
  88. package/dist/core/page/envelope.d.ts +55 -0
  89. package/dist/core/page/envelope.js +4 -0
  90. package/dist/core/page/fns.d.ts +448 -0
  91. package/dist/core/page/fns.js +539 -0
  92. package/dist/core/page/memory-reader.d.ts +27 -0
  93. package/dist/core/page/memory-reader.js +299 -0
  94. package/dist/core/page/page-host.d.ts +20 -0
  95. package/dist/core/page/page-host.js +57 -0
  96. package/dist/core/page/records-query.d.ts +234 -0
  97. package/dist/core/page/records-query.js +780 -0
  98. package/dist/core/page/render.d.ts +67 -25
  99. package/dist/core/page/render.js +492 -272
  100. package/dist/core/page/sandbox.d.ts +85 -0
  101. package/dist/core/page/sandbox.js +357 -0
  102. package/dist/core/page/time-format.d.ts +27 -0
  103. package/dist/core/page/time-format.js +84 -0
  104. package/dist/core/page/typed.d.ts +165 -159
  105. package/dist/core/page/workspace.d.ts +25 -1
  106. package/dist/core/page/workspace.js +48 -13
  107. package/dist/core/party/schema.d.ts +74 -3
  108. package/dist/core/party/schema.js +39 -6
  109. package/dist/core/party/types.d.ts +8 -13
  110. package/dist/core/sequence/define.d.ts +1 -2
  111. package/dist/core/slot/company-metadata.d.ts +11 -22
  112. package/dist/core/slot/company-metadata.js +12 -39
  113. package/dist/core/slot/compile-schema.js +6 -6
  114. package/dist/core/slot/field.d.ts +1 -2
  115. package/dist/core/slot/field.js +0 -2
  116. package/dist/core/slot/index.d.ts +3 -3
  117. package/dist/core/slot/index.js +3 -3
  118. package/dist/core/slot/naming.d.ts +1 -1
  119. package/dist/core/slot/naming.js +1 -1
  120. package/dist/core/slot/platform-fields.d.ts +4 -4
  121. package/dist/core/slot/platform-fields.js +5 -5
  122. package/dist/core/slot/schema-compat.d.ts +8 -7
  123. package/dist/core/slot/schema-compat.js +22 -31
  124. package/dist/core/slot/schema.d.ts +3 -59
  125. package/dist/core/slot/schema.js +0 -15
  126. package/dist/core/slot/types.d.ts +4 -10
  127. package/dist/core/slot/value.d.ts +2 -0
  128. package/dist/core/slot/value.js +6 -2
  129. package/dist/dev-overlay/protocol.d.ts +38 -5
  130. package/dist/fn-context/build-context.js +10 -4
  131. package/dist/fn-context/fake/index.d.ts +2 -0
  132. package/dist/fn-context/fake/index.js +5 -3
  133. package/dist/fn-context/fn-params.d.ts +2 -5
  134. package/dist/fn-context/fn-params.js +6 -9
  135. package/dist/fn-context/index.d.ts +1 -1
  136. package/dist/fn-context/make-ctx.js +5 -4
  137. package/dist/fn-context/rpc/case-orchestrator-tools.d.ts +11 -3
  138. package/dist/fn-context/rpc/case-orchestrator-tools.js +0 -1
  139. package/dist/fn-context/rpc/errors.d.ts +13 -0
  140. package/dist/fn-context/rpc/errors.js +22 -0
  141. package/dist/fn-context/rpc/index.d.ts +1 -1
  142. package/dist/fn-context/rpc/index.js +1 -1
  143. package/dist/fn-context/rpc/integration/index.d.ts +11 -1
  144. package/dist/fn-context/rpc/log.d.ts +11 -1
  145. package/dist/fn-context/rpc/platform.d.ts +11 -1
  146. package/dist/fn-context/rpc/records.d.ts +13 -6
  147. package/dist/fn-context/rpc/records.js +2 -4
  148. package/dist/fn-context/rpc/trpc-client.d.ts +42 -0
  149. package/dist/fn-context/rpc/trpc-client.js +151 -22
  150. package/dist/fn-context/rpc/trpc-init.d.ts +19 -1
  151. package/dist/fn-context/rpc/trpc-init.js +10 -4
  152. package/dist/fn-context/rpc/trpc-router-factory.d.ts +66 -8
  153. package/dist/fn-context/run-scope.js +1 -1
  154. package/dist/fn-context/types.d.ts +20 -6
  155. package/dist/index.d.ts +11 -12
  156. package/dist/index.js +5 -8
  157. package/dist/logger.d.ts +4 -0
  158. package/dist/logger.js +8 -0
  159. package/dist/platform.d.ts +32 -33
  160. package/dist/platform.js +23 -22
  161. package/dist/testing-compile-child.d.ts +2 -0
  162. package/dist/testing-compile-child.js +20 -0
  163. package/dist/testing.d.ts +368 -0
  164. package/dist/testing.js +439 -0
  165. package/dist/voice-service/actions.d.ts +12 -35
  166. package/dist/voice-service/index.d.ts +2 -0
  167. package/dist/voice-service/index.js +2 -0
  168. package/dist/voice-service/protocol.d.ts +604 -0
  169. package/dist/voice-service/protocol.js +67 -0
  170. package/dist/voice-service/types.d.ts +16 -6
  171. package/dist/worker.d.ts +2 -1
  172. package/dist/worker.js +2 -1
  173. package/package.json +20 -16
  174. package/dist/core/case-orchestrator/follow-ups.d.ts +0 -31
  175. package/dist/core/case-orchestrator/follow-ups.js +0 -103
  176. package/dist/core/case-orchestrator/tools.d.ts +0 -29
  177. package/dist/core/case-orchestrator/tools.js +0 -31
  178. package/dist/core/case-orchestrator/validate.d.ts +0 -11
  179. package/dist/core/case-orchestrator/validate.js +0 -63
  180. package/dist/core/functions/define/bind.d.ts +0 -25
  181. package/dist/core/functions/define/bind.js +0 -14
  182. package/dist/core/functions/define/case.d.ts +0 -9
  183. package/dist/core/functions/define/case.js +0 -13
  184. package/dist/core/functions/define/events.d.ts +0 -20
  185. package/dist/core/functions/define/events.js +0 -21
  186. package/dist/core/functions/define/no-case.d.ts +0 -4
  187. package/dist/core/functions/define/no-case.js +0 -9
  188. package/dist/core/functions/func-ref.d.ts +0 -25
  189. package/dist/core/functions/func-ref.js +0 -20
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * The page module's types bound to one sequence (15 §10.15): what `__gen/index.ts` exports as
3
- * `definePage`, `field`, `schemaField`, `createRow`, `deleteRow` and `defineAction`. Behaviour is
3
+ * `definePage`, `field`, `createRow` and `deleteRow`. Behaviour is
4
4
  * `define.ts`'s; this file only types it.
5
5
  *
6
6
  * Everything starts from `PageTables`, generated per sequence into `__gen/slots.type.ts`: for each
@@ -15,15 +15,34 @@
15
15
  * 18-record-search-index §4.6), `include` of lookups and ways back only, `some` / `every` / `none` on
16
16
  * a way back (13-data-model-v1 §23.4);
17
17
  * - an action's arguments come from its `parameters`; `db.<table>`'s writes take only that table's fields;
18
- * - the page is the conversation's: code (functions, event handlers, the Orchestrator) gets the case
19
- * (`CaseInput`) and reads with `ctx.api.db`.
18
+ * - the page is the conversation's: code (functions, event handlers, the Orchestrator) is called
19
+ * `(ctx, args)` and reads with `ctx.api.db`.
20
20
  */
21
- import type { PageTime, ChooseParam, CreateRowAction, CustomAction, DeleteRowAction, FieldCell, FieldOptions, RefFieldCell, RefOption, MessageAction, MessageOptions, PageBaseContext, PageChannel, PageChannelCaps, PageConversation, PageFaq, PageFaqEntry, PageHistory, PagePersona, PageRuns, PageScheduledAction, Placeable, SchemaFieldOptions, SlotState } from './define.js';
21
+ import type { Action, AllPrefilled, DoNode, FieldNode, FullPrefill, FunctionHandle, KeyedOf, LeftForAi, PageActionHandle, PageContent, PageNode, PageSettings, RequiredKeys, RunAction, AnyRunHandle, RunArgs, SameArgs, SayOf } from './fns.js';
22
+ import type { PageTime, ChooseParam, RefOption, PageBaseContext, PageChannel, PageChannelCaps, PageConversation, PageFaq, PageFaqEntry, PageHistory, PagePersona, PageRuns, PageScheduledAction, SlotState } from './define.js';
22
23
  declare const ID_BRAND: unique symbol;
23
24
  /** A row id of table `T`: an `Id<'injuries'>` is not an `Id<'providers'>`, and no string is an id. */
24
25
  export type Id<T extends string = string> = string & {
25
26
  readonly [ID_BRAND]: T;
26
27
  };
28
+ declare const FORMAT_BRAND: unique symbol;
29
+ /**
30
+ * A string in a format the AI is told (38 §2.4): any string is one, and the builder reads the mark to
31
+ * show the parameter as `date`, `phone`…; one format is not another.
32
+ */
33
+ type Formatted<F extends string> = string & {
34
+ readonly [FORMAT_BRAND]?: F;
35
+ };
36
+ /** A calendar date, `YYYY-MM-DD`: the AI sees `date`. */
37
+ export type IsoDate = Formatted<'date'>;
38
+ /** An instant, ISO 8601 with an offset or Z: the AI sees `datetime`. */
39
+ export type IsoDateTime = Formatted<'datetime'>;
40
+ /** A time of day, `HH:mm`: the AI sees `time`. */
41
+ export type TimeOfDay = Formatted<'time'>;
42
+ /** A phone number: the AI sees `phone`. */
43
+ export type Phone = Formatted<'phone'>;
44
+ /** An email address: the AI sees `email`. */
45
+ export type Email = Formatted<'email'>;
27
46
  /** One field, as `__gen/slots.type.ts` describes it. */
28
47
  export type PageFieldDesc = {
29
48
  value: unknown;
@@ -210,9 +229,10 @@ type ScalarFilterOf<V> = V | null | {
210
229
  lte?: V;
211
230
  gt?: V;
212
231
  gte?: V;
213
- contains?: string;
232
+ /** A text's beginning; there is no `contains` / `endsWith`: find by a beginning or an exact field (34 §10). */
214
233
  startsWith?: string;
215
- endsWith?: string;
234
+ /** `insensitive`: text compared in any case (`equals`, `in`, `startsWith`, ranges). */
235
+ mode?: 'default' | 'insensitive';
216
236
  /** By the slot's state (`asked_unknown`, `declined`, …): the one extension to Prisma's `where`. */
217
237
  state?: SlotState;
218
238
  };
@@ -320,14 +340,23 @@ type SelectKey<D, T extends keyof D, S> = {
320
340
  } | {
321
341
  select: S & ExactSelect<D, T, S>;
322
342
  };
343
+ /**
344
+ * `after`: a row of the table as read, whatever its `include` / `select` (normally the last of the page
345
+ * before); the rows strictly after it in the read's order come (define.ts `FindArgs.after`).
346
+ */
347
+ type AfterRow<T> = Readonly<{
348
+ id: Id<T & string>;
349
+ createdAt?: string;
350
+ updatedAt?: string;
351
+ }> & {
352
+ readonly [field: string]: unknown;
353
+ };
323
354
  export type FindManyArgs<D, T extends keyof D, I = IncludeArg<D, T>, S = SelectArg<D, T>> = IncludeKey<D, T, I> & SelectKey<D, T, S> & {
324
355
  where: Where<D, T>;
325
356
  orderBy?: OrderByArg<D, T>;
326
357
  take?: number;
327
358
  skip?: number;
328
- cursor?: {
329
- id: Id<T & string>;
330
- };
359
+ after?: AfterRow<T>;
331
360
  };
332
361
  /** `findUnique`: by `id`; null when there is no such row. */
333
362
  export type FindUniqueArgs<D, T extends keyof D, I = IncludeArg<D, T>, S = SelectArg<D, T>> = IncludeKey<D, T, I> & SelectKey<D, T, S> & {
@@ -428,10 +457,12 @@ type TypedPageBaseContext<D, S extends string> = {
428
457
  scheduledAction: PageScheduledAction | null;
429
458
  /** The sequence's office hours now; null when it has none. */
430
459
  officeHours: PageBaseContext['officeHours'];
431
- /** The contact's agreed follow-ups still to come (09). */
432
- followUps: PageBaseContext['followUps'];
460
+ /** The case's next step (40 §2.2): its pending contacts, soonest first; the platform shows it before the page. */
461
+ nextSteps: PageBaseContext['nextSteps'];
433
462
  /** The business root's id. */
434
463
  subject: Id<S>;
464
+ /** The instance this conversation is on: prefill it to a function that needs it (38 §2.2). */
465
+ instanceId: string;
435
466
  /** The contact of the conversation; null outside one. */
436
467
  caller: RowIfTable<D, 'contact'> | null;
437
468
  /** This conversation's session row (the session fields, cells); null outside one. */
@@ -442,21 +473,10 @@ type TypedPageBaseContext<D, S extends string> = {
442
473
  history: PageHistory;
443
474
  /** ISO timestamp of this render. */
444
475
  now: string;
445
- /** A text or an email to send now (25 §8): `ctx.messages.text('link', { body })`. */
446
- messages: TypedPageMessages<D>;
447
476
  /** The contact's time zone, today there, and `format` for a time as people read it (25 §11). */
448
477
  time: PageTime;
449
- };
450
- type TypedMessageOptions<D> = Omit<MessageOptions, 'to'> & {
451
- /** The contact it goes to (a contact row the page read); absent: the contact of this conversation. */
452
- to?: RowIfTable<D, 'contact'> | Id<'contact'>;
453
- };
454
- /** `ctx.messages`, its `to` a contact of this company. */
455
- export type TypedPageMessages<D> = {
456
- text(key: string, options?: TypedMessageOptions<D>): MessageAction;
457
- email(key: string, options?: TypedMessageOptions<D> & {
458
- subject?: string;
459
- }): MessageAction;
478
+ /** The environment's configuration, read only (76 §2.19): `ctx.env.get('callback_number')`. */
479
+ env: PageBaseContext['env'];
460
480
  };
461
481
  export type TypedActionContext<D, S extends string> = {
462
482
  db: TypedDb<D>;
@@ -466,106 +486,41 @@ export type TypedActionContext<D, S extends string> = {
466
486
  session: RowIfTable<D, 'session'> | null;
467
487
  now: string;
468
488
  };
469
- /** `table.field` (or `table.id`, a row) of this sequence: what `schemaField` takes. */
470
- export type SchemaFieldPath<D> = {
471
- [T in TableOf<D>]: `${T}.id` | `${T}.${FieldOf<D, T>}`;
472
- }[TableOf<D>];
473
- export type TypedSchemaFieldParam<P extends string> = {
474
- readonly $schemaField: P;
475
- readonly extraction?: string;
476
- };
477
- /** One parameter: a schema field, or plain JSON Schema (an LLM function call's `parameters`). */
478
- export type TypedActionParam<D> = TypedSchemaFieldParam<SchemaFieldPath<D>> | {
479
- readonly type: 'string';
480
- readonly description?: string;
481
- readonly enum?: readonly string[];
482
- } | {
483
- readonly type: 'number' | 'integer';
484
- readonly description?: string;
485
- readonly enum?: readonly number[];
486
- } | {
487
- readonly type: 'boolean';
488
- readonly description?: string;
489
- } | {
490
- readonly type: 'array';
491
- readonly items: TypedActionParam<D>;
492
- readonly description?: string;
493
- } | {
494
- readonly type: 'object';
495
- readonly properties: {
496
- readonly [key: string]: TypedActionParam<D>;
497
- };
498
- readonly required?: readonly string[];
499
- readonly description?: string;
500
- };
501
- export type TypedActionParameters<D> = {
502
- readonly type: 'object';
503
- readonly properties: {
504
- readonly [key: string]: TypedActionParam<D>;
505
- };
506
- readonly required?: readonly string[];
507
- };
508
- /** What `run` gets for one parameter. */
509
- type ParamValue<D, P> = P extends {
510
- $schemaField: `${infer T}.${infer F}`;
511
- } ? T extends keyof D ? F extends 'id' ? Id<T> : ValueOf<D, T, F> : never : P extends {
512
- type: 'string';
513
- enum: readonly (infer E)[];
514
- } ? E : P extends {
515
- type: 'string';
516
- } ? string : P extends {
517
- type: 'number' | 'integer';
518
- enum: readonly (infer E)[];
519
- } ? E : P extends {
520
- type: 'number' | 'integer';
521
- } ? number : P extends {
522
- type: 'boolean';
523
- } ? boolean : P extends {
524
- type: 'array';
525
- items: infer I;
526
- } ? Array<ParamValue<D, I>> : P extends {
527
- type: 'object';
528
- properties: infer Props;
529
- } ? ArgsOf<D, {
530
- properties: Props;
531
- required: P extends {
532
- required: infer R;
533
- } ? R : [];
534
- }> : never;
535
- type RequiredOf<P> = P extends {
536
- required: readonly (infer R)[];
537
- } ? R : never;
538
- /** What `run` gets: every parameter, required ones present, others optional. */
539
- export type ArgsOf<D, P> = P extends {
540
- properties: infer Props;
541
- } ? Simplify<{
542
- -readonly [K in Extract<keyof Props, RequiredOf<P>>]: ParamValue<D, Props[K]>;
543
- } & {
544
- -readonly [K in Exclude<keyof Props, RequiredOf<P>>]?: ParamValue<D, Props[K]>;
545
- }> : Record<never, never>;
546
489
  declare const CHOOSE_BRAND: unique symbol;
547
490
  /** `choose(rows, label)` over rows of `T` (25 §10): the rows a row parameter of `T` is chosen among. */
548
491
  export type TypedChoose<T extends string> = ChooseParam & {
549
492
  readonly [CHOOSE_BRAND]?: T;
550
493
  };
551
- /** What a placed action is called with: a value, or for a row parameter its row's id or `choose(...)` (25 §10). */
494
+ /**
495
+ * What the page fixed of an action's parameters (35 §2): values, a row parameter by its row's id; an
496
+ * optional one also `null`, none (the AI is not asked).
497
+ */
552
498
  export type PrefillOf<A> = {
553
- [K in keyof A]?: A[K] | (NonNullable<A[K]> extends {
554
- readonly [ID_BRAND]: infer T extends string;
555
- } ? TypedChoose<T> : never);
556
- };
499
+ [K in keyof A]?: A[K] | (undefined extends A[K] ? null : never);
500
+ };
501
+ /** The parameters of `A` that are rows. */
502
+ type RowKeys<A> = {
503
+ [K in keyof A]-?: NonNullable<A[K]> extends {
504
+ readonly [ID_BRAND]: string;
505
+ } ? K : never;
506
+ }[keyof A];
557
507
  /**
558
- * A custom action: called to prefill arguments (`markError({ injury: i.id })`, one button per row) or to
559
- * offer the rows a row argument is chosen among (`cancel({ appointment: choose(rows, label) })`, 25 §10).
560
- * Placing it as it is leaves a row argument to the rows the page happens to show: not recommended.
508
+ * The rows the AI chooses a row parameter among, each with its label (35 §2): `choose(rows, label)`.
509
+ * With no row parameter there is nothing to choose: `options` takes nothing (`{}` would take anything).
561
510
  */
562
- export type TypedDefinedAction<A> = Placeable<CustomAction, PrefillOf<A>> & {
563
- readonly actionName: string;
511
+ export type OptionsOf<A> = [RowKeys<A>] extends [never] ? never : {
512
+ [K in RowKeys<A>]?: NonNullable<A[K]> extends {
513
+ readonly [ID_BRAND]: infer T extends string;
514
+ } ? TypedChoose<T> : never;
564
515
  };
516
+ /** A page action's function (76 §2.11): `(ctx, args)`; what it is called with is the button's `prefill` and what the AI fills. */
517
+ export type PageActionFn<A> = (ctx: never, args: A) => unknown;
518
+ /** What a page function returns: `page(…)`, or `doNow(…)` to do something first (76). */
519
+ export type PageResult = PageNode | DoNode;
565
520
  /** The page module of one sequence: `D` its tables (`PageTables`), `S` its business root table. */
566
521
  export type TypedPageModule<D, S extends string> = {
567
522
  /** Each channel's page is its own (17b §9.1): what a call shows and what a text shows may differ. */
568
- definePage<Call = never, Sms = never, Email = never>(definition: {
523
+ definePage<Call extends PageResult = never, Sms extends PageResult = never, Email extends PageResult = never>(definition: {
569
524
  /** Called once when the conversation starts (24 §4): the persona for `ctx.channel`. */
570
525
  persona: (ctx: TypedPageContext<D, S>) => string | Promise<string>;
571
526
  /** Called once when the conversation starts (24 §4): the FAQ, at most 100 entries. */
@@ -583,60 +538,111 @@ export type TypedPageModule<D, S extends string> = {
583
538
  email?: (ctx: TypedPageContext<D, S, 'email'>) => Email | Promise<Email>;
584
539
  };
585
540
  };
586
- field<Sl extends TypedSlot<unknown, string, string>>(slot: Sl, options?: FieldOptions): FieldCell<Sl>;
587
- /** A ref field (26 §16.2 #7): the rows it may point at, `{ id, label }` each, or `choose(rows, label)`. */
588
- refField<Sl extends TypedSlot<unknown, string, string>>(slot: Sl, options?: {
589
- options?: ReadonlyArray<RefOption & {
590
- id: Sl['value'] & string;
591
- }> | ChooseParam;
592
- readOnly?: boolean;
593
- }): RefFieldCell<Sl>;
594
- schemaField<const P extends SchemaFieldPath<D>>(path: P, options?: SchemaFieldOptions): TypedSchemaFieldParam<P>;
595
- createRow<T extends TableOf<D>, const F extends readonly Exclude<FieldOf<D, T>, ParentOf<D, T>>[]>(table: T | {
596
- readonly $table: T;
597
- }, options: {
598
- fields: F;
599
- required?: ReadonlyArray<F[number]>;
600
- label?: string;
601
- description?: string;
602
- }): Placeable<CreateRowAction, Partial<CreateData<D, T>>>;
603
- deleteRow(row: Id<TableOf<D>> | {
604
- readonly id: Id<TableOf<D>>;
605
- }, options?: {
606
- reason?: string;
607
- label?: string;
608
- description?: string;
609
- }): DeleteRowAction;
610
541
  /** The rows a row parameter is chosen among, each with what the AI sees of it (25 §10). */
611
542
  choose<R extends {
612
543
  readonly id: Id<TableOf<D>>;
613
544
  }>(rows: readonly R[], label: (row: R) => string): TypedChoose<R['id'] extends {
614
545
  readonly [ID_BRAND]: infer T extends string;
615
546
  } ? T : never>;
616
- defineAction<const P extends TypedActionParameters<D> = {
617
- type: 'object';
618
- properties: {};
619
- }>(definition: {
620
- name: string;
621
- description: string;
622
- parameters?: P;
623
- run: (args: ArgsOf<D, P>, ctx: TypedActionContext<D, S>) => unknown;
624
- }): TypedDefinedAction<ArgsOf<D, P>>;
625
- };
626
- /** What a page function may return instead of the page (17b §9.8): an action to run first. */
627
- type ReturnedAction = {
628
- $action: string;
629
- } | {
630
- $perform: true;
631
- } | ((...args: never[]) => unknown);
547
+ };
548
+ type AnySlot = TypedSlot<unknown, string, string>;
549
+ /** What a function takes: its `args`, the second parameter of `(ctx, args)` (38 §2.1). */
550
+ export type FunctionParamsOf<Fn> = Fn extends (...args: infer A) => unknown ? A extends [unknown, infer P, ...unknown[]] ? P extends object ? P : Record<never, never> : Record<never, never> : Record<never, never>;
551
+ /** `table.field` of this sequence: how the page says to fill it (`page({ extraction })`). */
552
+ type FieldPathOf<D> = {
553
+ [T in TableOf<D>]: `${T}.${FieldOf<D, T>}`;
554
+ }[TableOf<D>];
555
+ /** `fn` of `__gen` (76 §2.15): a handle per company function, its `args` type the function's. */
556
+ export type FunctionHandles<F> = {
557
+ readonly [N in keyof F & string]: FunctionHandle<N, FunctionParamsOf<F[N]>>;
558
+ };
559
+ /**
560
+ * `act` of `__gen` (76 §2.11): a handle per page action of `actions.ts`, its `args` type the action's. An
561
+ * action takes the page action's `ctx` (`ActionCtx`): one written for another (a company function's `Ctx`)
562
+ * has no handle, and `run()` says so.
563
+ */
564
+ export type ActionHandles<A, D, S extends string> = {
565
+ readonly [N in keyof A & string]: A[N] extends (ctx: TypedActionContext<D, S>, ...rest: never[]) => unknown ? PageActionHandle<N, FunctionParamsOf<A[N]>> : `actions.${N} takes (ctx: ActionCtx, args): the page action's ctx`;
566
+ };
567
+ /** Add a row of `T` (15 §10.6): `fields` the AI fills, `prefill` what the page fixed, `options` a ref field's rows. */
568
+ type CreateRowOptions<D, T extends TableOf<D>> = {
569
+ /** What the AI fills; a ref field is chosen in `options` instead (35 §2). */
570
+ fields: ReadonlyArray<Exclude<FieldOf<D, T>, ParentOf<D, T> | RowKeys<CreateData<D, T>>>>;
571
+ required?: ReadonlyArray<Exclude<FieldOf<D, T>, ParentOf<D, T>>>;
572
+ prefill?: Partial<CreateData<D, T>>;
573
+ /** The rows the AI chooses a ref field's row among (35 §2). */
574
+ options?: OptionsOf<Omit<CreateData<D, T>, ParentOf<D, T>>>;
575
+ };
632
576
  /**
633
- * The document a page script returns: `PageDocumentOf<typeof page>` of `context.page.ts`'s default
634
- * export. An action the page function returns instead (a lookup, a keypad) is not part of it.
577
+ * The fields a prefill surely fills: there, and never null, undefined or a written-out '' (76 §2.14), as
578
+ * the write counts them; a `string` from code counts as filled (the render refuses it when it is ''). `{ body_part: null }`, `''` or a value that may be missing fills nothing: a required field prefilled so is still nobody's.
635
579
  */
636
- export type PageDocumentOf<P> = P extends {
637
- channels: infer C;
638
- } ? {
639
- [K in keyof C]: C[K] extends ((...args: never[]) => infer R) | undefined ? Exclude<Awaited<R>, ReturnedAction> : never;
640
- }[keyof C] : never;
580
+ type FilledKeys<P> = {
581
+ [X in keyof P]-?: undefined extends P[X] ? never : null extends P[X] ? never : string extends P[X] ? X : '' extends P[X] ? never : X;
582
+ }[keyof P];
583
+ /** The page's functions bound to one sequence (76 §2.17): what `__gen/index.ts` exports. */
584
+ export type TypedPageFns<D, S extends string, F> = {
585
+ /** The page (76 §2.7): settings, then content by name. */
586
+ page(settings: PageSettings<{
587
+ readonly [K in FieldPathOf<D>]?: string;
588
+ }>, content: PageContent): PageNode;
589
+ /** A field the AI may write, or read only. A ref field is `refField()`. */
590
+ field(slot: AnySlot, options?: {
591
+ readOnly?: boolean;
592
+ expand?: boolean;
593
+ }): FieldNode;
594
+ /** A ref field: writable, the rows it may be set to (`options`); read-only, who its value is (`label`). */
595
+ refField(slot: AnySlot, options: {
596
+ options: ReadonlyArray<RefOption> | ChooseParam;
597
+ readOnly?: false;
598
+ } | {
599
+ readOnly: true;
600
+ label?: string;
601
+ }): FieldNode;
602
+ /** Add a row (15 §10.6). */
603
+ createRow<T extends TableOf<D>, const R extends ReadonlyArray<Exclude<FieldOf<D, T>, ParentOf<D, T>>> = [], P extends Partial<CreateData<D, T>> = {}, K extends string | undefined = undefined>(table: T, options: Omit<CreateRowOptions<D, T>, 'options' | 'required' | 'prefill'> & {
604
+ required?: R;
605
+ prefill?: P & {
606
+ readonly [X in Exclude<keyof P, keyof CreateData<D, T>>]: `${X & string} is not a field of ${T}`;
607
+ };
608
+ options?: undefined;
609
+ runKey?: K;
610
+ }): Action<'createRow', KeyedOf<K>, [Exclude<R[number], FilledKeys<P>>] extends [never] ? AllPrefilled : LeftForAi>;
611
+ /** With `options`, the AI chooses a ref field's row: a button's, never `doNow()`'s. */
612
+ createRow<T extends TableOf<D>, K extends string | undefined = undefined>(table: T, options: CreateRowOptions<D, T> & {
613
+ options: NonNullable<CreateRowOptions<D, T>['options']>;
614
+ runKey?: K;
615
+ }): Action<'createRow', KeyedOf<K>, LeftForAi>;
616
+ /**
617
+ * Run a company function (`fn.x`) or a page action (76 §2.15): every required parameter prefilled
618
+ * (what `doNow()` takes), or some left for the AI. `options` are rows of each row parameter's own table.
619
+ */
620
+ run<H extends AnyRunHandle, K extends string | undefined = undefined>(handle: H & SameArgs<H>, options: ([RequiredKeys<RunArgs<H>>] extends [never] ? {
621
+ prefill?: PrefillOf<RunArgs<H>>;
622
+ } : {
623
+ prefill: FullPrefill<RunArgs<H>>;
624
+ }) & {
625
+ options?: undefined;
626
+ args?: undefined;
627
+ say?: SayOf<H>;
628
+ runKey?: K;
629
+ }): RunAction<KeyedOf<K>, AllPrefilled>;
630
+ run<H extends AnyRunHandle, K extends string | undefined = undefined>(handle: H & SameArgs<H>, options?: {
631
+ prefill?: PrefillOf<RunArgs<H>>;
632
+ options?: OptionsOf<RunArgs<H>>;
633
+ args?: {
634
+ [P in keyof RunArgs<H>]?: string;
635
+ };
636
+ say?: SayOf<H>;
637
+ runKey?: K;
638
+ }): RunAction<KeyedOf<K>, LeftForAi>;
639
+ /** Delete a row and everything under it (15 §10.6.1). */
640
+ deleteRow<K extends string | undefined = undefined>(row: Id<TableOf<D>> | {
641
+ readonly id: Id<TableOf<D>>;
642
+ }, options?: {
643
+ reason?: string;
644
+ runKey?: K;
645
+ }): Action<'deleteRow', KeyedOf<K>, AllPrefilled>;
646
+ };
641
647
  export {};
642
648
  //# sourceMappingURL=typed.d.ts.map
@@ -5,7 +5,10 @@
5
5
  * always a `value` attribute; flush left.
6
6
  *
7
7
  * <notes><note id about value/></notes> the model's own reminders (17b §8), first, all of them
8
- * <guide value/> the page's `$guide`: what to do now and how
8
+ * <next-step channel id name to at reason customer-quote booked-in/> the platform's, before the page
9
+ * (40 §2.2, 51 §2): the case's pending contacts, whatever the page
10
+ * shows; `value` "none" when nothing is booked
11
+ * <guide value/> the page's `$guide`: what to do now and how
9
12
  * <page title description fallback> the page:
10
13
  * <definitions> once for the page, a definition per `table.field` some field
11
14
  * <definition for label type …/> on it shows expanded (`field(slot, { expand: true })`)
@@ -15,7 +18,10 @@
15
18
  * definition is not shown (its type is); a ref's one-of: its rows
16
19
  * <text key value/> read-only text
17
20
  * <button id title does><arg …/></button>
21
+ * Now: 2026-11-11T20:05Z the platform's, the last line (76 §2.19): the time now, in UTC,
22
+ * to the minute; the only time the AI is given as "now"
18
23
  */
24
+ import type { PageNextStep } from './define.js';
19
25
  import type { BlockNode, RenderedPage } from './render.js';
20
26
  export type WorkspaceNote = {
21
27
  id: string;
@@ -24,8 +30,26 @@ export type WorkspaceNote = {
24
30
  };
25
31
  export type WorkspaceTextOptions = {
26
32
  notes?: readonly WorkspaceNote[];
33
+ /**
34
+ * The case's next step (40 §2.2), shown before the page whatever the page shows: the AI knows what is
35
+ * booked before it reads the page. Times in `timeZone` (the contact's). Left out: no conversation (nothing is shown).
36
+ */
37
+ nextSteps?: {
38
+ steps: readonly PageNextStep[];
39
+ timeZone: string;
40
+ };
41
+ /**
42
+ * The time now (76 §2.19): the page's last line, `Now: 2026-11-11T20:05Z`, in UTC, never the contact's
43
+ * zone. Every engine that shows the AI a page gives it; left out: no such line.
44
+ */
45
+ now?: Date;
27
46
  };
28
47
  export declare function workspaceText(page: RenderedPage, options?: WorkspaceTextOptions): string;
48
+ /** The last line of every page the AI reads (76 §2.19): the time now in UTC, to the minute (`Now: 2026-11-11T20:05Z`). */
49
+ export declare function nowLine(now: Date): string;
50
+ /** Stable, short appointment handle; the service resolves it within the authorized case, rejecting ambiguity. */
51
+ export declare function nextStepReference(id: string): string;
52
+ export declare function nextStepLines(steps: readonly PageNextStep[], timeZone: string): string[];
29
53
  /** The block at `path` (`''`: the root). */
30
54
  export declare function blockAt(page: RenderedPage, path: string): BlockNode | null;
31
55
  /** What `view` shows of one thing on the page (24 §3.2): a field with its definition, a button with its arguments. */
@@ -1,3 +1,4 @@
1
+ import { formatTime } from './time-format.js';
1
2
  export function workspaceText(page, options = {}) {
2
3
  const out = [];
3
4
  const notes = options.notes ?? [];
@@ -7,6 +8,8 @@ export function workspaceText(page, options = {}) {
7
8
  out.push(element('note', { id: n.id, about: n.about, value: n.text }));
8
9
  out.push('</notes>');
9
10
  }
11
+ if (options.nextSteps)
12
+ out.push(...nextStepLines(options.nextSteps.steps, options.nextSteps.timeZone));
10
13
  const root = page.root;
11
14
  if (root.guide)
12
15
  out.push(element('guide', { value: root.guide }));
@@ -29,8 +32,40 @@ export function workspaceText(page, options = {}) {
29
32
  for (const child of root.children)
30
33
  walk(page, child, defined, false, out);
31
34
  out.push('</page>');
35
+ if (options.now)
36
+ out.push(nowLine(options.now));
32
37
  return out.join('\n');
33
38
  }
39
+ /** The last line of every page the AI reads (76 §2.19): the time now in UTC, to the minute (`Now: 2026-11-11T20:05Z`). */
40
+ export function nowLine(now) {
41
+ return `Now: ${now.toISOString().slice(0, 16)}Z`;
42
+ }
43
+ /** Stable, short appointment handle; the service resolves it within the authorized case, rejecting ambiguity. */
44
+ export function nextStepReference(id) {
45
+ return `step-${id.replaceAll('-', '').slice(0, 12)}`;
46
+ }
47
+ export function nextStepLines(steps, timeZone) {
48
+ if (!steps.length)
49
+ return [element('next-step', { value: 'none: nothing is booked to contact anyone on this case' })];
50
+ const when = (at) => {
51
+ try {
52
+ return formatTime(at, { timeZone });
53
+ }
54
+ catch {
55
+ return at;
56
+ }
57
+ };
58
+ return steps.map((step) => element('next-step', {
59
+ channel: step.channel,
60
+ id: nextStepReference(step.id),
61
+ name: step.name ?? undefined,
62
+ to: step.contact.name ?? step.contact.id,
63
+ at: when(step.at),
64
+ reason: step.reason ?? undefined,
65
+ 'customer-quote': step.customerQuote ?? undefined,
66
+ 'booked-in': step.bookedIn ? `the ${step.bookedIn.channel === 'call' ? 'call' : step.bookedIn.channel === 'sms' ? 'texts' : 'email'} of ${when(step.bookedIn.at)}` : 'by the case\'s follow-up rules',
67
+ }));
68
+ }
34
69
  const kindOf = (cell) => `${cell.ref.table}.${cell.ref.field}`;
35
70
  /** Each `table.field` some field on the page shows expanded, in page order: the page gives its definition once. */
36
71
  function definedCells(page) {
@@ -51,7 +86,7 @@ function walk(page, node, defined, inList, out) {
51
86
  switch (node.kind) {
52
87
  case 'block': {
53
88
  const tag = node.list ? 'list' : 'block';
54
- out.push(open(tag, { key: inList ? undefined : node.key, title: node.title, description: node.description }));
89
+ out.push(open(tag, { key: inList ? undefined : node.key || undefined, title: node.title, description: node.description }));
55
90
  for (const child of node.children)
56
91
  walk(page, child, defined, node.list, out);
57
92
  out.push(`</${tag}>`);
@@ -66,13 +101,18 @@ function walk(page, node, defined, inList, out) {
66
101
  case 'text':
67
102
  // null: the page put nothing here now (a region that does not apply yet, a value it has not got).
68
103
  // In a list it has no key of its own, only its place, as a block there (26 §16.2 #10).
69
- out.push(element('text', { key: inList ? undefined : node.key, value: node.value }));
104
+ out.push(element('text', { key: inList ? undefined : node.key || undefined, value: node.value }));
70
105
  return;
71
106
  case 'action':
72
107
  out.push(...buttonLines(page.actions[node.actionId]));
73
108
  }
74
109
  }
75
- const editable = (node, cell) => !node.readOnly && cell.writable;
110
+ /**
111
+ * Whether the AI may change a field (30 §3): the field's final permission, the same wherever it is on the
112
+ * page. A place marked read-only cannot stop the AI writing a field writable elsewhere (it writes by id),
113
+ * so marking it there would only contradict the other place.
114
+ */
115
+ const editable = (_node, cell) => cell.writable;
76
116
  /**
77
117
  * A field's value (26 §16.2): `key` always (#10; in a list, where it has only its place, none); `readonly`
78
118
  * only on one the AI cannot change (#6a); `more` when its definition is not shown, with its type (#6);
@@ -81,12 +121,12 @@ const editable = (node, cell) => !node.readOnly && cell.writable;
81
121
  * labelled it.
82
122
  */
83
123
  function fieldAttrs(page, node, cell, more, inList = false) {
84
- const value = valueOf(cell, page);
124
+ const value = valueOf(cell);
85
125
  const open = editable(node, cell);
86
126
  const labels = cell.optionLabels ?? {};
87
127
  return {
88
128
  id: cell.slotId,
89
- key: inList ? undefined : node.key,
129
+ key: inList ? undefined : node.key || undefined,
90
130
  readonly: !open,
91
131
  more,
92
132
  type: more ? cell.type : undefined,
@@ -97,16 +137,11 @@ function fieldAttrs(page, node, cell, more, inList = false) {
97
137
  reason: (cell.state === 'asked_unknown' || cell.state === 'declined') && cell.reason ? cell.reason : undefined,
98
138
  };
99
139
  }
100
- /** The value as JSON holds it; a value that is a row's id is the row's page name; null when not filled. */
101
- function valueOf(cell, page) {
140
+ /** The value as JSON holds it; a ref's is its row's name (33); null when not filled. */
141
+ function valueOf(cell) {
102
142
  if (cell.state !== 'filled')
103
143
  return null;
104
- if (typeof cell.value === 'string') {
105
- const name = Object.entries(page.rows).find(([, row]) => row.id === cell.value)?.[0];
106
- if (name)
107
- return name;
108
- }
109
- return cell.value ?? null;
144
+ return cell.valueRow ?? cell.value ?? null;
110
145
  }
111
146
  /**
112
147
  * What a field means and takes (26 §15.3): label, type, allowed values (`one-of`; a ref field's rows are