@retinue/agentkit 0.1.0 → 0.3.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 (246) hide show
  1. package/README.md +59 -277
  2. package/dist/adapters/audio/openai.d.ts +36 -0
  3. package/dist/adapters/audio/openai.js +136 -0
  4. package/dist/adapters/bullmq/lock.d.ts +26 -2
  5. package/dist/adapters/embeddings/openai.d.ts +45 -0
  6. package/dist/adapters/embeddings/openai.js +109 -0
  7. package/dist/adapters/memory/connections.d.ts +10 -0
  8. package/dist/adapters/memory/connections.js +87 -0
  9. package/dist/adapters/memory/graph.d.ts +17 -0
  10. package/dist/adapters/memory/graph.js +267 -0
  11. package/dist/adapters/memory/index.d.ts +3 -0
  12. package/dist/adapters/memory/index.js +3 -0
  13. package/dist/adapters/memory/rate-limit.d.ts +14 -0
  14. package/dist/adapters/memory/rate-limit.js +28 -0
  15. package/dist/adapters/memory/runtime.js +0 -0
  16. package/dist/adapters/postgres/connections.d.ts +15 -0
  17. package/dist/adapters/postgres/connections.js +134 -0
  18. package/dist/adapters/postgres/graph.d.ts +24 -0
  19. package/dist/adapters/postgres/graph.js +325 -0
  20. package/dist/adapters/postgres/index.d.ts +2 -0
  21. package/dist/adapters/postgres/index.js +2 -0
  22. package/dist/adapters/postgres/migrations.js +198 -0
  23. package/dist/adapters/postgres/run-store.js +26 -2
  24. package/dist/adapters/postgres/schema.d.ts +61 -2
  25. package/dist/adapters/postgres/schema.js +95 -10
  26. package/dist/adapters/redis/index.d.ts +1 -0
  27. package/dist/adapters/redis/index.js +1 -0
  28. package/dist/adapters/redis/rate-limit.d.ts +46 -0
  29. package/dist/adapters/redis/rate-limit.js +55 -0
  30. package/dist/adapters/supabase/index.d.ts +15 -0
  31. package/dist/adapters/supabase/index.js +15 -0
  32. package/dist/adapters/supabase/rls.js +33 -0
  33. package/dist/agents/agent.d.ts +99 -1
  34. package/dist/agents/agent.js +215 -16
  35. package/dist/agents/define.js +24 -4
  36. package/dist/agents/engine.d.ts +48 -0
  37. package/dist/agents/engine.js +370 -13
  38. package/dist/audio/index.d.ts +151 -0
  39. package/dist/audio/index.js +183 -0
  40. package/dist/capabilities/index.d.ts +5 -1
  41. package/dist/capabilities/index.js +23 -0
  42. package/dist/capabilities/runtime.d.ts +8 -0
  43. package/dist/connections/cipher.d.ts +103 -0
  44. package/dist/connections/cipher.js +141 -0
  45. package/dist/connections/index.d.ts +156 -0
  46. package/dist/connections/index.js +72 -0
  47. package/dist/connections/oauth/client.d.ts +84 -0
  48. package/dist/connections/oauth/client.js +141 -0
  49. package/dist/connections/oauth/index.d.ts +166 -0
  50. package/dist/connections/oauth/index.js +226 -0
  51. package/dist/connections/oauth/service.d.ts +90 -0
  52. package/dist/connections/oauth/service.js +132 -0
  53. package/dist/connections/pause.d.ts +68 -0
  54. package/dist/connections/pause.js +97 -0
  55. package/dist/connections/resolver.d.ts +59 -0
  56. package/dist/connections/resolver.js +138 -0
  57. package/dist/connections/resume.d.ts +64 -0
  58. package/dist/connections/resume.js +99 -0
  59. package/dist/connections/vault-cipher.d.ts +95 -0
  60. package/dist/connections/vault-cipher.js +206 -0
  61. package/dist/core/budget.d.ts +55 -0
  62. package/dist/core/budget.js +56 -0
  63. package/dist/core/content-parts.d.ts +26 -2
  64. package/dist/core/content-parts.js +1 -0
  65. package/dist/core/context.d.ts +25 -0
  66. package/dist/core/errors.d.ts +1 -1
  67. package/dist/core/errors.js +15 -0
  68. package/dist/core/events.d.ts +110 -2
  69. package/dist/core/events.js +33 -2
  70. package/dist/core/index.d.ts +1 -0
  71. package/dist/core/index.js +1 -0
  72. package/dist/core/validation.js +19 -0
  73. package/dist/documents/index.d.ts +14 -0
  74. package/dist/documents/parsers/text.d.ts +16 -0
  75. package/dist/documents/parsers/text.js +54 -2
  76. package/dist/entries/connections.d.ts +10 -0
  77. package/dist/entries/connections.js +10 -0
  78. package/dist/entries/guardrails.d.ts +14 -0
  79. package/dist/entries/guardrails.js +14 -0
  80. package/dist/entries/knowledge.d.ts +17 -0
  81. package/dist/entries/knowledge.js +16 -0
  82. package/dist/entries/mcp-server.d.ts +9 -0
  83. package/dist/entries/mcp-server.js +9 -0
  84. package/dist/entries/runtime.d.ts +10 -0
  85. package/dist/entries/runtime.js +8 -0
  86. package/dist/entries/testing.d.ts +28 -0
  87. package/dist/entries/testing.js +28 -0
  88. package/dist/graphql/resolvers.d.ts +7 -1
  89. package/dist/graphql/resolvers.js +20 -0
  90. package/dist/graphql/schema.d.ts +1 -1
  91. package/dist/graphql/schema.js +44 -0
  92. package/dist/guardrails/index.d.ts +115 -0
  93. package/dist/guardrails/index.js +108 -0
  94. package/dist/guardrails/moderation.d.ts +53 -0
  95. package/dist/guardrails/moderation.js +75 -0
  96. package/dist/guardrails/pii.d.ts +75 -0
  97. package/dist/guardrails/pii.js +193 -0
  98. package/dist/knowledge/communities.d.ts +166 -0
  99. package/dist/knowledge/communities.js +377 -0
  100. package/dist/knowledge/graph-global.d.ts +153 -0
  101. package/dist/knowledge/graph-global.js +246 -0
  102. package/dist/knowledge/graph-retrieval.d.ts +150 -0
  103. package/dist/knowledge/graph-retrieval.js +303 -0
  104. package/dist/knowledge/graph.d.ts +223 -0
  105. package/dist/knowledge/graph.js +369 -0
  106. package/dist/knowledge/index.d.ts +25 -0
  107. package/dist/knowledge/index.js +37 -1
  108. package/dist/knowledge/navigate.d.ts +89 -0
  109. package/dist/knowledge/navigate.js +107 -0
  110. package/dist/knowledge/retrieval.d.ts +98 -5
  111. package/dist/knowledge/retrieval.js +158 -28
  112. package/dist/mcp-server/index.d.ts +168 -0
  113. package/dist/mcp-server/index.js +175 -0
  114. package/dist/models/extraction.d.ts +73 -0
  115. package/dist/models/extraction.js +118 -0
  116. package/dist/models/index.d.ts +40 -1
  117. package/dist/models/index.js +22 -1
  118. package/dist/models/pricing.d.ts +2 -0
  119. package/dist/models/pricing.js +17 -1
  120. package/dist/models/provider-factory.js +9 -7
  121. package/dist/models/streaming.d.ts +111 -1
  122. package/dist/models/streaming.js +184 -3
  123. package/dist/persistence/index.d.ts +230 -0
  124. package/dist/persistence/index.js +2 -0
  125. package/dist/runtime/index.d.ts +1 -1
  126. package/dist/runtime/index.js +12 -0
  127. package/dist/runtime/worker.d.ts +36 -1
  128. package/dist/runtime/worker.js +37 -4
  129. package/dist/security/checklist.js +55 -0
  130. package/dist/security/findings.js +18 -9
  131. package/dist/server/bin.d.ts +19 -0
  132. package/dist/server/bin.js +226 -0
  133. package/dist/server/boot.d.ts +20 -2
  134. package/dist/server/boot.js +29 -3
  135. package/dist/server/cli.d.ts +13 -0
  136. package/dist/server/cli.js +7 -4
  137. package/dist/server/doctor.d.ts +74 -0
  138. package/dist/server/doctor.js +280 -0
  139. package/dist/skills/catalogue.d.ts +49 -0
  140. package/dist/skills/catalogue.js +61 -0
  141. package/dist/skills/context.d.ts +61 -0
  142. package/dist/skills/context.js +107 -0
  143. package/dist/skills/index.d.ts +2 -0
  144. package/dist/skills/index.js +2 -0
  145. package/dist/telemetry/spans.js +17 -0
  146. package/dist/testing/conformance/artifact-exports.d.ts +20 -0
  147. package/dist/testing/conformance/artifact-exports.js +172 -0
  148. package/dist/testing/conformance/artifacts.d.ts +11 -0
  149. package/dist/testing/conformance/artifacts.js +316 -0
  150. package/dist/testing/conformance/audio.d.ts +38 -0
  151. package/dist/testing/conformance/audio.js +145 -0
  152. package/dist/testing/conformance/capability.d.ts +23 -0
  153. package/dist/testing/conformance/capability.js +27 -0
  154. package/dist/testing/conformance/checkpoint-store.d.ts +15 -0
  155. package/dist/testing/conformance/checkpoint-store.js +72 -0
  156. package/dist/testing/conformance/connections.d.ts +14 -0
  157. package/dist/testing/conformance/connections.js +171 -0
  158. package/dist/testing/conformance/conversation-store.d.ts +10 -0
  159. package/dist/testing/conformance/conversation-store.js +60 -0
  160. package/dist/testing/conformance/evaluation.d.ts +10 -0
  161. package/dist/testing/conformance/evaluation.js +295 -0
  162. package/dist/testing/conformance/files.d.ts +19 -0
  163. package/dist/testing/conformance/files.js +454 -0
  164. package/dist/testing/conformance/flows.d.ts +16 -0
  165. package/dist/testing/conformance/flows.js +193 -0
  166. package/dist/testing/conformance/graph.d.ts +22 -0
  167. package/dist/testing/conformance/graph.js +500 -0
  168. package/dist/testing/conformance/hitl.d.ts +25 -0
  169. package/dist/testing/conformance/hitl.js +523 -0
  170. package/dist/testing/conformance/index.d.ts +163 -0
  171. package/dist/testing/conformance/index.js +321 -0
  172. package/dist/testing/conformance/invariants.d.ts +23 -0
  173. package/dist/testing/conformance/invariants.js +80 -0
  174. package/dist/testing/conformance/knowledge.d.ts +41 -0
  175. package/dist/testing/conformance/knowledge.js +592 -0
  176. package/dist/testing/conformance/parents.d.ts +50 -0
  177. package/dist/testing/conformance/parents.js +39 -0
  178. package/dist/testing/conformance/rate-limit.d.ts +25 -0
  179. package/dist/testing/conformance/rate-limit.js +71 -0
  180. package/dist/testing/conformance/records.d.ts +36 -0
  181. package/dist/testing/conformance/records.js +400 -0
  182. package/dist/testing/conformance/rollups.d.ts +30 -0
  183. package/dist/testing/conformance/rollups.js +425 -0
  184. package/dist/testing/conformance/run-coordinator.d.ts +27 -0
  185. package/dist/testing/conformance/run-coordinator.js +150 -0
  186. package/dist/testing/conformance/run-event-log.d.ts +9 -0
  187. package/dist/testing/conformance/run-event-log.js +138 -0
  188. package/dist/testing/conformance/run-store.d.ts +12 -0
  189. package/dist/testing/conformance/run-store.js +294 -0
  190. package/dist/testing/conformance/session-state.d.ts +38 -0
  191. package/dist/testing/conformance/session-state.js +198 -0
  192. package/dist/testing/conformance/usage-limits.d.ts +10 -0
  193. package/dist/testing/conformance/usage-limits.js +309 -0
  194. package/dist/testing/conformance.d.ts +8 -0
  195. package/dist/testing/conformance.js +8 -0
  196. package/dist/testing/memory-backend.d.ts +39 -0
  197. package/dist/testing/memory-backend.js +41 -0
  198. package/dist/testing/pglite.d.ts +55 -0
  199. package/dist/testing/pglite.js +78 -0
  200. package/dist/testing/stub-model.d.ts +58 -0
  201. package/dist/testing/stub-model.js +71 -0
  202. package/dist/testing/supabase-storage-double.d.ts +35 -0
  203. package/dist/testing/supabase-storage-double.js +101 -0
  204. package/dist/toolkit/files.d.ts +125 -0
  205. package/dist/toolkit/files.js +320 -0
  206. package/dist/toolkit/http.d.ts +27 -0
  207. package/dist/toolkit/http.js +48 -0
  208. package/dist/toolkit/index.d.ts +31 -1
  209. package/dist/toolkit/index.js +26 -1
  210. package/dist/toolkit/mime.d.ts +97 -0
  211. package/dist/toolkit/mime.js +233 -0
  212. package/dist/toolkit/sandbox.d.ts +119 -0
  213. package/dist/toolkit/sandbox.js +239 -0
  214. package/dist/toolkit/ssrf.d.ts +139 -0
  215. package/dist/toolkit/ssrf.js +349 -0
  216. package/dist/toolkit/vendor.d.ts +91 -0
  217. package/dist/toolkit/vendor.js +153 -0
  218. package/dist/toolkit/web.d.ts +13 -0
  219. package/dist/toolkit/web.js +7 -1
  220. package/dist/tools/budget.d.ts +28 -0
  221. package/dist/tools/budget.js +35 -0
  222. package/dist/tools/credentials.d.ts +316 -0
  223. package/dist/tools/credentials.js +369 -0
  224. package/dist/tools/define.d.ts +45 -0
  225. package/dist/tools/define.js +26 -0
  226. package/dist/tools/find.d.ts +109 -0
  227. package/dist/tools/find.js +210 -0
  228. package/dist/tools/index.d.ts +35 -2
  229. package/dist/tools/index.js +11 -0
  230. package/dist/tools/library/audio.d.ts +63 -0
  231. package/dist/tools/library/audio.js +148 -0
  232. package/dist/tools/library/fs.d.ts +24 -0
  233. package/dist/tools/library/fs.js +102 -0
  234. package/dist/tools/library/index.d.ts +37 -2
  235. package/dist/tools/library/index.js +47 -0
  236. package/dist/tools/library/shell.d.ts +45 -0
  237. package/dist/tools/library/shell.js +70 -0
  238. package/dist/tools/meta-tools.js +8 -0
  239. package/dist/tools/registry.d.ts +146 -0
  240. package/dist/tools/registry.js +250 -4
  241. package/dist/usage/index.d.ts +21 -0
  242. package/dist/usage/index.js +1 -0
  243. package/dist/usage/rate-limit.d.ts +122 -0
  244. package/dist/usage/rate-limit.js +131 -0
  245. package/dist/usage/recorder.js +3 -0
  246. package/package.json +33 -2
