@retinue/agentkit 0.2.0 → 0.3.1

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 (209) hide show
  1. package/README.md +1 -1
  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/memory/connections.d.ts +10 -0
  6. package/dist/adapters/memory/connections.js +87 -0
  7. package/dist/adapters/memory/graph.d.ts +17 -0
  8. package/dist/adapters/memory/graph.js +267 -0
  9. package/dist/adapters/memory/index.d.ts +3 -0
  10. package/dist/adapters/memory/index.js +3 -0
  11. package/dist/adapters/memory/rate-limit.d.ts +14 -0
  12. package/dist/adapters/memory/rate-limit.js +28 -0
  13. package/dist/adapters/memory/runtime.js +0 -0
  14. package/dist/adapters/postgres/connections.d.ts +15 -0
  15. package/dist/adapters/postgres/connections.js +134 -0
  16. package/dist/adapters/postgres/graph.d.ts +24 -0
  17. package/dist/adapters/postgres/graph.js +325 -0
  18. package/dist/adapters/postgres/index.d.ts +2 -0
  19. package/dist/adapters/postgres/index.js +2 -0
  20. package/dist/adapters/postgres/migrations.js +198 -0
  21. package/dist/adapters/postgres/run-store.js +26 -2
  22. package/dist/adapters/postgres/schema.d.ts +61 -2
  23. package/dist/adapters/postgres/schema.js +95 -10
  24. package/dist/adapters/redis/index.d.ts +1 -0
  25. package/dist/adapters/redis/index.js +1 -0
  26. package/dist/adapters/redis/rate-limit.d.ts +46 -0
  27. package/dist/adapters/redis/rate-limit.js +55 -0
  28. package/dist/adapters/supabase/index.d.ts +15 -0
  29. package/dist/adapters/supabase/index.js +15 -0
  30. package/dist/adapters/supabase/rls.js +33 -0
  31. package/dist/agents/agent.d.ts +78 -1
  32. package/dist/agents/agent.js +118 -5
  33. package/dist/agents/define.js +24 -4
  34. package/dist/agents/engine.d.ts +20 -0
  35. package/dist/agents/engine.js +177 -6
  36. package/dist/audio/index.d.ts +151 -0
  37. package/dist/audio/index.js +183 -0
  38. package/dist/connections/cipher.d.ts +103 -0
  39. package/dist/connections/cipher.js +141 -0
  40. package/dist/connections/index.d.ts +156 -0
  41. package/dist/connections/index.js +72 -0
  42. package/dist/connections/oauth/client.d.ts +84 -0
  43. package/dist/connections/oauth/client.js +141 -0
  44. package/dist/connections/oauth/index.d.ts +166 -0
  45. package/dist/connections/oauth/index.js +226 -0
  46. package/dist/connections/oauth/service.d.ts +90 -0
  47. package/dist/connections/oauth/service.js +132 -0
  48. package/dist/connections/pause.d.ts +68 -0
  49. package/dist/connections/pause.js +97 -0
  50. package/dist/connections/resolver.d.ts +59 -0
  51. package/dist/connections/resolver.js +138 -0
  52. package/dist/connections/resume.d.ts +64 -0
  53. package/dist/connections/resume.js +99 -0
  54. package/dist/connections/vault-cipher.d.ts +95 -0
  55. package/dist/connections/vault-cipher.js +206 -0
  56. package/dist/core/content-parts.d.ts +18 -2
  57. package/dist/core/content-parts.js +1 -0
  58. package/dist/core/context.d.ts +25 -0
  59. package/dist/core/errors.d.ts +1 -1
  60. package/dist/core/errors.js +15 -0
  61. package/dist/core/events.d.ts +44 -2
  62. package/dist/core/events.js +31 -2
  63. package/dist/core/validation.js +19 -0
  64. package/dist/entries/connections.d.ts +10 -0
  65. package/dist/entries/connections.js +10 -0
  66. package/dist/entries/knowledge.d.ts +8 -0
  67. package/dist/entries/knowledge.js +8 -0
  68. package/dist/entries/mcp-server.d.ts +9 -0
  69. package/dist/entries/mcp-server.js +9 -0
  70. package/dist/entries/runtime.d.ts +10 -0
  71. package/dist/entries/runtime.js +8 -0
  72. package/dist/entries/testing.d.ts +28 -0
  73. package/dist/entries/testing.js +28 -0
  74. package/dist/graphql/resolvers.d.ts +3 -1
  75. package/dist/graphql/resolvers.js +14 -0
  76. package/dist/knowledge/communities.d.ts +166 -0
  77. package/dist/knowledge/communities.js +377 -0
  78. package/dist/knowledge/graph-global.d.ts +153 -0
  79. package/dist/knowledge/graph-global.js +246 -0
  80. package/dist/knowledge/graph-retrieval.d.ts +150 -0
  81. package/dist/knowledge/graph-retrieval.js +303 -0
  82. package/dist/knowledge/graph.d.ts +223 -0
  83. package/dist/knowledge/graph.js +369 -0
  84. package/dist/knowledge/index.d.ts +24 -0
  85. package/dist/knowledge/index.js +36 -1
  86. package/dist/knowledge/retrieval.d.ts +26 -1
  87. package/dist/knowledge/retrieval.js +77 -1
  88. package/dist/mcp-server/index.d.ts +168 -0
  89. package/dist/mcp-server/index.js +175 -0
  90. package/dist/models/extraction.d.ts +73 -0
  91. package/dist/models/extraction.js +118 -0
  92. package/dist/models/index.d.ts +40 -1
  93. package/dist/models/index.js +22 -1
  94. package/dist/models/pricing.d.ts +2 -0
  95. package/dist/models/pricing.js +17 -1
  96. package/dist/models/provider-factory.js +9 -7
  97. package/dist/models/streaming.d.ts +89 -0
  98. package/dist/models/streaming.js +179 -2
  99. package/dist/persistence/index.d.ts +230 -0
  100. package/dist/persistence/index.js +2 -0
  101. package/dist/runtime/index.d.ts +1 -1
  102. package/dist/runtime/index.js +12 -0
  103. package/dist/runtime/worker.d.ts +36 -1
  104. package/dist/runtime/worker.js +37 -4
  105. package/dist/security/checklist.js +46 -0
  106. package/dist/server/bin.d.ts +72 -0
  107. package/dist/server/bin.js +304 -0
  108. package/dist/server/boot.d.ts +20 -2
  109. package/dist/server/boot.js +29 -3
  110. package/dist/server/cli-worker.js +6 -3
  111. package/dist/server/cli.d.ts +13 -0
  112. package/dist/server/cli.js +10 -5
  113. package/dist/server/config.d.ts +11 -1
  114. package/dist/server/config.js +18 -0
  115. package/dist/server/doctor.d.ts +85 -0
  116. package/dist/server/doctor.js +280 -0
  117. package/dist/server/pool.d.ts +47 -0
  118. package/dist/server/pool.js +73 -0
  119. package/dist/skills/context.d.ts +61 -0
  120. package/dist/skills/context.js +107 -0
  121. package/dist/skills/index.d.ts +1 -0
  122. package/dist/skills/index.js +1 -0
  123. package/dist/telemetry/spans.js +5 -0
  124. package/dist/testing/conformance/artifact-exports.d.ts +20 -0
  125. package/dist/testing/conformance/artifact-exports.js +172 -0
  126. package/dist/testing/conformance/artifacts.d.ts +11 -0
  127. package/dist/testing/conformance/artifacts.js +316 -0
  128. package/dist/testing/conformance/audio.d.ts +38 -0
  129. package/dist/testing/conformance/audio.js +145 -0
  130. package/dist/testing/conformance/capability.d.ts +23 -0
  131. package/dist/testing/conformance/capability.js +27 -0
  132. package/dist/testing/conformance/checkpoint-store.d.ts +15 -0
  133. package/dist/testing/conformance/checkpoint-store.js +72 -0
  134. package/dist/testing/conformance/connections.d.ts +14 -0
  135. package/dist/testing/conformance/connections.js +171 -0
  136. package/dist/testing/conformance/conversation-store.d.ts +10 -0
  137. package/dist/testing/conformance/conversation-store.js +60 -0
  138. package/dist/testing/conformance/evaluation.d.ts +10 -0
  139. package/dist/testing/conformance/evaluation.js +295 -0
  140. package/dist/testing/conformance/files.d.ts +19 -0
  141. package/dist/testing/conformance/files.js +454 -0
  142. package/dist/testing/conformance/flows.d.ts +16 -0
  143. package/dist/testing/conformance/flows.js +193 -0
  144. package/dist/testing/conformance/graph.d.ts +22 -0
  145. package/dist/testing/conformance/graph.js +500 -0
  146. package/dist/testing/conformance/hitl.d.ts +25 -0
  147. package/dist/testing/conformance/hitl.js +523 -0
  148. package/dist/testing/conformance/index.d.ts +163 -0
  149. package/dist/testing/conformance/index.js +321 -0
  150. package/dist/testing/conformance/invariants.d.ts +23 -0
  151. package/dist/testing/conformance/invariants.js +80 -0
  152. package/dist/testing/conformance/knowledge.d.ts +41 -0
  153. package/dist/testing/conformance/knowledge.js +592 -0
  154. package/dist/testing/conformance/parents.d.ts +50 -0
  155. package/dist/testing/conformance/parents.js +39 -0
  156. package/dist/testing/conformance/rate-limit.d.ts +25 -0
  157. package/dist/testing/conformance/rate-limit.js +71 -0
  158. package/dist/testing/conformance/records.d.ts +36 -0
  159. package/dist/testing/conformance/records.js +400 -0
  160. package/dist/testing/conformance/rollups.d.ts +30 -0
  161. package/dist/testing/conformance/rollups.js +425 -0
  162. package/dist/testing/conformance/run-coordinator.d.ts +27 -0
  163. package/dist/testing/conformance/run-coordinator.js +150 -0
  164. package/dist/testing/conformance/run-event-log.d.ts +9 -0
  165. package/dist/testing/conformance/run-event-log.js +138 -0
  166. package/dist/testing/conformance/run-store.d.ts +12 -0
  167. package/dist/testing/conformance/run-store.js +294 -0
  168. package/dist/testing/conformance/session-state.d.ts +38 -0
  169. package/dist/testing/conformance/session-state.js +198 -0
  170. package/dist/testing/conformance/usage-limits.d.ts +10 -0
  171. package/dist/testing/conformance/usage-limits.js +309 -0
  172. package/dist/testing/conformance.d.ts +8 -0
  173. package/dist/testing/conformance.js +8 -0
  174. package/dist/testing/memory-backend.d.ts +39 -0
  175. package/dist/testing/memory-backend.js +41 -0
  176. package/dist/testing/pglite.d.ts +55 -0
  177. package/dist/testing/pglite.js +78 -0
  178. package/dist/testing/stub-model.d.ts +58 -0
  179. package/dist/testing/stub-model.js +71 -0
  180. package/dist/testing/supabase-storage-double.d.ts +35 -0
  181. package/dist/testing/supabase-storage-double.js +101 -0
  182. package/dist/toolkit/http.d.ts +27 -0
  183. package/dist/toolkit/http.js +48 -0
  184. package/dist/toolkit/index.d.ts +27 -1
  185. package/dist/toolkit/index.js +24 -1
  186. package/dist/toolkit/mime.d.ts +97 -0
  187. package/dist/toolkit/mime.js +233 -0
  188. package/dist/toolkit/ssrf.d.ts +139 -0
  189. package/dist/toolkit/ssrf.js +349 -0
  190. package/dist/toolkit/vendor.d.ts +91 -0
  191. package/dist/toolkit/vendor.js +153 -0
  192. package/dist/tools/credentials.d.ts +265 -6
  193. package/dist/tools/credentials.js +318 -3
  194. package/dist/tools/define.d.ts +14 -0
  195. package/dist/tools/define.js +3 -0
  196. package/dist/tools/index.d.ts +21 -0
  197. package/dist/tools/index.js +7 -0
  198. package/dist/tools/library/audio.d.ts +63 -0
  199. package/dist/tools/library/audio.js +148 -0
  200. package/dist/tools/library/index.d.ts +8 -0
  201. package/dist/tools/library/index.js +7 -0
  202. package/dist/tools/registry.d.ts +33 -0
  203. package/dist/tools/registry.js +72 -2
  204. package/dist/usage/index.d.ts +21 -0
  205. package/dist/usage/index.js +1 -0
  206. package/dist/usage/rate-limit.d.ts +122 -0
  207. package/dist/usage/rate-limit.js +131 -0
  208. package/dist/usage/recorder.js +3 -0
  209. package/package.json +29 -2
