@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
@@ -72,6 +72,13 @@ export const TENANT_SCOPED_TABLES = [
72
72
  { table: "idempotency_keys" },
73
73
  { table: "skills" },
74
74
  { table: "mcp_connections" },
75
+ /**
76
+ * #261. A tenant's third-party credentials, and therefore the table where a missing policy would be worst:
77
+ * every row is somebody's access to somebody else's system, and the plain `tenant_id` predicate is exactly
78
+ * the isolation the port already enforces in its `WHERE` clauses. Defence in depth, and #91 is the reminder
79
+ * that a method accepting a tenant scope and not using it typechecks perfectly.
80
+ */
81
+ { table: "connections" },
75
82
  { table: "principal_memory", extraPredicate: PRINCIPAL_PREDICATE },
76
83
  // `blobs`, not the SPEC's `blob_refs`: BlobStore stores the value, and the metadata-and-pointer
77
84
  // design belongs to FileMetadataStore (#129) / ArtifactStore (#133).
@@ -98,6 +105,32 @@ export const TENANT_SCOPED_TABLES = [
98
105
  */
99
106
  { table: "flow_definitions" },
100
107
  { table: "flow_executions" },
108
+ /**
109
+ * #271. The knowledge graph, all five tables, and they belong here rather than beside `knowledge_chunks`
110
+ * in the vector list: none of them needs pgvector, so they exist in every deployment and can be protected
111
+ * unconditionally.
112
+ *
113
+ * Worth stating why the *settings* table needs a policy as much as the content ones. `knowledge_graph_settings`
114
+ * holds one boolean, so it looks harmless — but it is the switch that decides whether a tenant is paying for
115
+ * extraction, and a missing policy would let one tenant read, and worse write, another's. Turning a
116
+ * neighbour's GraphRAG on is a bill they did not agree to.
117
+ */
118
+ { table: "knowledge_graph_settings" },
119
+ { table: "knowledge_graph_sources" },
120
+ /**
121
+ * The contributions table is the one to get right. It holds the entities and relationships each source
122
+ * asserted, as JSON — which is to say, the substance of the documents themselves in condensed form. A leak
123
+ * here is a leak of what a tenant's corpus says, not merely of which entities exist.
124
+ */
125
+ { table: "knowledge_graph_contributions" },
126
+ { table: "knowledge_graph_entities" },
127
+ { table: "knowledge_graph_relationships" },
128
+ /**
129
+ * #272. Community summaries are **generated prose about a tenant's corpus** — a paraphrase of what their
130
+ * documents say, which is if anything more sensitive than the entity names next door. A missing policy here
131
+ * would leak the substance of a corpus rather than its index.
132
+ */
133
+ { table: "knowledge_graph_communities" },
101
134
  ];
102
135
  /**
103
136
  * Tables that deliberately have no tenant policy, each with the reason.
@@ -15,13 +15,52 @@ import { type ProviderCredentials } from "../models/provider-factory.js";
15
15
  import type { ModelProvider } from "../models/index.js";
16
16
  import type { AuthorizationPolicy } from "../authorization/index.js";
17
17
  import { type ContextProvider } from "../context/index.js";
18
- import { type ToolProvider, type ToolSearch, type ToolsetResolver } from "../tools/index.js";
18
+ import { type ShadowRecorder, type ToolProvider, type ToolSearch, type ToolsetResolver } from "../tools/index.js";
19
19
  import type { TokenBudget } from "../core/budget.js";
20
20
  import { type AgentEngine, type ProcessOutcome } from "../runtime/index.js";
21
21
  import type { Guardrail } from "../guardrails/index.js";
22
+ import { type SkillResolver } from "../skills/index.js";
22
23
  import { type ResolvedModelInfo } from "./engine.js";
23
24
  import type { AgentManifest } from "./index.js";
24
25
  import { type AgentManifestInput } from "./define.js";
26
+ /**
27
+ * The policy this agent runs under — task #244, the interpreter for `authorizationPolicyId`.
28
+ *
29
+ * The field was declared and read by nothing, so an agent naming `"restricted"` ran under whatever the host had
30
+ * wired — very possibly the permissive default. Three rules, and the third is the one that matters:
31
+ *
32
+ * 1. A map is supplied and contains the id → that policy.
33
+ * 2. No map, and the id is `defineAgent`'s `"default"` → the single wired policy, or the permissive default.
34
+ * This is the normal case and stays a one-liner.
35
+ * 3. **Any other combination is an error.** An id the deployment cannot honour must never fall through to the
36
+ * permissive default: an agent asking to run under a narrow policy and silently getting allow-all is the
37
+ * worst possible reading of this field, and it is the reading it had. Failing at construction is loud, early
38
+ * and cheap.
39
+ *
40
+ * Note the platform does not check that a named policy is *narrower* than any other. It cannot: a policy is an
41
+ * interface the host implements, and composing two of them into an intersection would mean second-guessing a
42
+ * deployment's own authorization. What the platform guarantees is that the policy an agent named is the policy
43
+ * it got, or the run does not start.
44
+ */
45
+ export declare const selectAuthorization: (config: Pick<CreateAgentConfig, "authorization" | "authorizationPolicies">, manifest: Pick<AgentManifest, "authorizationPolicyId" | "id">) => AuthorizationPolicy;
46
+ /**
47
+ * The providers this agent asked for, in the order it asked — task #244.
48
+ *
49
+ * `AgentManifest.contextProviderIds` was declared and read by nothing, so a manifest naming two of four wired
50
+ * providers got all four, and naming none got all of them too. Two decisions make the field meaningful without
51
+ * making it a trap:
52
+ *
53
+ * - **An empty list means every wired provider**, not none. `defineAgent` defaults it to `[]`, so treating empty
54
+ * as "no context" would silently strip the memory, notes and attachments from every agent already written
55
+ * against the default. The field is a *selection*, and no selection means no narrowing.
56
+ * - **A named id that is not wired is an error**, not a silent omission. The failure it prevents is the one worth
57
+ * preventing: an agent whose manifest asks for `principal-memory`, a typo or a missing wire, and an assistant
58
+ * that quietly remembers nothing. That reads exactly like a model that chose not to use its memory.
59
+ *
60
+ * Order follows the manifest, because section order is prompt order and the manifest is where an author can see
61
+ * and control it.
62
+ */
63
+ export declare const selectContextProviders: (wired: readonly ContextProvider[], manifest: Pick<AgentManifest, "contextProviderIds" | "id">) => readonly ContextProvider[];
25
64
  export type CreateAgentConfig = {
26
65
  readonly manifest: AgentManifestInput;
27
66
  /** Model catalog; defaults to a small Anthropic catalog (smart/fast). */
@@ -40,6 +79,15 @@ export type CreateAgentConfig = {
40
79
  */
41
80
  readonly randomHex?: (bytes: number) => string;
42
81
  readonly authorization?: AuthorizationPolicy;
82
+ /**
83
+ * Named policies a manifest may select with `authorizationPolicyId` — task #244.
84
+ *
85
+ * Absent is the normal case: one `authorization` (or the permissive default), and every manifest carrying
86
+ * `defineAgent`'s `"default"`. Supplying a map is how a deployment runs several agents under different
87
+ * policies — a customer-facing agent under a narrow one, an internal agent under a wider one — without the
88
+ * host having to build a separate registry per agent.
89
+ */
90
+ readonly authorizationPolicies?: Readonly<Record<string, AuthorizationPolicy>>;
43
91
  /**
44
92
  * Checks to run before the model sees a turn and before anything leaves it — REQ-046 (#205), AC-5.
45
93
  *
@@ -59,7 +107,24 @@ export type CreateAgentConfig = {
59
107
  readonly catalogBudget?: TokenBudget;
60
108
  /** A tenant's category switches, applied before authorization — task #210, AC-4. */
61
109
  readonly toolsets?: ToolsetResolver;
110
+ /**
111
+ * A skill resolver — task #244, and what makes `manifest.skillPolicy` mean something.
112
+ *
113
+ * Wiring is the toggle, the rule `toolSearch` already follows. Supplying one adds a skills catalogue section
114
+ * to the prompt (names and descriptions, filtered by the manifest's `assigned`/`allowTenantSkills`) and makes
115
+ * `load_skill` real; omitting it means neither is advertised.
116
+ */
117
+ readonly skills?: SkillResolver;
62
118
  readonly tenantId?: string;
119
+ /**
120
+ * Where a shadow run's suppressed writes go — required to run one at all.
121
+ *
122
+ * Without it `createAgent` cannot run a shadow turn: the registry refuses a gated tool when
123
+ * `context.shadow` is true and no recorder is configured, which is the correct fail-closed rule but meant
124
+ * this runner had no way to satisfy it. Parity measurement needs a run driven to completion in one process,
125
+ * and this is that seam.
126
+ */
127
+ readonly shadow?: ShadowRecorder;
63
128
  /** Test/advanced seam: override how a manifest resolves to a model (e.g. a mock model). */
64
129
  readonly resolveModel?: (manifest: AgentManifest, context: ExecutionContext) => ResolvedModelInfo;
65
130
  /** Test/advanced seam: supply the engine directly instead of building the default one. */
@@ -71,6 +136,18 @@ export type RunInput = {
71
136
  readonly message: string;
72
137
  readonly principalId?: string;
73
138
  readonly roleIds?: readonly string[];
139
+ /**
140
+ * Run with every gated effect suppressed and recorded instead of performed.
141
+ *
142
+ * Per-run rather than per-agent, because the same agent serves both: a parity harness shadows one turn and
143
+ * runs the next for real. `shadow` without `config.shadow` is refused by the registry rather than performed
144
+ * — announcing a shadow run with nowhere to record it is not a licence to publish.
145
+ *
146
+ * Note what shadow mode does **not** suppress: an `internal-write` still happens, so a shadow run really
147
+ * does create drafts. It measures everything up to the external write and nothing after it, because what an
148
+ * agent does after publishing cannot be observed without publishing.
149
+ */
150
+ readonly shadow?: boolean;
74
151
  };
75
152
  export type RunResult = {
76
153
  readonly runId: string;
@@ -17,14 +17,95 @@ import { createProviderFactory, } from "../models/provider-factory.js";
17
17
  import { gatherSections } from "../context/index.js";
18
18
  import { randomBytes } from "node:crypto";
19
19
  import { makeNonce, renderContextBlock } from "../security/prompt-safety.js";
20
- import { createToolRegistry } from "../tools/index.js";
20
+ import { createToolRegistry, } from "../tools/index.js";
21
21
  import { createDurableWorker } from "../runtime/index.js";
22
22
  import { createMemoryCheckpointStore, createMemoryRunEventLog, createMemoryRunStore, } from "../adapters/memory/runtime.js";
23
23
  import { createMemoryConversationStore } from "../adapters/memory/index.js";
24
24
  import { createMemoryEventBus } from "../runtime/index.js";
25
25
  import { createMemoryMessageStore } from "../adapters/memory/message-store.js";
26
+ import { createSkillBodyLoader, createSkillCatalogueProvider, } from "../skills/index.js";
26
27
  import { createDefaultEngine } from "./engine.js";
27
28
  import { DEFAULT_MODEL_CATALOG, DEFAULT_ROLE_ASSIGNMENTS, defineAgent, } from "./define.js";
29
+ /**
30
+ * The policy this agent runs under — task #244, the interpreter for `authorizationPolicyId`.
31
+ *
32
+ * The field was declared and read by nothing, so an agent naming `"restricted"` ran under whatever the host had
33
+ * wired — very possibly the permissive default. Three rules, and the third is the one that matters:
34
+ *
35
+ * 1. A map is supplied and contains the id → that policy.
36
+ * 2. No map, and the id is `defineAgent`'s `"default"` → the single wired policy, or the permissive default.
37
+ * This is the normal case and stays a one-liner.
38
+ * 3. **Any other combination is an error.** An id the deployment cannot honour must never fall through to the
39
+ * permissive default: an agent asking to run under a narrow policy and silently getting allow-all is the
40
+ * worst possible reading of this field, and it is the reading it had. Failing at construction is loud, early
41
+ * and cheap.
42
+ *
43
+ * Note the platform does not check that a named policy is *narrower* than any other. It cannot: a policy is an
44
+ * interface the host implements, and composing two of them into an intersection would mean second-guessing a
45
+ * deployment's own authorization. What the platform guarantees is that the policy an agent named is the policy
46
+ * it got, or the run does not start.
47
+ */
48
+ export const selectAuthorization = (config, manifest) => {
49
+ const id = manifest.authorizationPolicyId ?? "default";
50
+ const named = config.authorizationPolicies;
51
+ if (named !== undefined) {
52
+ const chosen = named[id];
53
+ if (chosen !== undefined)
54
+ return chosen;
55
+ throw new AgentPlatformError({
56
+ code: "capability_unavailable",
57
+ message: `agent "${manifest.id}" runs under authorization policy "${id}" and no such policy is registered ` +
58
+ `(registered: ${Object.keys(named).map((k) => `"${k}"`).join(", ") || "none"}). Refusing rather than ` +
59
+ "falling back — an agent that asked for a narrow policy and silently got a permissive one is the " +
60
+ "failure this field exists to prevent.",
61
+ retryable: false,
62
+ });
63
+ }
64
+ if (id !== "default")
65
+ throw new AgentPlatformError({
66
+ code: "capability_unavailable",
67
+ message: `agent "${manifest.id}" runs under authorization policy "${id}" and none is registered. Pass ` +
68
+ "`authorizationPolicies: { \"" +
69
+ id +
70
+ "\": … }`, or leave the manifest's `authorizationPolicyId` at \"default\".",
71
+ retryable: false,
72
+ });
73
+ return config.authorization ?? allowAllAuthorization();
74
+ };
75
+ /**
76
+ * The providers this agent asked for, in the order it asked — task #244.
77
+ *
78
+ * `AgentManifest.contextProviderIds` was declared and read by nothing, so a manifest naming two of four wired
79
+ * providers got all four, and naming none got all of them too. Two decisions make the field meaningful without
80
+ * making it a trap:
81
+ *
82
+ * - **An empty list means every wired provider**, not none. `defineAgent` defaults it to `[]`, so treating empty
83
+ * as "no context" would silently strip the memory, notes and attachments from every agent already written
84
+ * against the default. The field is a *selection*, and no selection means no narrowing.
85
+ * - **A named id that is not wired is an error**, not a silent omission. The failure it prevents is the one worth
86
+ * preventing: an agent whose manifest asks for `principal-memory`, a typo or a missing wire, and an assistant
87
+ * that quietly remembers nothing. That reads exactly like a model that chose not to use its memory.
88
+ *
89
+ * Order follows the manifest, because section order is prompt order and the manifest is where an author can see
90
+ * and control it.
91
+ */
92
+ export const selectContextProviders = (wired, manifest) => {
93
+ const asked = manifest.contextProviderIds ?? [];
94
+ if (asked.length === 0)
95
+ return wired;
96
+ const byId = new Map(wired.map((p) => [p.id, p]));
97
+ const missing = asked.filter((id) => !byId.has(id));
98
+ if (missing.length > 0)
99
+ throw new AgentPlatformError({
100
+ code: "capability_unavailable",
101
+ message: `agent "${manifest.id}" asks for context provider(s) ${missing.map((m) => `"${m}"`).join(", ")} and ` +
102
+ `nothing wired supplies them (wired: ${wired.map((p) => `"${p.id}"`).join(", ") || "none"}). An agent ` +
103
+ "that silently runs without the context it declared is indistinguishable from a model choosing not to " +
104
+ "use it.",
105
+ retryable: false,
106
+ });
107
+ return asked.map((id) => byId.get(id));
108
+ };
28
109
  /** Permissive policy used by the embedded facade when a caller wires tools but no authorization. */
29
110
  const allowAllAuthorization = () => ({
30
111
  async can() {
@@ -55,13 +136,27 @@ export const createAgent = (config) => {
55
136
  roles: config.roleAssignments ?? DEFAULT_ROLE_ASSIGNMENTS,
56
137
  });
57
138
  const providerFactory = createProviderFactory({ credentials: config.providerCredentials ?? {} });
58
- const authorization = config.authorization ?? allowAllAuthorization();
139
+ const authorization = selectAuthorization(config, manifest);
140
+ /**
141
+ * Skills, when a resolver is wired — #244.
142
+ *
143
+ * Both halves read the *same* policy, and that is the point: `createSkillBodyLoader` re-derives the catalogue
144
+ * before loading, so `assigned` and `allowTenantSkills` gate loading as well as listing. A policy that
145
+ * filtered the list but not the load would be no policy at all — a model that guessed a name would get it.
146
+ */
147
+ const skillPolicy = {
148
+ assigned: manifest.skillPolicy?.assigned ?? [],
149
+ allowTenantSkills: manifest.skillPolicy?.allowTenantSkills ?? false,
150
+ };
151
+ const skillLoader = config.skills === undefined ? undefined : createSkillBodyLoader({ resolver: config.skills, policy: skillPolicy });
59
152
  const toolRegistry = createToolRegistry({
60
153
  providers: config.tools ?? [],
61
154
  authorization,
62
155
  ...(config.toolSearch === undefined ? {} : { search: config.toolSearch }),
63
156
  ...(config.toolsets === undefined ? {} : { toolsets: config.toolsets }),
64
157
  ...(config.catalogBudget === undefined ? {} : { catalogBudget: config.catalogBudget }),
158
+ ...(skillLoader === undefined ? {} : { skills: skillLoader }),
159
+ ...(config.shadow === undefined ? {} : { shadow: config.shadow }),
65
160
  });
66
161
  const resolveModel = config.resolveModel ??
67
162
  ((m) => {
@@ -70,10 +165,22 @@ export const createAgent = (config) => {
70
165
  model: providerFactory.languageModel(def),
71
166
  modelId: def.modelId,
72
167
  currency: def.pricing.currency,
73
- price: (u) => computeModelCostMinorUnits(def.pricing, { inputTokens: u.inputTokens, outputTokens: u.outputTokens, cachedInputTokens: u.cachedInputTokens }),
168
+ price: (u) => computeModelCostMinorUnits(def.pricing, {
169
+ inputTokens: u.inputTokens,
170
+ outputTokens: u.outputTokens,
171
+ cachedInputTokens: u.cachedInputTokens,
172
+ // Dropped here before #247, so a cache write was billed as fresh input — and on a provider that
173
+ // charges a premium for a write, under-billed.
174
+ ...(u.cacheWriteTokens === undefined ? {} : { cacheWriteTokens: u.cacheWriteTokens }),
175
+ }),
74
176
  };
75
177
  });
76
- const contextProviders = config.contextProviders ?? [];
178
+ const contextProviders = [
179
+ ...(config.contextProviders ?? []),
180
+ ...(config.skills === undefined
181
+ ? []
182
+ : [createSkillCatalogueProvider({ resolver: config.skills, policy: skillPolicy })]),
183
+ ];
77
184
  const engine = config.engine ?? createDefaultEngine({
78
185
  ...(config.guardrails === undefined ? {} : { guardrails: config.guardrails }),
79
186
  ...(config.catalogBudget === undefined ? {} : { catalogBudget: config.catalogBudget }),
@@ -96,7 +203,7 @@ export const createAgent = (config) => {
96
203
  * assembly, so content cannot learn the delimiter from a previous turn.
97
204
  */
98
205
  systemPrompt: async (m, context) => {
99
- const sections = await gatherSections(context, contextProviders);
206
+ const sections = await gatherSections(context, selectContextProviders(contextProviders, m));
100
207
  if (sections.length === 0)
101
208
  return m.instructions;
102
209
  const ctxText = renderContextBlock(sections, makeNonce(config.randomHex ?? defaultRandomHex));
@@ -141,6 +248,9 @@ export const createAgent = (config) => {
141
248
  const resolvedTools = descriptors.map((descriptor) => ({
142
249
  name: descriptor.name,
143
250
  description: descriptor.description,
251
+ // Carried through so the engine can honour `toolPolicy.categories` without reaching back into
252
+ // the registry for a descriptor it already handed over — #244.
253
+ category: descriptor.category,
144
254
  inputSchema: descriptor.inputSchema,
145
255
  execute: async (input) => {
146
256
  const result = await toolRegistry.execute(context, { name: descriptor.name, input });
@@ -259,6 +369,9 @@ export const createAgent = (config) => {
259
369
  requestId: asId(`req-${seq}`),
260
370
  conversationId,
261
371
  runId,
372
+ // Only when asked. Defaulting to shadow would make every existing run a shadow run; the dangerous
373
+ // direction — claiming shadow with nowhere to record it — is closed in the registry instead.
374
+ ...(input.shadow === true ? { shadow: true } : {}),
262
375
  };
263
376
  contextByRun.set(runId, context);
264
377
  if (!(await conversations.findById({ tenantId, id: conversationId }))) {
@@ -39,9 +39,22 @@ export const DEFAULT_MODEL_CATALOG = [
39
39
  label: "Claude Sonnet 5",
40
40
  lifecycle: "generally-available",
41
41
  inputModalities: ["text", "image"],
42
- capabilities: { tools: true, structuredOutput: true, reasoning: true, nativeSearch: false },
42
+ capabilities: { tools: true, structuredOutput: true, reasoning: true, nativeSearch: false, promptCaching: "explicit" },
43
43
  limits: { contextTokens: 200_000, maxOutputTokens: 8_192 },
44
- pricing: { currency: "USD", inputPerMillion: 3_000, outputPerMillion: 15_000 },
44
+ /**
45
+ * Cache rates, added by #247.
46
+ *
47
+ * Anthropic's published ratios: a cache **read** is 0.1× a fresh input token and a cache **write** is
48
+ * **1.25×** — a write costs *more*. `cacheWritePerMillion` was in `ModelPricing` and read by nothing, so a
49
+ * cache write was billed as ordinary input and the first turn of every conversation was under-billed.
50
+ */
51
+ pricing: {
52
+ currency: "USD",
53
+ inputPerMillion: 3_000,
54
+ outputPerMillion: 15_000,
55
+ cacheReadPerMillion: 300,
56
+ cacheWritePerMillion: 3_750,
57
+ },
45
58
  dataResidency: ["us"],
46
59
  },
47
60
  {
@@ -50,9 +63,16 @@ export const DEFAULT_MODEL_CATALOG = [
50
63
  label: "Claude Haiku 4.5",
51
64
  lifecycle: "generally-available",
52
65
  inputModalities: ["text", "image"],
53
- capabilities: { tools: true, structuredOutput: true, reasoning: false, nativeSearch: false },
66
+ capabilities: { tools: true, structuredOutput: true, reasoning: false, nativeSearch: false, promptCaching: "explicit" },
54
67
  limits: { contextTokens: 200_000, maxOutputTokens: 8_192 },
55
- pricing: { currency: "USD", inputPerMillion: 800, outputPerMillion: 4_000 },
68
+ pricing: {
69
+ currency: "USD",
70
+ inputPerMillion: 800,
71
+ outputPerMillion: 4_000,
72
+ // Same ratios as Sonnet above: read 0.1×, write 1.25×.
73
+ cacheReadPerMillion: 80,
74
+ cacheWritePerMillion: 1_000,
75
+ },
56
76
  dataResidency: ["us"],
57
77
  },
58
78
  ];
@@ -18,6 +18,7 @@
18
18
  * on a refusal the model cannot resolve.
19
19
  */
20
20
  import type { ExecutionContext } from "../core/context.js";
21
+ import { type ConnectionNeed } from "../connections/pause.js";
21
22
  import type { RunId, TenantId } from "../core/ids.js";
22
23
  import type { ModelDefinition, ModelTurnRequest, ModelTurnTool, NeutralStreamChunk, NeutralUsage, ResolvedModel, TurnMessage } from "../models/index.js";
23
24
  import { type Guardrail } from "../guardrails/index.js";
@@ -121,6 +122,25 @@ export type DefaultEngineDeps = {
121
122
  readonly now?: () => number;
122
123
  /** The streaming primitive. Defaults to the models-layer `streamModelTurn`; overridden in tests. */
123
124
  readonly streamTurn?: (req: ModelTurnRequest) => AsyncIterable<NeutralStreamChunk>;
125
+ /**
126
+ * Turns "this run needs a connection" into somewhere to send a person — task #264.
127
+ *
128
+ * **Optional, and its absence means today's behaviour**: the tool call fails and the run fails with it. That
129
+ * is the correct default, not a degraded one — a host with no OAuth flow wired has no login URL to offer, and
130
+ * pausing a run for a consent screen nobody can reach would hang it for ever.
131
+ *
132
+ * Returning `null` says the same thing for one provider: this one is token-only, so fail rather than pause.
133
+ * That decision comes from `ToolkitAuth.modes`, which is why #260 kept `AuthMode` separate from
134
+ * `CredentialScheme` — an OAuth token is presented as a bearer, so the wire format cannot answer it.
135
+ */
136
+ readonly connectionConsent?: (input: {
137
+ readonly context: ExecutionContext;
138
+ readonly need: ConnectionNeed;
139
+ }) => Promise<{
140
+ readonly loginUrl: string;
141
+ readonly scopes: readonly string[];
142
+ readonly expiresAt: string;
143
+ } | null>;
124
144
  };
125
145
  export declare const createDefaultEngine: (deps: DefaultEngineDeps) => AgentEngine;
126
146
  //# sourceMappingURL=engine.d.ts.map