@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
@@ -0,0 +1,112 @@
1
+ import type { TextCallEvent } from '../calls/index.js';
2
+ import type { TurnSummary } from './index.js';
3
+ export type TurnInput = {
4
+ /** The case; left out with `from` (the turn's). */
5
+ instanceId?: string;
6
+ channel?: 'sms' | 'email' | 'call';
7
+ /** What the customer writes or says; with `from`, in place of what they said then. */
8
+ say?: string;
9
+ /** The conversation's contact; the case's subject when it is a contact and none is named. */
10
+ caller?: string;
11
+ /** The conversation so far, by hand, oldest first; the case's real one when left out. */
12
+ history?: Array<{
13
+ customer?: string;
14
+ ai?: string;
15
+ }>;
16
+ /** Start where a turn started: its id, or the directory it was exported to (relative to the repository). */
17
+ from?: string;
18
+ /** Run it this many times (1–10), each from the same start: the model answers differently each time. */
19
+ repeat?: number;
20
+ /** Results for the company's functions the turn presses, by name; a recorded call (`from`) is replayed first. */
21
+ functions?: {
22
+ results?: Record<string, unknown>;
23
+ };
24
+ };
25
+ /** One run of the turn. */
26
+ export type ShadowRun = {
27
+ runId: string | null;
28
+ /** A text or email run: how it ended (`reply` with its `text`, not sent). A call's turn: null. */
29
+ outcome: {
30
+ kind: 'reply';
31
+ text: string;
32
+ email?: {
33
+ subject?: string;
34
+ cc?: string[];
35
+ };
36
+ steps: number;
37
+ } | {
38
+ kind: 'noReply' | 'handoff' | 'abnormal';
39
+ reason: string;
40
+ steps: number;
41
+ } | null;
42
+ /** What the AI said, or would have sent. */
43
+ said: string[];
44
+ /** `act`: what the platform told the model after running the actions. */
45
+ stepResult?: string;
46
+ actions: Array<{
47
+ name: string;
48
+ kind: string;
49
+ args: unknown;
50
+ status: string;
51
+ error: string | null;
52
+ result: unknown;
53
+ }>;
54
+ /** Each field it wrote: its row, table and field, the value and state it took, and what it was. */
55
+ writes: Array<{
56
+ rowId: string;
57
+ table: string;
58
+ field: string;
59
+ value: unknown;
60
+ state: string | null;
61
+ from: {
62
+ value: unknown;
63
+ state: string | null;
64
+ };
65
+ reason: string | null;
66
+ }>;
67
+ /** A call: what it did on the line (a keypad, a transfer, a voicemail). */
68
+ events: TextCallEvent[];
69
+ /** A call the AI ended in this turn. */
70
+ ended: boolean;
71
+ turns: TurnSummary[];
72
+ /** Each company function the turn pressed: `replayed` (as it ran then), `given` (yours), `unrecorded` (not run), `skipped` (an event handler). */
73
+ functions: Array<{
74
+ name: string;
75
+ args: unknown;
76
+ how: 'replayed' | 'given' | 'unrecorded' | 'skipped';
77
+ result?: unknown;
78
+ error?: string;
79
+ }>;
80
+ /** What it would have done outside the database (a text, an email, an Orchestrator run): none of it happened. */
81
+ held: string[];
82
+ /** Where it could not start exactly as asked (a row gone since). */
83
+ warnings: string[];
84
+ /** Whether it pressed the button with this run key (or name). */
85
+ clicked(key: string): boolean;
86
+ /** Whether it wrote `table.field` (to `value`, when given). */
87
+ wrote(field: string, value?: unknown): boolean;
88
+ };
89
+ /** What a `turn` or an `act` did: its first run's, and every run with `repeat`. */
90
+ export type RunResult = ShadowRun & {
91
+ runs: ShadowRun[];
92
+ /** The turn it started from. */
93
+ from: {
94
+ turnId: string;
95
+ kind: string;
96
+ heard: string | null;
97
+ } | null;
98
+ /** The share of runs `check` holds for, 0–1. */
99
+ rate(check: (run: ShadowRun) => boolean): number;
100
+ };
101
+ type Post = <T>(what: string, body: unknown) => Promise<T>;
102
+ /** `from` as the backend takes it: a turn id as it is, a directory's files read. */
103
+ export declare function fromOf(repoRoot: string, from: string): string | {
104
+ turn: unknown;
105
+ history: unknown;
106
+ data: unknown;
107
+ };
108
+ export declare function shadowTurn(post: Post, repoRoot: string, what: 'turn' | 'act', input: TurnInput & {
109
+ actions?: unknown[];
110
+ }): Promise<RunResult>;
111
+ export {};
112
+ //# sourceMappingURL=shadow.d.ts.map
@@ -0,0 +1,54 @@
1
+ /**
2
+ * A single turn, always a shadow (68 §8): `sandbox.turn` / `sandbox.act` and `superpanel-sdk-runtime turn`.
3
+ * The backend runs it on a shadow of the environment's database, rolled back afterwards; what it did comes
4
+ * back. `from` names a turn by id, or the directory `turns export` wrote it to (kept in the repository,
5
+ * it outlives the 30 days a turn's snapshot is kept): its files are sent as they are.
6
+ */
7
+ import { existsSync, readFileSync, statSync } from 'node:fs';
8
+ import path from 'node:path';
9
+ /** `from` as the backend takes it: a turn id as it is, a directory's files read. */
10
+ export function fromOf(repoRoot, from) {
11
+ const dir = path.resolve(repoRoot, from);
12
+ const isDir = (() => {
13
+ try {
14
+ return statSync(dir).isDirectory();
15
+ }
16
+ catch {
17
+ return false;
18
+ }
19
+ })();
20
+ if (!isDir) {
21
+ if (/[\\/]/.test(from))
22
+ throw new Error(`turn: from ${from}: no such directory`);
23
+ return from;
24
+ }
25
+ const json = (name, required) => {
26
+ const file = path.join(dir, name);
27
+ if (!existsSync(file)) {
28
+ if (required)
29
+ throw new Error(`turn: ${from} has no ${name} (export a turn with \`turns export <turnId>\`)`);
30
+ return null;
31
+ }
32
+ return JSON.parse(readFileSync(file, 'utf8'));
33
+ };
34
+ return { turn: json('turn.json', true), history: json('history.json', true), data: json('data.json', false) ?? {} };
35
+ }
36
+ function withChecks(run) {
37
+ return {
38
+ ...run,
39
+ clicked: (key) => run.actions.some((a) => a.status !== 'failed' && (a.name === key || run.turns.some((t) => t.actions.some((ta) => ta.key === key && ta.status !== 'failed')))),
40
+ wrote: (field, ...value) => run.writes.some((w) => `${w.table}.${w.field}` === field && (value.length === 0 || JSON.stringify(w.value) === JSON.stringify(value[0]))),
41
+ };
42
+ }
43
+ export async function shadowTurn(post, repoRoot, what, input) {
44
+ const body = { ...input, ...(input.from ? { from: fromOf(repoRoot, input.from) } : {}) };
45
+ const wire = await post(what, body);
46
+ const runs = wire.runs.map(withChecks);
47
+ return {
48
+ ...runs[0],
49
+ runs,
50
+ from: wire.from,
51
+ rate: (check) => (runs.length ? runs.filter(check).length / runs.length : 0),
52
+ };
53
+ }
54
+ //# sourceMappingURL=shadow.js.map
@@ -9,7 +9,6 @@ async function runOnce(repoRoot, sequenceIds) {
9
9
  }