@@ -0,0 +1,95 @@
1
+ /**
2
+ * A `SecretCipher` over Supabase Vault — REQ-063 (#259), task #268.
3
+ *
4
+ * #261 shipped the seam with an app-side AES-256-GCM implementation and deliberately left this one out: there
5
+ * was no Vault to run it against, and an untested crypto implementation behind a seam that *looks* tested is
6
+ * worse than none. There is one now, and everything below has been run against it.
7
+ *
8
+ * ## The mapping, and the property it changes — AC-3
9
+ *
10
+ * Vault owns the ciphertext and hands back an **id**. The seam expects the caller to hold
11
+ * `{ keyId, algorithm, nonce, ciphertext }`. So:
12
+ *
13
+ * | Field | Holds |
14
+ * |---|---|
15
+ * | `algorithm` | `supabase-vault` — a sentinel, not a cipher name. The algorithm is Vault's business. |
16
+ * | `keyId` | `supabase-vault` — see the rotation note. Vault's own `key_id` column comes back **null**: the root key is held outside the database and Supabase does not expose an identity for it. |
17
+ * | `nonce` | A random **binding token**, not a nonce. See the tamper note. |
18
+ * | `ciphertext` | The Vault secret's **uuid**. Not ciphertext. |
19
+ *
20
+ * **A Vault-sealed row contains no secret material at all.** That is the property worth stating rather than
21
+ * discovering, and it cuts both ways:
22
+ *
23
+ * - A backup of the application's own tables carries no credentials — strictly better than app-side sealing,
24
+ * where the backup carries ciphertext that a leaked key would open.
25
+ * - The secret lives in `vault.secrets`, so **restoring the application tables without the `vault` schema
26
+ * leaves every connection pointing at nothing.** With app-side sealing the ciphertext travels with the row;
27
+ * here it does not, and a backup strategy that covers `public` and not `vault` silently loses every
28
+ * credential. That is a deployment fact, not a code one, and it belongs in a runbook.
29
+ *
30
+ * ## Deletion — AC-3, and a gap this found
31
+ *
32
+ * Deleting a connection row does **not** delete the Vault secret: the row held a pointer, and dropping a
33
+ * pointer leaves what it pointed at. `docs/18`'s deletion promises are therefore *not* satisfied by deleting
34
+ * the row alone, which is a difference from app-side sealing that nobody would notice until an audit.
35
+ *
36
+ * So `SecretCipher` gained an optional `forget`. App-side sealing has nothing to implement — the ciphertext
37
+ * dies with the row — and this implementation removes the Vault secret. A deployment on Vault that never calls
38
+ * it accumulates orphaned secrets that outlive the connections they belonged to.
39
+ *
40
+ * ## Tampering — AC-2, and where the guarantee genuinely differs
41
+ *
42
+ * AES-GCM authenticates: a flipped byte fails rather than decrypting to something else. A pointer cannot be
43
+ * authenticated by the thing it points at, so an attacker able to *write* the connections table could repoint
44
+ * one connection at another secret in the same Vault and read a credential they were not entitled to. That is
45
+ * a real weakening and it is not hypothetical — it is exactly the shape of a tenant-isolation bypass.
46
+ *
47
+ * Closed here with a **binding token**: `seal` stores a fresh 32-byte random value in the Vault secret's
48
+ * `description` and keeps it in the sealed value's `nonce` field; `open` compares them in constant time and
49
+ * refuses on mismatch. Repointing at another secret now fails, because that secret carries a different token.
50
+ * No application-side key is involved, so the "database cannot decrypt on its own" property is untouched.
51
+ *
52
+ * It is not equivalent to AEAD. An attacker who can write *both* the connections table and `vault.secrets`
53
+ * defeats it, where GCM would still fail. Stated rather than implied.
54
+ */
55
+ import type { SecretCipher } from "./cipher.js";
56
+ import type { SqlExecutor } from "../adapters/postgres/sql.js";
57
+ /**
58
+ * The sentinel in `algorithm`, and the reason it is not a cipher name.
59
+ *
60
+ * A reader of a stored row needs to know *which implementation* sealed it, not which primitive. `aes-256-gcm`
61
+ * tells the app-side cipher how to open the value; `supabase-vault` tells this one that the value is a pointer.
62
+ * Putting libsodium's actual construction here would be a claim about Vault's internals that could stop being
63
+ * true without this package noticing.
64
+ */
65
+ export declare const SUPABASE_VAULT: "supabase-vault";
66
+ export type VaultCipherOptions = {
67
+ /**
68
+ * Runs the `vault.*` calls. A **service-role** executor: `vault.decrypted_secrets` is not readable by
69
+ * `anon` or `authenticated`, which is most of what makes Vault worth using.
70
+ */
71
+ readonly sql: SqlExecutor;
72
+ /**
73
+ * Prefix for the Vault secret's `name`.
74
+ *
75
+ * Named at all because `vault.secrets.name` is unique and a deployment sharing a database with anything else
76
+ * that writes to Vault would otherwise collide. The name carries no secret and is not used for integrity —
77
+ * that is the token's job.
78
+ */
79
+ readonly namePrefix?: string;
80
+ /** Injectable so a test can assert the token is fresh per seal rather than trusting that it is. */
81
+ readonly randomToken?: () => string;
82
+ };
83
+ export declare const createSupabaseVaultCipher: (options: VaultCipherOptions) => SecretCipher;
84
+ /**
85
+ * Rotation is Vault's, not ours — AC-4, stated as a value so a test can assert it.
86
+ *
87
+ * `resealConnections` walks `sealed.keyId`; this cipher's is constant, so that walk finds nothing forever.
88
+ * Correct, and indistinguishable from a misconfigured rotation job unless somebody wrote it down.
89
+ */
90
+ export declare const vaultRotationIsExternal: {
91
+ readonly cipher: "supabase-vault";
92
+ readonly resealFinds: "nothing, by design";
93
+ readonly why: string;
94
+ };
95
+ //# sourceMappingURL=vault-cipher.d.ts.map
@@ -0,0 +1,206 @@
1
+ /**
2
+ * A `SecretCipher` over Supabase Vault — REQ-063 (#259), task #268.
3
+ *
4
+ * #261 shipped the seam with an app-side AES-256-GCM implementation and deliberately left this one out: there
5
+ * was no Vault to run it against, and an untested crypto implementation behind a seam that *looks* tested is
6
+ * worse than none. There is one now, and everything below has been run against it.
7
+ *
8
+ * ## The mapping, and the property it changes — AC-3
9
+ *
10
+ * Vault owns the ciphertext and hands back an **id**. The seam expects the caller to hold
11
+ * `{ keyId, algorithm, nonce, ciphertext }`. So:
12
+ *
13
+ * | Field | Holds |
14
+ * |---|---|
15
+ * | `algorithm` | `supabase-vault` — a sentinel, not a cipher name. The algorithm is Vault's business. |
16
+ * | `keyId` | `supabase-vault` — see the rotation note. Vault's own `key_id` column comes back **null**: the root key is held outside the database and Supabase does not expose an identity for it. |
17
+ * | `nonce` | A random **binding token**, not a nonce. See the tamper note. |
18
+ * | `ciphertext` | The Vault secret's **uuid**. Not ciphertext. |
19
+ *
20
+ * **A Vault-sealed row contains no secret material at all.** That is the property worth stating rather than
21
+ * discovering, and it cuts both ways:
22
+ *
23
+ * - A backup of the application's own tables carries no credentials — strictly better than app-side sealing,
24
+ * where the backup carries ciphertext that a leaked key would open.
25
+ * - The secret lives in `vault.secrets`, so **restoring the application tables without the `vault` schema
26
+ * leaves every connection pointing at nothing.** With app-side sealing the ciphertext travels with the row;
27
+ * here it does not, and a backup strategy that covers `public` and not `vault` silently loses every
28
+ * credential. That is a deployment fact, not a code one, and it belongs in a runbook.
29
+ *
30
+ * ## Deletion — AC-3, and a gap this found
31
+ *
32
+ * Deleting a connection row does **not** delete the Vault secret: the row held a pointer, and dropping a
33
+ * pointer leaves what it pointed at. `docs/18`'s deletion promises are therefore *not* satisfied by deleting
34
+ * the row alone, which is a difference from app-side sealing that nobody would notice until an audit.
35
+ *
36
+ * So `SecretCipher` gained an optional `forget`. App-side sealing has nothing to implement — the ciphertext
37
+ * dies with the row — and this implementation removes the Vault secret. A deployment on Vault that never calls
38
+ * it accumulates orphaned secrets that outlive the connections they belonged to.
39
+ *
40
+ * ## Tampering — AC-2, and where the guarantee genuinely differs
41
+ *
42
+ * AES-GCM authenticates: a flipped byte fails rather than decrypting to something else. A pointer cannot be
43
+ * authenticated by the thing it points at, so an attacker able to *write* the connections table could repoint
44
+ * one connection at another secret in the same Vault and read a credential they were not entitled to. That is
45
+ * a real weakening and it is not hypothetical — it is exactly the shape of a tenant-isolation bypass.
46
+ *
47
+ * Closed here with a **binding token**: `seal` stores a fresh 32-byte random value in the Vault secret's
48
+ * `description` and keeps it in the sealed value's `nonce` field; `open` compares them in constant time and
49
+ * refuses on mismatch. Repointing at another secret now fails, because that secret carries a different token.
50
+ * No application-side key is involved, so the "database cannot decrypt on its own" property is untouched.
51
+ *
52
+ * It is not equivalent to AEAD. An attacker who can write *both* the connections table and `vault.secrets`
53
+ * defeats it, where GCM would still fail. Stated rather than implied.
54
+ */
55
+ import { timingSafeEqual } from "node:crypto";
56
+ import { randomBytes } from "node:crypto";
57
+ import { AgentPlatformError } from "../core/errors.js";
58
+ /**
59
+ * The sentinel in `algorithm`, and the reason it is not a cipher name.
60
+ *
61
+ * A reader of a stored row needs to know *which implementation* sealed it, not which primitive. `aes-256-gcm`
62
+ * tells the app-side cipher how to open the value; `supabase-vault` tells this one that the value is a pointer.
63
+ * Putting libsodium's actual construction here would be a claim about Vault's internals that could stop being
64
+ * true without this package noticing.
65
+ */
66
+ export const SUPABASE_VAULT = "supabase-vault";
67
+ /** 32 bytes of binding token. Long enough that guessing it is not a strategy. */
68
+ const TOKEN_BYTES = 32;
69
+ const failed = (message, code = "invalid_input") => {
70
+ throw new AgentPlatformError({ code, message, retryable: false });
71
+ };
72
+ /** Constant-time compare that does not leak *which* byte differed, and tolerates unequal lengths. */
73
+ const tokensMatch = (a, b) => {
74
+ const left = Buffer.from(a, "utf8");
75
+ const right = Buffer.from(b, "utf8");
76
+ // `timingSafeEqual` throws on a length mismatch, which would itself be a length oracle. Compared against a
77
+ // padded copy so the answer is false rather than an exception.
78
+ if (left.length !== right.length) {
79
+ const padded = Buffer.alloc(Math.max(left.length, right.length));
80
+ const other = Buffer.alloc(padded.length);
81
+ left.copy(padded);
82
+ right.copy(other);
83
+ timingSafeEqual(padded, other);
84
+ return false;
85
+ }
86
+ return timingSafeEqual(left, right);
87
+ };
88
+ export const createSupabaseVaultCipher = (options) => {
89
+ const prefix = options.namePrefix ?? "retinue";
90
+ const newToken = options.randomToken ?? (() => randomBytes(TOKEN_BYTES).toString("base64"));
91
+ return {
92
+ /**
93
+ * `keyId` is a constant, and that is Vault's answer to rotation — AC-4.
94
+ *
95
+ * `resealConnections` walks `sealed.keyId` looking for rows sealed under a retired key. Under Vault that
96
+ * loop finds **nothing, forever**, because there is one key identity and it never changes.
97
+ *
98
+ * That is correct rather than broken, and the distinction matters: Supabase rotates the Vault root key
99
+ * outside the database, and re-sealing through this cipher would mean decrypting and re-encrypting with
100
+ * the *same* key — work that changes nothing. But a rotation job that silently does nothing looks
101
+ * identical to a rotation job that is wired wrong, so a deployment on Vault should not run one and expect
102
+ * output. `vaultRotationIsExternal` exists to be asserted in a test rather than remembered.
103
+ */
104
+ currentKeyId() {
105
+ return SUPABASE_VAULT;
106
+ },
107
+ async seal(plaintext) {
108
+ const token = newToken();
109
+ /**
110
+ * The name is unique per secret, so two seals of the same plaintext are two Vault rows.
111
+ *
112
+ * Vault's `create_secret` raises on a duplicate name, and a cipher that reused one would make the
113
+ * second seal of a value fail — which would look like an intermittent credential bug.
114
+ */
115
+ const name = `${prefix}:${randomBytes(16).toString("hex")}`;
116
+ const rows = await options.sql.query("select vault.create_secret($1, $2, $3) as id", [plaintext, name, token]);
117
+ const id = rows[0]?.id;
118
+ if (typeof id !== "string" || id === "") {
119
+ return failed("Supabase Vault did not return an id for the secret it was asked to store.", "internal");
120
+ }
121
+ return {
122
+ keyId: SUPABASE_VAULT,
123
+ algorithm: SUPABASE_VAULT,
124
+ // The binding token, not a nonce — see the header. Kept in this field so the shape of a stored row is
125
+ // unchanged and no migration is needed to select this implementation.
126
+ nonce: token,
127
+ // The pointer. **Not ciphertext**; see the backup note in the header.
128
+ ciphertext: id,
129
+ };
130
+ },
131
+ async open(sealed) {
132
+ if (sealed.algorithm !== SUPABASE_VAULT) {
133
+ // Refused rather than guessed: a row sealed by the app-side cipher must not be handed to this one, and
134
+ // the mistake is a misconfiguration worth naming rather than a decryption failure to debug.
135
+ return failed(`This secret was sealed with "${sealed.algorithm}", not ${SUPABASE_VAULT}. A deployment that has ` +
136
+ "switched ciphers must re-seal its existing rows; opening one with the wrong implementation " +
137
+ "cannot work and is not attempted.");
138
+ }
139
+ if (sealed.ciphertext === "")
140
+ return failed("This Vault-sealed secret carries no id.");
141
+ const rows = await options.sql.query("select decrypted_secret, description from vault.decrypted_secrets where id = $1::uuid", [sealed.ciphertext]);
142
+ const row = rows[0];
143
+ if (row === undefined) {
144
+ /**
145
+ * A pointer with nothing behind it.
146
+ *
147
+ * The likeliest cause is the one named in the header: application tables restored without the `vault`
148
+ * schema. Said explicitly, because "secret not found" against a row that plainly has an id sends an
149
+ * operator looking for a code bug.
150
+ */
151
+ return failed(`Supabase Vault has no secret ${sealed.ciphertext}. Either it was deleted, or these rows were ` +
152
+ "restored from a backup that did not include the vault schema — a Vault-sealed row holds a " +
153
+ "pointer, not the ciphertext.");
154
+ }
155
+ /**
156
+ * The binding check — AC-2.
157
+ *
158
+ * Without it, an attacker who can write the connections table repoints a row at another secret in the
159
+ * same Vault and reads a credential they were not entitled to. A pointer cannot be authenticated by
160
+ * what it points at, so the token is what makes the pair inseparable.
161
+ */
162
+ if (!tokensMatch(sealed.nonce, row.description ?? "")) {
163
+ return failed("This Vault secret does not belong to this row. The binding token does not match, which means the " +
164
+ "stored id was changed — refused rather than returning a credential from somewhere else.");
165
+ }
166
+ const plaintext = row.decrypted_secret;
167
+ if (plaintext === null) {
168
+ return failed("Supabase Vault returned no plaintext for this secret.", "internal");
169
+ }
170
+ return plaintext;
171
+ },
172
+ /**
173
+ * Removes the Vault secret — the deletion half of AC-3.
174
+ *
175
+ * Deleting a connection row drops a pointer and leaves the secret. A deployment on Vault that never calls
176
+ * this accumulates credentials that outlive the connections they belonged to, which is the opposite of
177
+ * what `docs/18` promises.
178
+ */
179
+ async forget(sealed) {
180
+ if (sealed.algorithm !== SUPABASE_VAULT || sealed.ciphertext === "")
181
+ return;
182
+ /**
183
+ * Guarded by the binding token, like `open`.
184
+ *
185
+ * A `forget` that deleted whatever the id pointed at would be a way to destroy *another* connection's
186
+ * credential by writing an id into a row — a smaller blast radius than reading it, and the same class of
187
+ * bug. The delete is scoped to a row whose description matches.
188
+ */
189
+ await options.sql.query("delete from vault.secrets where id = $1::uuid and description = $2", [sealed.ciphertext, sealed.nonce]);
190
+ },
191
+ };
192
+ };
193
+ /**
194
+ * Rotation is Vault's, not ours — AC-4, stated as a value so a test can assert it.
195
+ *
196
+ * `resealConnections` walks `sealed.keyId`; this cipher's is constant, so that walk finds nothing forever.
197
+ * Correct, and indistinguishable from a misconfigured rotation job unless somebody wrote it down.
198
+ */
199
+ export const vaultRotationIsExternal = {
200
+ cipher: SUPABASE_VAULT,
201
+ resealFinds: "nothing, by design",
202
+ why: "Supabase rotates the Vault root key outside the database. Re-sealing through this cipher would decrypt " +
203
+ "and re-encrypt under the same key identity, which changes nothing. A deployment on Vault should not run " +
204
+ "a re-seal job and expect it to report work.",
205
+ };
206
+ //# sourceMappingURL=vault-cipher.js.map
@@ -7,7 +7,7 @@
7
7
  */
