@kindgi/agents 0.0.0-bootstrap.0 → 0.1.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 (240) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +131 -2
  3. package/dist/agent-turn-flow.d.ts +57 -0
  4. package/dist/agent-turn-flow.d.ts.map +1 -0
  5. package/dist/agent-turn-flow.js +172 -0
  6. package/dist/agent-turn-flow.js.map +1 -0
  7. package/dist/conversation-binding.d.ts +111 -0
  8. package/dist/conversation-binding.d.ts.map +1 -0
  9. package/dist/conversation-binding.js +4 -0
  10. package/dist/conversation-binding.js.map +1 -0
  11. package/dist/define.d.ts +180 -0
  12. package/dist/define.d.ts.map +1 -0
  13. package/dist/define.js +361 -0
  14. package/dist/define.js.map +1 -0
  15. package/dist/errors.d.ts +62 -0
  16. package/dist/errors.d.ts.map +1 -0
  17. package/dist/errors.js +4 -0
  18. package/dist/errors.js.map +1 -0
  19. package/dist/guardrails-gate.d.ts +169 -0
  20. package/dist/guardrails-gate.d.ts.map +1 -0
  21. package/dist/guardrails-gate.js +202 -0
  22. package/dist/guardrails-gate.js.map +1 -0
  23. package/dist/handlers/budget-check.d.ts +22 -0
  24. package/dist/handlers/budget-check.d.ts.map +1 -0
  25. package/dist/handlers/budget-check.js +109 -0
  26. package/dist/handlers/budget-check.js.map +1 -0
  27. package/dist/handlers/build-initial-messages.d.ts +17 -0
  28. package/dist/handlers/build-initial-messages.d.ts.map +1 -0
  29. package/dist/handlers/build-initial-messages.js +86 -0
  30. package/dist/handlers/build-initial-messages.js.map +1 -0
  31. package/dist/handlers/compose-result.d.ts +10 -0
  32. package/dist/handlers/compose-result.d.ts.map +1 -0
  33. package/dist/handlers/compose-result.js +79 -0
  34. package/dist/handlers/compose-result.js.map +1 -0
  35. package/dist/handlers/constants.d.ts +8 -0
  36. package/dist/handlers/constants.d.ts.map +1 -0
  37. package/dist/handlers/constants.js +10 -0
  38. package/dist/handlers/constants.js.map +1 -0
  39. package/dist/handlers/context.d.ts +210 -0
  40. package/dist/handlers/context.d.ts.map +1 -0
  41. package/dist/handlers/context.js +4 -0
  42. package/dist/handlers/context.js.map +1 -0
  43. package/dist/handlers/dispatch-tools.d.ts +15 -0
  44. package/dist/handlers/dispatch-tools.d.ts.map +1 -0
  45. package/dist/handlers/dispatch-tools.js +511 -0
  46. package/dist/handlers/dispatch-tools.js.map +1 -0
  47. package/dist/handlers/errors.d.ts +133 -0
  48. package/dist/handlers/errors.d.ts.map +1 -0
  49. package/dist/handlers/errors.js +134 -0
  50. package/dist/handlers/errors.js.map +1 -0
  51. package/dist/handlers/evaluate-guardrails.d.ts +14 -0
  52. package/dist/handlers/evaluate-guardrails.d.ts.map +1 -0
  53. package/dist/handlers/evaluate-guardrails.js +128 -0
  54. package/dist/handlers/evaluate-guardrails.js.map +1 -0
  55. package/dist/handlers/final-iteration.d.ts +7 -0
  56. package/dist/handlers/final-iteration.d.ts.map +1 -0
  57. package/dist/handlers/final-iteration.js +26 -0
  58. package/dist/handlers/final-iteration.js.map +1 -0
  59. package/dist/handlers/index.d.ts +5 -0
  60. package/dist/handlers/index.d.ts.map +1 -0
  61. package/dist/handlers/index.js +41 -0
  62. package/dist/handlers/index.js.map +1 -0
  63. package/dist/handlers/model-call.d.ts +13 -0
  64. package/dist/handlers/model-call.d.ts.map +1 -0
  65. package/dist/handlers/model-call.js +136 -0
  66. package/dist/handlers/model-call.js.map +1 -0
  67. package/dist/handlers/persist-final-message.d.ts +14 -0
  68. package/dist/handlers/persist-final-message.d.ts.map +1 -0
  69. package/dist/handlers/persist-final-message.js +54 -0
  70. package/dist/handlers/persist-final-message.js.map +1 -0
  71. package/dist/handlers/persist-provenance.d.ts +12 -0
  72. package/dist/handlers/persist-provenance.d.ts.map +1 -0
  73. package/dist/handlers/persist-provenance.js +34 -0
  74. package/dist/handlers/persist-provenance.js.map +1 -0
  75. package/dist/handlers/persist-user-message.d.ts +15 -0
  76. package/dist/handlers/persist-user-message.d.ts.map +1 -0
  77. package/dist/handlers/persist-user-message.js +59 -0
  78. package/dist/handlers/persist-user-message.js.map +1 -0
  79. package/dist/handlers/public-types.d.ts +201 -0
  80. package/dist/handlers/public-types.d.ts.map +1 -0
  81. package/dist/handlers/public-types.js +4 -0
  82. package/dist/handlers/public-types.js.map +1 -0
  83. package/dist/handlers/rehydrate.d.ts +8 -0
  84. package/dist/handlers/rehydrate.d.ts.map +1 -0
  85. package/dist/handlers/rehydrate.js +94 -0
  86. package/dist/handlers/rehydrate.js.map +1 -0
  87. package/dist/handlers/render-prompt.d.ts +12 -0
  88. package/dist/handlers/render-prompt.d.ts.map +1 -0
  89. package/dist/handlers/render-prompt.js +41 -0
  90. package/dist/handlers/render-prompt.js.map +1 -0
  91. package/dist/handlers/resolve-tools.d.ts +10 -0
  92. package/dist/handlers/resolve-tools.d.ts.map +1 -0
  93. package/dist/handlers/resolve-tools.js +55 -0
  94. package/dist/handlers/resolve-tools.js.map +1 -0
  95. package/dist/handlers/result-shape.d.ts +94 -0
  96. package/dist/handlers/result-shape.d.ts.map +1 -0
  97. package/dist/handlers/result-shape.js +19 -0
  98. package/dist/handlers/result-shape.js.map +1 -0
  99. package/dist/handlers/run-retrievals.d.ts +10 -0
  100. package/dist/handlers/run-retrievals.d.ts.map +1 -0
  101. package/dist/handlers/run-retrievals.js +64 -0
  102. package/dist/handlers/run-retrievals.js.map +1 -0
  103. package/dist/handlers/run-snapshot.d.ts +4 -0
  104. package/dist/handlers/run-snapshot.d.ts.map +1 -0
  105. package/dist/handlers/run-snapshot.js +26 -0
  106. package/dist/handlers/run-snapshot.js.map +1 -0
  107. package/dist/handlers/setup.d.ts +25 -0
  108. package/dist/handlers/setup.d.ts.map +1 -0
  109. package/dist/handlers/setup.js +231 -0
  110. package/dist/handlers/setup.js.map +1 -0
  111. package/dist/handlers/structured-output.d.ts +38 -0
  112. package/dist/handlers/structured-output.d.ts.map +1 -0
  113. package/dist/handlers/structured-output.js +89 -0
  114. package/dist/handlers/structured-output.js.map +1 -0
  115. package/dist/handlers/tool-errors.d.ts +56 -0
  116. package/dist/handlers/tool-errors.d.ts.map +1 -0
  117. package/dist/handlers/tool-errors.js +73 -0
  118. package/dist/handlers/tool-errors.js.map +1 -0
  119. package/dist/handlers/tool-hitl.d.ts +45 -0
  120. package/dist/handlers/tool-hitl.d.ts.map +1 -0
  121. package/dist/handlers/tool-hitl.js +81 -0
  122. package/dist/handlers/tool-hitl.js.map +1 -0
  123. package/dist/handlers/turn-environment.d.ts +26 -0
  124. package/dist/handlers/turn-environment.d.ts.map +1 -0
  125. package/dist/handlers/turn-environment.js +154 -0
  126. package/dist/handlers/turn-environment.js.map +1 -0
  127. package/dist/hitl-policy.d.ts +45 -0
  128. package/dist/hitl-policy.d.ts.map +1 -0
  129. package/dist/hitl-policy.js +74 -0
  130. package/dist/hitl-policy.js.map +1 -0
  131. package/dist/index.d.ts +31 -0
  132. package/dist/index.d.ts.map +1 -0
  133. package/dist/index.js +19 -0
  134. package/dist/index.js.map +1 -0
  135. package/dist/invoke.d.ts +36 -0
  136. package/dist/invoke.d.ts.map +1 -0
  137. package/dist/invoke.js +228 -0
  138. package/dist/invoke.js.map +1 -0
  139. package/dist/migrations-dir.d.ts +11 -0
  140. package/dist/migrations-dir.d.ts.map +1 -0
  141. package/dist/migrations-dir.js +14 -0
  142. package/dist/migrations-dir.js.map +1 -0
  143. package/dist/project-run-result.d.ts +23 -0
  144. package/dist/project-run-result.d.ts.map +1 -0
  145. package/dist/project-run-result.js +116 -0
  146. package/dist/project-run-result.js.map +1 -0
  147. package/dist/prompt.d.ts +83 -0
  148. package/dist/prompt.d.ts.map +1 -0
  149. package/dist/prompt.js +119 -0
  150. package/dist/prompt.js.map +1 -0
  151. package/dist/provenance-emit.d.ts +44 -0
  152. package/dist/provenance-emit.d.ts.map +1 -0
  153. package/dist/provenance-emit.js +51 -0
  154. package/dist/provenance-emit.js.map +1 -0
  155. package/dist/registry.d.ts +38 -0
  156. package/dist/registry.d.ts.map +1 -0
  157. package/dist/registry.js +125 -0
  158. package/dist/registry.js.map +1 -0
  159. package/dist/retrieval.d.ts +47 -0
  160. package/dist/retrieval.d.ts.map +1 -0
  161. package/dist/retrieval.js +155 -0
  162. package/dist/retrieval.js.map +1 -0
  163. package/dist/run-snapshot-binding.d.ts +77 -0
  164. package/dist/run-snapshot-binding.d.ts.map +1 -0
  165. package/dist/run-snapshot-binding.js +4 -0
  166. package/dist/run-snapshot-binding.js.map +1 -0
  167. package/dist/schema.d.ts +497 -0
  168. package/dist/schema.d.ts.map +1 -0
  169. package/dist/schema.js +133 -0
  170. package/dist/schema.js.map +1 -0
  171. package/dist/streaming.d.ts +118 -0
  172. package/dist/streaming.d.ts.map +1 -0
  173. package/dist/streaming.js +17 -0
  174. package/dist/streaming.js.map +1 -0
  175. package/dist/tenant-policy.d.ts +16 -0
  176. package/dist/tenant-policy.d.ts.map +1 -0
  177. package/dist/tenant-policy.js +77 -0
  178. package/dist/tenant-policy.js.map +1 -0
  179. package/dist/types.d.ts +435 -0
  180. package/dist/types.d.ts.map +1 -0
  181. package/dist/types.js +4 -0
  182. package/dist/types.js.map +1 -0
  183. package/dist/versioning.d.ts +29 -0
  184. package/dist/versioning.d.ts.map +1 -0
  185. package/dist/versioning.js +58 -0
  186. package/dist/versioning.js.map +1 -0
  187. package/migrations/0000_sparkling_talkback.sql +18 -0
  188. package/migrations/0001_tired_warhawk.sql +16 -0
  189. package/migrations/0002_violet_ezekiel.sql +2 -0
  190. package/migrations/meta/0000_snapshot.json +172 -0
  191. package/migrations/meta/0001_snapshot.json +275 -0
  192. package/migrations/meta/0002_snapshot.json +287 -0
  193. package/migrations/meta/_journal.json +27 -0
  194. package/package.json +76 -4
  195. package/src/agent-turn-flow.ts +183 -0
  196. package/src/conversation-binding.ts +147 -0
  197. package/src/define.ts +572 -0
  198. package/src/errors.ts +80 -0
  199. package/src/guardrails-gate.ts +342 -0
  200. package/src/handlers/budget-check.ts +143 -0
  201. package/src/handlers/build-initial-messages.ts +103 -0
  202. package/src/handlers/compose-result.ts +90 -0
  203. package/src/handlers/constants.ts +10 -0
  204. package/src/handlers/context.ts +226 -0
  205. package/src/handlers/dispatch-tools.ts +633 -0
  206. package/src/handlers/errors.ts +282 -0
  207. package/src/handlers/evaluate-guardrails.ts +153 -0
  208. package/src/handlers/final-iteration.ts +30 -0
  209. package/src/handlers/index.ts +63 -0
  210. package/src/handlers/model-call.ts +151 -0
  211. package/src/handlers/persist-final-message.ts +67 -0
  212. package/src/handlers/persist-provenance.ts +39 -0
  213. package/src/handlers/persist-user-message.ts +70 -0
  214. package/src/handlers/public-types.ts +209 -0
  215. package/src/handlers/rehydrate.ts +161 -0
  216. package/src/handlers/render-prompt.ts +46 -0
  217. package/src/handlers/resolve-tools.ts +68 -0
  218. package/src/handlers/result-shape.ts +113 -0
  219. package/src/handlers/run-retrievals.ts +77 -0
  220. package/src/handlers/run-snapshot.ts +44 -0
  221. package/src/handlers/setup.ts +269 -0
  222. package/src/handlers/structured-output.ts +117 -0
  223. package/src/handlers/tool-errors.ts +122 -0
  224. package/src/handlers/tool-hitl.ts +126 -0
  225. package/src/handlers/turn-environment.ts +191 -0
  226. package/src/hitl-policy.ts +128 -0
  227. package/src/index.ts +154 -0
  228. package/src/invoke.ts +299 -0
  229. package/src/migrations-dir.ts +17 -0
  230. package/src/project-run-result.ts +131 -0
  231. package/src/prompt.ts +185 -0
  232. package/src/provenance-emit.ts +100 -0
  233. package/src/registry.ts +164 -0
  234. package/src/retrieval.ts +219 -0
  235. package/src/run-snapshot-binding.ts +87 -0
  236. package/src/schema.ts +154 -0
  237. package/src/streaming.ts +153 -0
  238. package/src/tenant-policy.ts +78 -0
  239. package/src/types.ts +453 -0
  240. package/src/versioning.ts +77 -0