10
10
  for (const result of results) {
11
11
  console.log(`wrote ${path.relative(repoRoot, result.outPath)}`);
12
- console.log(`wrote ${path.relative(repoRoot, result.caseContextOutPath)}`);
13
12
  console.log(`wrote ${path.relative(repoRoot, result.indexOutPath)}`);
14
13
  }
15
14
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Pluggable host for sequence functions (case / noCase / orchestrator functions).
2
+ * Pluggable host for sequence functions: every one is `(ctx, args)` (38 §2.1); the host builds `ctx`.
3
3
  *
4
4
  * - Local unit tests / company CI: {@link createLocalComputeHost} (direct JS call)
5
5
  * - Author FuncMap: `createSourceComputeHost`
@@ -11,18 +11,17 @@ export interface ComputeHost {
11
11
  /** Release host-owned resources. No-op when the host has nothing to free. */
12
12
  dispose(): void;
13
13
  }
14
- /** Input for case, noCase and orchestrator functions. */
14
+ /** What a function is called with besides `ctx`: its `args`, one object (`{}` when left out). */
15
15
  export type FunctionInvocation = {
16
- caseContext?: unknown;
17
- params?: object;
16
+ args?: object;
18
17
  };
19
18
  export type ComputeInvocation = FunctionInvocation;
