@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
@@ -12,7 +12,17 @@ export type PlatformInvokeHandler = (input: {
12
12
  export declare function createPlatformRouter(invoke: PlatformInvokeHandler): import("@trpc/server").TRPCBuiltRouter<{
13
13
  ctx: RpcContext;
14
14
  meta: object;
15
- errorShape: import("@trpc/server").TRPCDefaultErrorShape;
15
+ errorShape: import("@trpc/server").TRPCDefaultErrorShape | {
16
+ message: string;
17
+ code: import("@trpc/server").TRPC_ERROR_CODE_NUMBER;
18
+ data: {
19
+ code: import("@trpc/server").TRPC_ERROR_CODE_KEY;
20
+ httpStatus: number;
21
+ path?: string;
22
+ stack?: string;
23
+ reason: string;
24
+ };
25
+ };
16
26
  transformer: false;
17
27
  }, import("@trpc/server").TRPCDecorateCreateRouterOptions<{
18
28
  invoke: import("@trpc/server").TRPCMutationProcedure<{
@@ -2,9 +2,8 @@ import { z } from 'zod';
2
2
  import { type RpcContext } from './trpc-init.js';
3
3
  /**
4
4
  * `ctx.api.db` of every function and the Orchestrator (15 §10.16): the same
5
- * reads and writes as a page script and its actions, over RPC, one call per request. A write carries
6
- * its call key (its place in the run: `write:1`, `write:2`, …): within an Orchestrator Run the same
7
- * request sent again returns the first answer instead of writing twice.
5
+ * reads and writes as a page script and its actions, over RPC, one call per request. A write is done
6
+ * each time it is sent (38 §2.7): the platform keeps no key to answer a repeat with the first answer.
8
7
  */
9
8
  declare const readInput: z.ZodObject<{
10
9
  table: z.ZodString;
@@ -25,7 +24,6 @@ declare const writeInput: z.ZodObject<{
25
24
  upsert: "upsert";
26
25
  }>;
27
26
  args: z.ZodUnknown;
28
- callKey: z.ZodString;
29
27
  }, z.core.$strip>;
30
28
  export type RecordsReadInput = z.infer<typeof readInput>;
31
29
  export type RecordsWriteInput = z.infer<typeof writeInput>;
@@ -51,7 +49,17 @@ export type RecordsHandlers = {
51
49
  export declare function createRecordsRouter(handlers: RecordsHandlers): import("@trpc/server").TRPCBuiltRouter<{
52
50
  ctx: RpcContext;
53
51
  meta: object;
54
- errorShape: import("@trpc/server").TRPCDefaultErrorShape;
52
+ errorShape: import("@trpc/server").TRPCDefaultErrorShape | {
53
+ message: string;
54
+ code: import("@trpc/server").TRPC_ERROR_CODE_NUMBER;
55
+ data: {
56
+ code: import("@trpc/server").TRPC_ERROR_CODE_KEY;
57
+ httpStatus: number;
58
+ path?: string;
59
+ stack?: string;
60
+ reason: string;
61
+ };
62
+ };
55
63
  transformer: false;
56
64
  }, import("@trpc/server").TRPCDecorateCreateRouterOptions<{
57
65
  read: import("@trpc/server").TRPCMutationProcedure<{
@@ -68,7 +76,6 @@ export declare function createRecordsRouter(handlers: RecordsHandlers): import("
68
76
  table: string;
69
77
  method: "create" | "delete" | "update" | "upsert";
70
78
  args: unknown;
71
- callKey: string;
72
79
  };
73
80
  output: RecordsResult;
74
81
  meta: object;
@@ -2,9 +2,8 @@ import { z } from 'zod';
2
2
  import { publicProcedure, router } from './trpc-init.js';
3
3
  /**
4
4
  * `ctx.api.db` of every function and the Orchestrator (15 §10.16): the same
5
- * reads and writes as a page script and its actions, over RPC, one call per request. A write carries
6
- * its call key (its place in the run: `write:1`, `write:2`, …): within an Orchestrator Run the same
7
- * request sent again returns the first answer instead of writing twice.
5
+ * reads and writes as a page script and its actions, over RPC, one call per request. A write is done
6
+ * each time it is sent (38 §2.7): the platform keeps no key to answer a repeat with the first answer.
8
7
  */
9
8
  const readInput = z.object({
10
9
  table: z.string().min(1),
@@ -15,7 +14,6 @@ const writeInput = z.object({
15
14
  table: z.string().min(1),
16
15
  method: z.enum(['create', 'update', 'upsert', 'delete']),
17
16
  args: z.unknown(),
18
- callKey: z.string().min(1).max(200),
19
17
  });
20
18
  export function createRecordsRouter(handlers) {
21
19
  return router({
@@ -1,4 +1,41 @@
1
1
  import type { ApiHost, CaseOrchestratorApiHost } from '../types.js';
2
+ /**
3
+ * A request that got no answer from the platform itself (74 §3.2): the platform always answers JSON, so any
4
+ * other answer is a gateway's. `reached: false` only when the request certainly never got to the platform.
5
+ */
6
+ export declare class RpcTransportError extends Error {
7
+ readonly reached: boolean | 'unknown';
8
+ readonly name = "RpcTransportError";
9
+ constructor(message: string, reached: boolean | 'unknown');
10
+ }
11
+ /**
12
+ * `fetch` for the platform's RPC: a failure to connect, or an answer that is not the platform's (not JSON),
13
+ * becomes an {@link RpcTransportError} saying whether the request reached it. The answer is read whole here:
14
+ * one that breaks as it is read (a body cut off, bad JSON) is an {@link RpcTransportError} too, so whatever
15
+ * fails before this is called (the code's arguments that cannot be sent as JSON) stays the code's.
16
+ */
17
+ export declare function platformFetch(input: Parameters<typeof fetch>[0], init?: Parameters<typeof fetch>[1]): Promise<Response>;
18
+ /** Sent again: the platform was busy (`TOO_MANY_REQUESTS`), or the request never reached it. */
19
+ export declare function shouldRetryRpc(error: {
20
+ data?: {
21
+ code?: string;
22
+ } | null;
23
+ cause?: unknown;
24
+ }, attempts: number): boolean;
25
+ /**
26
+ * A failed call as company code sees it: an `Error` with the platform's message and, when the platform
27
+ * refused it for a reason of the business (`subject_busy`, `run_ended`, a write's code), that reason as
28
+ * `error.code` (64 §12).
29
+ */
30
+ export declare function rpcSafe<T>(promise: Promise<T>): Promise<T>;
31
+ /**
32
+ * A read's arguments as sent, `after` cut down to the place the platform reads off it (18 §4.6): the
33
+ * row's `id`, `createdAt` and `updatedAt`, and of each field this read's `orderBy` names, the slot's
34
+ * `value`. `after: rows.at(-1)` is a row with whatever it `include`d; sent whole, a big include tree
35
+ * could pass the request's size limit for nothing. Anything that is no row goes as given, for the
36
+ * platform to refuse with its reason.
37
+ */
38
+ export declare function placeOnly(args: unknown): unknown;
2
39
  export declare function createRpcApiHost(options: {
3
40
  baseUrl: string;
4
41
  getToken: () => string | Promise<string>;
@@ -7,5 +44,10 @@ export declare function createRpcApiHost(options: {
7
44
  getRequestId?: () => string | undefined;
8
45
  getTags?: () => string[] | undefined;
9
46
  waitUntil?: (promise: Promise<unknown>) => void;
47
+ /**
48
+ * Asked before each call is sent, with its procedure's path (`platform.invoke`, `log.emit`, …): throws
49
+ * to refuse it. The Worker refuses a `ctx` kept from another call (64 §6).
50
+ */
51
+ checkCall?: (path: string) => void;
10
52
  }): ApiHost & CaseOrchestratorApiHost;
11
53
  //# sourceMappingURL=trpc-client.d.ts.map
@@ -4,6 +4,7 @@ import { integrationSurfaceKeys, } from '../../core/context/types.js';
4
4
  import { bindEnvService } from '../../core/context/env.js';
5
5
  import { CASE_ORCHESTRATOR_TOOL_NAMES, } from '../../core/case-orchestrator/types.js';
6
6
  import { getLogger } from '../../logger.js';
7
+ import { PlatformUnavailableError } from '../../core/functions/errors.js';
7
8
  import { REQUEST_ID_HEADER, RUNTIME_VERSION_HEADER, formatRuntimeVersion } from './request-id.js';
8
9
  const logger = getLogger(['runtime', 'rpc']);
9
10
  /**
@@ -12,6 +13,83 @@ const logger = getLogger(['runtime', 'rpc']);
12
13
  */
13
14
  const BUSY_RETRIES = 5;
14
15
  const busyDelayMs = (attempt) => Math.min(500 * 2 ** attempt, 5_000) + Math.floor(Math.random() * 250);
16
+ /**
17
+ * A call that certainly never reached the platform (a gateway's error page, a refused connection, a name that
18
+ * did not resolve) is sent again this many times, after the same growing pause (74 §3.2). One that may have
19
+ * reached it (504, a connection cut after it was sent) is not, read or write alike.
20
+ */
21
+ const UNREACHED_RETRIES = 3;
22
+ /**
23
+ * A request that got no answer from the platform itself (74 §3.2): the platform always answers JSON, so any
24
+ * other answer is a gateway's. `reached: false` only when the request certainly never got to the platform.
25
+ */
26
+ export class RpcTransportError extends Error {
27
+ reached;
28
+ name = 'RpcTransportError';
29
+ constructor(message, reached) {
30
+ super(message);
31
+ this.reached = reached;
32
+ }
33
+ }
34
+ /** A gateway's own answer that may come after the platform got the request: its timeouts (504; Cloudflare 520, 524). */
35
+ const MAYBE_REACHED_STATUSES = new Set([504, 520, 524]);
36
+ /** Fetch failures where no connection was ever made. */
37
+ const NOT_CONNECTED = /ECONNREFUSED|ENOTFOUND|EAI_AGAIN|getaddrinfo/;
38
+ function connectFailure(error) {
39
+ for (let current = error, depth = 0; current && depth < 5; depth += 1) {
40
+ const { code, message, errors } = current;
41
+ if (NOT_CONNECTED.test(`${String(code ?? '')} ${String(message ?? '')}`))
42
+ return true;
43
+ if (Array.isArray(errors) && errors.some(connectFailure))
44
+ return true;
45
+ current = current.cause;
46
+ }
47
+ return false;
48
+ }
49
+ /**
50
+ * `fetch` for the platform's RPC: a failure to connect, or an answer that is not the platform's (not JSON),
51
+ * becomes an {@link RpcTransportError} saying whether the request reached it. The answer is read whole here:
52
+ * one that breaks as it is read (a body cut off, bad JSON) is an {@link RpcTransportError} too, so whatever
53
+ * fails before this is called (the code's arguments that cannot be sent as JSON) stays the code's.
54
+ */
55
+ export async function platformFetch(input, init) {
56
+ let response;
57
+ try {
58
+ response = await fetch(input, init);
59
+ }
60
+ catch (error) {
61
+ const reached = connectFailure(error) ? false : 'unknown';
62
+ throw new RpcTransportError(`the platform could not be reached: ${error instanceof Error ? error.message : String(error)}`, reached);
63
+ }
64
+ if (!/json/i.test(response.headers.get('content-type') ?? '')) {
65
+ const body = (await response.text().catch(() => '')).slice(0, 120).replace(/\s+/g, ' ');
66
+ throw new RpcTransportError(`HTTP ${response.status} from a gateway, not the platform: ${body}`, MAYBE_REACHED_STATUSES.has(response.status) ? 'unknown' : false);
67
+ }
68
+ let text;
69
+ try {
70
+ text = await response.text();
71
+ }
72
+ catch (error) {
73
+ throw new RpcTransportError(`the platform's answer was cut off: ${error instanceof Error ? error.message : String(error)}`, 'unknown');
74
+ }
75
+ try {
76
+ JSON.parse(text);
77
+ }
78
+ catch {
79
+ throw new RpcTransportError(`the platform's answer is not valid JSON: ${text.slice(0, 120)}`, 'unknown');
80
+ }
81
+ return new Response(text, { status: response.status, statusText: response.statusText, headers: response.headers });
82
+ }
83
+ function transportErrorOf(error) {
84
+ const cause = error?.cause;
85
+ return cause instanceof RpcTransportError ? cause : null;
86
+ }
87
+ /** Sent again: the platform was busy (`TOO_MANY_REQUESTS`), or the request never reached it. */
88
+ export function shouldRetryRpc(error, attempts) {
89
+ if (error.data?.code === 'TOO_MANY_REQUESTS')
90
+ return attempts <= BUSY_RETRIES;
91
+ return transportErrorOf(error)?.reached === false && attempts <= UNREACHED_RETRIES;
92
+ }
15
93
  function rpcLogger(c, getTags, waitUntil) {
16
94
  const emit = (level, message, extra) => {
17
95
  const pending = c.log.emit.mutate({ level, message, extra, tags: getTags?.() }).catch(() => { });
@@ -28,10 +106,25 @@ function rpcLogger(c, getTags, waitUntil) {
28
106
  error: (message, extra) => emit('error', message, extra),
29
107
  };
30
108
  }
31
- function rpcSafe(promise) {
109
+ /**
110
+ * A failed call as company code sees it: an `Error` with the platform's message and, when the platform
111
+ * refused it for a reason of the business (`subject_busy`, `run_ended`, a write's code), that reason as
112
+ * `error.code` (64 §12).
113
+ */
114
+ export function rpcSafe(promise) {
32
115
  return promise.catch((err) => {
33
116
  if (err instanceof Error && err.constructor?.name === 'TRPCClientError') {
34
- throw new Error(`RPC call failed: ${err.message}`);
117
+ // No answer from the platform, one that broke as it was read (`platformFetch` says so), or its own
118
+ // failure (5xx): the platform's (74 §3.2), marked so. What failed before the request was sent (the
119
+ // code's arguments that are no JSON, a ctx kept from another call) stays the code's.
120
+ const status = err.data?.httpStatus;
121
+ const platform = transportErrorOf(err) !== null || (typeof status === 'number' && status >= 500);
122
+ const message = `RPC call failed: ${transportErrorOf(err)?.message ?? err.message}`;
123
+ const failed = (platform ? new PlatformUnavailableError(message) : new Error(message));
124
+ const reason = err.data?.reason;
125
+ if (typeof reason === 'string')
126
+ failed.code = reason;
127
+ throw failed;
35
128
  }
36
129
  throw err;
37
130
  });
@@ -93,7 +186,7 @@ function buildPlatformApis(c) {
93
186
  function buildCaseOrchestratorTools(c) {
94
187
  const tools = CASE_ORCHESTRATOR_TOOL_NAMES.map((tool) => [
95
188
  tool,
96
- (input, callOptions) => rpcSafe(c.caseOrchestratorTools.invoke.mutate({ tool, input, callKey: callOptions.callKey })),
189
+ (input) => rpcSafe(c.caseOrchestratorTools.invoke.mutate({ tool, input })),
97
190
  ]);
98
191
  // The tool registry guarantees the shape; TS cannot follow it through Object.fromEntries.
99
192
  return Object.fromEntries(tools);
@@ -106,24 +199,49 @@ function unwrapRecords(result) {
106
199
  error.code = result.error.code;
107
200
  throw error;
108
201
  }
202
+ function isPlainObject(value) {
203
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
204
+ }
205
+ /** The keys of a row the platform always reads off `after`: its id and times. */
206
+ const PLACE_KEYS = new Set(['id', 'createdAt', 'updatedAt']);
207
+ /**
208
+ * A read's arguments as sent, `after` cut down to the place the platform reads off it (18 §4.6): the
209
+ * row's `id`, `createdAt` and `updatedAt`, and of each field this read's `orderBy` names, the slot's
210
+ * `value`. `after: rows.at(-1)` is a row with whatever it `include`d; sent whole, a big include tree
211
+ * could pass the request's size limit for nothing. Anything that is no row goes as given, for the
212
+ * platform to refuse with its reason.
213
+ */
214
+ export function placeOnly(args) {
215
+ if (!isPlainObject(args) || !isPlainObject(args.after))
216
+ return args;
217
+ const ordered = new Set();
218
+ for (const entry of Array.isArray(args.orderBy) ? args.orderBy : [args.orderBy]) {
219
+ if (isPlainObject(entry))
220
+ for (const field of Object.keys(entry))
221
+ ordered.add(field);
222
+ }
223
+ const place = Object.entries(args.after).flatMap(([key, value]) => {
224
+ if (PLACE_KEYS.has(key))
225
+ return [[key, value]];
226
+ if (!ordered.has(key))
227
+ return [];
228
+ // A slot: only its value places the row (a lookup's included `row` does not).
229
+ return [[key, isPlainObject(value) ? (Object.hasOwn(value, 'value') ? { value: value.value } : {}) : value]];
230
+ });
231
+ return { ...args, after: Object.fromEntries(place) };
232
+ }
109
233
  /**
110
234
  * `ctx.api.db` of every function and the Orchestrator (15 §10.15 task 4, §10.16): every table, as a
111
- * page script and its actions see them, one request per call. Writes are numbered in the order this
112
- * run makes them; for an Orchestrator Run the same request sent again (a retried request) carries the
113
- * same key and is not made twice.
235
+ * page script and its actions see them, one request per call; a write is done each time (38 §2.7). A
236
+ * read's `after` goes as its place only ({@link placeOnly}).
114
237
  */
115
238
  function buildDbApi(c) {
116
- let writes = 0;
117
239
  const db = new Proxy({}, {
118
240
  get(_, table) {
119
241
  if (typeof table !== 'string')
120
242
  return undefined;
121
- const read = (method) => async (args) => unwrapRecords(await rpcSafe(c.records.read.mutate({ table, method, args: args ?? {} })));
122
- const write = (method) => async (args) => {
123
- writes += 1;
124
- const callKey = `write:${writes}`;
125
- return unwrapRecords(await rpcSafe(c.records.write.mutate({ table, method, args: args ?? {}, callKey })));
126
- };
243
+ const read = (method) => async (args) => unwrapRecords(await rpcSafe(c.records.read.mutate({ table, method, args: placeOnly(args ?? {}) })));
244
+ const write = (method) => async (args) => unwrapRecords(await rpcSafe(c.records.write.mutate({ table, method, args: args ?? {} })));
127
245
  return {
128
246
  $table: table,
129
247
  findUnique: read('findUnique'),
@@ -147,12 +265,14 @@ export function createRpcApiHost(options) {
147
265
  client = createTRPCClient({
148
266
  links: [
149
267
  retryLink({
150
- retry: ({ error, attempts }) => error.data?.code === 'TOO_MANY_REQUESTS' && attempts <= BUSY_RETRIES,
268
+ retry: ({ error, attempts }) => shouldRetryRpc(error, attempts),
151
269
  retryDelayMs: busyDelayMs,
152
270
  }),
153
271
  httpLink({
154
272
  url: baseUrl,
155
- headers: async () => {
273
+ fetch: platformFetch,
274
+ headers: async ({ op }) => {
275
+ options.checkCall?.(op.path);
156
276
  const headers = {
157
277
  authorization: `Bearer ${await options.getToken()}`,
158
278
  [RUNTIME_VERSION_HEADER]: formatRuntimeVersion(options.runtimeVersion, options.runtimeSource),
@@ -169,24 +289,33 @@ export function createRpcApiHost(options) {
169
289
  }
170
290
  return client;
171
291
  }
292
+ /** The integrations the call brought; without them, the backend lists them. */
293
+ async function surfacesOf(c, given) {
294
+ if (given.surfaces)
295
+ return given.surfaces;
296
+ const started = Date.now();
297
+ logger.info('integration.listSurfaces start', { url: options.baseUrl });
298
+ const metas = await rpcSafe(c.integration.listSurfaces.query());
299
+ logger.info('integration.listSurfaces ok', { durationMs: Date.now() - started, count: metas.length });
300
+ return metas;
301
+ }
172
302
  return {
173
- async buildApi(_run) {
303
+ async buildApi(_run, given = {}) {
174
304
  const c = getClient(options.baseUrl);
175
- const started = Date.now();
176
- logger.info('integration.listSurfaces start', { url: options.baseUrl });
177
- const metas = await rpcSafe(c.integration.listSurfaces.query());
178
- logger.info('integration.listSurfaces ok', { durationMs: Date.now() - started, count: metas.length });
305
+ const metas = await surfacesOf(c, given);
179
306
  return {
180
307
  ...buildPlatformApis(c),
181
308
  integration: buildSurfaces(c, metas),
182
309
  log: rpcLogger(c, options.getTags, options.waitUntil),
310
+ // Every function's (38 §2.7): each call names its instance.
311
+ tools: buildCaseOrchestratorTools(c),
183
312
  ...buildDbApi(c),
184
313
  };
185
314
  },
186
- async buildCaseOrchestratorApi(_run) {
315
+ async buildCaseOrchestratorApi(_run, given = {}) {
187
316
  const c = getClient(options.baseUrl);
188
317
  // A function's api (25 §7), so a function called from the Orchestrator with its ctx works the same.
189
- const metas = await rpcSafe(c.integration.listSurfaces.query());
318
+ const metas = await surfacesOf(c, given);
190
319
  return {
191
320
  ...buildPlatformApis(c),
192
321
  integration: buildSurfaces(c, metas),
@@ -3,11 +3,29 @@ export type RpcContext = {
3
3
  run: RunClaims;
4
4
  requestId?: string;
5
5
  runtimeVersion?: string;
6
+ /**
7
+ * The platform's gate around every procedure (64 §5): the rate, and whether an Orchestrator's task still
8
+ * runs. Set by the platform's context; every procedure built on {@link publicProcedure} passes through it,
9
+ * so a new one cannot miss it. Absent (a test calling one router directly), the procedure just runs.
10
+ */
11
+ around?: <T>(call: {
12
+ path: string;
13
+ }, next: () => Promise<T>) => Promise<T>;
6
14
  };
7
15
  export declare const router: import("@trpc/server").TRPCRouterBuilder<{
8
16
  ctx: RpcContext;
9
17
  meta: object;
10
- errorShape: import("@trpc/server").TRPCDefaultErrorShape;
18
+ errorShape: import("@trpc/server").TRPCDefaultErrorShape | {
19
+ message: string;
20
+ code: import("@trpc/server").TRPC_ERROR_CODE_NUMBER;
21
+ data: {
22
+ code: import("@trpc/server").TRPC_ERROR_CODE_KEY;
23
+ httpStatus: number;
24
+ path?: string;
25
+ stack?: string;
26
+ reason: string;
27
+ };
28
+ };
11
29
  transformer: false;
12
30
  }>;
13
31
  /**
@@ -1,13 +1,19 @@
1
1
  import { initTRPC, TRPCError } from '@trpc/server';
2
- import { isRpcCodedError } from './errors.js';
3
- const t = initTRPC.context().create();
2
+ import { isRpcCodedError, rpcErrorReason } from './errors.js';
3
+ const t = initTRPC.context().create({
4
+ // A refusal's business code (`subject_busy`, `run_ended`, …) goes back beside its message (64 §12).
5
+ errorFormatter({ shape, error }) {
6
+ const reason = rpcErrorReason(error.cause);
7
+ return reason ? { ...shape, data: { ...shape.data, reason } } : shape;
8
+ },
9
+ });
4
10
  export const router = t.router;
5
11
  /**
6
12
  * Applied to every procedure so domain errors reach the caller with the status they declared,
7
13
  * without each dispatcher wrapping its own body in try/catch.
8
14
  */
9
- export const publicProcedure = t.procedure.use(async ({ next }) => {
10
- const result = await next();
15
+ export const publicProcedure = t.procedure.use(async ({ ctx, path, next }) => {
16
+ const result = ctx.around ? await ctx.around({ path }, () => next()) : await next();
11
17
  if (!result.ok && isRpcCodedError(result.error.cause)) {
12
18
  const cause = result.error.cause;
13
19
  throw new TRPCError({ code: cause.rpcCode, message: cause.message, cause });
@@ -12,13 +12,33 @@ export declare function createAppRouter(modules: {
12
12
  }): import("@trpc/server").TRPCBuiltRouter<{
13
13
  ctx: import("./trpc-init.js").RpcContext;
14
14
  meta: object;
15
- errorShape: import("@trpc/server").TRPCDefaultErrorShape;
15
+ errorShape: import("@trpc/server").TRPCDefaultErrorShape | {
16
+ message: string;
17
+ code: import("@trpc/server").TRPC_ERROR_CODE_NUMBER;
18
+ data: {
19
+ code: import("@trpc/server").TRPC_ERROR_CODE_KEY;
20
+ httpStatus: number;
21
+ path?: string;
22
+ stack?: string;
23
+ reason: string;
24
+ };
25
+ };
16
26
  transformer: false;
17
27
  }, import("@trpc/server").TRPCDecorateCreateRouterOptions<{
18
28
  integration: import("@trpc/server").TRPCBuiltRouter<{
19
29
  ctx: import("./trpc-init.js").RpcContext;
20
30
  meta: object;
21
- errorShape: import("@trpc/server").TRPCDefaultErrorShape;
31
+ errorShape: import("@trpc/server").TRPCDefaultErrorShape | {
32
+ message: string;
33
+ code: import("@trpc/server").TRPC_ERROR_CODE_NUMBER;
34
+ data: {
35
+ code: import("@trpc/server").TRPC_ERROR_CODE_KEY;
36
+ httpStatus: number;
37
+ path?: string;
38
+ stack?: string;
39
+ reason: string;
40
+ };
41
+ };
22
42
  transformer: false;
23
43
  }, import("@trpc/server").TRPCDecorateCreateRouterOptions<{
24
44
  listSurfaces: import("@trpc/server").TRPCQueryProcedure<{
@@ -52,7 +72,17 @@ export declare function createAppRouter(modules: {
52
72
  platform: import("@trpc/server").TRPCBuiltRouter<{
53
73
  ctx: import("./trpc-init.js").RpcContext;
54
74
  meta: object;
55
- errorShape: import("@trpc/server").TRPCDefaultErrorShape;
75
+ errorShape: import("@trpc/server").TRPCDefaultErrorShape | {
76
+ message: string;
77
+ code: import("@trpc/server").TRPC_ERROR_CODE_NUMBER;
78
+ data: {
79
+ code: import("@trpc/server").TRPC_ERROR_CODE_KEY;
80
+ httpStatus: number;
81
+ path?: string;
82
+ stack?: string;
83
+ reason: string;
84
+ };
85
+ };
56
86
  transformer: false;
57
87
  }, import("@trpc/server").TRPCDecorateCreateRouterOptions<{
58
88
  invoke: import("@trpc/server").TRPCMutationProcedure<{
@@ -68,7 +98,17 @@ export declare function createAppRouter(modules: {
68
98
  log: import("@trpc/server").TRPCBuiltRouter<{
69
99
  ctx: import("./trpc-init.js").RpcContext;
70
100
  meta: object;
71
- errorShape: import("@trpc/server").TRPCDefaultErrorShape;
101
+ errorShape: import("@trpc/server").TRPCDefaultErrorShape | {
102
+ message: string;
103
+ code: import("@trpc/server").TRPC_ERROR_CODE_NUMBER;
104
+ data: {
105
+ code: import("@trpc/server").TRPC_ERROR_CODE_KEY;
106
+ httpStatus: number;
107
+ path?: string;
108
+ stack?: string;
109
+ reason: string;
110
+ };
111
+ };
72
112
  transformer: false;
73
113
  }, import("@trpc/server").TRPCDecorateCreateRouterOptions<{
74
114
  emit: import("@trpc/server").TRPCMutationProcedure<{
@@ -87,14 +127,23 @@ export declare function createAppRouter(modules: {
87
127
  caseOrchestratorTools: import("@trpc/server").TRPCBuiltRouter<{
88
128
  ctx: import("./trpc-init.js").RpcContext;
89
129
  meta: object;
90
- errorShape: import("@trpc/server").TRPCDefaultErrorShape;
130
+ errorShape: import("@trpc/server").TRPCDefaultErrorShape | {
131
+ message: string;
132
+ code: import("@trpc/server").TRPC_ERROR_CODE_NUMBER;
133
+ data: {
134
+ code: import("@trpc/server").TRPC_ERROR_CODE_KEY;
135
+ httpStatus: number;
136
+ path?: string;
137
+ stack?: string;
138
+ reason: string;
139
+ };
140
+ };
91
141
  transformer: false;
92
142
  }, import("@trpc/server").TRPCDecorateCreateRouterOptions<{
93
143
  invoke: import("@trpc/server").TRPCMutationProcedure<{
94
144
  input: {
95
145
  tool: string;
96
146
  input: unknown;
97
- callKey: string;
98
147
  };
99
148
  output: unknown;
100
149
  meta: object;
@@ -103,7 +152,17 @@ export declare function createAppRouter(modules: {
103
152
  records: import("@trpc/server").TRPCBuiltRouter<{
104
153
  ctx: import("./trpc-init.js").RpcContext;
105
154
  meta: object;
106
- errorShape: import("@trpc/server").TRPCDefaultErrorShape;
155
+ errorShape: import("@trpc/server").TRPCDefaultErrorShape | {
156
+ message: string;
157
+ code: import("@trpc/server").TRPC_ERROR_CODE_NUMBER;
158
+ data: {
159
+ code: import("@trpc/server").TRPC_ERROR_CODE_KEY;
160
+ httpStatus: number;
161
+ path?: string;
162
+ stack?: string;
163
+ reason: string;
164
+ };
165
+ };
107
166
  transformer: false;
108
167
  }, import("@trpc/server").TRPCDecorateCreateRouterOptions<{
109
168
  read: import("@trpc/server").TRPCMutationProcedure<{
@@ -120,7 +179,6 @@ export declare function createAppRouter(modules: {
120
179
  table: string;
121
180
  method: "create" | "delete" | "update" | "upsert";
122
181
  args: unknown;
123
- callKey: string;
124
182
  };
125
183
  output: import("./records.js").RecordsResult;
126
184
  meta: object;
@@ -2,7 +2,7 @@ import { ORCHESTRATOR_ENTRY } from '../core/case-orchestrator/types.js';
2
2
  /** The Case Orchestrator scope of a run, or null when it is not a complete Case Orchestrator run. */
3
3
  export function asCaseOrchestratorRun(run) {
4
4
  const scoped = run;
5
- if (run.entry !== ORCHESTRATOR_ENTRY || !scoped.instanceId || !scoped.agentRunId)
5
+ if (run.entry !== ORCHESTRATOR_ENTRY || !scoped.instanceId || !scoped.orchestratorTaskId)
6
6
  return null;
7
7
  return run;
8
8
  }
@@ -1,5 +1,5 @@
1
1
  import type { ORCHESTRATOR_ENTRY, CaseOrchestratorApi, CaseOrchestratorContext } from '../core/case-orchestrator/types.js';
2
- import type { SystemContext } from '../core/context/types.js';
2
+ import type { IntegrationSurfaceMeta, SystemContext } from '../core/context/types.js';
3
3
  /** Alias for {@link SystemContext} (08 ActionContext shape). */
4
4
  export type ActionContext = SystemContext;
5
5
  /**
@@ -7,8 +7,19 @@ export type ActionContext = SystemContext;
7
7
  * function runs). Its ids are plain strings on the wire; the function's `ctx.meta` types them.
8
8
  */
9
9
  export type ActionDataSlice = Pick<ActionContext, 'config'> & {
10
- time: Omit<ActionContext['time'], 'format' | 'day'>;
10
+ /** `ignore` absent (a platform before 68 §6.2): nothing is ignored. */
11
+ time: Omit<ActionContext['time'], 'format' | 'day' | 'at' | 'parts' | 'ignore'> & Partial<Pick<ActionContext['time'], 'ignore'>>;
11
12
  meta: ActionContext['meta'];
13
+ /**
14
+ * What `ctx.api.integration` offers this run (its connections and mailboxes, with their credentials),
15
+ * listed by the platform as it calls the function: the host builds the api from it instead of asking
16
+ * the backend first. Not part of `ctx` itself.
17
+ */
18
+ surfaces?: IntegrationSurfaceMeta[];
19
+ };
20
+ /** What the call brought for the api ({@link ActionDataSlice.surfaces}): a host lists what is left out. */
21
+ export type ApiGiven = {
22
+ surfaces?: IntegrationSurfaceMeta[];
12
23
  };
13
24
  /** Identity / scope for a single function invocation (ApiHost may key caches on this). */
14
25
  export type RunScope = {
@@ -31,19 +42,22 @@ export type RunScope = {
31
42
  };
32
43
  /** Who fulfills `ctx.api` (Fake local vs Rpc remote). */
33
44
  export type ApiHost = {
34
- buildApi(run: RunScope): Promise<ActionContext['api']>;
45
+ buildApi(run: RunScope, given?: ApiGiven): Promise<ActionContext['api']>;
35
46
  };
36
47
  /** `ctx` of `orchestrator` before the host adds `params`. */
37
48
  export type CaseOrchestratorActionContext = Omit<CaseOrchestratorContext, 'params'>;
38
49
  /** Who fulfills the Case Orchestrator's `ctx.api` (Fake local vs Rpc remote). */
39
50
  export type CaseOrchestratorApiHost = {
40
- buildCaseOrchestratorApi(run: RunScope): Promise<CaseOrchestratorApi>;
51
+ buildCaseOrchestratorApi(run: RunScope, given?: ApiGiven): Promise<CaseOrchestratorApi>;
41
52
  };
42
- /** Run scope of an Orchestrator call: the instance it is limited to and its active run (`AgentRun`). */
53
+ /**
54
+ * Run scope of an Orchestrator call: the instance it is limited to and the `OrchestratorTask` it runs
55
+ * for (40 §2.3: no `AgentRun`), refused once that task stops running.
56
+ */
43
57
  export type CaseOrchestratorRunScope = RunScope & {
44
58
  entry: typeof ORCHESTRATOR_ENTRY;
45
59
  instanceId: string;
46
- agentRunId: string;
60
+ orchestratorTaskId: string;
47
61
  };
48
62
  /** A verified run token's scope; narrow with {@link asCaseOrchestratorRun}. */
49
63
  export type RunClaims = RunScope | CaseOrchestratorRunScope;