package/src/types.ts ADDED
@@ -0,0 +1,453 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (C) 2026 Kindgi Inc.
3
+
4
+ import type { Capability } from '@kindgi/capabilities';
5
+ import type { Fact, MemoryScope } from '@kindgi/memory';
6
+ import type { ToolErrorsSpec, ToolHitlMode, ToolHitlRule } from '@kindgi/policy-contract';
7
+ import type { Brand, ConversationId, Semver, TenantId, Timestamp } from '@kindgi/types';
8
+
9
+ /**
10
+ * Branded agent id. Convention: dotted namespace under the tenant's
11
+ * pack — e.g. `acme.citation-verifier`, `acme.drafting`.
12
+ */
13
+ export type AgentId = Brand<string, 'AgentId'>;
14
+
15
+ /**
16
+ * `ConversationId` lives in `@kindgi/types` (the single source of
17
+ * truth for branded identifiers) and is re-exported here for
18
+ * convenience; `@kindgi/types` (or `@kindgi/sdk/types`) is the
19
+ * canonical import.
20
+ */
21
+ export type { ConversationId };
22
+
23
+ /**
24
+ * A single message inside a conversation. Persisted as a `Fact` in
25
+ * the memory subsystem scoped to the conversation, so it inherits
26
+ * retention + provenance + supersession from the fact substrate.
27
+ *
28
+ * The `role` discriminates who authored the message:
29
+ * - `user` — end-user input to the agent.
30
+ * - `agent` — the agent's assistant-role response.
31
+ * - `tool` — a tool-call result (the tool's own output).
32
+ * - `system` — a system-authored instruction (rare; e.g. HITL
33
+ * intervention, out-of-band context injection).
34
+ */
35
+ export type MessageRole = 'user' | 'agent' | 'tool' | 'system';
36
+
37
+ export interface ConversationMessage {
38
+ /**
39
+ * Monotonic index within the conversation. Assigned by the writer.
40
+ * Combined with `conversationId` this gives a stable ordering
41
+ * independent of clock skew.
42
+ */
43
+ readonly sequence: number;
44
+ readonly role: MessageRole;
45
+ /**
46
+ * Free-form text OR structured content. `string` is the common case
47
+ * (a chat message); `Record<string, unknown>` supports tool-result
48
+ * shapes and multi-modal payloads without a v2 bump.
49
+ */
50
+ readonly content: string | Readonly<Record<string, unknown>>;
51
+ /**
52
+ * For `role: 'tool'` messages: the tool id and the invocation id
53
+ * that produced this output. Enables provenance edges from the
54
+ * originating agent turn to the tool's result.
55
+ */
56
+ readonly toolCall?: {
57
+ readonly toolId: string;
58
+ readonly invocationId: string;
59
+ };
60
+ /**
61
+ * Attribution — user id, agent id, tool id, or system source. The
62
+ * literal role-vs-actor split lets the same agent produce many
63
+ * messages while the reviewer sees who was actually authoring.
64
+ */
65
+ readonly actor?: string;
66
+ readonly createdAt: Timestamp;
67
+ }
68
+
69
+ /**
70
+ * A named, typed input to an agent's instructions template. Every
71
+ * `{{ var }}` referenced in `instructions` must correspond to either a
72
+ * declared `PromptParameter` OR a framework-supplied auto-variable
73
+ * (see `AUTO_INJECTED_VARS`).
74
+ *
75
+ * Types are declared so a UI can build a proper form for the caller
76
+ * ("enter firm name", "pick jurisdiction").
77
+ */
78
+ export interface PromptParameter {
79
+ readonly name: string;
80
+ readonly description?: string;
81
+ readonly type: 'string' | 'number' | 'boolean' | 'date';
82
+ /** Default true — omit to require the caller supply a value. */
83
+ readonly required?: boolean;
84
+ /** Default value used when the caller omits this parameter. */
85
+ readonly default?: string | number | boolean;
86
+ }
87
+
88
+ /**
89
+ * Declarative statement of what the agent retrieves before each turn.
90
+ * Distinct from tool invocation — retrieval is background reading the
91
+ * agent does silently to ground its response.
92
+ *
93
+ * Scope options:
94
+ * - `same-conversation`: prior messages in this conversation only.
95
+ * - `same-project`: facts under the project (from Scope.projectId).
96
+ * - `tenant`: any tenant-scoped fact of the declared type.
97
+ */
98
+ export interface RetrievalIntent {
99
+ readonly types: readonly string[];
100
+ readonly scope: 'same-conversation' | 'same-project' | 'tenant';
101
+ /** Cap on facts loaded per turn to keep the prompt small. Default 10. */
102
+ readonly limit?: number;
103
+ /** If `keyword` or `semantic`, biases which retrieval mode is used. */
104
+ readonly mode?: 'keyword' | 'semantic' | 'both';
105
+ }
106
+
107
+ /**
108
+ * Typed reference to a tool. Every agent tool binding is `{ id, version }`
109
+ * — no bare-id "latest" shortcut. `version` is a semver **range**
110
+ * (npm-style grammar), resolved at run start against the tenant's tool
111
+ * registry via `semver.maxSatisfying`.
112
+ *
113
+ * Range examples:
114
+ * - `'1.2.3'` — exact pin (matches only 1.2.3)
115
+ * - `'^1.2.3'` — compatible-updates (>=1.2.3 <2.0.0)
116
+ * - `'~1.2.3'` — patch-updates-only (>=1.2.3 <1.3.0)
117
+ * - `'>=1.0.0 <2.0.0'` — explicit range
118
+ */
119
+ export interface ToolRef {
120
+ readonly id: string;
121
+ readonly version: string;
122
+ }
123
+
124
+ /**
125
+ * Per-agent behavior for multi-turn conversations. The agent chooses:
126
+ * - How many prior messages to load (`historyLimit`; unset = all).
127
+ * - Whether to auto-close a conversation after a period of inactivity
128
+ * (`autoCloseAfterInactiveSeconds` — checked at read time; unset =
129
+ * never auto-close).
130
+ * - A turn count after which each new turn waits for HITL approval
131
+ * before it runs (`hitlAfterTurns`; `hitl.afterTurns` takes
132
+ * precedence when both are set).
133
+ */
134
+ export interface ConversationPolicy {
135
+ readonly historyLimit?: number;
136
+ readonly autoCloseAfterInactiveSeconds?: number;
137
+ readonly hitlAfterTurns?: number;
138
+ /**
139
+ * HITL policy: the session gate (`afterTurns`) and the tool-level
140
+ * gates that decide whether each tool call inside a turn waits for
141
+ * review before dispatch. Absent = no tool gates.
142
+ */
143
+ readonly hitl?: ConversationHitlPolicy;
144
+ }
145
+
146
+ /**
147
+ * Tool-level HITL mode.
148
+ *
149
+ * - `never_ask` — dispatch straight through, no gate. Read-only tools
150
+ * (`Tool.mutating: false`) get this default.
151
+ * - `ask_on_first_use` — on the first call to `(toolId, hashOfArgs)`
152
+ * within a conversation, park + require approval. Approved decisions
153
+ * cache on the conversation so subsequent identical calls dispatch
154
+ * without re-parking. Mutating tools default to this.
155
+ * - `always_ask` — park + require approval on EVERY invocation. No
156
+ * caching. Highest friction; used for external-effect actions
157
+ * (email/payment/deploy) where every occurrence is a real event.
158
+ */
159
+ /**
160
+ * A tool's gate (`never_ask` | `ask_on_first_use` | `always_ask`), and a
161
+ * per-tool rule — a mode plus the reviewer role its approvals need
162
+ * (deployments that route "always-ask-financial-tools" through senior
163
+ * reviewers use the rule; a mode alone routes through the agent's
164
+ * `defaultReviewerRole`). Shared with the tenant `hitl` policy, so they
165
+ * live in `@kindgi/policy-contract`.
166
+ */
167
+ export type { ToolHitlMode, ToolHitlRule } from '@kindgi/policy-contract';
168
+
169
+ export interface ConversationHitlPolicy {
170
+ /**
171
+ * Session-turn count gate: once the conversation has this many
172
+ * completed turns, each new turn waits for HITL approval before it
173
+ * runs. Same meaning as `ConversationPolicy.hitlAfterTurns`; when both
174
+ * are set, `hitl.afterTurns` wins. Absent in both = no session gate.
175
+ */
176
+ readonly afterTurns?: number;
177
+ /**
178
+ * Tool-level policy. Applies to every tool call inside the turn
179
+ * unless the `tools.overrides` map has a specific rule.
180
+ */
181
+ readonly tools?: {
182
+ /**
183
+ * Mode for every tool without an entry in `overrides`. When absent,
184
+ * the framework falls back to a per-tool default: read-only tools
185
+ * (`mutating: false`) → `never_ask`, other tools →
186
+ * `ask_on_first_use`.
187
+ */
188
+ readonly default?: ToolHitlMode;
189
+ /**
190
+ * Explicit overrides keyed by ToolId. A string value is shorthand
191
+ * for `{ mode: <string> }` — the required-role stays the agent
192
+ * default.
193
+ */
194
+ readonly overrides?: Readonly<Record<string, ToolHitlMode | ToolHitlRule>>;
195
+ };
196
+ /**
197
+ * Reviewer role assigned to approvals materialized by tool + session
198
+ * gates. Absent = `'standard'`.
199
+ */
200
+ readonly defaultReviewerRole?: 'standard' | 'senior' | 'admin';
201
+ /**
202
+ * Timeout the approval carries at enqueue time. Absent = the
203
+ * framework default (24h). A tenant policy's `maxTimeoutMs` caps it
204
+ * (see `resolveEffectiveHitlPolicy`).
205
+ */
206
+ readonly timeoutMs?: number;
207
+ }
208
+
209
+ /**
210
+ * Budgets guard a single agent turn. Every field is optional; unset
211
+ * fields take the defaults below or have no cap. Steps and cost are
212
+ * enforced by the turn's `budget-check` step; wall-clock time by a
213
+ * timer in `invokeAgent`.
214
+ */
215
+ export interface TurnBudget {
216
+ /** Maximum model→tool→model iterations per turn. Default 8. */
217
+ readonly maxSteps?: number;
218
+ /** Maximum USD spend per turn, summed over the turn's model calls. */
219
+ readonly maxCostUsd?: number;
220
+ /** Wall-clock cap in ms. Default 120_000 (2 min). */
221
+ readonly maxWallMs?: number;
222
+ }
223
+
224
+ /**
225
+ * A typed result for an agent turn: the agent's final answer must be
226
+ * JSON matching `schema`. When it doesn't parse or doesn't match, the
227
+ * turn tells the model what was wrong and asks again, up to
228
+ * `maxRepairs` times; after that the turn fails with
229
+ * `output-schema-violation`. The parsed value is
230
+ * `AgentTurnResult.output`.
231
+ */
232
+ export interface AgentOutputSpec {
233
+ /** JSON Schema (draft 2020-12) the final answer must match. */
234
+ readonly schema: Readonly<Record<string, unknown>>;
235
+ /** A name for the output (shown to the model and in errors). Default `'output'`. */
236
+ readonly name?: string;
237
+ /** How many times the model is asked to repair an invalid answer. Default 1. */
238
+ readonly maxRepairs?: number;
239
+ }
240
+
241
+ /**
242
+ * An agent definition. Declarative: no runtime state, no closures. The
243
+ * same definition can be serialized, versioned, and reloaded — every
244
+ * field is either a primitive or a reference to a registered
245
+ * substrate object (tool id, guardrail id, capability declaration).
246
+ *
247
+ * The `version` field is authoritative for the agent's identity — an
248
+ * agent at a different version is a different agent for provenance
249
+ * and audit purposes. Callers pin agents by `{ id, version }` the way
250
+ * kernel runs pin flows.
251
+ */
252
+ export interface Agent {
253
+ /**
254
+ * Globally-unique agent identifier. Convention:
255
+ * `<pack-id>.<agent-name>` (kebab-case, dot-namespaced). See the
256
+ * `AgentId` brand for the naming rule.
257
+ */
258
+ readonly id: AgentId;
259
+ /**
260
+ * Semver — REQUIRED. Distinct from tools' `version` in that agents
261
+ * are versioned per-tenant (rotate + rollout independently of tool
262
+ * versions). `agent_conversations.agentVersion` pins each thread to
263
+ * a specific version; resume-across-version bumps rejects with
264
+ * `agent-version-mismatch`.
265
+ */
266
+ readonly version: Semver;
267
+ /**
268
+ * Human-readable name shown in UI. Doesn't affect execution.
269
+ */
270
+ readonly name: string;
271
+ /**
272
+ * Optional prose describing what the agent does. Shows up in the
273
+ * agent catalog and in provenance metadata.
274
+ */
275
+ readonly description?: string;
276
+ /**
277
+ * System prompt rendered at the top of every turn. LiquidJS template
278
+ * syntax: `{{ variable }}` for substitution, `{% if %}` / `{% for %}`
279
+ * for control flow, filters via `{{ value | filter }}`. Every
280
+ * referenced variable must be either a declared `PromptParameter`
281
+ * or a framework-supplied auto-var (see `AUTO_INJECTED_VARS`).
282
+ *
283
+ * Templates are rendered with `strictVariables: true` — an
284
+ * unresolved reference fails the turn at invoke time
285
+ * (`model-invocation-failed` whose `cause` is the `missing-parameter`
286
+ * render error), never a silent empty string.
287
+ */
288
+ readonly instructions: string;
289
+ /**
290
+ * Typed parameters the caller supplies at invoke time. The UI reads
291
+ * this to build a "configure agent" form; the runtime validates each
292
+ * required parameter is provided before the model call.
293
+ *
294
+ * Framework auto-vars (`today`, `now`, `agent.*`, `conversation.*`)
295
+ * do not need to be declared here — they're supplied by the runtime.
296
+ */
297
+ readonly parameters?: readonly PromptParameter[];
298
+ /**
299
+ * Capability declarations the agent needs at runtime. Each entry is
300
+ * a separate resource-kind request (LLM inference, embedding, ...,
301
+ * distinguished by `Capability.kind`). The router picks providers at
302
+ * turn time; the agent definition doesn't hard-bind.
303
+ *
304
+ * The agent turn routes the first entry to pick its model; further
305
+ * entries are part of the definition but are not routed by the turn.
306
+ */
307
+ readonly capabilities: readonly Capability[];
308
+ /**
309
+ * Typed tool references. Each entry declares the tool id AND the
310
+ * semver range (or exact pin) the agent expects to invoke — the
311
+ * dispatch resolver uses `semver.maxSatisfying` to pick the highest
312
+ * active version matching the range at run start. Follows the
313
+ * "docker-tag pin vs latest" discipline npm/docker take: no implicit
314
+ * `:latest`, ever.
315
+ *
316
+ * `version` is a semver **range** in the wire shape:
317
+ * `'1.2.3'` = exact pin, `'^1.2.3'` = compatible-updates, `'~1.2.3'`
318
+ * = patch-updates-only, `'>=1.0.0 <2.0.0'` = explicit range. Full
319
+ * npm-compatible grammar (grammar handled by the `semver` library
320
+ * in the resolver — validation of the range shape lives there).
321
+ *
322
+ * An empty list means the agent is chat-only.
323
+ */
324
+ readonly tools: readonly ToolRef[];
325
+ /**
326
+ * Fact-retrieval intents. Zero or more; each triggers an independent
327
+ * retrieval pass before the model call.
328
+ */
329
+ readonly retrieval: readonly RetrievalIntent[];
330
+ /**
331
+ * Guardrail ids the agent is subject to. Resolved at turn start
332
+ * against the guardrail definitions bound for the run
333
+ * (`GuardrailsBindings.guardrails`, evaluated through its `checks`
334
+ * registry from `@kindgi/guardrails`); an unknown id fails the turn
335
+ * with `unresolved-guardrail`. Evaluated once per turn, on the final
336
+ * response before it is stored.
337
+ */
338
+ readonly guardrails: readonly string[];
339
+ /**
340
+ * Preferred model provider by id. Soft hint — the router prefers
341
+ * this provider when it satisfies the agent's `capabilities.needs`,
342
+ * falling back to normal capability-based selection when the
343
+ * preferred provider is unregistered or filtered out by tenant
344
+ * policy. Enables A/B'ing agents across providers without
345
+ * churning provider registrations: register several, pin the agent
346
+ * to the one you want to test.
347
+ *
348
+ * The value is a `ProviderMetadata.id` string (e.g. `'anthropic'`).
349
+ * Use in combination with `preferredModel` for `(provider, model)`
350
+ * tuple pinning. Unset = capability-match only.
351
+ */
352
+ readonly preferredProvider?: string;
353
+ /**
354
+ * Preferred model NAME within the selected provider. Soft hint —
355
+ * the router prefers `(provider, model)` tuples matching this
356
+ * name, falling back to capability-based ranking when no tuple
357
+ * matches. Combined semantics with `preferredProvider`:
358
+ *
359
+ * - Both set → promote the exact `(provider, model)` tuple.
360
+ * - Only `preferredModel` set → promote any provider exposing
361
+ * that model.
362
+ * - Only `preferredProvider` set → any model of that provider is
363
+ * promoted.
364
+ *
365
+ * The value is a `ModelInfo.name` string (e.g. `'claude-sonnet-4-6'`).
366
+ * Enables model-level A/B'ing under one connection: register
367
+ * Anthropic once with `models: [sonnet, opus, haiku]`, then pin
368
+ * per-agent.
369
+ */
370
+ readonly preferredModel?: string;
371
+ /**
372
+ * Multi-turn behavior. Optional — when unset, each turn loads the
373
+ * full conversation history and no HITL gates apply.
374
+ */
375
+ readonly conversationPolicy?: ConversationPolicy;
376
+ /**
377
+ * Per-turn budget. Enforced by `invokeAgent`. Missing fields default
378
+ * (see `TurnBudget`).
379
+ */
380
+ readonly budget?: TurnBudget;
381
+ /**
382
+ * Free-form tags for filtering in the agent catalog (UI + admin).
383
+ * Not consumed by execution.
384
+ */
385
+ readonly tags?: readonly string[];
386
+ /**
387
+ * A typed result: the final answer is JSON matching this schema,
388
+ * validated (and repaired, see `AgentOutputSpec`) before the turn
389
+ * completes. Absent = the answer is free text.
390
+ */
391
+ readonly output?: AgentOutputSpec;
392
+ /**
393
+ * What the turn does when a tool call fails: the failure goes back to
394
+ * the model as the call's result, so it can correct the call, up to
395
+ * `maxRetries` times per turn, for the kinds in `retryOn`. Default:
396
+ * one retry, for `invalid-arguments` and `unknown-tool` (nothing ran).
397
+ * A tenant's `tool-errors` policy can lower it. See `ToolErrorsSpec`.
398
+ */
399
+ readonly toolErrors?: ToolErrorsSpec;
400
+ }
401
+
402
+ /**
403
+ * A conversation record (the `agent_conversations` table in this
404
+ * package's Postgres schema). Messages are stored separately, as memory
405
+ * facts scoped to this conversation's id.
406
+ */
407
+ export interface Conversation {
408
+ readonly id: ConversationId;
409
+ readonly tenantId: TenantId;
410
+ readonly agentId: AgentId;
411
+ readonly agentVersion: Semver;
412
+ /**
413
+ * Auto-generated on first turn or explicitly set. Shown in the UI
414
+ * conversation list.
415
+ */
416
+ readonly title: string;
417
+ /** UUID or free-form identifier for the human participant. */
418
+ readonly participantId?: string;
419
+ /** Additional attributes (project id, matter id, etc.). */
420
+ readonly scope: MemoryScope;
421
+ readonly openedAt: Timestamp;
422
+ /** Set when the conversation is closed. Reopening is not supported. */
423
+ readonly closedAt?: Timestamp;
424
+ /**
425
+ * Denormalized turn counter — one +1 per completed agent turn.
426
+ * Incremented by the conversation binding when a turn's final
427
+ * (non-intermediate) agent message is appended.
428
+ */
429
+ readonly turnCount: number;
430
+ readonly lastMessageAt?: Timestamp;
431
+ /**
432
+ * Free-form metadata. Persisted via the versioning envelope, so a
433
+ * schema change can be migrated on read.
434
+ */
435
+ readonly metadata?: Readonly<Record<string, unknown>>;
436
+ }
437
+
438
+ /**
439
+ * Optional bindings for agent operations. Not read by `invokeAgent`,
440
+ * which takes `InvokeAgentBindings`.
441
+ */
442
+ export interface AgentBindings {
443
+ /** A retrieval-policy registry from the memory implementation (untyped here). */
444
+ readonly memoryPolicyRegistry?: unknown;
445
+ }
446
+
447
+ /** A retrieved fact + the retrieval intent that pulled it. */
448
+ export interface RetrievedFact {
449
+ readonly fact: Fact<unknown>;
450
+ readonly intent: RetrievalIntent;
451
+ /** Similarity or keyword-rank score, if the retrieval mode produced one. */
452
+ readonly score?: number;
453
+ }
@@ -0,0 +1,77 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (C) 2026 Kindgi Inc.
3
+
4
+ import type { Result } from '@kindgi/types';
5
+
6
+ /**
7
+ * JSONB payload versioning for agents-owned tables. Nested
8
+ * `{ v: 1, doc: <content> }` envelope; `unwrap` accepts only
9
+ * `CURRENT_AGENTS_PAYLOAD_VERSION`.
10
+ */
11
+
12
+ export const CURRENT_AGENTS_PAYLOAD_VERSION = 1;
13
+
14
+ export interface UnsupportedPayloadVersionError {
15
+ readonly code: 'unsupported-payload-version';
16
+ readonly message: string;
17
+ readonly version: number;
18
+ readonly currentVersion: number;
19
+ }
20
+
21
+ export interface MalformedEnvelopeError {
22
+ readonly code: 'malformed-envelope';
23
+ readonly message: string;
24
+ }
25
+
26
+ export type EnvelopeError = UnsupportedPayloadVersionError | MalformedEnvelopeError;
27
+
28
+ export class EnvelopeThrown extends Error {
29
+ readonly error: EnvelopeError;
30
+ constructor(error: EnvelopeError) {
31
+ super(error.message);
32
+ this.error = error;
33
+ this.name = 'EnvelopeThrown';
34
+ }
35
+ }
36
+
37
+ export function wrap<T>(value: T): { readonly v: number; readonly doc: T } {
38
+ return { v: CURRENT_AGENTS_PAYLOAD_VERSION, doc: value };
39
+ }
40
+
41
+ export function unwrap<T = unknown>(raw: unknown): Result<T | null | undefined, EnvelopeError> {
42
+ if (raw === null || raw === undefined) return { kind: 'ok', value: raw };
43
+ if (typeof raw !== 'object' || Array.isArray(raw)) {
44
+ return {
45
+ kind: 'err',
46
+ error: {
47
+ code: 'malformed-envelope',
48
+ message: `Expected { v, doc } envelope, got ${typeof raw === 'object' ? 'array' : typeof raw}`,
49
+ },
50
+ };
51
+ }
52
+ const envelope = raw as { readonly v?: unknown; readonly doc?: unknown };
53
+ if (typeof envelope.v !== 'number' || !('doc' in envelope)) {
54
+ return {
55
+ kind: 'err',
56
+ error: { code: 'malformed-envelope', message: 'Payload is missing v or doc field' },
57
+ };
58
+ }
59
+ if (envelope.v === CURRENT_AGENTS_PAYLOAD_VERSION) {
60
+ return { kind: 'ok', value: envelope.doc as T };
61
+ }
62
+ return {
63
+ kind: 'err',
64
+ error: {
65
+ code: 'unsupported-payload-version',
66
+ message: `Unsupported payload version ${envelope.v} (this reader handles version ${CURRENT_AGENTS_PAYLOAD_VERSION})`,
67
+ version: envelope.v,
68
+ currentVersion: CURRENT_AGENTS_PAYLOAD_VERSION,
69
+ },
70
+ };
71
+ }
72
+
73
+ export function unwrapOrThrow<T = unknown>(raw: unknown): T | null | undefined {
74
+ const r = unwrap<T>(raw);
75
+ if (r.kind === 'err') throw new EnvelopeThrown(r.error);
76
+ return r.value;
77
+ }