specrails-core 6.0.0 → 6.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 (173) hide show
  1. package/README.md +1 -1
  2. package/dist/agent-runtime/capabilities.js +3 -1
  3. package/dist/agent-runtime/change-scope.d.ts +63 -0
  4. package/dist/agent-runtime/change-scope.js +297 -0
  5. package/dist/agent-runtime/cli-executor.d.ts +3 -0
  6. package/dist/agent-runtime/cli-executor.js +50 -13
  7. package/dist/agent-runtime/cli-process.js +6 -2
  8. package/dist/agent-runtime/cli.d.ts +15 -0
  9. package/dist/agent-runtime/cli.js +43 -9
  10. package/dist/agent-runtime/compact/chat-client.js +2 -1
  11. package/dist/agent-runtime/compact/environment.d.ts +12 -0
  12. package/dist/agent-runtime/compact/environment.js +31 -1
  13. package/dist/agent-runtime/compact/test-reachability.js +6 -2
  14. package/dist/agent-runtime/config.d.ts +4 -1
  15. package/dist/agent-runtime/config.js +41 -3
  16. package/dist/agent-runtime/core-host.d.ts +10 -4
  17. package/dist/agent-runtime/core-host.js +22 -19
  18. package/dist/agent-runtime/efficiency-summary.d.ts +4 -1
  19. package/dist/agent-runtime/efficiency-summary.js +30 -27
  20. package/dist/agent-runtime/efficiency-types.d.ts +4 -4
  21. package/dist/agent-runtime/efficiency.d.ts +1 -1
  22. package/dist/agent-runtime/efficiency.js +7 -6
  23. package/dist/agent-runtime/engine/__fixtures__/implementation-component.json +90 -0
  24. package/dist/agent-runtime/engine/__fixtures__/implementation-fixture.d.ts +11 -0
  25. package/dist/agent-runtime/engine/__fixtures__/implementation-fixture.js +60 -0
  26. package/dist/agent-runtime/engine/__fixtures__/implementation.json +43 -0
  27. package/dist/agent-runtime/engine/budget.d.ts +9 -0
  28. package/dist/agent-runtime/engine/budget.js +33 -0
  29. package/dist/agent-runtime/engine/canonical-json.d.ts +9 -0
  30. package/dist/agent-runtime/engine/canonical-json.js +162 -0
  31. package/dist/agent-runtime/engine/checkpoint/database.d.ts +67 -0
  32. package/dist/agent-runtime/engine/checkpoint/database.js +304 -0
  33. package/dist/agent-runtime/engine/checkpoint/lease.d.ts +17 -0
  34. package/dist/agent-runtime/engine/checkpoint/lease.js +82 -0
  35. package/dist/agent-runtime/engine/checkpoint/ledger.d.ts +133 -0
  36. package/dist/agent-runtime/engine/checkpoint/ledger.js +510 -0
  37. package/dist/agent-runtime/engine/checkpoint/saver.d.ts +25 -0
  38. package/dist/agent-runtime/engine/checkpoint/saver.js +146 -0
  39. package/dist/agent-runtime/engine/cli.d.ts +10 -0
  40. package/dist/agent-runtime/engine/cli.js +172 -0
  41. package/dist/agent-runtime/engine/compiler.d.ts +23 -0
  42. package/dist/agent-runtime/engine/compiler.js +261 -0
  43. package/dist/agent-runtime/engine/composition.d.ts +16 -0
  44. package/dist/agent-runtime/engine/composition.js +28 -0
  45. package/dist/agent-runtime/engine/concurrency.d.ts +14 -0
  46. package/dist/agent-runtime/engine/concurrency.js +55 -0
  47. package/dist/agent-runtime/engine/contracts.d.ts +273 -0
  48. package/dist/agent-runtime/engine/contracts.js +11 -0
  49. package/dist/agent-runtime/engine/definition-schema.d.ts +250 -0
  50. package/dist/agent-runtime/engine/definition-schema.js +313 -0
  51. package/dist/agent-runtime/engine/definition-types.d.ts +84 -0
  52. package/dist/agent-runtime/engine/definition-types.js +3 -0
  53. package/dist/agent-runtime/engine/definition-validator.d.ts +7 -0
  54. package/dist/agent-runtime/engine/definition-validator.js +187 -0
  55. package/dist/agent-runtime/engine/efficiency-summary.d.ts +62 -0
  56. package/dist/agent-runtime/engine/efficiency-summary.js +31 -0
  57. package/dist/agent-runtime/engine/events.d.ts +19 -0
  58. package/dist/agent-runtime/engine/events.js +88 -0
  59. package/dist/agent-runtime/engine/execution.d.ts +29 -0
  60. package/dist/agent-runtime/engine/execution.js +121 -0
  61. package/dist/agent-runtime/engine/expressions.d.ts +11 -0
  62. package/dist/agent-runtime/engine/expressions.js +172 -0
  63. package/dist/agent-runtime/engine/fork.d.ts +28 -0
  64. package/dist/agent-runtime/engine/fork.js +211 -0
  65. package/dist/agent-runtime/engine/graph-description.d.ts +48 -0
  66. package/dist/agent-runtime/engine/graph-description.js +16 -0
  67. package/dist/agent-runtime/engine/implementation-compiler.d.ts +214 -0
  68. package/dist/agent-runtime/engine/implementation-compiler.js +91 -0
  69. package/dist/agent-runtime/engine/index.d.ts +14 -0
  70. package/dist/agent-runtime/engine/index.js +14 -0
  71. package/dist/agent-runtime/engine/invocation-context.d.ts +11 -0
  72. package/dist/agent-runtime/engine/invocation-context.js +128 -0
  73. package/dist/agent-runtime/engine/otel.d.ts +15 -0
  74. package/dist/agent-runtime/engine/otel.js +132 -0
  75. package/dist/agent-runtime/engine/output.d.ts +4 -0
  76. package/dist/agent-runtime/engine/output.js +73 -0
  77. package/dist/agent-runtime/engine/piece-dependencies.d.ts +50 -0
  78. package/dist/agent-runtime/engine/piece-dependencies.js +193 -0
  79. package/dist/agent-runtime/engine/piece-registry.d.ts +19 -0
  80. package/dist/agent-runtime/engine/piece-registry.js +62 -0
  81. package/dist/agent-runtime/engine/pieces/assign.d.ts +4 -0
  82. package/dist/agent-runtime/engine/pieces/assign.js +35 -0
  83. package/dist/agent-runtime/engine/pieces/control.d.ts +5 -0
  84. package/dist/agent-runtime/engine/pieces/control.js +49 -0
  85. package/dist/agent-runtime/engine/pieces/decider.d.ts +5 -0
  86. package/dist/agent-runtime/engine/pieces/decider.js +70 -0
  87. package/dist/agent-runtime/engine/pieces/implementation-binding.d.ts +12 -0
  88. package/dist/agent-runtime/engine/pieces/implementation-binding.js +40 -0
  89. package/dist/agent-runtime/engine/pieces/implementation-journal.d.ts +25 -0
  90. package/dist/agent-runtime/engine/pieces/implementation-journal.js +192 -0
  91. package/dist/agent-runtime/engine/pieces/implementation.d.ts +23 -0
  92. package/dist/agent-runtime/engine/pieces/implementation.js +176 -0
  93. package/dist/agent-runtime/engine/pieces/index.d.ts +7 -0
  94. package/dist/agent-runtime/engine/pieces/index.js +22 -0
  95. package/dist/agent-runtime/engine/pieces/openspec.d.ts +3 -0
  96. package/dist/agent-runtime/engine/pieces/openspec.js +69 -0
  97. package/dist/agent-runtime/engine/pieces/ports.d.ts +48 -0
  98. package/dist/agent-runtime/engine/pieces/ports.js +1 -0
  99. package/dist/agent-runtime/engine/pieces/project-memory.d.ts +3 -0
  100. package/dist/agent-runtime/engine/pieces/project-memory.js +13 -0
  101. package/dist/agent-runtime/engine/pieces/prompt.d.ts +5 -0
  102. package/dist/agent-runtime/engine/pieces/prompt.js +130 -0
  103. package/dist/agent-runtime/engine/pieces/role-turn.d.ts +7 -0
  104. package/dist/agent-runtime/engine/pieces/role-turn.js +73 -0
  105. package/dist/agent-runtime/engine/pieces/shared.d.ts +67 -0
  106. package/dist/agent-runtime/engine/pieces/shared.js +40 -0
  107. package/dist/agent-runtime/engine/pieces/shell.d.ts +6 -0
  108. package/dist/agent-runtime/engine/pieces/shell.js +47 -0
  109. package/dist/agent-runtime/engine/pieces/verify.d.ts +7 -0
  110. package/dist/agent-runtime/engine/pieces/verify.js +52 -0
  111. package/dist/agent-runtime/engine/preflight.d.ts +27 -0
  112. package/dist/agent-runtime/engine/preflight.js +54 -0
  113. package/dist/agent-runtime/engine/regex.d.ts +3 -0
  114. package/dist/agent-runtime/engine/regex.js +19 -0
  115. package/dist/agent-runtime/engine/run-status.d.ts +563 -0
  116. package/dist/agent-runtime/engine/run-status.js +61 -0
  117. package/dist/agent-runtime/engine/runs.d.ts +605 -0
  118. package/dist/agent-runtime/engine/runs.js +247 -0
  119. package/dist/agent-runtime/engine/state.d.ts +64 -0
  120. package/dist/agent-runtime/engine/state.js +98 -0
  121. package/dist/agent-runtime/engine/steering/inbox.d.ts +54 -0
  122. package/dist/agent-runtime/engine/steering/inbox.js +113 -0
  123. package/dist/agent-runtime/engine/storage/fork-allocation.d.ts +21 -0
  124. package/dist/agent-runtime/engine/storage/fork-allocation.js +143 -0
  125. package/dist/agent-runtime/engine/storage/private-path.d.ts +11 -0
  126. package/dist/agent-runtime/engine/storage/private-path.js +74 -0
  127. package/dist/agent-runtime/engine/store/sqlite-store.d.ts +26 -0
  128. package/dist/agent-runtime/engine/store/sqlite-store.js +227 -0
  129. package/dist/agent-runtime/evaluation-corpus.d.ts +125 -0
  130. package/dist/agent-runtime/evaluation-corpus.js +4 -0
  131. package/dist/agent-runtime/evaluation.d.ts +9 -3
  132. package/dist/agent-runtime/evaluation.js +50 -8
  133. package/dist/agent-runtime/executor-types.d.ts +53 -3
  134. package/dist/agent-runtime/executor-types.js +35 -2
  135. package/dist/agent-runtime/executors.js +2 -2
  136. package/dist/agent-runtime/graph/artifacts.d.ts +7 -0
  137. package/dist/agent-runtime/graph/artifacts.js +86 -10
  138. package/dist/agent-runtime/graph/convergence.d.ts +26 -0
  139. package/dist/agent-runtime/graph/convergence.js +62 -0
  140. package/dist/agent-runtime/graph/nodes.d.ts +2 -0
  141. package/dist/agent-runtime/graph/nodes.js +171 -24
  142. package/dist/agent-runtime/graph/roles.d.ts +6 -0
  143. package/dist/agent-runtime/graph/roles.js +23 -17
  144. package/dist/agent-runtime/graph/state.d.ts +5 -0
  145. package/dist/agent-runtime/graph/state.js +2 -0
  146. package/dist/agent-runtime/kimi-acp.js +6 -6
  147. package/dist/agent-runtime/openai-executor.js +17 -12
  148. package/dist/agent-runtime/openspec.d.ts +17 -2
  149. package/dist/agent-runtime/openspec.js +54 -20
  150. package/dist/agent-runtime/prompts.d.ts +11 -5
  151. package/dist/agent-runtime/prompts.js +71 -20
  152. package/dist/agent-runtime/recovery.d.ts +1 -1
  153. package/dist/agent-runtime/repository-context.js +20 -1
  154. package/dist/agent-runtime/role-routing.js +4 -2
  155. package/dist/agent-runtime/role-state.d.ts +5 -0
  156. package/dist/agent-runtime/verification-plan.d.ts +2 -0
  157. package/dist/agent-runtime/verification-plan.js +18 -3
  158. package/dist/agent-runtime/workflow-types.d.ts +2 -0
  159. package/dist/agent-runtime/workflow.d.ts +3 -1
  160. package/dist/agent-runtime/workflow.js +7 -3
  161. package/dist/agent-runtime/workspace-tools.d.ts +2 -2
  162. package/dist/agent-runtime/workspace-tools.js +6 -6
  163. package/dist/installer/cli.js +5 -0
  164. package/dist/installer/phases/scaffold.d.ts +2 -2
  165. package/dist/installer/phases/scaffold.js +3 -7
  166. package/dist/pipeline/pipeline-state.d.ts +49 -4
  167. package/dist/pipeline/pipeline-state.js +150 -23
  168. package/integration-contract.json +84 -12
  169. package/package.json +12 -5
  170. package/schemas/agent-runtime.schema.json +93 -1
  171. package/schemas/workflow-definition.schema.json +312 -0
  172. package/templates/kimi/specrails/run-skill.mjs +15 -1
  173. package/templates/commands/specrails/batch-implement.md +0 -32