8
8
  import type { ArtifactId, ArtifactVersionId, BlobRef, ConversationId, FileId, InteractionId, MessageId, MessagePartId, RunId, ToolCallId } from "./ids.js";
9
9
  import type { PlatformError } from "./errors.js";
10
- export declare const MESSAGE_PART_TYPES: readonly ["text", "reasoning", "tool-call", "tool-result", "question", "approval", "file", "image", "citation", "source", "artifact", "status", "error"];
10
+ export declare const MESSAGE_PART_TYPES: readonly ["text", "reasoning", "tool-call", "tool-result", "question", "approval", "file", "image", "citation", "source", "artifact", "status", "error", "structured"];
11
11
  export type MessagePartType = (typeof MESSAGE_PART_TYPES)[number];
12
12
  type PartBase<T extends MessagePartType> = {
13
13
  readonly id: MessagePartId;
@@ -21,6 +21,22 @@ type PartBase<T extends MessagePartType> = {
21
21
  export type TextPart = PartBase<"text"> & {
22
22
  readonly text: string;
23
23
  };
24
+ /**
25
+ * The validated answer of a structured agent — task #243.
26
+ *
27
+ * Its own part rather than a `TextPart` holding JSON, for the reason the whole task exists: a consumer that
28
+ * asked for structured output wants the object, and handing back text would leave every reader re-parsing it
29
+ * and deciding for itself whether the parse succeeded. The part carries the value **after** validation, so its
30
+ * presence is the guarantee — an agent whose model produced nothing conforming fails the run instead of
31
+ * emitting this with something unchecked in it.
32
+ *
33
+ * There is no partial form. A half-built object does not satisfy the schema the caller asked for, so streaming
34
+ * one would mean emitting values that violate the contract; the part is added once, complete, at the end of the
35
+ * turn. Tool calls still stream normally around it.
36
+ */
37
+ export type StructuredPart = PartBase<"structured"> & {
38
+ readonly value: unknown;
39
+ };
24
40
  export type ReasoningPart = PartBase<"reasoning"> & {
25
41
  readonly text: string;
26
42
  /** Reasoning is pruned before recent semantic turns when the budget is tight. */
@@ -188,7 +204,7 @@ export type StatusPart = PartBase<"status"> & {
188
204
  export type ErrorPart = PartBase<"error"> & {
189
205
  readonly error: PlatformError;
190
206
  };
191
- export type MessagePart = TextPart | ReasoningPart | ToolCallPart | ToolResultPart | QuestionPart | ApprovalPart | FilePart | ImagePart | CitationPart | SourcePart | ArtifactPart | StatusPart | ErrorPart;
207
+ export type MessagePart = TextPart | ReasoningPart | ToolCallPart | ToolResultPart | QuestionPart | ApprovalPart | FilePart | ImagePart | CitationPart | SourcePart | ArtifactPart | StatusPart | StructuredPart | ErrorPart;
192
208
  export type MessageRole = "system" | "user" | "assistant" | "tool";
193
209
  export type Message = {
194
210
  readonly id: MessageId;
@@ -19,5 +19,6 @@ export const MESSAGE_PART_TYPES = [
19
19
  "artifact",
20
20
  "status",
21
21
  "error",
22
+ "structured",
22
23
  ];
23
24
  //# sourceMappingURL=content-parts.js.map
@@ -31,6 +31,31 @@ export type ExecutionContext = {
31
31
  * it is shadow and has nowhere to record the suppression is refused, not performed.
32
32
  */
33
33
  readonly shadow?: boolean;
34
+ /**
35
+ * The running agent's tool policy — task #244.
36
+ *
37
+ * On the context for the same reason `shadow` is, and the argument is the one at the top of this file: context
38
+ * identity is constructed by the host, and **nothing originating in a tool argument, a skill body or an MCP
39
+ * tool description may reach these fields.** A model must never be able to widen `excluded`, and the only way
40
+ * to guarantee that is for the policy to travel on the one object a model cannot write to.
41
+ *
42
+ * The engine sets it from `AgentManifest.toolPolicy` before it builds tools, so every path that reaches the
43
+ * registry — a direct call, `execute_tool`, a delegating tool — sees the same policy. That breadth is the
44
+ * point: `toolPolicy.excluded` reads as a security control, and a control enforced only where the catalogue is
45
+ * built is bypassed by the first caller who already knows the tool's name.
46
+ *
47
+ * Structural (three name lists) rather than importing `ToolPolicyView`, because `core` must not depend on
48
+ * `tools`. The same choice `ApprovalCheck` makes to avoid a tools→hitl dependency.
49
+ *
50
+ * Absent means no agent-level policy — every authorized tool is available. That is the right default here, and
51
+ * it is the *safe* direction unlike `shadow`: a missing policy grants nothing that authorization has not
52
+ * already granted, whereas a missing `shadow` flag publishes.
53
+ */
54
+ readonly agentToolPolicy?: {
55
+ readonly preloaded: readonly string[];
56
+ readonly categories: readonly string[];
57
+ readonly excluded: readonly string[];
58
+ };
34
59
  };
35
60
  /**
36
61
  * The tenant scope every store method receives explicitly. Governing principle 1:
@@ -5,7 +5,7 @@
5
5
  * is shown to a model and a user: no credentials, no connection strings, no internal
6
6
  * hostnames, no raw provider stack traces.
7
7
  */
8
- export declare const ERROR_CODES: readonly ["unauthorized", "forbidden", "not_found", "invalid_input", "conflict", "rate_limited", "provider_unavailable", "provider_error", "timeout", "cancelled", "budget_exceeded", "context_overflow", "approval_required", "approval_denied", "approval_expired", "question_pending", "idempotency_conflict", "capability_unavailable", "internal"];
8
+ export declare const ERROR_CODES: readonly ["unauthorized", "forbidden", "not_found", "invalid_input", "conflict", "rate_limited", "provider_unavailable", "provider_error", "timeout", "cancelled", "budget_exceeded", "context_overflow", "approval_required", "approval_denied", "approval_expired", "question_pending", "admission_rate_limited", "idempotency_conflict", "capability_unavailable", "internal"];
9
9
  export type ErrorCode = (typeof ERROR_CODES)[number];
10
10
  export type PlatformError = {
11
11
  readonly code: ErrorCode;
@@ -33,6 +33,21 @@ export const ERROR_CODES = [
33
33
  * Carries `details.interactionId`.
34
34
  */
35
35
  "question_pending",
36
+ /**
37
+ * A tenant asked for more runs than their rate allows — task #248.
38
+ *
39
+ * **Its own code, not `rate_limited`.** That one means *a provider* throttled us, and `decideRetry` treats it
40
+ * as retryable inside the run — which is right there and wrong here: this refusal happens at admission, before
41
+ * a run exists, so there is nothing to retry and no run event to carry it. Conflating the two would let a
42
+ * platform-level capacity refusal be handled by the engine's provider-backoff loop, which is the wrong
43
+ * mechanism for the wrong problem.
44
+ *
45
+ * Distinct from `budget_exceeded` too: that is about *spend over a period* and this is about *capacity right
46
+ * now*. A tenant can be well inside budget and still asking too fast.
47
+ *
48
+ * Carries `details.retryAfter` and `details.retryAfterMs`, so a client can wait rather than guess.
49
+ */
50
+ "admission_rate_limited",
36
51
  "idempotency_conflict",
37
52
  "capability_unavailable",
38
53
  "internal",
@@ -8,7 +8,7 @@
8
8
  import type { MessagePart } from "./content-parts.js";
9
9
  import type { PlatformError } from "./errors.js";
10
10
  import type { InteractionId, MessageId, RunId, TenantId, ToolCallId } from "./ids.js";
11
- export declare const RUN_EVENT_TYPES: readonly ["run.queued", "run.started", "run.checkpointed", "run.completed", "run.failed", "run.cancelled", "run.retry-pending", "part.added", "part.updated", "tool.started", "tool.completed", "tool.failed", "question.requested", "question.answered", "approval.requested", "approval.decided", "usage.updated", "context.compacted", "guardrail.verdict", "catalog.truncated"];
11
+ export declare const RUN_EVENT_TYPES: readonly ["run.queued", "run.started", "run.checkpointed", "run.completed", "run.failed", "run.cancelled", "run.retry-pending", "part.added", "part.updated", "tool.started", "tool.completed", "tool.failed", "question.requested", "question.answered", "approval.requested", "approval.decided", "connection.requested", "connection.completed", "usage.updated", "context.compacted", "guardrail.verdict", "catalog.truncated"];
12
12
  export type RunEventType = (typeof RUN_EVENT_TYPES)[number];
13
13
  type EventBase<T extends RunEventType> = {
14
14
  readonly type: T;
@@ -53,6 +53,31 @@ export type ToolEvent = EventBase<"tool.started" | "tool.completed" | "tool.fail
53
53
  export type InteractionEvent = EventBase<"question.requested" | "question.answered" | "approval.requested" | "approval.decided"> & {
54
54
  readonly interactionId: InteractionId;
55
55
  };
56
+ /**
57
+ * The run needs a connection, and here is where to get one — task #264.
58
+ *
59
+ * Its own event rather than an `InteractionEvent`, because it carries different things: an interaction id names
60
+ * a stored question or approval, and this names a *provider*, the scopes being asked for, and a URL. Folding it
61
+ * in would mean an `interactionId` for something that is not one.
62
+ *
63
+ * **It carries no secret.** Not the client secret, not a token, not the PKCE verifier. This is rendered in a UI
64
+ * and clicked by a person, so it goes wherever a screenshot goes.
65
+ */
66
+ export type ConnectionRequestedEvent = EventBase<"connection.requested"> & {
67
+ readonly provider: string;
68
+ /** Where to send the person. Single-use and TTL-bounded — see the OAuth flow's `state`. */
69
+ readonly loginUrl: string;
70
+ /** What the consent will ask for, so a UI can say what is being granted. */
71
+ readonly scopes: readonly string[];
72
+ /** Which tool stalled, so a transcript reads coherently. */
73
+ readonly toolName?: string;
74
+ /** When the login URL stops working, so a client can offer a fresh one rather than a dead link. */
75
+ readonly expiresAt: string;
76
+ };
77
+ export type ConnectionCompletedEvent = EventBase<"connection.completed"> & {
78
+ readonly provider: string;
79
+ readonly connectionId: string;
80
+ };
56
81
  export type UsageUpdatedEvent = EventBase<"usage.updated"> & {
57
82
  readonly inputTokens: number;
58
83
  readonly outputTokens: number;
@@ -60,6 +85,8 @@ export type UsageUpdatedEvent = EventBase<"usage.updated"> & {
60
85
  /** When present, the worker records a durable `UsageEvent` for this realized step. */
61
86
  readonly modelId?: string;
62
87
  readonly cachedInputTokens?: number;
88
+ /** Tokens written into a prompt cache — task #247. Absent means the provider reported no breakdown. */
89
+ readonly cacheWriteTokens?: number;
63
90
  readonly reasoningTokens?: number;
64
91
  /**
65
92
  * Non-text input this step carried — #185 AC-4.
@@ -136,7 +163,7 @@ export type CatalogTruncatedEvent = EventBase<"catalog.truncated"> & {
136
163
  */
137
164
  readonly overBudget?: boolean;
138
165
  };
139
- export type RunEvent = RunLifecycleEvent | RunFailedEvent | RunRetryPendingEvent | PartEvent | ToolEvent | InteractionEvent | UsageUpdatedEvent | ContextCompactedEvent | GuardrailVerdictEvent | CatalogTruncatedEvent;
166
+ export type RunEvent = RunLifecycleEvent | RunFailedEvent | RunRetryPendingEvent | PartEvent | ToolEvent | InteractionEvent | ConnectionRequestedEvent | ConnectionCompletedEvent | UsageUpdatedEvent | ContextCompactedEvent | GuardrailVerdictEvent | CatalogTruncatedEvent;
140
167
  /** Fan-out port. Adapters: Supabase Realtime, Redis pub/sub, in-memory for tests. */
141
168
  export interface RealtimePublisher {
142
169
  publish(channel: string, event: RunEvent): Promise<void>;
@@ -194,6 +221,21 @@ export type RunStreamState = {
194
221
  readonly nextAttemptAt: string;
195
222
  };
196
223
  readonly error?: PlatformError;
224
+ /**
225
+ * Present while the run is waiting for somebody to connect a provider — task #264.
226
+ *
227
+ * On the stream state rather than as a message part, because it is not a thing the model said: it is the
228
+ * platform telling a client *this run is stopped and here is the way to unstick it*. Cleared by
229
+ * `connection.completed`, so a client that reconnects mid-consent still sees the button and one that
230
+ * reconnects after it does not.
231
+ */
232
+ readonly connectionRequest?: {
233
+ readonly provider: string;
234
+ readonly loginUrl: string;
235
+ readonly scopes: readonly string[];
236
+ readonly toolName?: string;
237
+ readonly expiresAt: string;
238
+ };
197
239
  readonly terminal: boolean;
198
240
  };
199
241
  export declare const EMPTY_RUN_STREAM_STATE: RunStreamState;
@@ -22,6 +22,14 @@ export const RUN_EVENT_TYPES = [
22
22
  "question.answered",
23
23
  "approval.requested",
24
24
  "approval.decided",
25
+ /**
26
+ * The run needs a connection, and here is where to go and get one — task #264.
27
+ *
28
+ * Carries the provider, the scopes being asked for and a login URL. It carries **no secret**: not the client
29
+ * secret, not a token, not the PKCE verifier. This is an event a client renders and a person clicks.
30
+ */
31
+ "connection.requested",
32
+ "connection.completed",
25
33
  "usage.updated",
26
34
  "context.compacted",
27
35
  "guardrail.verdict",
@@ -82,11 +90,32 @@ export const reduceRunEvent = (state, event) => {
82
90
  ...base,
83
91
  retry: { attempt: event.attempt, maxAttempts: event.maxAttempts, nextAttemptAt: event.nextAttemptAt },
84
92
  };
93
+ /**
94
+ * The connection request, held until consent completes — task #264.
95
+ *
96
+ * Set and cleared here rather than derived by each client, so a reconnecting client sees the button exactly
97
+ * when the run is actually waiting: the durable log replays both events in order, and the fold gives the
98
+ * same answer as if it had been watching live.
99
+ */
100
+ case "connection.requested":
101
+ return {
102
+ ...base,
103
+ connectionRequest: {
104
+ provider: event.provider,
105
+ loginUrl: event.loginUrl,
106
+ scopes: event.scopes,
107
+ ...(event.toolName === undefined ? {} : { toolName: event.toolName }),
108
+ expiresAt: event.expiresAt,
109
+ },
110
+ };
111
+ case "connection.completed":
112
+ return { ...base, connectionRequest: undefined };
85
113
  case "run.failed":
86
- return { ...base, error: event.error, terminal: true };
114
+ // Cleared: a failed run's login URL is a button that leads nowhere.
115
+ return { ...base, connectionRequest: undefined, error: event.error, terminal: true };
87
116
  case "run.completed":
88
117
  case "run.cancelled":
89
- return { ...base, terminal: true };
118
+ return { ...base, connectionRequest: undefined, terminal: true };
90
119
  default:
91
120
  return base;
92
121
  }
@@ -133,6 +133,25 @@ const messagePartSchema = z.discriminatedUnion("type", [
133
133
  z.object({ ...base, type: z.literal("artifact"), artifactId: idString, versionId: idString, title: z.string() }),
134
134
  z.object({ ...base, type: z.literal("status"), status: z.string(), detail: z.string().optional() }),
135
135
  z.object({ ...base, type: z.literal("error"), error: platformErrorSchema }),
136
+ /**
137
+ * A structured agent's validated answer — #243.
138
+ *
139
+ * `value` is `unknown` on purpose. The schema that constrains it belongs to the *agent*, is chosen by whoever
140
+ * wrote the manifest, and has already been enforced before the part was emitted — so re-describing it here is
141
+ * impossible (there is no one shape) and re-validating it would be validating against the wrong schema.
142
+ *
143
+ * What this layer does check is that `value` is **present**. `z.unknown()` accepts `undefined`, which would let
144
+ * a part claiming to be a structured answer round-trip carrying nothing — the empty version of the exact defect
145
+ * #243 fixed. `z.unknown().refine(v => v !== undefined)` is the narrowest thing that closes it, and `null`
146
+ * stays legal because `null` is a valid JSON value a schema may well permit.
147
+ */
148
+ z.object({
149
+ ...base,
150
+ type: z.literal("structured"),
151
+ value: z.unknown().refine((v) => v !== undefined, {
152
+ message: "a structured part must carry a value; an absent one is an answer that was never validated",
153
+ }),
154
+ }),
136
155
  ]);
137
156
  const executionContextSchema = z.object({
138
157
  tenantId: idString,
@@ -0,0 +1,10 @@
1
+ /**
2
+ * `@retinue/agentkit/connections` — a tenant's links to third-party providers, and the cipher that protects
3
+ * them (#261).
4
+ *
5
+ * Its own subpath rather than part of `./tools`, because the two have different readers: a toolkit author needs
6
+ * `credentialRef` and `Credential`, and a *deployment* needs the store, the cipher and the resolver that
7
+ * connects them. Nothing here is needed to write a tool.
8
+ */
9
+ export * from "../connections/index.js";
10
+ //# sourceMappingURL=connections.d.ts.map
@@ -0,0 +1,10 @@
1
+ /**
2
+ * `@retinue/agentkit/connections` — a tenant's links to third-party providers, and the cipher that protects
3
+ * them (#261).
4
+ *
5
+ * Its own subpath rather than part of `./tools`, because the two have different readers: a toolkit author needs
6
+ * `credentialRef` and `Credential`, and a *deployment* needs the store, the cipher and the resolver that
7
+ * connects them. Nothing here is needed to write a tool.
8
+ */
9
+ export * from "../connections/index.js";
10
+ //# sourceMappingURL=connections.js.map
@@ -24,4 +24,12 @@ export * from "../export/markdown.js";
24
24
  */
25
25
  export { DEFAULT_EMBEDDING_BATCH, DEFAULT_EMBEDDING_MODEL, createOpenAiEmbeddings } from "../adapters/embeddings/openai.js";
26
26
  export type { OpenAiEmbeddingsConfig } from "../adapters/embeddings/openai.js";
27
+ /**
28
+ * The extraction prompt, exported so a harness can use the same one the runtime does — task #275.
29
+ *
30
+ * `extractGraph` itself is not exported here: it takes a `LanguageModel` from the AI SDK, and a consumer that
31
+ * wanted it would be pulling a provider package through the knowledge entrypoint. The prompt is the part worth
32
+ * sharing — a measurement run against a *different* prompt is measuring a different system.
33
+ */
34
+ export { DEFAULT_EXTRACTION_PROMPT } from "../models/extraction.js";
27
35
  //# sourceMappingURL=knowledge.d.ts.map
@@ -24,4 +24,12 @@ export * from "../export/markdown.js";
24
24
  * own subpaths because they each pull a client library in.
25
25
  */
26
26
  export { DEFAULT_EMBEDDING_BATCH, DEFAULT_EMBEDDING_MODEL, createOpenAiEmbeddings } from "../adapters/embeddings/openai.js";
27
+ /**
28
+ * The extraction prompt, exported so a harness can use the same one the runtime does — task #275.
29
+ *
30
+ * `extractGraph` itself is not exported here: it takes a `LanguageModel` from the AI SDK, and a consumer that
31
+ * wanted it would be pulling a provider package through the knowledge entrypoint. The prompt is the part worth
32
+ * sharing — a measurement run against a *different* prompt is measuring a different system.
33
+ */
34
+ export { DEFAULT_EXTRACTION_PROMPT } from "../models/extraction.js";
27
35
  //# sourceMappingURL=knowledge.js.map
@@ -0,0 +1,9 @@
1
+ /**
2
+ * `@retinue/agentkit/mcp-server` — expose this deployment's tools to MCP clients (#250).
3
+ *
4
+ * Its own subpath, and separate from `./mcp`, because the two are opposite directions: `./mcp` consumes a
5
+ * tenant's MCP servers, and this **is** one. Sharing an entry would put the SDK's server half in the way of
6
+ * every consumer who only wanted the client half.
7
+ */
8
+ export * from "../mcp-server/index.js";
9
+ //# sourceMappingURL=mcp-server.d.ts.map