20
19
  export declare function parseComputeInvocation(value: unknown): ComputeInvocation | null;
21
20
  /**
22
- * Name → plain function of `caseContext` (local tests stand in sequence functions with these).
21
+ * Name → plain function of `args` (local tests stand in sequence functions with these).
23
22
  * Parameter typed as `never` so company-narrowed maps remain assignable (TS contravariance).
24
23
  */
25
- export type ComputeFnMap = Record<string, (caseContext: never) => unknown | Promise<unknown>>;
24
+ export type ComputeFnMap = Record<string, (args: never) => unknown | Promise<unknown>>;
26
25
  export type ComputeHostOrFns = ComputeHost | ComputeFnMap;
27
26
  export declare function createLocalComputeHost(fns: ComputeFnMap): ComputeHost;
28
27
  /** Accept either a host or a name→fn map (map is wrapped as local host). */
@@ -2,24 +2,15 @@ export function parseComputeInvocation(value) {
2
2
  if (typeof value !== 'object' || value === null || Array.isArray(value))
3
3
  return null;
4
4
  const record = value;
5
- if (record.params !== undefined &&
6
- (typeof record.params !== 'object' || record.params === null || Array.isArray(record.params))) {
5
+ if (record.args !== undefined &&
6
+ (typeof record.args !== 'object' || record.args === null || Array.isArray(record.args))) {
7
7
  return null;
8
8
  }
9
- return {
10
- ...(Object.hasOwn(record, 'caseContext') ? { caseContext: record.caseContext } : {}),
11
- ...(record.params !== undefined ? { params: record.params } : {}),
12
- };
9
+ return record.args !== undefined ? { args: record.args } : {};
13
10
  }
14
11
  function isComputeHost(value) {
15
12
  return typeof value.execute === 'function';
16
13
  }
17
- function requireCaseContext(name, invocation) {
18
- if (!Object.hasOwn(invocation, 'caseContext')) {
19
- throw new Error(`Function ${name}: caseContext required`);
20
- }
21
- return invocation.caseContext;
22
- }
23
14
  export function createLocalComputeHost(fns) {
24
15
  return {
25
16
  async execute(name, invocation = {}) {
@@ -27,7 +18,7 @@ export function createLocalComputeHost(fns) {
27
18
  if (!fn) {
28
19
  throw new Error(`Missing function implementation: ${name}`);
29
20
  }
30
- return fn(requireCaseContext(name, invocation));
21
+ return fn((invocation.args ?? {}));
31
22
  },
32
23
  dispose() { },
33
24
  };
@@ -4,11 +4,11 @@ import type { ComputeHost } from './compute-host.js';
4
4
  import type { ActionDataSlice, ApiHost, CaseOrchestratorApiHost } from '../fn-context/types.js';
5
5
  export type SourceComputeHostOptions = {
6
6
  functions: FuncMap;
7
- /** Company `events` export; handlers are registered under the event name with the table-fixed kind. */
7
+ /** Company `events` export; handlers are registered under the event name. */
8
8
  events?: AnyEventHandlers;
9
9
  /** Override ApiHost; default Fake, or Rpc when runToken + data.config.backendUrl. */
10
10
  apiHost?: ApiHost & CaseOrchestratorApiHost;
11
- /** ActionDataSlice used to build ctx for case / noCase / orchestrator. */
11
+ /** ActionDataSlice used to build every function's ctx. */
12
12
  data?: ActionDataSlice;
13
13
  /** When set with data.config.backendUrl, uses Http RpcApiHost. */
14
14
  runToken?: string;
@@ -23,15 +23,13 @@ export type SourceComputeHostOptions = {
23
23
  };
24
24
  export interface SourceComputeHost extends ComputeHost {
25
25
  }
26
- /** Company `events` handlers as DefinedFunc entries (kind from the platform event table). */
26
+ /** Company `events` handlers, by event name. */
27
27
  export declare function eventsToFuncMap(events: AnyEventHandlers | undefined): FuncMap;
28
28
  /**
29
29
  * Local host: call author FuncMap implementations directly (no SequenceBuild.code).
30
30
  *
31
- * Lookup by name, then dispatch on `kind`:
32
- * - case: `fn(caseContext, { ...ctx, params })`; caseContext required.
33
- * - orchestrator: same, with the Case Orchestrator ctx.
34
- * - noCase: `fn({ ...ctx, params })`; caseContext must be absent.
31
+ * Lookup by name, then `fn(ctx, args)` (38 §2.1): the Orchestrator gets its own ctx (with `api.tools`),
32
+ * every other function a function's.
35
33
  */
36
34
  export declare function createSourceComputeHost(options: SourceComputeHostOptions): SourceComputeHost;
37
35
  //# sourceMappingURL=source-compute-host.d.ts.map
@@ -1,4 +1,5 @@
1
- import { isSystemEventName, systemEventKind } from '../core/functions/events.js';
1
+ import { isSystemEventName } from '../core/functions/events.js';
2
+ import { ORCHESTRATOR_FUNCTION } from '../core/case-orchestrator/types.js';
2
3
  import { buildActionContext, buildCaseOrchestratorContext } from '../fn-context/build-context.js';
3
4
  import { resolveFnParams } from '../fn-context/fn-params.js';
4
5
  import { createFakeApiHost } from '../fn-context/fake/index.js';
@@ -6,28 +7,20 @@ import { createRpcApiHost } from '../fn-context/rpc/index.js';
6
7
  import { makeCaseOrchestratorCtx, makeCtx } from '../fn-context/make-ctx.js';
7
8
  import { getLogger } from '../logger.js';
8
9
  const logger = getLogger(['runtime', 'source']);
9
- function requireCaseContext(name, invocation) {
10
- if (!Object.hasOwn(invocation, 'caseContext'))
11
- throw new Error(`Function ${name}: caseContext required`);
12
- return invocation.caseContext;
13
- }
14
- /** Company `events` handlers as DefinedFunc entries (kind from the platform event table). */
10
+ /** Company `events` handlers, by event name. */
15
11
  export function eventsToFuncMap(events) {
16
12
  const out = {};
17
13
  for (const [name, handler] of Object.entries(events ?? {})) {
18
- if (!isSystemEventName(name) || typeof handler !== 'function')
19
- continue;
20
- out[name] = { kind: systemEventKind(name), main: handler };
14
+ if (isSystemEventName(name) && typeof handler === 'function')
15
+ out[name] = handler;
21
16
  }
22
17
  return out;
23
18
  }
24
19
  /**
25
20
  * Local host: call author FuncMap implementations directly (no SequenceBuild.code).
26
21
  *
27
- * Lookup by name, then dispatch on `kind`:
28
- * - case: `fn(caseContext, { ...ctx, params })`; caseContext required.
29
- * - orchestrator: same, with the Case Orchestrator ctx.
30
- * - noCase: `fn({ ...ctx, params })`; caseContext must be absent.
22
+ * Lookup by name, then `fn(ctx, args)` (38 §2.1): the Orchestrator gets its own ctx (with `api.tools`),
23
+ * every other function a function's.
31
24
  */
32
25
  export function createSourceComputeHost(options) {
33
26
  const { functions, data } = options;
@@ -47,30 +40,16 @@ export function createSourceComputeHost(options) {
47
40
  }
48
41
  return {
49
42
  async execute(name, invocation = {}) {
50
- const def = registry[name];
51
- if (!def) {
43
+ const fn = registry[name];
44
+ if (!fn) {
52
45
  throw new Error(`Missing function: ${name}`);
53
46
  }
54
- const functionInvocation = invocation;
55
- const params = resolveFnParams(functionInvocation.params);
56
- if (def.kind === 'case') {
57
- const caseContext = requireCaseContext(name, functionInvocation);
58
- const ctx = await systemContext();
59
- return def.main(caseContext, { ...ctx, params });
60
- }
61
- if (def.kind === 'orchestrator') {
62
- const caseContext = requireCaseContext(name, functionInvocation);
63
- const ctx = await caseOrchestratorContext();
64
- return def.main(caseContext, { ...ctx, params: params });
65
- }
66
- if (Object.hasOwn(functionInvocation, 'caseContext')) {
67
- throw new Error(`Function ${name}: noCase function must not receive caseContext`);
68
- }
47
+ const args = resolveFnParams(invocation.args);
69
48
  const contextStarted = Date.now();
70
- const ctx = await systemContext();
49
+ const ctx = name === ORCHESTRATOR_FUNCTION ? await caseOrchestratorContext() : await systemContext();
71
50
  logger.info('context built', { name, durationMs: Date.now() - contextStarted });
72
51
  const functionStarted = Date.now();
73
- const result = await def.main({ ...ctx, params });
52
+ const result = await fn(ctx, args);
74
53
  logger.info('function executed', { name, durationMs: Date.now() - functionStarted });
75
54
  return result;
76
55
  },
@@ -24,7 +24,18 @@ export type ContactHours = {
24
24
  date: string;
25
25
  hours: ContactHoursRange[];
26
26
  }[];
27
+ /**
28
+ * Whose hours: left out, the company's contact-hours rules; `contact_schedule`, a contact's own
29
+ * (`contactScheduleHours`); `cadence`, the code's own pacing (one call a day: today closed once it had
30
+ * its call), which holds whatever an environment being tested lets go (`ctx.time.ignore`).
31
+ */
32
+ source?: 'contact_schedule' | 'cadence';
27
33
  };
34
+ /** What does not hold while the environment is tested (`ctx.time.ignore`, 68 §6.2). */
35
+ export type ContactHoursIgnore = Readonly<{
36
+ contactHours: boolean;
37
+ contactSchedule: boolean;
38
+ }>;
28
39
  /** The contact's own `contact_schedule` value (when they said they may be reached). */
29
40
  export type ContactScheduleValue = {
30
41
  weekly: Record<ContactHoursWeekday, ContactHoursRange[]> | null;
@@ -39,12 +50,21 @@ export declare function everyDay(hours: ContactHoursRange[]): Record<ContactHour
39
50
  export declare function contactScheduleHours(schedule: ContactScheduleValue | null | undefined): ContactHours;
40
51
  /** The hours on a local date (`YYYY-MM-DD`): an override, else a holiday, else the weekday. */
41
52
  export declare function contactHoursOn(hours: ContactHours, day: string): ContactHoursRange[];
42
- /** Whether `at` (a Date, ISO string or epoch ms) falls inside `hours` (one, or a list that must all allow it) in `timezone`. */
43
- export declare function withinContactHours(hours: ContactHours | readonly ContactHours[], at: Date | string | number, timezone: string): boolean;
44
53
  /**
45
- * The first time at or after `from` (a Date, ISO string or epoch ms, e.g. `ctx.time.now`) inside `hours` (one, or a list that must all allow it: the
46
- * company's rules and the contact's own schedule), in `timezone`, as an ISO string for `at`; null
47
- * when there is none within `days` (default 14).
54
+ * Whether `at` (a Date, ISO string or epoch ms) falls inside `hours` (one, or a list that must all allow it)
55
+ * in `timezone`. `ignore` (`ctx.time.ignore`): what does not hold while the environment is tested; when
56
+ * nothing is left to hold, any time is.
48
57
  */
49
- export declare function nextContactTime(hours: ContactHours | readonly ContactHours[], from: Date | string | number, timezone: string, days?: number): string | null;
58
+ export declare function withinContactHours(hours: ContactHours | readonly ContactHours[], at: Date | string | number, timezone: string, ignore?: ContactHoursIgnore): boolean;
59
+ /**
60
+ * The first time at or after `from` inside `hours` (one, or a list that must all allow it: the company's
61
+ * rules and the contact's own schedule), in `timezone`, as an ISO string for `at`; null when there is none
62
+ * within `days` (default 14). `from` is `ctx.time` (its `now`, and what does not hold while the environment
63
+ * is tested: `ctx.time.ignore`, 68 §6.2), or a Date, ISO string or epoch ms. When nothing is left to hold,
64
+ * `from` itself.
65
+ */
66
+ export declare function nextContactTime(hours: ContactHours | readonly ContactHours[], from: Date | string | number | {
67
+ now: number;
68
+ ignore?: ContactHoursIgnore;
69
+ }, timezone: string, days?: number): string | null;
50
70
  //# sourceMappingURL=contact-hours.d.ts.map
@@ -17,8 +17,15 @@ export function contactScheduleHours(schedule) {
17
17
  return {
18
18
  week: schedule?.weekly ?? everyDay(ALL_DAY),
19
19
  overrides: (schedule?.dateOverrides ?? []).flatMap((override) => override.dates.map((date) => ({ date, hours: override.windows }))),
20
+ source: 'contact_schedule',
20
21
  };
21
22
  }
23
+ /** `hours` without what `ignore` lets go: the company's rules, the contact's own schedule; never a cadence. */
24
+ function held(hours, ignore) {
25
+ if (!ignore)
26
+ return hours;
27
+ return hours.filter((entry) => entry.source === 'cadence' ? true : entry.source === 'contact_schedule' ? !ignore.contactSchedule : !ignore.contactHours);
28
+ }
22
29
  /** The hours on a local date (`YYYY-MM-DD`): an override, else a holiday, else the weekday. */
23
30
  export function contactHoursOn(hours, day) {
24
31
  const override = hours.overrides?.find((entry) => entry.date === day);
@@ -78,21 +85,33 @@ function addDays(day, count) {
78
85
  function list(hours) {
79
86
  return Array.isArray(hours) ? hours : [hours];
80
87
  }
81
- /** Whether `at` (a Date, ISO string or epoch ms) falls inside `hours` (one, or a list that must all allow it) in `timezone`. */
82
- export function withinContactHours(hours, at, timezone) {
88
+ /**
89
+ * Whether `at` (a Date, ISO string or epoch ms) falls inside `hours` (one, or a list that must all allow it)
90
+ * in `timezone`. `ignore` (`ctx.time.ignore`): what does not hold while the environment is tested; when
91
+ * nothing is left to hold, any time is.
92
+ */
93
+ export function withinContactHours(hours, at, timezone, ignore) {
94
+ const kept = held(list(hours), ignore);
95
+ if (kept.length === 0)
96
+ return true;
83
97
  const local = localParts(new Date(at), timezone);
84
- return rangesOn(list(hours), local.day).some((range) => local.minute >= minutes(range.start) && local.minute < minutes(range.end));
98
+ return rangesOn(kept, local.day).some((range) => local.minute >= minutes(range.start) && local.minute < minutes(range.end));
85
99
  }
86
100
  /**
87
- * The first time at or after `from` (a Date, ISO string or epoch ms, e.g. `ctx.time.now`) inside `hours` (one, or a list that must all allow it: the
88
- * company's rules and the contact's own schedule), in `timezone`, as an ISO string for `at`; null
89
- * when there is none within `days` (default 14).
101
+ * The first time at or after `from` inside `hours` (one, or a list that must all allow it: the company's
102
+ * rules and the contact's own schedule), in `timezone`, as an ISO string for `at`; null when there is none
103
+ * within `days` (default 14). `from` is `ctx.time` (its `now`, and what does not hold while the environment
104
+ * is tested: `ctx.time.ignore`, 68 §6.2), or a Date, ISO string or epoch ms. When nothing is left to hold,
105
+ * `from` itself.
90
106
  */
91
107
  export function nextContactTime(hours, from, timezone, days = 14) {
92
- const start = new Date(from).getTime();
108
+ const time = typeof from === 'object' && !(from instanceof Date) ? from : null;
109
+ const start = new Date(time ? time.now : from).getTime();
93
110
  if (!Number.isFinite(start))
94
111
  throw new Error('nextContactTime: from must be a time');
95
- const all = list(hours);
112
+ const all = held(list(hours), time?.ignore);
113
+ if (all.length === 0)
114
+ return new Date(start).toISOString();
96
115
  const today = localParts(new Date(start), timezone).day;
97
116
  for (let offset = 0; offset <= days; offset += 1) {
98
117
  const day = addDays(today, offset);
@@ -1,6 +1,3 @@
1
- export { ACTIVITY_RECENT_LIMIT, ORCHESTRATOR_ENTRY, ORCHESTRATOR_CONTACTS_FUNCTION, ORCHESTRATOR_FUNCTION, CASE_ORCHESTRATOR_TOOL_NAMES, EMPTY_CASE_ACTIVITY, RECENT_AGREED_ACTIONS_DAYS, type ActivityStats, type AgentRequest, type AgreementFields, type CaseActivity, type CaseOrchestrator, type CaseOrchestratorApi, type CaseOrchestratorContacts, type CaseOrchestratorContactsContext, type CaseOrchestratorContext, type CaseInput, type CaseOrchestratorInput, type CaseOrchestratorManifest, type CaseOrchestratorParams, type CaseOrchestratorResult, type CaseOrchestratorToolName, type CaseOrchestratorTools, type ChannelAvailability, type ChannelUnavailableReason, type ExchangeEntry, type ExchangeEvent, type ExchangeSearchHit, type ContactActionInput, type ContactActionView, type ContactChannel, type PartyContactHours, type FinalOrchestrationResult, type FinishedAgreedActionView, type PartyActivity, type ReadExchangeResult, type ResolvedAgentRequest, type RescheduledResult, type RunTrigger, type RunTriggerType, type ScheduleUpdateSlotsInput, type SlotFieldWriteInput, type SlotInsertInput, type SlotWriteInput, type ScheduleTiming, type ScheduledActionInput, type ScheduledActionInputs, type ScheduledActionKind, type ScheduledActionTiming, type ScheduledActionView, type SearchExchangesResult, type ToolCallArgs, type ToolCallOptions, type ToolCallResult, type ToolResult, type ToolRef, type UpdateSlotsActionInput, type UpdateSlotsActionView, type ScheduleWakeInput, type WakeActionInput, type WakeActionView, type WakeResult, } from './types.js';
1
+ export { ACTIVITY_RECENT_LIMIT, ORCHESTRATOR_ENTRY, ORCHESTRATOR_FUNCTION, CASE_ORCHESTRATOR_TOOL_NAMES, EMPTY_CASE_ACTIVITY, RECENT_AGREED_ACTIONS_DAYS, type ActivityStats, type AgreementFields, type CaseActivity, type CaseOrchestrator, type CaseOrchestratorApi, type CaseOrchestratorArgs, type CaseOrchestratorContext, type CaseOrchestratorManifest, type CaseOrchestratorParams, type CaseOrchestratorToolName, type CaseOrchestratorTools, type ChannelAvailability, type ChannelUnavailableReason, type ExchangeEntry, type ExchangeEvent, type ExchangeSearchHit, type ContactActionInput, type ContactActionView, type ContactChannel, type PartyContactHours, type FinishedAgreedActionView, type PartyActivity, type ReadExchangeResult, type RescheduledResult, type RunTrigger, type RunTriggerType, type ScheduleUpdateSlotsInput, type SlotFieldWriteInput, type SlotInsertInput, type SlotWriteInput, type ScheduleTiming, type ScheduledActionInput, type ScheduledActionInputs, type ScheduledActionKind, type ScheduledActionTiming, type ScheduledActionView, type SearchExchangesResult, type ToolCallArgs, type ToolCallResult, type ToolResult, type UpdateSlotsActionInput, type UpdateSlotsActionView, type ScheduleWakeInput, type WakeActionInput, type WakeActionView, type WakeResult, } from './types.js';
2
2
  export { localDateTime, localDay, render, zonedIso, type ConversationOptions, type TimelineOptions } from './render.js';
3
- export { tools } from './tools.js';
4
- export { followUps } from './follow-ups.js';
5
- export { validateCaseOrchestratorResult, type CaseOrchestratorResultCheck } from './validate.js';
6
3
  //# sourceMappingURL=index.d.ts.map
@@ -1,6 +1,3 @@
1
- export { ACTIVITY_RECENT_LIMIT, ORCHESTRATOR_ENTRY, ORCHESTRATOR_CONTACTS_FUNCTION, ORCHESTRATOR_FUNCTION, CASE_ORCHESTRATOR_TOOL_NAMES, EMPTY_CASE_ACTIVITY, RECENT_AGREED_ACTIONS_DAYS, } from './types.js';
1
+ export { ACTIVITY_RECENT_LIMIT, ORCHESTRATOR_ENTRY, ORCHESTRATOR_FUNCTION, CASE_ORCHESTRATOR_TOOL_NAMES, EMPTY_CASE_ACTIVITY, RECENT_AGREED_ACTIONS_DAYS, } from './types.js';
2
2
  export { localDateTime, localDay, render, zonedIso } from './render.js';
3
- export { tools } from './tools.js';
4
- export { followUps } from './follow-ups.js';
5
- export { validateCaseOrchestratorResult } from './validate.js';
6
3
  //# sourceMappingURL=index.js.map
@@ -55,7 +55,7 @@ export type ConversationOptions = {
55
55
  */
56
56
  declare function conversation(result: ReadExchangeResult, timezone: string, options?: ConversationOptions): string;
57
57
  /**
58
- * Text for company prompts (docs/design/08-case-orchestrator.md §5.3), in the mars next-step
58
+ * Text for company code to show or log (docs/design/08-case-orchestrator.md §5.3), in the mars next-step
59
59
  * format. Party functions take one contact's activity (`activity.byContact[contactId]`) and
60
60
  * write its local wall-clock times.
61
61
  */
@@ -158,20 +158,22 @@ function pendingActions(actions, timezone) {
158
158
  return actions
159
159
  .map((action) => {
160
160
  const head = `- ${action.id}: ${action.kind} at ${localDateTime(action.scheduledFor, timezone)}`;
161
+ const why = action.reason ? ` (${action.reason})` : '';
161
162
  if (action.kind === 'update_slots') {
162
- const { timing, writes, reason } = action.input;
163
- return `${head} (${reason}) ${JSON.stringify({ timing, writes })}`;
163
+ const { timing, writes } = action.input;
164
+ return `${head}${why} ${JSON.stringify({ timing, writes })}`;
164
165
  }
165
166
  if (action.kind === 'next_sequence') {
166
- const { timing, sequence, subjectRecordId, reason } = action.input;
167
- return `${head} (${reason}) ${JSON.stringify({ timing, sequence, ...(subjectRecordId ? { subjectRecordId } : {}) })}`;
167
+ const { timing, sequence, subjectRecordId } = action.input;
168
+ return `${head}${why} ${JSON.stringify({ timing, sequence, ...(subjectRecordId ? { subjectRecordId } : {}) })}`;
168
169
  }
169
170
  if (action.kind === 'wake') {
170
- const { timing, name, note } = action.input;
171
- return `${head} ${name}${note ? ` (${note})` : ''} ${JSON.stringify({ timing })}`;
171
+ const { timing } = action.input;
172
+ const name = action.name;
173
+ return `${head} ${name}${why} ${JSON.stringify({ timing })}`;
172
174
  }
173
- const { timing, note } = action.input;
174
- return `${head}${agreedMark(action, timezone)}${note ? ` (${note})` : ''} ${JSON.stringify({ timing })}`;
175
+ const { timing } = action.input;
176
+ return `${head}${agreedMark(action, timezone)}${why} ${JSON.stringify({ timing })}`;
175
177
  })
176
178
  .join('\n');
177
179
  }
@@ -190,6 +192,9 @@ function wakeUps(triggers, timezone) {
190
192
  return [`- external ${trigger.reason} at ${at}`];
191
193
  if (trigger.type === 'inbound.paused')
192
194
  return [`- inbound ${trigger.channel} ${trigger.exchangeId} at ${at}, not answered: the case was paused`];
195
+ if (trigger.type === 'scheduled_action.ended') {
196
+ return [`- scheduled ${trigger.kind} ${trigger.scheduledActionId} at ${at}: ${trigger.outcome === 'not_sent' ? `not sent (${trigger.reason ?? 'decided'})` : 'slots written'}`];
197
+ }
193
198
  return [];
194
199
  });
195
200
  return lines.length > 0 ? lines.join('\n') : '(none)';
@@ -202,7 +207,7 @@ function recentAgreedActions(actions, timezone) {
202
207
  if (actions.length === 0)
203
208
  return '(none)';
204
209
  return actions
205
- .map((action) => `- ${action.id}: ${action.kind} at ${localDateTime(action.scheduledFor, timezone)}${agreedMark(action, timezone)}, ${action.status} at ${localDateTime(action.finishedAt, timezone)}${action.input.note ? ` (${action.input.note})` : ''}`)
210
+ .map((action) => `- ${action.id}: ${action.kind} at ${localDateTime(action.scheduledFor, timezone)}${agreedMark(action, timezone)}, ${action.status} at ${localDateTime(action.finishedAt, timezone)}${action.reason ? ` (${action.reason})` : ''}`)
206
211
  .join('\n');
207
212
  }
208
213
  /**
@@ -220,7 +225,7 @@ function conversation(result, timezone, options = {}) {
220
225
  return `${head}\n${earlier}${lines.join('\n')}`;
221
226
  }
222
227
  /**
223
- * Text for company prompts (docs/design/08-case-orchestrator.md §5.3), in the mars next-step
228
+ * Text for company code to show or log (docs/design/08-case-orchestrator.md §5.3), in the mars next-step
224
229
  * format. Party functions take one contact's activity (`activity.byContact[contactId]`) and
225
230
  * write its local wall-clock times.
226
231
  */