@@ -0,0 +1,313 @@
1
+ /** Public JSON schema; parity is checked against the packaged schema artifact. */
2
+ export const workflowDefinitionSchema = {
3
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
4
+ "$id": "https://specrails.dev/schemas/workflow-definition/1",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": [
8
+ "schemaVersion",
9
+ "id",
10
+ "version",
11
+ "title",
12
+ "journal",
13
+ "change",
14
+ "entry",
15
+ "maxTransitions",
16
+ "roles",
17
+ "nodes"
18
+ ],
19
+ "properties": {
20
+ "schemaVersion": {
21
+ "const": 1
22
+ },
23
+ "id": {
24
+ "type": "string",
25
+ "pattern": "^[a-z0-9][a-z0-9-]{0,63}$"
26
+ },
27
+ "version": {
28
+ "type": "string",
29
+ "pattern": "^[0-9a-f]{64}$"
30
+ },
31
+ "title": {
32
+ "type": "string",
33
+ "minLength": 1,
34
+ "maxLength": 120
35
+ },
36
+ "journal": {
37
+ "enum": [
38
+ "ledger-only",
39
+ "implementation"
40
+ ]
41
+ },
42
+ "change": {
43
+ "enum": [
44
+ "new",
45
+ "existing",
46
+ "none"
47
+ ]
48
+ },
49
+ "entry": {
50
+ "$ref": "#/$defs/nodeId"
51
+ },
52
+ "maxTransitions": {
53
+ "type": "integer",
54
+ "minimum": 1,
55
+ "maximum": 10000
56
+ },
57
+ "budget": {
58
+ "type": "object",
59
+ "additionalProperties": false,
60
+ "properties": {
61
+ "maxCostUsd": {
62
+ "type": "number",
63
+ "exclusiveMinimum": 0
64
+ },
65
+ "maxTokens": {
66
+ "type": "integer",
67
+ "minimum": 1
68
+ },
69
+ "maxDurationMs": {
70
+ "type": "integer",
71
+ "minimum": 1000
72
+ }
73
+ }
74
+ },
75
+ "policies": {
76
+ "type": "object",
77
+ "additionalProperties": false,
78
+ "properties": {
79
+ "failFast": {
80
+ "type": "integer",
81
+ "minimum": 1,
82
+ "maximum": 10,
83
+ "default": 2
84
+ },
85
+ "noProgress": {
86
+ "type": "integer",
87
+ "minimum": 1,
88
+ "maximum": 10,
89
+ "default": 2
90
+ },
91
+ "historyMaxChars": {
92
+ "type": "integer",
93
+ "minimum": 200,
94
+ "maximum": 20000,
95
+ "default": 1500
96
+ },
97
+ "concurrency": {
98
+ "type": "integer",
99
+ "minimum": 1,
100
+ "maximum": 8,
101
+ "default": 1
102
+ }
103
+ }
104
+ },
105
+ "roles": {
106
+ "type": "array",
107
+ "items": {
108
+ "$ref": "#/$defs/roleId"
109
+ },
110
+ "uniqueItems": true
111
+ },
112
+ "nodes": {
113
+ "type": "object",
114
+ "minProperties": 1,
115
+ "propertyNames": {
116
+ "$ref": "#/$defs/nodeId"
117
+ },
118
+ "additionalProperties": {
119
+ "$ref": "#/$defs/node"
120
+ }
121
+ },
122
+ "components": {
123
+ "type": "object",
124
+ "propertyNames": {
125
+ "pattern": "^[a-z0-9][a-z0-9-]{0,63}$"
126
+ },
127
+ "additionalProperties": {
128
+ "$ref": "#/$defs/componentBody"
129
+ }
130
+ },
131
+ "delivery": {
132
+ "type": "object",
133
+ "additionalProperties": false,
134
+ "properties": {
135
+ "requiresVerified": {
136
+ "type": "boolean"
137
+ }
138
+ }
139
+ }
140
+ },
141
+ "$defs": {
142
+ "nodeId": {
143
+ "type": "string",
144
+ "pattern": "^[A-Za-z0-9][A-Za-z0-9_-]{0,119}$",
145
+ "not": {
146
+ "enum": [
147
+ "START",
148
+ "END",
149
+ "__start__",
150
+ "__end__",
151
+ "next"
152
+ ]
153
+ }
154
+ },
155
+ "roleId": {
156
+ "type": "string",
157
+ "pattern": "^[a-z][a-z0-9-]{0,63}$"
158
+ },
159
+ "engine": {
160
+ "type": "object",
161
+ "additionalProperties": false,
162
+ "required": [
163
+ "provider"
164
+ ],
165
+ "properties": {
166
+ "provider": {
167
+ "type": "string"
168
+ },
169
+ "model": {
170
+ "type": "string"
171
+ },
172
+ "effort": {
173
+ "type": "string"
174
+ },
175
+ "thinking": {
176
+ "enum": [
177
+ "on",
178
+ "off"
179
+ ]
180
+ }
181
+ }
182
+ },
183
+ "retry": {
184
+ "type": "object",
185
+ "additionalProperties": false,
186
+ "properties": {
187
+ "maxAttempts": {
188
+ "type": "integer",
189
+ "minimum": 1,
190
+ "maximum": 10
191
+ },
192
+ "backoffMs": {
193
+ "type": "integer",
194
+ "minimum": 0,
195
+ "maximum": 600000
196
+ },
197
+ "retryOn": {
198
+ "type": "array",
199
+ "items": {
200
+ "enum": [
201
+ "idle_timeout",
202
+ "timeout",
203
+ "provider_request_error",
204
+ "session_not_found",
205
+ "invalid_structured_output"
206
+ ]
207
+ }
208
+ }
209
+ }
210
+ },
211
+ "ends": {
212
+ "type": "object",
213
+ "propertyNames": {
214
+ "pattern": "^[a-z][a-z0-9-]{0,31}$"
215
+ },
216
+ "additionalProperties": {
217
+ "oneOf": [
218
+ {
219
+ "$ref": "#/$defs/nodeId"
220
+ },
221
+ {
222
+ "type": "null"
223
+ }
224
+ ]
225
+ }
226
+ },
227
+ "node": {
228
+ "type": "object",
229
+ "additionalProperties": false,
230
+ "required": [
231
+ "kind",
232
+ "params",
233
+ "ends"
234
+ ],
235
+ "properties": {
236
+ "kind": {
237
+ "enum": [
238
+ "prompt",
239
+ "role-turn",
240
+ "decider",
241
+ "condition",
242
+ "assign",
243
+ "verify",
244
+ "shell",
245
+ "openspec-validate",
246
+ "openspec-archive",
247
+ "approval",
248
+ "question",
249
+ "gate",
250
+ "map",
251
+ "join",
252
+ "component",
253
+ "implementation",
254
+ "end"
255
+ ]
256
+ },
257
+ "params": {
258
+ "type": "object"
259
+ },
260
+ "ends": {
261
+ "$ref": "#/$defs/ends"
262
+ },
263
+ "retry": {
264
+ "$ref": "#/$defs/retry"
265
+ },
266
+ "label": {
267
+ "type": "string",
268
+ "maxLength": 80
269
+ }
270
+ }
271
+ },
272
+ "componentBody": {
273
+ "type": "object",
274
+ "additionalProperties": false,
275
+ "required": [
276
+ "entry",
277
+ "nodes"
278
+ ],
279
+ "properties": {
280
+ "entry": {
281
+ "$ref": "#/$defs/nodeId"
282
+ },
283
+ "maxTransitions": {
284
+ "type": "integer",
285
+ "minimum": 1,
286
+ "maximum": 10000
287
+ },
288
+ "inputs": {
289
+ "type": "array",
290
+ "items": {
291
+ "type": "string"
292
+ }
293
+ },
294
+ "outputs": {
295
+ "type": "array",
296
+ "items": {
297
+ "type": "string"
298
+ }
299
+ },
300
+ "nodes": {
301
+ "type": "object",
302
+ "minProperties": 1,
303
+ "propertyNames": {
304
+ "$ref": "#/$defs/nodeId"
305
+ },
306
+ "additionalProperties": {
307
+ "$ref": "#/$defs/node"
308
+ }
309
+ }
310
+ }
311
+ }
312
+ }
313
+ };
@@ -0,0 +1,84 @@
1
+ import type { EngineEffect, JsonObject, WorkflowBudget } from './contracts.js';
2
+ export declare const PIECE_KINDS: readonly ["prompt", "role-turn", "decider", "condition", "assign", "verify", "shell", "openspec-validate", "openspec-archive", "approval", "question", "gate", "map", "join", "component", "implementation", "end"];
3
+ export type PieceKind = typeof PIECE_KINDS[number];
4
+ export interface DefinitionNode {
5
+ kind: PieceKind;
6
+ params: JsonObject;
7
+ ends: Record<string, string | null>;
8
+ retry?: {
9
+ maxAttempts?: number;
10
+ backoffMs?: number;
11
+ retryOn?: string[];
12
+ };
13
+ label?: string;
14
+ }
15
+ export interface ComponentBody {
16
+ entry: string;
17
+ nodes: Record<string, DefinitionNode>;
18
+ maxTransitions?: number;
19
+ inputs?: string[];
20
+ outputs?: string[];
21
+ }
22
+ export interface WorkflowDefinition {
23
+ schemaVersion: 1;
24
+ id: string;
25
+ version: string;
26
+ title: string;
27
+ journal: 'ledger-only' | 'implementation';
28
+ change: 'new' | 'existing' | 'none';
29
+ entry: string;
30
+ maxTransitions: number;
31
+ roles: string[];
32
+ nodes: Record<string, DefinitionNode>;
33
+ budget?: WorkflowBudget;
34
+ policies?: {
35
+ failFast?: number;
36
+ noProgress?: number;
37
+ historyMaxChars?: number;
38
+ concurrency?: number;
39
+ };
40
+ components?: Record<string, ComponentBody>;
41
+ delivery?: {
42
+ requiresVerified?: boolean;
43
+ };
44
+ }
45
+ export type WorkflowDefinitionDraft = Omit<WorkflowDefinition, 'version'> & {
46
+ version?: string;
47
+ };
48
+ export type RoleCatalog = Readonly<Record<string, {
49
+ access: EngineEffect;
50
+ }>>;
51
+ export interface DefinitionIssue {
52
+ code: string;
53
+ path: string;
54
+ message: string;
55
+ }
56
+ export interface DefinitionGraphNode {
57
+ id: string;
58
+ nodePath: string;
59
+ kind: PieceKind;
60
+ effect: EngineEffect;
61
+ outcomes: string[];
62
+ }
63
+ export interface DefinitionGraph {
64
+ id: string;
65
+ version: string;
66
+ entry: string;
67
+ nodes: DefinitionGraphNode[];
68
+ edges: Array<{
69
+ from: string;
70
+ outcome: string;
71
+ to: string | null;
72
+ }>;
73
+ }
74
+ export type DefinitionValidation = {
75
+ type: 'runtime-definition-validated';
76
+ ok: true;
77
+ version: string;
78
+ definition: WorkflowDefinition;
79
+ graph: DefinitionGraph;
80
+ } | {
81
+ type: 'runtime-definition-validated';
82
+ ok: false;
83
+ errors: DefinitionIssue[];
84
+ };
@@ -0,0 +1,3 @@
1
+ export const PIECE_KINDS = ['prompt', 'role-turn', 'decider', 'condition', 'assign', 'verify', 'shell',
2
+ 'openspec-validate', 'openspec-archive', 'approval', 'question', 'gate', 'map', 'join',
3
+ 'component', 'implementation', 'end'];
@@ -0,0 +1,7 @@
1
+ import type { DefinitionValidation, RoleCatalog } from './definition-types.js';
2
+ import type { PieceRegistry } from './piece-registry.js';
3
+ /** Returns a published, detached document. Defaults never change its canonical identity. */
4
+ export declare function validateWorkflowDefinition(input: unknown, registry: PieceRegistry, roles?: RoleCatalog, options?: {
5
+ published?: boolean;
6
+ structural?: boolean;
7
+ }): DefinitionValidation;
@@ -0,0 +1,187 @@
1
+ import { Ajv2020 } from 'ajv/dist/2020.js';
2
+ import { canonicalJson, definitionVersion, MAX_DEFINITION_BYTES, parseDefinitionJson } from './canonical-json.js';
3
+ import { EngineError } from './contracts.js';
4
+ import { workflowDefinitionSchema } from './definition-schema.js';
5
+ import { boundedPattern, parseExpression, validateInterpolations } from './expressions.js';
6
+ const ajv = new Ajv2020({ allErrors: true, strict: true, validateFormats: false, ownProperties: true });
7
+ const draftSchema = { ...workflowDefinitionSchema, required: workflowDefinitionSchema.required.filter(key => key !== 'version') };
8
+ const validateShape = ajv.compile(draftSchema);
9
+ /** Returns a published, detached document. Defaults never change its canonical identity. */
10
+ export function validateWorkflowDefinition(input, registry, roles = {}, options = {}) {
11
+ const errors = [];
12
+ const add = (code, path, message) => { errors.push({ code, path, message }); };
13
+ try {
14
+ const raw = typeof input === 'string' || input instanceof Uint8Array ? parseDefinitionJson(input) : input;
15
+ const canonical = canonicalJson(raw);
16
+ if (Buffer.byteLength(canonical) > MAX_DEFINITION_BYTES)
17
+ throw new EngineError('invalid_definition', `Definition exceeds ${MAX_DEFINITION_BYTES} bytes`);
18
+ if (!validateShape(raw))
19
+ return { type: 'runtime-definition-validated', ok: false, errors: (validateShape.errors ?? []).map(error => ({ code: 'invalid_definition', path: error.instancePath, message: error.message ?? 'Invalid definition' })) };
20
+ const draft = JSON.parse(canonical);
21
+ const version = definitionVersion(draft);
22
+ if (options.published && !draft.version)
23
+ add('definition_version_missing', '/version', 'Execution requires a published definition');
24
+ if (draft.version && draft.version !== version)
25
+ add('definition_hash_mismatch', '/version', 'Definition version does not match its canonical content');
26
+ const definition = { ...draft, version };
27
+ // Portable authoring cannot resolve project bindings. Execution always calls
28
+ // this validator again with the real role catalog and structural=false.
29
+ const unresolved = options.structural ? definition.roles.filter(role => !Object.hasOwn(roles, role)) : [];
30
+ if (unresolved.length)
31
+ roles = { ...roles, ...Object.fromEntries(unresolved.map(role => [role, { access: 'read' }])) };
32
+ const graph = { id: definition.id, version, entry: definition.entry, nodes: [], edges: [] };
33
+ for (const role of definition.roles)
34
+ if (!Object.hasOwn(roles, role))
35
+ add('role_not_found', '/roles', `Role ${role} is not configured`);
36
+ let implementations = 0;
37
+ let writes = false;
38
+ const successfulEnds = [];
39
+ const bodies = definition.components ?? {};
40
+ const validatedBodies = new Set();
41
+ let referenceChecks = 0;
42
+ function effectOf(node, ancestry = []) {
43
+ if (++referenceChecks > 100_000)
44
+ throw new EngineError('definition_complexity', 'Definition reference analysis exceeds its bounded work limit');
45
+ if (node.kind !== 'component' && node.kind !== 'map')
46
+ return registry.effect(node.kind, node.params, roles);
47
+ const ref = node.params[node.kind === 'component' ? 'ref' : 'body'];
48
+ if (typeof ref !== 'string' || !Object.hasOwn(bodies, ref) || ancestry.includes(ref))
49
+ return 'read';
50
+ return Object.values(bodies[ref].nodes).some(child => effectOf(child, [...ancestry, ref]) === 'write') ? 'write' : 'read';
51
+ }
52
+ // Check every ancestry even when a shared body was already validated along a shorter path.
53
+ function inspectReferences(body, ancestry, prefix) {
54
+ for (const [id, node] of Object.entries(body.nodes)) {
55
+ if (++referenceChecks > 100_000)
56
+ throw new EngineError('definition_complexity', 'Definition reference analysis exceeds its bounded work limit');
57
+ if (node.kind !== 'component' && node.kind !== 'map')
58
+ continue;
59
+ const ref = node.params[node.kind === 'component' ? 'ref' : 'body'];
60
+ if (typeof ref !== 'string' || !Object.hasOwn(bodies, ref))
61
+ continue;
62
+ if (ancestry.includes(ref))
63
+ add('component_cycle', prefix + '/nodes/' + id, 'Recursive component reference is not permitted');
64
+ else if (ancestry.length >= 3)
65
+ add('component_depth', prefix + '/nodes/' + id, 'Component nesting cannot exceed three levels');
66
+ else
67
+ inspectReferences(bodies[ref], [...ancestry, ref], '/components/' + ref);
68
+ }
69
+ }
70
+ function inspect(body, prefix, ancestry) {
71
+ if (!Object.hasOwn(body.nodes, body.entry))
72
+ add('node_not_found', prefix + '/entry', `Entry ${body.entry} does not exist`);
73
+ const reachable = new Set();
74
+ const pending = [body.entry];
75
+ let terminal = false;
76
+ while (pending.length) {
77
+ const id = pending.pop();
78
+ if (reachable.has(id))
79
+ continue;
80
+ reachable.add(id);
81
+ const node = Object.hasOwn(body.nodes, id) ? body.nodes[id] : undefined;
82
+ if (!node)
83
+ continue;
84
+ if (node.kind === 'end' || Object.values(node.ends).includes(null))
85
+ terminal = true;
86
+ pending.push(...Object.values(node.ends).filter((target) => target !== null));
87
+ }
88
+ if (!terminal)
89
+ add('no_terminal_path', prefix + '/entry', 'No terminal path is reachable from this entry');
90
+ for (const [id, node] of Object.entries(body.nodes)) {
91
+ const path = prefix + '/nodes/' + id;
92
+ const nodePath = prefix ? prefix.replace(/^\/components\//, '') + '/' + id : id;
93
+ errors.push(...registry.validateParams(node.kind, node.params, path));
94
+ for (const [outcome, target] of Object.entries(node.ends)) {
95
+ if (target !== null && !Object.hasOwn(body.nodes, target))
96
+ add('node_not_found', path + '/ends/' + outcome, `Target ${target} does not exist`);
97
+ graph.edges.push({ from: nodePath, outcome, to: target === null ? null : (prefix ? prefix.replace(/^\/components\//, '') + '/' : '') + target });
98
+ }
99
+ try {
100
+ validateInterpolations(node.params);
101
+ if (node.kind === 'condition' && typeof node.params.expr === 'string')
102
+ parseExpression(node.params.expr);
103
+ if (node.kind === 'decider' && typeof node.params.continueWhen === 'string')
104
+ parseExpression(node.params.continueWhen);
105
+ if (Array.isArray(node.params.captureVars))
106
+ for (const capture of node.params.captureVars) {
107
+ if (capture && typeof capture === 'object' && !Array.isArray(capture) && typeof capture.pattern === 'string')
108
+ boundedPattern(capture.pattern);
109
+ }
110
+ const roleId = node.params.roleId;
111
+ if (node.kind === 'role-turn' || node.kind === 'decider') {
112
+ if (typeof roleId !== 'string' || !definition.roles.includes(roleId))
113
+ add('role_undeclared', path + '/params/roleId', 'Used role must appear in definition.roles');
114
+ if (typeof roleId !== 'string' || !Object.hasOwn(roles, roleId))
115
+ add('role_not_found', path + '/params/roleId', 'Used role is not configured');
116
+ if (node.kind === 'decider' && typeof roleId === 'string' && roles[roleId]?.access !== 'read')
117
+ add('invalid_role_access', path + '/params/roleId', 'Decider role must be read-only');
118
+ }
119
+ const effect = effectOf(node);
120
+ writes ||= effect === 'write';
121
+ let outcomes = registry.outcomes(node.kind, node.params);
122
+ if (node.kind === 'component') {
123
+ const ref = node.params.ref;
124
+ if (typeof ref === 'string' && Object.hasOwn(bodies, ref))
125
+ outcomes = bodies[ref].outputs ?? ['next', 'failed'];
126
+ }
127
+ if (Object.keys(node.ends).sort().join('\0') !== [...outcomes].sort().join('\0'))
128
+ add('invalid_outcomes', path + '/ends', `Expected exactly ${outcomes.join(', ') || 'no outcomes'}`);
129
+ graph.nodes.push({ id, nodePath, kind: node.kind, effect, outcomes: [...outcomes] });
130
+ }
131
+ catch (error) {
132
+ add(error instanceof EngineError ? error.code : 'invalid_piece_params', path, error instanceof Error ? error.message : String(error));
133
+ }
134
+ if (node.kind === 'implementation')
135
+ implementations += 1;
136
+ if (node.kind === 'end' && node.params.outcome === 'success')
137
+ successfulEnds.push({ path, verified: node.params.requiresVerified === true });
138
+ if (node.kind === 'end' && node.params.exit !== undefined) {
139
+ if (!prefix || typeof node.params.exit !== 'string' || !(body.outputs ?? ['next', 'failed']).includes(node.params.exit))
140
+ add('invalid_component_exit', path + '/params/exit', 'Exit must be a declared output of the containing component');
141
+ }
142
+ if (node.kind === 'map') {
143
+ const target = node.ends.next;
144
+ if (!target || body.nodes[target]?.kind !== 'join')
145
+ add('invalid_map_join', path + '/ends', 'Map next must target its join');
146
+ }
147
+ if (node.kind === 'join') {
148
+ const predecessors = Object.entries(body.nodes).filter(([, predecessor]) => Object.values(predecessor.ends).includes(id));
149
+ if (predecessors.length !== 1 || predecessors[0][1].kind !== 'map')
150
+ add('invalid_map_join', path, 'Join must have exactly one map predecessor');
151
+ }
152
+ if (node.kind === 'map' || node.kind === 'component') {
153
+ const ref = node.params[node.kind === 'map' ? 'body' : 'ref'];
154
+ if (typeof ref !== 'string' || !Object.hasOwn(bodies, ref))
155
+ add('component_not_found', path + '/params', 'Referenced component does not exist');
156
+ else if (ancestry.includes(ref))
157
+ add('component_cycle', path + '/params', 'Recursive component reference is not permitted');
158
+ else if (ancestry.length >= 3)
159
+ add('component_depth', path + '/params', 'Component nesting cannot exceed three levels');
160
+ else if (!validatedBodies.has(ref)) {
161
+ validatedBodies.add(ref);
162
+ inspect(bodies[ref], '/components/' + ref, [...ancestry, ref]);
163
+ }
164
+ }
165
+ }
166
+ }
167
+ inspect(definition, '', []);
168
+ for (const [name, body] of Object.entries(bodies))
169
+ if (!validatedBodies.has(name)) {
170
+ validatedBodies.add(name);
171
+ inspect(body, '/components/' + name, [name]);
172
+ }
173
+ inspectReferences(definition, [], '');
174
+ for (const [name, body] of Object.entries(bodies))
175
+ inspectReferences(body, [name], '/components/' + name);
176
+ if ((definition.journal === 'ledger-only' && implementations !== 0) || (definition.journal === 'implementation' && (implementations !== 1 || definition.change === 'none')))
177
+ add('journal_mismatch', '/journal', 'Implementation journals require exactly one implementation piece and an existing or new change');
178
+ if (writes && definition.delivery?.requiresVerified !== false && definition.delivery?.requiresVerified !== true)
179
+ for (const end of successfulEnds)
180
+ if (!end.verified)
181
+ add('verification_required', end.path, 'Successful write workflows require verification or explicit delivery opt-out');
182
+ return errors.length ? { type: 'runtime-definition-validated', ok: false, errors } : { type: 'runtime-definition-validated', ok: true, version, definition, graph };
183
+ }
184
+ catch (error) {
185
+ return { type: 'runtime-definition-validated', ok: false, errors: [{ code: error instanceof EngineError ? error.code : 'invalid_definition', path: '', message: error instanceof Error ? error.message : String(error) }] };
186
+ }
187
+ }
@@ -0,0 +1,62 @@
1
+ import type { WorkflowState } from '../workflow-types.js';
2
+ import type { RunLedger } from './checkpoint/ledger.js';
3
+ /** Derive diagnostics from committed records; status never inspects or mutates a journal. */
4
+ export declare function definitionEfficiencySummary(ledger: RunLedger, state: WorkflowState): {
5
+ reservations: {
6
+ pendingInvocations: number;
7
+ knownTokens: number;
8
+ knownCostUsd: number;
9
+ tokensUnknown: boolean;
10
+ costUnknown: boolean;
11
+ };
12
+ schemaVersion: 1;
13
+ currentEvidenceIds: string[];
14
+ planHash: string | null;
15
+ candidateHash: string | null;
16
+ runId: string;
17
+ workflowVersion: string;
18
+ technicalAcceptance: string;
19
+ archive: import("../../pipeline/pipeline-state.js").PhaseStatus;
20
+ delivery: "pending" | "complete" | "pending-host";
21
+ roles: {
22
+ role: string;
23
+ provider: string | null;
24
+ model: string | null;
25
+ effort: string | null;
26
+ observedModel: null;
27
+ observedEffort: null;
28
+ origin: string;
29
+ tier: "base" | "escalation";
30
+ }[];
31
+ invocations: {
32
+ byKind: {
33
+ [k: string]: number;
34
+ };
35
+ total: number;
36
+ complete: boolean;
37
+ promptBytes: number | null;
38
+ contextBytes: number | null;
39
+ handoffBytes: number | null;
40
+ fullContexts: number;
41
+ incrementalContexts: number;
42
+ };
43
+ escalations: {
44
+ role: string;
45
+ attemptId: string;
46
+ provider: string;
47
+ model: string | null;
48
+ effort: string | null;
49
+ reason: string | null;
50
+ }[];
51
+ escalationsTruncated: boolean;
52
+ checks: {
53
+ invalidated: number | null;
54
+ available: boolean;
55
+ complete: boolean;
56
+ executed: number | null;
57
+ reused: number | null;
58
+ notRun: number | null;
59
+ durationMs: number | null;
60
+ };
61
+ metrics: import("../efficiency-types.js").RuntimeEfficiency;
62
+ };