@kindgi/agents 0.1.3 → 0.1.4-rc.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 (119) hide show
  1. package/dist/blocks.d.ts +101 -0
  2. package/dist/blocks.d.ts.map +1 -0
  3. package/dist/blocks.js +149 -0
  4. package/dist/blocks.js.map +1 -0
  5. package/dist/define.d.ts +17 -2
  6. package/dist/define.d.ts.map +1 -1
  7. package/dist/define.js +73 -6
  8. package/dist/define.js.map +1 -1
  9. package/dist/handlers/compose-result.d.ts.map +1 -1
  10. package/dist/handlers/compose-result.js +3 -0
  11. package/dist/handlers/compose-result.js.map +1 -1
  12. package/dist/handlers/context.d.ts +10 -0
  13. package/dist/handlers/context.d.ts.map +1 -1
  14. package/dist/handlers/dispatch-tools.d.ts.map +1 -1
  15. package/dist/handlers/dispatch-tools.js +51 -1
  16. package/dist/handlers/dispatch-tools.js.map +1 -1
  17. package/dist/handlers/errors.d.ts +12 -1
  18. package/dist/handlers/errors.d.ts.map +1 -1
  19. package/dist/handlers/errors.js.map +1 -1
  20. package/dist/handlers/evaluate-guardrails.d.ts.map +1 -1
  21. package/dist/handlers/evaluate-guardrails.js +2 -0
  22. package/dist/handlers/evaluate-guardrails.js.map +1 -1
  23. package/dist/handlers/model-call.d.ts.map +1 -1
  24. package/dist/handlers/model-call.js +11 -0
  25. package/dist/handlers/model-call.js.map +1 -1
  26. package/dist/handlers/public-types.d.ts +21 -1
  27. package/dist/handlers/public-types.d.ts.map +1 -1
  28. package/dist/handlers/rehydrate.d.ts.map +1 -1
  29. package/dist/handlers/rehydrate.js +6 -1
  30. package/dist/handlers/rehydrate.js.map +1 -1
  31. package/dist/handlers/render-prompt.d.ts.map +1 -1
  32. package/dist/handlers/render-prompt.js +4 -1
  33. package/dist/handlers/render-prompt.js.map +1 -1
  34. package/dist/handlers/replay.d.ts +143 -0
  35. package/dist/handlers/replay.d.ts.map +1 -0
  36. package/dist/handlers/replay.js +177 -0
  37. package/dist/handlers/replay.js.map +1 -0
  38. package/dist/handlers/resolve-blocks.d.ts +32 -0
  39. package/dist/handlers/resolve-blocks.d.ts.map +1 -0
  40. package/dist/handlers/resolve-blocks.js +129 -0
  41. package/dist/handlers/resolve-blocks.js.map +1 -0
  42. package/dist/handlers/resolve-tools.d.ts +6 -2
  43. package/dist/handlers/resolve-tools.d.ts.map +1 -1
  44. package/dist/handlers/resolve-tools.js +59 -27
  45. package/dist/handlers/resolve-tools.js.map +1 -1
  46. package/dist/handlers/result-shape.d.ts +7 -0
  47. package/dist/handlers/result-shape.d.ts.map +1 -1
  48. package/dist/handlers/result-shape.js.map +1 -1
  49. package/dist/handlers/run-retrievals.d.ts.map +1 -1
  50. package/dist/handlers/run-retrievals.js +34 -17
  51. package/dist/handlers/run-retrievals.js.map +1 -1
  52. package/dist/handlers/run-snapshot.d.ts.map +1 -1
  53. package/dist/handlers/run-snapshot.js +1 -0
  54. package/dist/handlers/run-snapshot.js.map +1 -1
  55. package/dist/handlers/setup.d.ts +2 -0
  56. package/dist/handlers/setup.d.ts.map +1 -1
  57. package/dist/handlers/setup.js +14 -2
  58. package/dist/handlers/setup.js.map +1 -1
  59. package/dist/handlers/turn-environment.d.ts +13 -2
  60. package/dist/handlers/turn-environment.d.ts.map +1 -1
  61. package/dist/handlers/turn-environment.js +12 -3
  62. package/dist/handlers/turn-environment.js.map +1 -1
  63. package/dist/index.d.ts +8 -1
  64. package/dist/index.d.ts.map +1 -1
  65. package/dist/index.js +4 -0
  66. package/dist/index.js.map +1 -1
  67. package/dist/invoke.d.ts.map +1 -1
  68. package/dist/invoke.js +3 -0
  69. package/dist/invoke.js.map +1 -1
  70. package/dist/pins.d.ts +54 -0
  71. package/dist/pins.d.ts.map +1 -0
  72. package/dist/pins.js +51 -0
  73. package/dist/pins.js.map +1 -0
  74. package/dist/prompt.d.ts +12 -2
  75. package/dist/prompt.d.ts.map +1 -1
  76. package/dist/prompt.js +41 -4
  77. package/dist/prompt.js.map +1 -1
  78. package/dist/run-snapshot-binding.d.ts +5 -0
  79. package/dist/run-snapshot-binding.d.ts.map +1 -1
  80. package/dist/schema.d.ts +17 -0
  81. package/dist/schema.d.ts.map +1 -1
  82. package/dist/schema.js +5 -0
  83. package/dist/schema.js.map +1 -1
  84. package/dist/streaming.d.ts +5 -0
  85. package/dist/streaming.d.ts.map +1 -1
  86. package/dist/streaming.js.map +1 -1
  87. package/dist/types.d.ts +50 -1
  88. package/dist/types.d.ts.map +1 -1
  89. package/migrations/0004_stormy_moondragon.sql +1 -0
  90. package/migrations/meta/0004_snapshot.json +327 -0
  91. package/migrations/meta/_journal.json +7 -0
  92. package/package.json +15 -15
  93. package/src/blocks.ts +231 -0
  94. package/src/define.ts +84 -7
  95. package/src/handlers/compose-result.ts +3 -0
  96. package/src/handlers/context.ts +10 -0
  97. package/src/handlers/dispatch-tools.ts +54 -1
  98. package/src/handlers/errors.ts +13 -0
  99. package/src/handlers/evaluate-guardrails.ts +2 -0
  100. package/src/handlers/model-call.ts +14 -0
  101. package/src/handlers/public-types.ts +22 -1
  102. package/src/handlers/rehydrate.ts +21 -6
  103. package/src/handlers/render-prompt.ts +13 -7
  104. package/src/handlers/replay.ts +314 -0
  105. package/src/handlers/resolve-blocks.ts +188 -0
  106. package/src/handlers/resolve-tools.ts +76 -28
  107. package/src/handlers/result-shape.ts +8 -0
  108. package/src/handlers/run-retrievals.ts +44 -23
  109. package/src/handlers/run-snapshot.ts +1 -0
  110. package/src/handlers/setup.ts +13 -2
  111. package/src/handlers/turn-environment.ts +28 -3
  112. package/src/index.ts +37 -0
  113. package/src/invoke.ts +3 -0
  114. package/src/pins.ts +98 -0
  115. package/src/prompt.ts +52 -4
  116. package/src/run-snapshot-binding.ts +6 -0
  117. package/src/schema.ts +5 -0
  118. package/src/streaming.ts +5 -0
  119. package/src/types.ts +53 -1