@@ -35,15 +35,18 @@ export const runApiHost = async (env = process.env) => {
35
35
  // twice: `loadConfig` is pure and `boot` validates again.
36
36
  loadConfig(env);
37
37
  const app = await loadApp(env);
38
- const { config, sql } = await boot({
38
+ const { config, sql, runner } = await boot({
39
39
  env,
40
40
  connect: async (loaded) => {
41
41
  const { Pool } = await import("pg");
42
- const { createPgExecutor } = await import("../entries/adapters-postgres.js");
43
- return { sql: createPgExecutor(new Pool({ connectionString: loaded.databaseUrl })) };
42
+ const { createPgExecutor, createPoolOpener } = await import("../entries/adapters-postgres.js");
43
+ const pool = new Pool({ connectionString: loaded.databaseUrl });
44
+ // `open` is what lets `boot` build a transaction scope. Without it the API host had no runner and
45
+ // `sendMessage` could not claim a conversation — #254.
46
+ return { sql: createPgExecutor(pool), open: createPoolOpener(pool) };
44
47
  },
45
48
  });
46
- const deps = await app.deps({ config, sql });
49
+ const deps = await app.deps({ config, sql, ...(runner === undefined ? {} : { runner }) });
47
50
  const { createSchemaManager } = await import("../entries/adapters-postgres.js");
48
51
  const probes = [postgresProbe(sql), schemaProbe(createSchemaManager(sql))];
49
52
  if (app.redis)
@@ -0,0 +1,74 @@
1
+ /**
2
+ * `retinue doctor` — task #252 AC-4.
3
+ *
4
+ * The one command here that is new rather than a wrapper, and the one that pays for itself: every failure it
5
+ * names is otherwise a support conversation. A deployment that will not start currently produces one error, from
6
+ * whichever check happened to run first, and the operator fixes it and runs again to find the next.
7
+ *
8
+ * ## Every failure, not the first
9
+ *
10
+ * The whole value is in the plural. A doctor that stops at the first problem sends the user round the loop once
11
+ * per problem — which is exactly what starting the server already does, so it would add nothing. Each check is
12
+ * therefore independent and its own failure is caught, so a Postgres that will not connect does not prevent the
13
+ * Redis check from running.
14
+ *
15
+ * ## Nothing here prints a secret
16
+ *
17
+ * A diagnostic tool is exactly where a URL with a password in it gets pasted into a support ticket. So a
18
+ * connection string is reported by its *shape* — host and database, never userinfo — and a failure message is
19
+ * the driver's, scrubbed. `mcp/egress.ts` already refuses credentials in a URL for the same reason.
20
+ */
21
+ export type CheckResult = {
22
+ readonly name: string;
23
+ readonly ok: boolean;
24
+ /** What was found. Never a secret. */
25
+ readonly detail: string;
26
+ /** Present when `ok` is false: what to do about it. */
27
+ readonly remedy?: string;
28
+ /**
29
+ * A check that could not run rather than one that failed.
30
+ *
31
+ * Distinct because the two need different responses: a failed check is a broken deployment, and a skipped one
32
+ * is a deployment this tool cannot see into. Reporting a skip as a pass would be the "passes having checked
33
+ * nothing" failure; reporting it as a failure would tell an operator to fix something that is not wrong.
34
+ */
35
+ readonly skipped?: boolean;
36
+ };
37
+ export declare const describeUrl: (value: string) => string;
38
+ /** The driver's message, with anything URL-shaped reduced. Errors quote the connection string routinely. */
39
+ export declare const scrub: (message: string) => string;
40
+ export type DoctorDeps = {
41
+ readonly env?: Readonly<Record<string, string | undefined>>;
42
+ /** Injected so the checks are testable without a database or a Redis. */
43
+ readonly connectPostgres?: (url: string) => Promise<{
44
+ query(text: string, params?: readonly unknown[]): Promise<unknown>;
45
+ end(): Promise<void>;
46
+ }>;
47
+ readonly connectRedis?: (url: string) => Promise<{
48
+ ping(): Promise<string>;
49
+ quit(): Promise<unknown>;
50
+ }>;
51
+ readonly schemaVersions?: (sql: {
52
+ query(text: string, params?: readonly unknown[]): Promise<unknown>;
53
+ }) => Promise<{
54
+ current: number;
55
+ target: number;
56
+ }>;
57
+ };
58
+ /** How long any one check may take before it is reported as a timeout. */
59
+ export declare const CHECK_TIMEOUT_MS = 5000;
60
+ /**
61
+ * A backstop, because **a diagnostic that hangs is worse than one that fails.**
62
+ *
63
+ * Found by running `doctor` against a refused port: `pg.Pool` has no default connect timeout and `ioredis`
64
+ * retries a refused connection for ever, so the command sat there silently instead of reporting the two
65
+ * failures it existed to report. Both drivers are now configured to fail fast, and this is the belt to that
66
+ * braces — a driver whose timeout option is wrong or renamed must not be able to hang the tool.
67
+ *
68
+ * The timer is unref'd so a completed check never holds the process open past its work.
69
+ */
70
+ export declare const withTimeout: <T>(what: string, work: Promise<T>, ms?: number) => Promise<T>;
71
+ export declare const runChecks: (deps?: DoctorDeps) => Promise<readonly CheckResult[]>;
72
+ /** Human output. Returns the exit code, so the caller does not decide what a failure means. */
73
+ export declare const report: (results: readonly CheckResult[], write?: (line: string) => void) => number;
74
+ //# sourceMappingURL=doctor.d.ts.map
@@ -0,0 +1,280 @@
1
+ /**
2
+ * `retinue doctor` — task #252 AC-4.
3
+ *
4
+ * The one command here that is new rather than a wrapper, and the one that pays for itself: every failure it
5
+ * names is otherwise a support conversation. A deployment that will not start currently produces one error, from
6
+ * whichever check happened to run first, and the operator fixes it and runs again to find the next.
7
+ *
8
+ * ## Every failure, not the first
9
+ *
10
+ * The whole value is in the plural. A doctor that stops at the first problem sends the user round the loop once
11
+ * per problem — which is exactly what starting the server already does, so it would add nothing. Each check is
12
+ * therefore independent and its own failure is caught, so a Postgres that will not connect does not prevent the
13
+ * Redis check from running.
14
+ *
15
+ * ## Nothing here prints a secret
16
+ *
17
+ * A diagnostic tool is exactly where a URL with a password in it gets pasted into a support ticket. So a
18
+ * connection string is reported by its *shape* — host and database, never userinfo — and a failure message is
19
+ * the driver's, scrubbed. `mcp/egress.ts` already refuses credentials in a URL for the same reason.
20
+ */
21
+ import { ConfigurationError, loadConfig } from "./config.js";
22
+ /**
23
+ * A connection string reduced to what is safe to print.
24
+ *
25
+ * Deliberately not a regex over the whole string: `URL` parsing means a password containing a `@` or a `/`
26
+ * cannot smuggle itself into the output. An unparseable value is reported as unparseable rather than echoed,
27
+ * because echoing it is how a secret reaches a log.
28
+ */
29
+ /**
30
+ * A connection string reduced to what is safe to print, by **whitelist**.
31
+ *
32
+ * Two earlier versions of this leaked, and both leaked the same way — by trying to *remove* the secret from a
33
+ * string whose structure was not what they assumed:
34
+ *
35
+ * 1. Parsing with `URL` and printing `hostname`/`port`/`pathname`. For
36
+ * `postgres://user:p@ss/word@db.internal:5432/app`, `URL` reads `user:p` as the userinfo and `ss` as the
37
+ * hostname, so the "safe" output was `postgres://ss/word@db.internal:5432/app` — half the password, spread
38
+ * across the host and path.
39
+ * 2. Refusing when the *authority* contained two `@`. The authority ends at the first `/`, and in that example
40
+ * the second `@` is after it. So the check passed and the leak stood.
41
+ *
42
+ * A redaction function must therefore not try to find the secret. It matches a strict, unambiguous shape and
43
+ * prints only the groups it captured; anything else is refused unprinted. An unescaped `@` or `/` in a password
44
+ * is common, and it is exactly what makes a connection string ambiguous — so ambiguity is the thing to refuse,
45
+ * not to parse harder.
46
+ */
47
+ const SAFE_URL = /^([a-z][a-z0-9+.-]*):\/\/(?:[^@/?#]*@)?([A-Za-z0-9._-]+|\[[0-9A-Fa-f:]+\])(?::(\d+))?(?:\/([A-Za-z0-9._-]*))?$/;
48
+ export const describeUrl = (value) => {
49
+ const match = SAFE_URL.exec(value);
50
+ if (match === null)
51
+ return "(not printed — the value does not match an unambiguous connection-string shape)";
52
+ const [, scheme, host, port, database] = match;
53
+ return `${scheme}://${host}${port === undefined ? "" : `:${port}`}${database ? `/${database}` : ""}`;
54
+ };
55
+ /** The driver's message, with anything URL-shaped reduced. Errors quote the connection string routinely. */
56
+ export const scrub = (message) => message.replace(/\b[a-z][a-z0-9+.-]*:\/\/[^\s'"]+/gi, (m) => describeUrl(m));
57
+ const message = (error) => scrub(error instanceof Error ? error.message : String(error));
58
+ /** True when `describeUrl` refused to print a value, so nothing derived from it can be trusted either. */
59
+ const unsafe = (described) => described.startsWith("(");
60
+ /**
61
+ * What to say about a connection failure — the URL and the driver's message, or neither.
62
+ *
63
+ * A driver interpolates whatever *it* parsed, so `postgres://u:p@ss/word@host/db` fails with
64
+ * `getaddrinfo ENOTFOUND ss` — a fragment of the password, in a message no scrubber can recognise as one
65
+ * because `ss` is not URL-shaped. The rule that follows is simple and the only safe one: if the value could not
66
+ * be printed, the error derived from it is not printed either. The remedy still names the variable, which is
67
+ * what the operator needs.
68
+ */
69
+ const failureDetail = (url, error) => {
70
+ const described = describeUrl(url);
71
+ return unsafe(described)
72
+ ? `${described} The driver's message is withheld for the same reason — it can quote the value it parsed.`
73
+ : `${described}: ${message(error)}`;
74
+ };
75
+ /** How long any one check may take before it is reported as a timeout. */
76
+ export const CHECK_TIMEOUT_MS = 5_000;
77
+ /**
78
+ * A backstop, because **a diagnostic that hangs is worse than one that fails.**
79
+ *
80
+ * Found by running `doctor` against a refused port: `pg.Pool` has no default connect timeout and `ioredis`
81
+ * retries a refused connection for ever, so the command sat there silently instead of reporting the two
82
+ * failures it existed to report. Both drivers are now configured to fail fast, and this is the belt to that
83
+ * braces — a driver whose timeout option is wrong or renamed must not be able to hang the tool.
84
+ *
85
+ * The timer is unref'd so a completed check never holds the process open past its work.
86
+ */
87
+ export const withTimeout = async (what, work, ms = CHECK_TIMEOUT_MS) => {
88
+ let timer;
89
+ try {
90
+ return await Promise.race([
91
+ work,
92
+ new Promise((_, reject) => {
93
+ timer = setTimeout(() => reject(new Error(`${what} did not respond within ${ms}ms`)), ms);
94
+ timer.unref?.();
95
+ }),
96
+ ]);
97
+ }
98
+ finally {
99
+ if (timer !== undefined)
100
+ clearTimeout(timer);
101
+ }
102
+ };
103
+ export const runChecks = async (deps = {}) => {
104
+ const env = deps.env ?? process.env;
105
+ const results = [];
106
+ // 1. Configuration. Everything else needs it, so a failure here skips the rest rather than reporting
107
+ // cascading failures that are all the same problem.
108
+ let config;
109
+ try {
110
+ config = loadConfig(env);
111
+ results.push({ name: "configuration", ok: true, detail: `schema mode ${config.schemaMode}, port ${config.port}` });
112
+ }
113
+ catch (error) {
114
+ results.push({
115
+ name: "configuration",
116
+ ok: false,
117
+ detail: message(error),
118
+ remedy: error instanceof ConfigurationError
119
+ ? `Set or correct: ${error.variables.join(", ")}. See .env.example.`
120
+ : "See .env.example for the variables this deployment needs.",
121
+ });
122
+ for (const name of ["postgres", "schema", "redis"]) {
123
+ results.push({
124
+ name,
125
+ ok: true,
126
+ skipped: true,
127
+ detail: "not checked — configuration is unusable, so this would fail for the same reason",
128
+ });
129
+ }
130
+ return results;
131
+ }
132
+ // 2. The app module, which `serve` and `worker` need and `migrate` does not.
133
+ const appModule = env["RETINUE_APP_MODULE"];
134
+ if (appModule === undefined || appModule.trim() === "") {
135
+ results.push({
136
+ name: "app module",
137
+ ok: true,
138
+ skipped: true,
139
+ detail: "RETINUE_APP_MODULE is unset — `migrate` and `doctor` work without it; `serve` and `worker` do not",
140
+ remedy: "Point RETINUE_APP_MODULE at a module default-exporting { authenticate, deps } before serving.",
141
+ });
142
+ }
143
+ else {
144
+ try {
145
+ const loaded = (await import(appModule));
146
+ const app = loaded.default;
147
+ const ok = typeof app?.authenticate === "function" && typeof app?.deps === "function";
148
+ results.push({
149
+ name: "app module",
150
+ ok,
151
+ detail: ok ? `${appModule} exports { authenticate, deps }` : `${appModule} loaded but is missing exports`,
152
+ ...(ok ? {} : { remedy: "It must default-export { authenticate, deps }. `authenticate` has no default." }),
153
+ });
154
+ }
155
+ catch (error) {
156
+ results.push({
157
+ name: "app module",
158
+ ok: false,
159
+ detail: `${appModule} could not be loaded: ${message(error)}`,
160
+ remedy: "Check the path is resolvable from the working directory, and that the module builds.",
161
+ });
162
+ }
163
+ }
164
+ // 3. Postgres, and 4. the schema — one connection, two findings.
165
+ const connectPostgres = deps.connectPostgres;
166
+ if (connectPostgres === undefined) {
167
+ results.push({ name: "postgres", ok: true, skipped: true, detail: "no driver supplied" });
168
+ results.push({ name: "schema", ok: true, skipped: true, detail: "no driver supplied" });
169
+ }
170
+ else {
171
+ let sql;
172
+ /**
173
+ * Whether Postgres *answered*, which is not the same as whether a pool object exists.
174
+ *
175
+ * `new Pool()` does not connect, so `sql` is defined even when the database is unreachable — and keying the
176
+ * schema check on `sql !== undefined` reported "0 of 30 migrations applied → Run `retinue migrate`" against
177
+ * a database nobody could reach. That is a diagnostic sending an operator to fix the wrong thing, which is
178
+ * worse than reporting nothing.
179
+ */
180
+ let reachable = false;
181
+ try {
182
+ sql = await withTimeout("postgres", connectPostgres(config.databaseUrl));
183
+ await withTimeout("postgres", sql.query("select 1"));
184
+ reachable = true;
185
+ results.push({ name: "postgres", ok: true, detail: `reachable at ${describeUrl(config.databaseUrl)}` });
186
+ }
187
+ catch (error) {
188
+ results.push({
189
+ name: "postgres",
190
+ ok: false,
191
+ detail: failureDetail(config.databaseUrl, error),
192
+ remedy: "Check the database is running and RETINUE_DATABASE_URL points at it.",
193
+ });
194
+ }
195
+ if (reachable && sql !== undefined && deps.schemaVersions !== undefined) {
196
+ try {
197
+ const { current, target } = await withTimeout("schema", deps.schemaVersions(sql));
198
+ // Ahead is reported as a failure, not a pass: a database migrated by a newer build than this one is a
199
+ // deployment about to behave unpredictably, and "current >= target" would call it healthy.
200
+ const ok = current === target;
201
+ results.push({
202
+ name: "schema",
203
+ ok,
204
+ detail: `${current} of ${target} migrations applied`,
205
+ ...(ok
206
+ ? {}
207
+ : {
208
+ remedy: current < target
209
+ ? "Run `retinue migrate`."
210
+ : "This database is ahead of this build. Deploy the matching version rather than migrating down.",
211
+ }),
212
+ });
213
+ }
214
+ catch (error) {
215
+ results.push({
216
+ name: "schema",
217
+ ok: false,
218
+ detail: message(error),
219
+ remedy: "Run `retinue migrate --status` for detail.",
220
+ });
221
+ }
222
+ }
223
+ else if (reachable) {
224
+ results.push({ name: "schema", ok: true, skipped: true, detail: "no schema reader supplied" });
225
+ }
226
+ else {
227
+ results.push({
228
+ name: "schema",
229
+ ok: true,
230
+ skipped: true,
231
+ detail: "not checked — Postgres is unreachable, so this would fail for the same reason",
232
+ });
233
+ }
234
+ if (sql !== undefined)
235
+ await sql.end().catch(() => undefined);
236
+ }
237
+ // 5. Redis.
238
+ if (deps.connectRedis === undefined) {
239
+ results.push({ name: "redis", ok: true, skipped: true, detail: "no client supplied" });
240
+ }
241
+ else {
242
+ let redis;
243
+ try {
244
+ redis = await withTimeout("redis", deps.connectRedis(config.redisUrl));
245
+ const pong = await withTimeout("redis", redis.ping());
246
+ results.push({ name: "redis", ok: true, detail: `reachable at ${describeUrl(config.redisUrl)} (${pong})` });
247
+ }
248
+ catch (error) {
249
+ results.push({
250
+ name: "redis",
251
+ ok: false,
252
+ detail: failureDetail(config.redisUrl, error),
253
+ remedy: "Check Redis is running and RETINUE_REDIS_URL points at it.",
254
+ });
255
+ }
256
+ finally {
257
+ // Always, including after a failure. Leaving a client open kept the socket handle alive and the command
258
+ // never exited — a diagnostic that reports correctly and then hangs is still a diagnostic that hangs.
259
+ if (redis !== undefined)
260
+ await redis.quit().catch(() => undefined);
261
+ }
262
+ }
263
+ return results;
264
+ };
265
+ /** Human output. Returns the exit code, so the caller does not decide what a failure means. */
266
+ export const report = (results, write = console.log) => {
267
+ const failed = results.filter((r) => !r.ok);
268
+ for (const r of results) {
269
+ const mark = !r.ok ? "✗" : r.skipped === true ? "–" : "✓";
270
+ write(`${mark} ${r.name}: ${r.detail}`);
271
+ if (r.remedy !== undefined)
272
+ write(` → ${r.remedy}`);
273
+ }
274
+ write("");
275
+ write(failed.length === 0
276
+ ? `✓ ${results.filter((r) => r.skipped !== true).length} check(s) passed`
277
+ : `✗ ${failed.length} of ${results.length} check(s) failed`);
278
+ return failed.length === 0 ? 0 : 1;
279
+ };
280
+ //# sourceMappingURL=doctor.js.map
@@ -0,0 +1,49 @@
1
+ /**
2
+ * A token ceiling on the skill catalogue — REQ-045 (#204), task #210, AC-5.
3
+ *
4
+ * Skills have exactly the tool catalogue's problem: a compact entry per skill sits in context on every turn, and
5
+ * the bodies already load on demand, so what is left is linear in how many skills a tenant has. 25 skills at
6
+ * `descriptionMaxLength` is a page of prompt before the conversation starts.
7
+ *
8
+ * The budget is `core/budget.ts` — the same one the tool catalogue uses, not a second copy.
9
+ *
10
+ * ## The notice is part of the catalogue, not part of the log
11
+ *
12
+ * The tool path can be loud in a run event because the engine assembles the tool list and the engine owns the
13
+ * event stream. The skill catalogue is assembled by a *context provider*, which has no event stream, so the same
14
+ * report reaches the same place by a different route: `truncationNotice` puts it in the text the model reads.
15
+ *
16
+ * That is arguably the stronger channel of the two. A run event tells whoever reviews the run afterwards; this
17
+ * tells the model *during* the turn, so it can say "there are more skills than I was shown" instead of
18
+ * confidently reporting that no skill exists for the job. Callers still get the report, and a host with an event
19
+ * stream to hand should log it too.
20
+ */
21
+ /**
22
+ * The specific core modules, **not** `core/index.js`.
23
+ *
24
+ * The barrel re-exports `core/validation.ts`, which imports `zod`. Importing it from here put zod into the
25
+ * dependency graph of `@retinue/agentkit/persistence` — a subpath whose whole claim is that it reaches nothing
26
+ * outside the standard library, so a test or a prototype needs no install beyond the package. Caught by
27
+ * `root-import-weight.test.ts`, which walks the graph transitively; a barrel import is how that guarantee gets
28
+ * lost, and it is invisible in review.
29
+ */
30
+ import { type BudgetOutcome, type TokenBudget } from "../core/budget.js";
31
+ import type { SkillCatalogEntry } from "./index.js";
32
+ /**
33
+ * Per-entry scaffolding: the bullet, the name emphasis, the version.
34
+ *
35
+ * Slightly larger than the tool catalogue's because a skill entry is rendered as prose in Markdown rather than
36
+ * as a JSON tool definition.
37
+ */
38
+ export declare const SKILL_ENTRY_OVERHEAD_TOKENS = 8;
39
+ export declare const skillEntryTokens: (entry: SkillCatalogEntry) => number;
40
+ export declare const budgetSkillCatalogue: (entries: readonly SkillCatalogEntry[], budget: TokenBudget) => BudgetOutcome<SkillCatalogEntry>;
41
+ /**
42
+ * What the model is told when the catalogue was shortened.
43
+ *
44
+ * Names the skills rather than counting them, for the same reason the run event does: "3 more skills exist" is
45
+ * something a model can only ignore, while a name is something it can ask for. Empty string when nothing was
46
+ * dropped, so a caller can concatenate unconditionally.
47
+ */
48
+ export declare const truncationNotice: (outcome: BudgetOutcome<SkillCatalogEntry>) => string;
49
+ //# sourceMappingURL=catalogue.d.ts.map
@@ -0,0 +1,61 @@
1
+ /**
2
+ * A token ceiling on the skill catalogue — REQ-045 (#204), task #210, AC-5.
3
+ *
4
+ * Skills have exactly the tool catalogue's problem: a compact entry per skill sits in context on every turn, and
5
+ * the bodies already load on demand, so what is left is linear in how many skills a tenant has. 25 skills at
6
+ * `descriptionMaxLength` is a page of prompt before the conversation starts.
7
+ *
8
+ * The budget is `core/budget.ts` — the same one the tool catalogue uses, not a second copy.
9
+ *
10
+ * ## The notice is part of the catalogue, not part of the log
11
+ *
12
+ * The tool path can be loud in a run event because the engine assembles the tool list and the engine owns the
13
+ * event stream. The skill catalogue is assembled by a *context provider*, which has no event stream, so the same
14
+ * report reaches the same place by a different route: `truncationNotice` puts it in the text the model reads.
15
+ *
16
+ * That is arguably the stronger channel of the two. A run event tells whoever reviews the run afterwards; this
17
+ * tells the model *during* the turn, so it can say "there are more skills than I was shown" instead of
18
+ * confidently reporting that no skill exists for the job. Callers still get the report, and a host with an event
19
+ * stream to hand should log it too.
20
+ */
21
+ /**
22
+ * The specific core modules, **not** `core/index.js`.
23
+ *
24
+ * The barrel re-exports `core/validation.ts`, which imports `zod`. Importing it from here put zod into the
25
+ * dependency graph of `@retinue/agentkit/persistence` — a subpath whose whole claim is that it reaches nothing
26
+ * outside the standard library, so a test or a prototype needs no install beyond the package. Caught by
27
+ * `root-import-weight.test.ts`, which walks the graph transitively; a barrel import is how that guarantee gets
28
+ * lost, and it is invisible in review.
29
+ */
30
+ import { applyTokenBudget } from "../core/budget.js";
31
+ import { estimateTokens } from "../core/tokens.js";
32
+ /**
33
+ * Per-entry scaffolding: the bullet, the name emphasis, the version.
34
+ *
35
+ * Slightly larger than the tool catalogue's because a skill entry is rendered as prose in Markdown rather than
36
+ * as a JSON tool definition.
37
+ */
38
+ export const SKILL_ENTRY_OVERHEAD_TOKENS = 8;
39
+ export const skillEntryTokens = (entry) => estimateTokens(`${entry.name} v${entry.version} ${entry.description}`) + SKILL_ENTRY_OVERHEAD_TOKENS;
40
+ export const budgetSkillCatalogue = (entries, budget) => applyTokenBudget({
41
+ items: entries,
42
+ budget,
43
+ tokensOf: skillEntryTokens,
44
+ nameOf: (entry) => entry.name,
45
+ });
46
+ /**
47
+ * What the model is told when the catalogue was shortened.
48
+ *
49
+ * Names the skills rather than counting them, for the same reason the run event does: "3 more skills exist" is
50
+ * something a model can only ignore, while a name is something it can ask for. Empty string when nothing was
51
+ * dropped, so a caller can concatenate unconditionally.
52
+ */
53
+ export const truncationNotice = (outcome) => outcome.dropped.length === 0
54
+ ? ""
55
+ : [
56
+ "",
57
+ `Not every skill is listed above: ${outcome.dropped.length} more exist but did not fit this turn's`,
58
+ `catalogue budget (${outcome.dropped.join(", ")}). If one of them is what the task needs, say so rather`,
59
+ "than concluding there is no skill for it.",
60
+ ].join("\n");
61
+ //# sourceMappingURL=catalogue.js.map
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Skills in context — task #244, the interpreter for `AgentManifest.skillPolicy`.
3
+ *
4
+ * `skillPolicy` was declared and read by nothing. That was not one missing line: the *subsystem* was complete and
5
+ * unreachable. `SkillResolver.listCatalog` already takes `{ tenantId, assigned, allowTenantSkills }` — the
6
+ * manifest's two fields verbatim — the store had memory and Postgres adapters under a conformance suite, and
7
+ * `ContextKind`/`ContextBudget` already reserved a `skills` bucket for a section nothing produced. Two things
8
+ * were missing: something to emit that section, and an implementation of `load_skill` (see
9
+ * `tools/registry.ts`).
10
+ *
11
+ * ## Why the catalogue and not the bodies
12
+ *
13
+ * A skill body is up to 20,000 characters (`SKILL_LIMITS.instructionsMaxLength`). Twenty-five of those is a
14
+ * context window. So the section lists **names and descriptions**, and the model calls `load_skill` for the one
15
+ * it wants — the same two-tier shape the tool catalogue uses, for the same reason.
16
+ *
17
+ * ## Origin is `platform`, and the interpolated values are neutralised
18
+ *
19
+ * A skill body may instruct the agent — that is what a skill *is* — so this section is `platform` rather than
20
+ * `external`. But a *tenant-authored* skill's name and description are written by a customer, and this section
21
+ * carries them. Wrapping the whole section in an untrusted envelope would say "nothing in here is an
22
+ * instruction", which is false and would break skills; so the section stays `platform` and neutralises the
23
+ * values it interpolates, which is exactly the case `ContextSection.origin`'s own documentation describes.
24
+ */
25
+ import type { ContextProvider } from "../context/index.js";
26
+ import { type SkillResolver } from "./index.js";
27
+ import type { SkillBodyLoader } from "../tools/registry.js";
28
+ /** The provider id a manifest names in `contextProviderIds`. */
29
+ export declare const SKILL_CATALOGUE_PROVIDER_ID = "skill-catalogue";
30
+ /**
31
+ * Strips what could end a delimited block or forge a heading in the rendered prompt.
32
+ *
33
+ * Narrow on purpose: a skill description is prose a customer wrote, and mangling it would make the catalogue
34
+ * unreadable. Newlines go because the section is one line per skill and a description containing a line break
35
+ * could otherwise invent a row; backticks and the sequences that open a fenced block go because the surrounding
36
+ * render uses them structurally.
37
+ */
38
+ export declare const neutralise: (value: string) => string;
39
+ export declare const createSkillCatalogueProvider: (deps: {
40
+ readonly resolver: SkillResolver;
41
+ readonly policy: {
42
+ readonly assigned: readonly string[];
43
+ readonly allowTenantSkills: boolean;
44
+ };
45
+ }) => ContextProvider;
46
+ /**
47
+ * Adapts a `SkillResolver` to the registry's structural `SkillBodyLoader`.
48
+ *
49
+ * The version is not a parameter. `loadBody` pins to an exact version so a mid-run edit cannot change behaviour,
50
+ * and the version the model should get is the one the catalogue it just read advertised — so it is looked up
51
+ * here rather than trusted from the model's arguments. A model naming a version would be a model choosing which
52
+ * revision of an instruction to follow.
53
+ */
54
+ export declare const createSkillBodyLoader: (deps: {
55
+ readonly resolver: SkillResolver;
56
+ readonly policy: {
57
+ readonly assigned: readonly string[];
58
+ readonly allowTenantSkills: boolean;
59
+ };
60
+ }) => SkillBodyLoader;
61
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Skills in context — task #244, the interpreter for `AgentManifest.skillPolicy`.
3
+ *
4
+ * `skillPolicy` was declared and read by nothing. That was not one missing line: the *subsystem* was complete and
5
+ * unreachable. `SkillResolver.listCatalog` already takes `{ tenantId, assigned, allowTenantSkills }` — the
6
+ * manifest's two fields verbatim — the store had memory and Postgres adapters under a conformance suite, and
7
+ * `ContextKind`/`ContextBudget` already reserved a `skills` bucket for a section nothing produced. Two things
8
+ * were missing: something to emit that section, and an implementation of `load_skill` (see
9
+ * `tools/registry.ts`).
10
+ *
11
+ * ## Why the catalogue and not the bodies
12
+ *
13
+ * A skill body is up to 20,000 characters (`SKILL_LIMITS.instructionsMaxLength`). Twenty-five of those is a
14
+ * context window. So the section lists **names and descriptions**, and the model calls `load_skill` for the one
15
+ * it wants — the same two-tier shape the tool catalogue uses, for the same reason.
16
+ *
17
+ * ## Origin is `platform`, and the interpolated values are neutralised
18
+ *
19
+ * A skill body may instruct the agent — that is what a skill *is* — so this section is `platform` rather than
20
+ * `external`. But a *tenant-authored* skill's name and description are written by a customer, and this section
21
+ * carries them. Wrapping the whole section in an untrusted envelope would say "nothing in here is an
22
+ * instruction", which is false and would break skills; so the section stays `platform` and neutralises the
23
+ * values it interpolates, which is exactly the case `ContextSection.origin`'s own documentation describes.
24
+ */
25
+ import { estimateTokens } from "../core/tokens.js";
26
+ import { SKILL_LIMITS } from "./index.js";
27
+ /** The provider id a manifest names in `contextProviderIds`. */
28
+ export const SKILL_CATALOGUE_PROVIDER_ID = "skill-catalogue";
29
+ /**
30
+ * Strips what could end a delimited block or forge a heading in the rendered prompt.
31
+ *
32
+ * Narrow on purpose: a skill description is prose a customer wrote, and mangling it would make the catalogue
33
+ * unreadable. Newlines go because the section is one line per skill and a description containing a line break
34
+ * could otherwise invent a row; backticks and the sequences that open a fenced block go because the surrounding
35
+ * render uses them structurally.
36
+ */
37
+ export const neutralise = (value) => value
38
+ .replace(/[\r\n]+/g, " ")
39
+ .replace(/```+/g, "'''")
40
+ .replace(/[`]/g, "'")
41
+ .trim();
42
+ export const createSkillCatalogueProvider = (deps) => ({
43
+ id: SKILL_CATALOGUE_PROVIDER_ID,
44
+ async provide(context) {
45
+ const entries = await deps.resolver.listCatalog({
46
+ tenantId: context.tenantId,
47
+ assigned: deps.policy.assigned,
48
+ allowTenantSkills: deps.policy.allowTenantSkills,
49
+ });
50
+ // No section at all rather than an empty one. A heading saying "Skills" over nothing tells the model it has
51
+ // a capability and then shows it none, which is worse than silence — and it would still cost tokens.
52
+ if (entries.length === 0)
53
+ return [];
54
+ const lines = entries.map((e) => `- \`${neutralise(e.name)}\` — ${neutralise(e.description)}`);
55
+ const body = [
56
+ `You have ${entries.length} skill${entries.length === 1 ? "" : "s"} available. Each is a set of`,
57
+ "instructions for a particular kind of task. Read the list, and when one applies call",
58
+ "`load_skill` with its name to get the full instructions before you start.",
59
+ `You may load at most ${SKILL_LIMITS.maxLoadedPerRun} in a single run, so choose.`,
60
+ "",
61
+ ...lines,
62
+ ].join("\n");
63
+ return [
64
+ {
65
+ providerId: SKILL_CATALOGUE_PROVIDER_ID,
66
+ title: "Skills",
67
+ body,
68
+ // Above ordinary user context: a skill changes *how* the agent works, so it should survive pruning
69
+ // longer than the material it works on.
70
+ priority: 70,
71
+ estimatedTokens: estimateTokens(body),
72
+ provenance: "skill catalogue",
73
+ sensitivity: "internal",
74
+ // See the header: the section may instruct, and the customer-authored values inside it are neutralised
75
+ // rather than the whole section being disclaimed.
76
+ origin: "platform",
77
+ cacheable: true,
78
+ kind: "skills",
79
+ },
80
+ ];
81
+ },
82
+ });
83
+ /**
84
+ * Adapts a `SkillResolver` to the registry's structural `SkillBodyLoader`.
85
+ *
86
+ * The version is not a parameter. `loadBody` pins to an exact version so a mid-run edit cannot change behaviour,
87
+ * and the version the model should get is the one the catalogue it just read advertised — so it is looked up
88
+ * here rather than trusted from the model's arguments. A model naming a version would be a model choosing which
89
+ * revision of an instruction to follow.
90
+ */
91
+ export const createSkillBodyLoader = (deps) => ({
92
+ async load(context, name) {
93
+ const entries = await deps.resolver.listCatalog({
94
+ tenantId: context.tenantId,
95
+ assigned: deps.policy.assigned,
96
+ allowTenantSkills: deps.policy.allowTenantSkills,
97
+ });
98
+ // Resolved against the catalogue *this* agent may see, so `assigned` and `allowTenantSkills` gate loading
99
+ // and not merely listing. A policy that filtered the list but not the load would be no policy at all.
100
+ const entry = entries.find((e) => e.name === name);
101
+ if (entry === undefined)
102
+ return null;
103
+ const version = await deps.resolver.loadBody({ tenantId: context.tenantId, name, version: entry.version });
104
+ return { name: version.name, version: version.version, instructions: version.instructions };
105
+ },
106
+ });
107
+ //# sourceMappingURL=context.js.map