@@ -0,0 +1,327 @@
1
+ {
2
+ "id": "cfefefb3-52e4-4b8b-b5ec-18d54c0263a0",
3
+ "prevId": "39c9de18-7608-4826-84f7-af1dda0ba078",
4
+ "version": "7",
5
+ "dialect": "postgresql",
6
+ "tables": {
7
+ "public.agent_conversations": {
8
+ "name": "agent_conversations",
9
+ "schema": "",
10
+ "columns": {
11
+ "id": {
12
+ "name": "id",
13
+ "type": "uuid",
14
+ "primaryKey": true,
15
+ "notNull": true,
16
+ "default": "gen_random_uuid()"
17
+ },
18
+ "tenant_id": {
19
+ "name": "tenant_id",
20
+ "type": "uuid",
21
+ "primaryKey": false,
22
+ "notNull": true
23
+ },
24
+ "agent_id": {
25
+ "name": "agent_id",
26
+ "type": "text",
27
+ "primaryKey": false,
28
+ "notNull": true
29
+ },
30
+ "agent_version": {
31
+ "name": "agent_version",
32
+ "type": "text",
33
+ "primaryKey": false,
34
+ "notNull": true
35
+ },
36
+ "title": {
37
+ "name": "title",
38
+ "type": "text",
39
+ "primaryKey": false,
40
+ "notNull": true
41
+ },
42
+ "participant_id": {
43
+ "name": "participant_id",
44
+ "type": "text",
45
+ "primaryKey": false,
46
+ "notNull": false
47
+ },
48
+ "project_id": {
49
+ "name": "project_id",
50
+ "type": "uuid",
51
+ "primaryKey": false,
52
+ "notNull": false
53
+ },
54
+ "scope": {
55
+ "name": "scope",
56
+ "type": "jsonb",
57
+ "primaryKey": false,
58
+ "notNull": true
59
+ },
60
+ "opened_at": {
61
+ "name": "opened_at",
62
+ "type": "timestamp with time zone",
63
+ "primaryKey": false,
64
+ "notNull": true,
65
+ "default": "now()"
66
+ },
67
+ "closed_at": {
68
+ "name": "closed_at",
69
+ "type": "timestamp with time zone",
70
+ "primaryKey": false,
71
+ "notNull": false
72
+ },
73
+ "turn_count": {
74
+ "name": "turn_count",
75
+ "type": "integer",
76
+ "primaryKey": false,
77
+ "notNull": true,
78
+ "default": 0
79
+ },
80
+ "last_message_at": {
81
+ "name": "last_message_at",
82
+ "type": "timestamp with time zone",
83
+ "primaryKey": false,
84
+ "notNull": false
85
+ },
86
+ "metadata": {
87
+ "name": "metadata",
88
+ "type": "jsonb",
89
+ "primaryKey": false,
90
+ "notNull": false
91
+ }
92
+ },
93
+ "indexes": {
94
+ "agent_conversations_tenant_agent_idx": {
95
+ "name": "agent_conversations_tenant_agent_idx",
96
+ "columns": [
97
+ {
98
+ "expression": "tenant_id",
99
+ "isExpression": false,
100
+ "asc": true,
101
+ "nulls": "last"
102
+ },
103
+ {
104
+ "expression": "agent_id",
105
+ "isExpression": false,
106
+ "asc": true,
107
+ "nulls": "last"
108
+ }
109
+ ],
110
+ "isUnique": false,
111
+ "concurrently": false,
112
+ "method": "btree",
113
+ "with": {}
114
+ },
115
+ "agent_conversations_tenant_participant_idx": {
116
+ "name": "agent_conversations_tenant_participant_idx",
117
+ "columns": [
118
+ {
119
+ "expression": "tenant_id",
120
+ "isExpression": false,
121
+ "asc": true,
122
+ "nulls": "last"
123
+ },
124
+ {
125
+ "expression": "participant_id",
126
+ "isExpression": false,
127
+ "asc": true,
128
+ "nulls": "last"
129
+ }
130
+ ],
131
+ "isUnique": false,
132
+ "concurrently": false,
133
+ "method": "btree",
134
+ "with": {}
135
+ },
136
+ "agent_conversations_tenant_project_opened_idx": {
137
+ "name": "agent_conversations_tenant_project_opened_idx",
138
+ "columns": [
139
+ {
140
+ "expression": "tenant_id",
141
+ "isExpression": false,
142
+ "asc": true,
143
+ "nulls": "last"
144
+ },
145
+ {
146
+ "expression": "project_id",
147
+ "isExpression": false,
148
+ "asc": true,
149
+ "nulls": "last"
150
+ },
151
+ {
152
+ "expression": "opened_at",
153
+ "isExpression": false,
154
+ "asc": false,
155
+ "nulls": "last"
156
+ }
157
+ ],
158
+ "isUnique": false,
159
+ "where": "\"agent_conversations\".\"project_id\" IS NOT NULL",
160
+ "concurrently": false,
161
+ "method": "btree",
162
+ "with": {}
163
+ },
164
+ "agent_conversations_open_recent_idx": {
165
+ "name": "agent_conversations_open_recent_idx",
166
+ "columns": [
167
+ {
168
+ "expression": "tenant_id",
169
+ "isExpression": false,
170
+ "asc": true,
171
+ "nulls": "last"
172
+ },
173
+ {
174
+ "expression": "last_message_at",
175
+ "isExpression": false,
176
+ "asc": false,
177
+ "nulls": "last"
178
+ }
179
+ ],
180
+ "isUnique": false,
181
+ "where": "\"agent_conversations\".\"closed_at\" IS NULL",
182
+ "concurrently": false,
183
+ "method": "btree",
184
+ "with": {}
185
+ }
186
+ },
187
+ "foreignKeys": {},
188
+ "compositePrimaryKeys": {},
189
+ "uniqueConstraints": {},
190
+ "policies": {},
191
+ "checkConstraints": {},
192
+ "isRLSEnabled": false
193
+ },
194
+ "public.agent_run_snapshots": {
195
+ "name": "agent_run_snapshots",
196
+ "schema": "",
197
+ "columns": {
198
+ "id": {
199
+ "name": "id",
200
+ "type": "uuid",
201
+ "primaryKey": true,
202
+ "notNull": true
203
+ },
204
+ "tenant_id": {
205
+ "name": "tenant_id",
206
+ "type": "uuid",
207
+ "primaryKey": false,
208
+ "notNull": true
209
+ },
210
+ "project_id": {
211
+ "name": "project_id",
212
+ "type": "uuid",
213
+ "primaryKey": false,
214
+ "notNull": true
215
+ },
216
+ "agent_id": {
217
+ "name": "agent_id",
218
+ "type": "text",
219
+ "primaryKey": false,
220
+ "notNull": true
221
+ },
222
+ "agent_version": {
223
+ "name": "agent_version",
224
+ "type": "text",
225
+ "primaryKey": false,
226
+ "notNull": true
227
+ },
228
+ "conversation_id": {
229
+ "name": "conversation_id",
230
+ "type": "uuid",
231
+ "primaryKey": false,
232
+ "notNull": true
233
+ },
234
+ "user_message": {
235
+ "name": "user_message",
236
+ "type": "text",
237
+ "primaryKey": false,
238
+ "notNull": true
239
+ },
240
+ "parameters": {
241
+ "name": "parameters",
242
+ "type": "jsonb",
243
+ "primaryKey": false,
244
+ "notNull": false
245
+ },
246
+ "input": {
247
+ "name": "input",
248
+ "type": "jsonb",
249
+ "primaryKey": false,
250
+ "notNull": false
251
+ },
252
+ "participant_id": {
253
+ "name": "participant_id",
254
+ "type": "text",
255
+ "primaryKey": false,
256
+ "notNull": false
257
+ },
258
+ "dry_run": {
259
+ "name": "dry_run",
260
+ "type": "integer",
261
+ "primaryKey": false,
262
+ "notNull": true,
263
+ "default": 0
264
+ },
265
+ "principal": {
266
+ "name": "principal",
267
+ "type": "jsonb",
268
+ "primaryKey": false,
269
+ "notNull": false
270
+ },
271
+ "authz": {
272
+ "name": "authz",
273
+ "type": "jsonb",
274
+ "primaryKey": false,
275
+ "notNull": false
276
+ },
277
+ "replay": {
278
+ "name": "replay",
279
+ "type": "jsonb",
280
+ "primaryKey": false,
281
+ "notNull": false
282
+ },
283
+ "created_at": {
284
+ "name": "created_at",
285
+ "type": "timestamp with time zone",
286
+ "primaryKey": false,
287
+ "notNull": true,
288
+ "default": "now()"
289
+ }
290
+ },
291
+ "indexes": {
292
+ "agent_run_snapshots_tenant_idx": {
293
+ "name": "agent_run_snapshots_tenant_idx",
294
+ "columns": [
295
+ {
296
+ "expression": "tenant_id",
297
+ "isExpression": false,
298
+ "asc": true,
299
+ "nulls": "last"
300
+ }
301
+ ],
302
+ "isUnique": false,
303
+ "concurrently": false,
304
+ "method": "btree",
305
+ "with": {}
306
+ }
307
+ },
308
+ "foreignKeys": {},
309
+ "compositePrimaryKeys": {},
310
+ "uniqueConstraints": {},
311
+ "policies": {},
312
+ "checkConstraints": {},
313
+ "isRLSEnabled": false
314
+ }
315
+ },
316
+ "enums": {},
317
+ "schemas": {},
318
+ "sequences": {},
319
+ "roles": {},
320
+ "policies": {},
321
+ "views": {},
322
+ "_meta": {
323
+ "columns": {},
324
+ "schemas": {},
325
+ "tables": {}
326
+ }
327
+ }
@@ -29,6 +29,13 @@
29
29
  "when": 1791208016784,
30
30
  "tag": "0003_hesitant_captain_cross",
31
31
  "breakpoints": true
32
+ },
33
+ {
34
+ "idx": 4,
35
+ "version": "7",
36
+ "when": 1791247643606,
37
+ "tag": "0004_stormy_moondragon",
38
+ "breakpoints": true
32
39
  }
33
40
  ]
34
41
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kindgi/agents",
3
- "version": "0.1.3",
3
+ "version": "0.1.4-rc.0",
4
4
  "description": "Agent primitive for Kindgi. Composes capabilities (model routing), tools (execution), memory (facts + retrieval), guardrails (declarative guardrails), and provenance into a single declarative unit. Agents are data-as-code — a defineAgent() call produces a durable definition that runs on the kernel, with first-class multi-turn conversations, streaming output, and a provenance record per turn.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -31,20 +31,20 @@
31
31
  "type-manifest.json"
32
32
  ],
33
33
  "dependencies": {
34
- "@kindgi/authz": "0.1.3",
35
- "@kindgi/capabilities": "0.1.3",
36
- "@kindgi/compliance": "0.1.3",
37
- "@kindgi/embedding": "0.1.3",
38
- "@kindgi/flow": "0.1.3",
39
- "@kindgi/handler": "0.1.3",
40
- "@kindgi/guardrails": "0.1.3",
41
- "@kindgi/memory": "0.1.3",
42
- "@kindgi/policy-contract": "0.1.3",
43
- "@kindgi/provenance": "0.1.3",
44
- "@kindgi/runtime": "0.1.3",
45
- "@kindgi/schema": "0.1.3",
46
- "@kindgi/tools": "0.1.3",
47
- "@kindgi/types": "0.1.3",
34
+ "@kindgi/authz": "0.1.4-rc.0",
35
+ "@kindgi/capabilities": "0.1.4-rc.0",
36
+ "@kindgi/compliance": "0.1.4-rc.0",
37
+ "@kindgi/embedding": "0.1.4-rc.0",
38
+ "@kindgi/flow": "0.1.4-rc.0",
39
+ "@kindgi/handler": "0.1.4-rc.0",
40
+ "@kindgi/guardrails": "0.1.4-rc.0",
41
+ "@kindgi/memory": "0.1.4-rc.0",
42
+ "@kindgi/policy-contract": "0.1.4-rc.0",
43
+ "@kindgi/provenance": "0.1.4-rc.0",
44
+ "@kindgi/runtime": "0.1.4-rc.0",
45
+ "@kindgi/schema": "0.1.4-rc.0",
46
+ "@kindgi/tools": "0.1.4-rc.0",
47
+ "@kindgi/types": "0.1.4-rc.0",
48
48
  "drizzle-orm": "^0.45.3",
49
49
  "liquidjs": "^10.29.0"
50
50
  },
package/src/blocks.ts ADDED
@@ -0,0 +1,231 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ // Copyright (C) 2026 Kindgi Inc.
3
+
4
+ import { Liquid } from 'liquidjs';
5
+
6
+ import { compileInlineSchema } from '@kindgi/schema';
7
+ import type { Result, TenantId } from '@kindgi/types';
8
+
9
+ import { validatePromptParameters } from './define.js';
10
+ import type { PromptParameter } from './types.js';
11
+
12
+ /**
13
+ * Data blocks: versioned content an agent version pins and an expert
14
+ * can edit without code: a prompt, or settings.
15
+ *
16
+ * A block is published like a tool (head plus immutable versions,
17
+ * unregister and reinstate), into a project. An agent references one by
18
+ * range, and the version it runs is pinned when the agent version is
19
+ * published, so an edit reaches an agent only through a new agent
20
+ * version, which is what gets compared, tested and promoted.
21
+ */
22
+ export const BLOCK_KINDS = ['prompt', 'settings'] as const;
23
+ export type BlockKind = (typeof BLOCK_KINDS)[number];
24
+
25
+ /**
26
+ * A prompt: a Liquid template rendered as an agent's instructions are,
27
+ * with the same declared parameters and auto-injected variables.
28
+ */
29
+ export interface PromptBlockContent {
30
+ readonly template: string;
31
+ readonly parameters?: readonly PromptParameter[];
32
+ }
33
+
34
+ /**
35
+ * Settings: a JSON object a tool reads (`ToolContext.settings`) and a
36
+ * prompt template reads (`settings.<block id>.<key>`). With `schema`,
37
+ * `values` must satisfy it, and so must a later version's.
38
+ */
39
+ export interface SettingsBlockContent {
40
+ readonly values: Readonly<Record<string, unknown>>;
41
+ /** JSON Schema (draft 2020-12) the values must satisfy. */
42
+ readonly schema?: Readonly<Record<string, unknown>>;
43
+ }
44
+
45
+ interface BlockBase {
46
+ /** Dotted id, like an agent's (`acme.intake-prompt`). */
47
+ readonly id: string;
48
+ /** Plain `major.minor.patch`. */
49
+ readonly version: string;
50
+ readonly description?: string;
51
+ }
52
+
53
+ /** A block version as it's published. */
54
+ export type BlockDefinition =
55
+ | (BlockBase & { readonly kind: 'prompt'; readonly content: PromptBlockContent })
56
+ | (BlockBase & { readonly kind: 'settings'; readonly content: SettingsBlockContent });
57
+
58
+ export interface BlockIssue {
59
+ readonly path: string;
60
+ readonly message: string;
61
+ }
62
+
63
+ export interface InvalidBlock {
64
+ readonly code: 'invalid-block';
65
+ readonly message: string;
66
+ readonly issues: readonly BlockIssue[];
67
+ }
68
+
69
+ const ID = /^[a-z0-9][a-z0-9-]*(?:\.[a-z0-9][a-z0-9-]*)+$/;
70
+ const VERSION = /^\d+\.\d+\.\d+$/;
71
+
72
+ /**
73
+ * Validate a block version's definition: its id, plain semver version,
74
+ * kind, and content for that kind. A prompt's template must parse as
75
+ * Liquid and its parameters follow an agent's rules. Settings values
76
+ * must be an object, satisfying `schema` when it's set (and `schema`
77
+ * must compile).
78
+ */
79
+ export function validateBlock(input: unknown): Result<BlockDefinition, InvalidBlock> {
80
+ const issues: BlockIssue[] = [];
81
+ if (input === null || typeof input !== 'object' || Array.isArray(input)) {
82
+ return invalid([{ path: '', message: 'a block must be an object' }]);
83
+ }
84
+ const b = input as Record<string, unknown>;
85
+ if (typeof b.id !== 'string' || !ID.test(b.id)) {
86
+ issues.push({
87
+ path: '/id',
88
+ message: 'id must be a dotted lowercase id, e.g. "acme.intake-prompt"',
89
+ });
90
+ }
91
+ if (typeof b.version !== 'string' || !VERSION.test(b.version)) {
92
+ issues.push({ path: '/version', message: 'version must be major.minor.patch, e.g. "1.0.0"' });
93
+ }
94
+ if (b.description !== undefined && typeof b.description !== 'string') {
95
+ issues.push({ path: '/description', message: 'description must be a string' });
96
+ }
97
+ if (b.kind === 'prompt') issues.push(...promptIssues(b.content));
98
+ else if (b.kind === 'settings') issues.push(...settingsIssues(b.content));
99
+ else issues.push({ path: '/kind', message: `kind must be one of: ${BLOCK_KINDS.join(', ')}` });
100
+ if (issues.length > 0) return invalid(issues);
101
+
102
+ const { id, version, kind, content, description } = b as unknown as BlockDefinition;
103
+ return {
104
+ kind: 'ok',
105
+ value: {
106
+ id,
107
+ version,
108
+ kind,
109
+ content,
110
+ ...(description !== undefined && { description }),
111
+ } as BlockDefinition,
112
+ };
113
+ }
114
+
115
+ /**
116
+ * Whether settings `values` satisfy a JSON `schema`: the problems, none
117
+ * when they do. A later version's values are checked against the
118
+ * latest version's schema too, so a tool reading them sees a stable
119
+ * shape.
120
+ */
121
+ export function settingsSchemaIssues(
122
+ values: unknown,
123
+ schema: Readonly<Record<string, unknown>>,
124
+ path = '/content/values',
125
+ ): BlockIssue[] {
126
+ const compiled = compileInlineSchema(schema);
127
+ if (compiled.kind === 'err') {
128
+ return [{ path: '/content/schema', message: compiled.error.message }];
129
+ }
130
+ const checked = compiled.value.validate(values);
131
+ if (checked.kind === 'ok') return [];
132
+ return checked.error.errors.map((e) => {
133
+ const err = e as { readonly instancePath?: string; readonly message?: string };
134
+ return { path: `${path}${err.instancePath ?? ''}`, message: err.message ?? 'invalid' };
135
+ });
136
+ }
137
+
138
+ function promptIssues(content: unknown): BlockIssue[] {
139
+ if (content === null || typeof content !== 'object') {
140
+ return [
141
+ { path: '/content', message: 'a prompt block needs content: { template, parameters? }' },
142
+ ];
143
+ }
144
+ const c = content as Record<string, unknown>;
145
+ if (typeof c.template !== 'string' || c.template.trim().length === 0) {
146
+ return [{ path: '/content/template', message: 'template must be a non-empty string' }];
147
+ }
148
+ const issues: BlockIssue[] = [];
149
+ try {
150
+ new Liquid().parse(c.template);
151
+ } catch (cause) {
152
+ issues.push({
153
+ path: '/content/template',
154
+ message: `template isn't valid Liquid: ${cause instanceof Error ? cause.message : String(cause)}`,
155
+ });
156
+ }
157
+ if (c.parameters !== undefined && !Array.isArray(c.parameters)) {
158
+ issues.push({ path: '/content/parameters', message: 'parameters must be an array' });
159
+ } else {
160
+ issues.push(
161
+ ...validatePromptParameters(c.parameters as readonly PromptParameter[] | undefined).map(
162
+ (i) => ({ path: `/content${i.path}`, message: i.message }),
163
+ ),
164
+ );
165
+ }
166
+ return issues;
167
+ }
168
+
169
+ function settingsIssues(content: unknown): BlockIssue[] {
170
+ if (content === null || typeof content !== 'object') {
171
+ return [{ path: '/content', message: 'a settings block needs content: { values, schema? }' }];
172
+ }
173
+ const c = content as Record<string, unknown>;
174
+ if (c.values === null || typeof c.values !== 'object' || Array.isArray(c.values)) {
175
+ return [{ path: '/content/values', message: 'values must be a JSON object' }];
176
+ }
177
+ if (c.schema === undefined) return [];
178
+ if (c.schema === null || typeof c.schema !== 'object' || Array.isArray(c.schema)) {
179
+ return [{ path: '/content/schema', message: 'schema must be a JSON Schema object' }];
180
+ }
181
+ return settingsSchemaIssues(c.values, c.schema as Readonly<Record<string, unknown>>);
182
+ }
183
+
184
+ function invalid(issues: readonly BlockIssue[]): Result<never, InvalidBlock> {
185
+ return {
186
+ kind: 'err',
187
+ error: {
188
+ code: 'invalid-block',
189
+ message: `The block is invalid (${issues.length} issue${issues.length === 1 ? '' : 's'})`,
190
+ issues,
191
+ },
192
+ };
193
+ }
194
+
195
+ /**
196
+ * What a model-settings block's values may set: the model-call knobs a
197
+ * turn passes on. Checked when an agent version that references the
198
+ * block (`Agent.modelSettings`) is published.
199
+ */
200
+ export const MODEL_SETTINGS_SCHEMA: Readonly<Record<string, unknown>> = {
201
+ type: 'object',
202
+ additionalProperties: false,
203
+ properties: {
204
+ temperature: { type: 'number', minimum: 0, maximum: 2 },
205
+ maxOutputTokens: { type: 'integer', minimum: 1 },
206
+ },
207
+ };
208
+
209
+ /** The model-call knobs of a model-settings block. */
210
+ export interface ModelSettings {
211
+ readonly temperature?: number;
212
+ readonly maxOutputTokens?: number;
213
+ }
214
+
215
+ /**
216
+ * Reads data blocks for an agent turn: an exact version (an unregistered
217
+ * one included, for the agent versions that pin it), and a block's
218
+ * active versions (to resolve a range for an agent version with no
219
+ * pins). The runtime supplies it over its block registry.
220
+ */
221
+ export interface BlockReader {
222
+ getVersion(input: {
223
+ readonly tenantId: TenantId;
224
+ readonly blockId: string;
225
+ readonly version: string;
226
+ }): Promise<BlockDefinition | null>;
227
+ activeVersions(input: {
228
+ readonly tenantId: TenantId;
229
+ readonly blockId: string;
230
+ }): Promise<readonly string[]>;
231
+ }