@osovv/vv-opencode 1.4.5 → 1.6.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 (143) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +47 -16
  3. package/dist/commands/patch-provider.d.ts +5 -5
  4. package/dist/commands/patch-provider.js +10 -6
  5. package/dist/commands/patch-provider.js.map +1 -1
  6. package/dist/lib/agent-tool-catalog.d.ts +144 -0
  7. package/dist/lib/agent-tool-catalog.js +2551 -0
  8. package/dist/lib/agent-tool-catalog.js.map +1 -0
  9. package/dist/lib/agent-tool-contract.d.ts +200 -0
  10. package/dist/lib/agent-tool-contract.js +407 -0
  11. package/dist/lib/agent-tool-contract.js.map +1 -0
  12. package/dist/lib/opencode/agent-registrations.d.ts +33 -0
  13. package/dist/lib/opencode/agent-registrations.js +469 -0
  14. package/dist/lib/opencode/agent-registrations.js.map +1 -0
  15. package/dist/lib/opencode/inspection.d.ts +84 -0
  16. package/dist/lib/opencode/inspection.js +367 -0
  17. package/dist/lib/opencode/inspection.js.map +1 -0
  18. package/dist/lib/opencode/model-overrides.d.ts +19 -0
  19. package/dist/lib/opencode/model-overrides.js +221 -0
  20. package/dist/lib/opencode/model-overrides.js.map +1 -0
  21. package/dist/lib/opencode/paths.d.ts +21 -0
  22. package/dist/lib/opencode/paths.js +62 -0
  23. package/dist/lib/opencode/paths.js.map +1 -0
  24. package/dist/lib/opencode/plugin-registration.d.ts +17 -0
  25. package/dist/lib/opencode/plugin-registration.js +210 -0
  26. package/dist/lib/opencode/plugin-registration.js.map +1 -0
  27. package/dist/lib/opencode/shared-utils.d.ts +30 -0
  28. package/dist/lib/opencode/shared-utils.js +183 -0
  29. package/dist/lib/opencode/shared-utils.js.map +1 -0
  30. package/dist/lib/opencode/vvoc-config-io.d.ts +9 -0
  31. package/dist/lib/opencode/vvoc-config-io.js +58 -0
  32. package/dist/lib/opencode/vvoc-config-io.js.map +1 -0
  33. package/dist/lib/opencode.d.ts +15 -174
  34. package/dist/lib/opencode.js +19 -1387
  35. package/dist/lib/opencode.js.map +1 -1
  36. package/dist/lib/spec-lint/cross-file-rules.d.ts +2 -0
  37. package/dist/lib/spec-lint/cross-file-rules.js +33 -0
  38. package/dist/lib/spec-lint/cross-file-rules.js.map +1 -0
  39. package/dist/lib/spec-lint/delegated-extraction.d.ts +8 -0
  40. package/dist/lib/spec-lint/delegated-extraction.js +112 -0
  41. package/dist/lib/spec-lint/delegated-extraction.js.map +1 -0
  42. package/dist/lib/spec-lint/lint.d.ts +7 -0
  43. package/dist/lib/spec-lint/lint.js +92 -0
  44. package/dist/lib/spec-lint/lint.js.map +1 -0
  45. package/dist/lib/spec-lint/parser.d.ts +101 -0
  46. package/dist/lib/spec-lint/parser.js +482 -0
  47. package/dist/lib/spec-lint/parser.js.map +1 -0
  48. package/dist/lib/spec-lint/plan-rules.d.ts +38 -0
  49. package/dist/lib/spec-lint/plan-rules.js +745 -0
  50. package/dist/lib/spec-lint/plan-rules.js.map +1 -0
  51. package/dist/lib/spec-lint/spec-rules.d.ts +8 -0
  52. package/dist/lib/spec-lint/spec-rules.js +188 -0
  53. package/dist/lib/spec-lint/spec-rules.js.map +1 -0
  54. package/dist/lib/spec-lint.d.ts +4 -63
  55. package/dist/lib/spec-lint.js +13 -1505
  56. package/dist/lib/spec-lint.js.map +1 -1
  57. package/dist/lib/vvoc-preset-registry.d.ts +7 -7
  58. package/dist/lib/vvoc-preset-registry.js +8 -8
  59. package/dist/lib/vvoc-preset-registry.js.map +1 -1
  60. package/dist/lib/workflow-contract.d.ts +12 -0
  61. package/dist/lib/workflow-contract.js +71 -18
  62. package/dist/lib/workflow-contract.js.map +1 -1
  63. package/dist/plugins/hashline-edit/index.js +102 -97
  64. package/dist/plugins/hashline-edit/index.js.map +1 -1
  65. package/dist/plugins/hashline-edit/normalize-edits.d.ts +2 -8
  66. package/dist/plugins/hashline-edit/normalize-edits.js +32 -17
  67. package/dist/plugins/hashline-edit/normalize-edits.js.map +1 -1
  68. package/dist/plugins/hashline-edit/schemas.d.ts +246 -0
  69. package/dist/plugins/hashline-edit/schemas.js +659 -0
  70. package/dist/plugins/hashline-edit/schemas.js.map +1 -0
  71. package/dist/plugins/hashline-edit/str-replace-editor.d.ts +6 -17
  72. package/dist/plugins/hashline-edit/str-replace-editor.js +19 -21
  73. package/dist/plugins/hashline-edit/str-replace-editor.js.map +1 -1
  74. package/dist/plugins/hashline-edit/tool-description.d.ts +1 -0
  75. package/dist/plugins/hashline-edit/tool-description.js +20 -4
  76. package/dist/plugins/hashline-edit/tool-description.js.map +1 -1
  77. package/dist/plugins/web-tools/fetch-service.d.ts +8 -5
  78. package/dist/plugins/web-tools/fetch-service.js +30 -45
  79. package/dist/plugins/web-tools/fetch-service.js.map +1 -1
  80. package/dist/plugins/web-tools/index.js +23 -5
  81. package/dist/plugins/web-tools/index.js.map +1 -1
  82. package/dist/plugins/web-tools/schemas.d.ts +469 -0
  83. package/dist/plugins/web-tools/schemas.js +523 -0
  84. package/dist/plugins/web-tools/schemas.js.map +1 -0
  85. package/dist/plugins/web-tools/search-service.d.ts +6 -3
  86. package/dist/plugins/web-tools/search-service.js +24 -29
  87. package/dist/plugins/web-tools/search-service.js.map +1 -1
  88. package/dist/plugins/workflow/authority.d.ts +6 -0
  89. package/dist/plugins/workflow/authority.js +45 -10
  90. package/dist/plugins/workflow/authority.js.map +1 -1
  91. package/dist/plugins/workflow/authorization.d.ts +30 -0
  92. package/dist/plugins/workflow/authorization.js +147 -0
  93. package/dist/plugins/workflow/authorization.js.map +1 -0
  94. package/dist/plugins/workflow/checkpoints.d.ts +41 -2
  95. package/dist/plugins/workflow/checkpoints.js +176 -47
  96. package/dist/plugins/workflow/checkpoints.js.map +1 -1
  97. package/dist/plugins/workflow/delegated.d.ts +15 -1
  98. package/dist/plugins/workflow/delegated.js +65 -35
  99. package/dist/plugins/workflow/delegated.js.map +1 -1
  100. package/dist/plugins/workflow/execution.d.ts +15 -30
  101. package/dist/plugins/workflow/execution.js +133 -33
  102. package/dist/plugins/workflow/execution.js.map +1 -1
  103. package/dist/plugins/workflow/index.js +197 -435
  104. package/dist/plugins/workflow/index.js.map +1 -1
  105. package/dist/plugins/workflow/input-validation.d.ts +48 -0
  106. package/dist/plugins/workflow/input-validation.js +536 -0
  107. package/dist/plugins/workflow/input-validation.js.map +1 -0
  108. package/dist/plugins/workflow/inspection.d.ts +60 -0
  109. package/dist/plugins/workflow/inspection.js +316 -0
  110. package/dist/plugins/workflow/inspection.js.map +1 -0
  111. package/dist/plugins/workflow/protocol.d.ts +2 -0
  112. package/dist/plugins/workflow/protocol.js +26 -4
  113. package/dist/plugins/workflow/protocol.js.map +1 -1
  114. package/dist/plugins/workflow/recovery.d.ts +18 -0
  115. package/dist/plugins/workflow/recovery.js +217 -0
  116. package/dist/plugins/workflow/recovery.js.map +1 -0
  117. package/dist/plugins/workflow/repair.d.ts +1 -1
  118. package/dist/plugins/workflow/repair.js +9 -10
  119. package/dist/plugins/workflow/repair.js.map +1 -1
  120. package/dist/plugins/workflow/results.d.ts +2566 -0
  121. package/dist/plugins/workflow/results.js +1111 -0
  122. package/dist/plugins/workflow/results.js.map +1 -0
  123. package/dist/plugins/workflow/schemas.d.ts +398 -0
  124. package/dist/plugins/workflow/schemas.js +399 -0
  125. package/dist/plugins/workflow/schemas.js.map +1 -0
  126. package/dist/plugins/workflow/state.d.ts +3 -2
  127. package/dist/plugins/workflow/state.js +15 -8
  128. package/dist/plugins/workflow/state.js.map +1 -1
  129. package/dist/plugins/workflow/system-instruction.md +17 -1
  130. package/dist/plugins/workflow/tooling.d.ts +11 -82
  131. package/dist/plugins/workflow/tooling.js +478 -461
  132. package/dist/plugins/workflow/tooling.js.map +1 -1
  133. package/dist/plugins/workflow/transactions.d.ts +13 -0
  134. package/dist/plugins/workflow/transactions.js +13 -3
  135. package/dist/plugins/workflow/transactions.js.map +1 -1
  136. package/package.json +6 -3
  137. package/schemas/vvoc/v3.json +1 -1
  138. package/templates/agents/vv-code-reviewer.md +13 -4
  139. package/templates/agents/vv-implementer.md +16 -9
  140. package/templates/agents/vv-spec-reviewer.md +13 -4
  141. package/templates/skills/vv-execute/SKILL.md +3 -3
  142. package/templates/skills/vv-execute/references/tool-contracts.md +1750 -0
  143. package/templates/skills/vv-review/SKILL.md +1 -1
@@ -0,0 +1,1111 @@
1
+ // FILE: src/plugins/workflow/results.ts
2
+ // VERSION: 1.0.0
3
+ // START_MODULE_CONTRACT
4
+ // PURPOSE: Own the concrete public workflow result contracts: failure category taxonomy and classification, bounded failure DTOs with preserved tokenized issues and state/prerequisite guidance, closed result schemas and types for every owned workflow response family (standalone batch, generic mutation, list views, close, decide, checkpoint/authority actions, and failures), producer/test result validation, and production serialization with a truthful post-side-effect failure fallback.
5
+ // SCOPE: Pure result/response shape ownership only. No store lookup, no state eligibility, no permission decisions, no mutation, no persistence. Serialization never throws after a side effect and never parses free-form messages to recover paths or state.
6
+ // DEPENDS: [@opencode-ai/plugin (tool.schema), zod (types), src/lib/agent-tool-contract.ts]
7
+ // LINKS: [M-WORKFLOW-TOOLING, M-AGENT-TOOL-CONTRACT, M-PLUGIN-WORKFLOW]
8
+ // ROLE: RUNTIME
9
+ // MAP_MODE: EXPORTS
10
+ // END_MODULE_CONTRACT
11
+ //
12
+ // START_MODULE_MAP
13
+ // WORKFLOW_FAILURE_CATEGORIES - Stable failure categories distinguishing input/state/authorization/host_context/persistence/internal.
14
+ // WorkflowFailureCategory - Union of the stable failure categories.
15
+ // WORKFLOW_MUTATION_OUTCOMES - Truthful post-side-effect mutation outcome vocabulary.
16
+ // WorkflowMutationOutcome - Union of the mutation outcome vocabulary.
17
+ // MAX_FAILURE_MESSAGE_CHARS - Documented finite top-level failure message allowance.
18
+ // MAX_FAILURE_FIELD_CHARS - Documented finite bound for failure context/identity fields.
19
+ // WorkflowFailure - Bounded, enumerated public failure DTO with category, issues, and typed state/attempt/budget context.
20
+ // WorkflowFailureResult - Record-compatible alias for the bounded failure DTO.
21
+ // boundContractIssues - Bound a failure issue list to the shared finite limits.
22
+ // categoryForWorkflowErrorCode - Classify an existing workflow error code into a stable failure category.
23
+ // failureGuidance - Bounded prerequisite/next-action guidance for a known state/authority failure family.
24
+ // normalizeWorkflowFailure - Add category/guidance to one failure object without mutating it.
25
+ // normalizeWorkflowResult - Add category/guidance to failure results (including batch items) without mutating input.
26
+ // finalizeWorkflowResult - Producer-side finalization wrapper over normalizeWorkflowResult.
27
+ // workflowFailureSchema - Closed schema for the generic public failure envelope.
28
+ // workflowInputFailure - Bounded caller-input failure carrying tokenized issues (agrees with the early hook).
29
+ // workflowHostContextFailure - Trusted-host-context failure that is not a caller argument error.
30
+ // workflowInternalResultFailure - Internal result-contract/serialization failure with observed outcome metadata.
31
+ // WorkflowDiagnosticError - Thrown owned diagnostic carrying a stable code, category, and optional outcome.
32
+ // serializeWorkflowResult - Serialize a public workflow result with a truthful post-side-effect fallback.
33
+ // validateWorkflowToolResult - Producer/test validation of one owned workflow tool result (no repair).
34
+ // workflowToolResultSchema - Closed result schema for one owned workflow tool id, preserving its inferred type.
35
+ // workflowToolResultSchemas - Exported closed per-tool result schema map.
36
+ // WorkflowToolResultToolId - Union of the five owned workflow tool ids with result schemas.
37
+ // WorkflowToolResultMap - Schema-derived per-tool result DTO map.
38
+ // WorkflowContractIdentity - Loaded package/revision/reference identity carried by work_item_list.contract.
39
+ // WorkflowExecutionView - Read-only execution view DTO.
40
+ // WorkflowDelegatedRunView - Read-only native plan-run view DTO.
41
+ // WorkflowWorkItemView - Read-only work-item view DTO with delegated budget and guidance.
42
+ // WorkflowInspectionView - Additive work_item_list inspection DTO.
43
+ // END_MODULE_MAP
44
+ //
45
+ // START_CHANGE_SUMMARY
46
+ // LAST_CHANGE: [C-AGENT-TOOL-CONTRACTS T-004 - Added the additive work_item_list inspection contract: loaded package/contract identity, generic/native execution views with derived task status, checkpoint generation/outcome/current-obligation detail, delegated latest-attempt and gate-derived guidance, and the launch_blocked next action. Prior T-003: concrete public workflow result contracts with failure categories, preserved tokenized issues, typed state/attempt/budget context, closed per-tool schemas/views, producer-side finalization, and truthful post-side-effect serialization.]
47
+ // END_CHANGE_SUMMARY
48
+ import { tool } from "@opencode-ai/plugin";
49
+ import { MAX_CONTRACT_ISSUES, MAX_ISSUE_MESSAGE_CHARS, MAX_ISSUE_PATH_CHARS, MAX_ISSUE_VALUE_CHARS, formatContractIssues, toContractIssues, } from "../../lib/agent-tool-contract.js";
50
+ import { AUTHORITY_STAGES, REVIEWER_ROLES, WORK_ITEM_MODES } from "../../lib/workflow-contract.js";
51
+ /**
52
+ * Documented finite bounds for failure diagnostics only. Success payloads,
53
+ * history, scope files, and batch arrays are never bounded here.
54
+ */
55
+ export const MAX_FAILURE_MESSAGE_CHARS = MAX_ISSUE_MESSAGE_CHARS * (MAX_CONTRACT_ISSUES + 1);
56
+ export const MAX_FAILURE_FIELD_CHARS = MAX_ISSUE_MESSAGE_CHARS;
57
+ // START_BLOCK_CATEGORIES
58
+ /** Stable failure categories. Unrecognized owned codes classify as `state`. */
59
+ export const WORKFLOW_FAILURE_CATEGORIES = [
60
+ "input",
61
+ "state",
62
+ "authorization",
63
+ "host_context",
64
+ "persistence",
65
+ "internal",
66
+ ];
67
+ /** Truthful post-side-effect outcome vocabulary; keep claims to what was observed. */
68
+ export const WORKFLOW_MUTATION_OUTCOMES = [
69
+ "committed",
70
+ "not_applied",
71
+ "rolled_back",
72
+ "unknown",
73
+ ];
74
+ const INPUT_ERROR_CODES = new Set(["INVALID_INPUT"]);
75
+ const HOST_CONTEXT_ERROR_CODES = new Set(["HOST_CONTEXT_UNAVAILABLE", "PLAN_LOAD_FAILED"]);
76
+ const PERSISTENCE_ERROR_CODES = new Set(["PERSISTENCE_FAILED", "SNAPSHOT_FAILED"]);
77
+ const INTERNAL_ERROR_CODES = new Set(["RESULT_CONTRACT_INVALID", "RESULT_SERIALIZATION_FAILED"]);
78
+ // Authorization covers access denial, session/run ownership, and recorded
79
+ // authority eligibility/provenance. Domain state (attempts, reviews, budgets,
80
+ // checkpoints, bindings) stays `state`; genuine caller-input rejection stays
81
+ // `input`, and missing host context stays `host_context`.
82
+ const AUTHORIZATION_ERROR_CODES = new Set([
83
+ "CONTROL_DENIED",
84
+ "WORKFLOW_TOOL_DENIED",
85
+ "SESSION_MISMATCH",
86
+ "AUTHORITY_DENIED",
87
+ "AUTHORITY_NOT_FOUND",
88
+ "AUTHORITY_REVOKED",
89
+ "AUTHORIZATION_LOOKUP_FAILED",
90
+ "AUTHORIZATION_NOT_FOUND",
91
+ "AUTHORIZATION_SESSION_MISMATCH",
92
+ "AUTHORIZATION_NOT_USER_MESSAGE",
93
+ "AUTHORIZATION_ID_MISMATCH",
94
+ "AUTHORIZATION_REUSED",
95
+ "AUTHORIZATION_STALE",
96
+ "FOREIGN_SESSION",
97
+ "ASSISTANT_MESSAGE",
98
+ "IGNORED_MESSAGE",
99
+ "SYNTHETIC_ONLY",
100
+ "MISSING_IDENTITY",
101
+ "EMPTY_MESSAGE",
102
+ "MESSAGE_REPLAY",
103
+ "STALE_EXTENSION",
104
+ "DUPLICATE_GRANT",
105
+ "DUPLICATE_DEBIT",
106
+ "RESERVE_EXHAUSTED",
107
+ "SCOPE_NOT_PRESERVED",
108
+ ]);
109
+ /** Classify an existing workflow error code into a stable failure category. */
110
+ export function categoryForWorkflowErrorCode(errorCode) {
111
+ if (INPUT_ERROR_CODES.has(errorCode))
112
+ return "input";
113
+ if (HOST_CONTEXT_ERROR_CODES.has(errorCode))
114
+ return "host_context";
115
+ if (PERSISTENCE_ERROR_CODES.has(errorCode))
116
+ return "persistence";
117
+ if (INTERNAL_ERROR_CODES.has(errorCode))
118
+ return "internal";
119
+ if (AUTHORIZATION_ERROR_CODES.has(errorCode))
120
+ return "authorization";
121
+ return "state";
122
+ }
123
+ /**
124
+ * Bounded prerequisite/next-step guidance for the state and authority families
125
+ * that need it. Guidance describes the unmet prerequisite and a safe inspection
126
+ * step; it never accepts work, grants recovery, resets identity/counters, or
127
+ * promises that a retry will remain valid after concurrent state changes.
128
+ */
129
+ const FAILURE_GUIDANCE = {
130
+ WORK_ITEM_NOT_FOUND: {
131
+ prerequisite: "an existing same-session work item for the supplied id",
132
+ nextAction: "call work_item_list to read the current work item ids",
133
+ },
134
+ WORK_ITEM_ALREADY_CLOSED: {
135
+ prerequisite: "a work item that is not closed",
136
+ },
137
+ READY_TO_CLOSE_REQUIRED: {
138
+ prerequisite: "all assigned reviews complete and no open concerns for this item",
139
+ nextAction: "call work_item_list to inspect pending reviews or concerns",
140
+ },
141
+ WRONG_MODE: {
142
+ prerequisite: "a work item of the mode this operation targets",
143
+ nextAction: "call work_item_list to confirm the item mode",
144
+ },
145
+ INVALID_STATE: {
146
+ prerequisite: "the work item in the state this operation requires",
147
+ nextAction: "call work_item_list to read the current state",
148
+ },
149
+ INVALID_ATTEMPT: {
150
+ prerequisite: "the current completed attempt number for this work item",
151
+ nextAction: "call work_item_list to read the latest attempt",
152
+ },
153
+ ATTEMPT_MISMATCH: {
154
+ prerequisite: "the latest terminal attempt number for this work item",
155
+ nextAction: "call work_item_list to read the latest attempt",
156
+ },
157
+ ATTEMPT_NOT_TERMINAL: {
158
+ prerequisite: "a terminal targeted attempt",
159
+ },
160
+ ATTEMPT_IN_FLIGHT: {
161
+ prerequisite: "no in-flight attempt for this work item",
162
+ nextAction: "collect the in-flight attempt result before launching again",
163
+ },
164
+ ATTEMPTS_EXHAUSTED: {
165
+ prerequisite: "an explicit recovery grant or checkpoint-authorized rework",
166
+ nextAction: "inspect the item with work_item_list before choosing a recovery path",
167
+ },
168
+ AUTONOMOUS_GRANT_EXHAUSTED: {
169
+ prerequisite: "a recorded advance authority or a fresh root-user message",
170
+ nextAction: "inspect available authority and budgets with work_item_list",
171
+ },
172
+ CONCERNS_DISPOSITION_REQUIRED: {
173
+ prerequisite: "an explicit concernsDisposition for a DONE_WITH_CONCERNS attempt",
174
+ },
175
+ UNEXPECTED_CONCERNS_DISPOSITION: {
176
+ prerequisite: "an attempt that completed DONE_WITH_CONCERNS",
177
+ nextAction: "inspect the latest attempt status with work_item_list",
178
+ },
179
+ REVIEW_ROUND_NOT_ACTIVE: {
180
+ prerequisite: "an active review round for this item",
181
+ nextAction: "inspect review state with work_item_list",
182
+ },
183
+ REVIEW_ROUND_NEEDS_CONTEXT: {
184
+ prerequisite: "resolved reviewer NEEDS_CONTEXT before another review round",
185
+ nextAction: "inspect review state with work_item_list",
186
+ },
187
+ REVIEWER_NOT_REQUIRED: {
188
+ prerequisite: "a reviewer role assigned to the active round",
189
+ },
190
+ REVIEWER_NOT_IN_FLIGHT: {
191
+ prerequisite: "a launched reviewer for the active round",
192
+ },
193
+ REVIEWER_ALREADY_COMPLETED: {
194
+ prerequisite: "an outstanding reviewer result",
195
+ },
196
+ REVIEWER_ALREADY_IN_FLIGHT: {
197
+ prerequisite: "a reviewer that has not already launched",
198
+ },
199
+ AUTHORITY_NOT_FOUND: {
200
+ prerequisite: "a recorded authority matching the supplied authorityId for the owned run",
201
+ nextAction: "inspect the owned execution and its authority with work_item_list",
202
+ },
203
+ AUTHORITY_DENIED: {
204
+ prerequisite: "a verified eligible root-user instruction",
205
+ },
206
+ AUTHORITY_REVOKED: {
207
+ prerequisite: "an authority that has not been revoked",
208
+ },
209
+ RESERVE_EXHAUSTED: {
210
+ prerequisite: "a remaining advance unit on the recorded authority",
211
+ nextAction: "inspect the authority budget with work_item_list",
212
+ },
213
+ CHECKPOINT_NOT_FOUND: {
214
+ prerequisite: "a checkpoint bound to the target run",
215
+ nextAction: "inspect run checkpoints with work_item_list",
216
+ },
217
+ CHECKPOINT_NOT_FAILED: {
218
+ prerequisite: "a failed checkpoint for rework authorization",
219
+ },
220
+ RUN_NOT_FOUND: {
221
+ prerequisite: "an existing owned plan run",
222
+ nextAction: "call work_item_list to read the current plan runs",
223
+ },
224
+ EXECUTION_NOT_FOUND: {
225
+ prerequisite: "an existing owned generic execution",
226
+ nextAction: "call work_item_list to read the current executions",
227
+ },
228
+ EXECUTION_SEALED: {
229
+ prerequisite: "an execution that is not sealed",
230
+ nextAction: "inspect execution status with work_item_list",
231
+ },
232
+ EXECUTION_INCOMPLETE: {
233
+ prerequisite: "accepted covered tasks before the checkpoint may start",
234
+ nextAction: "inspect task acceptance with work_item_list",
235
+ },
236
+ UNKNOWN_REFERENCE: {
237
+ prerequisite: "a referenced task/checkpoint present in the target run",
238
+ nextAction: "inspect the run with work_item_list",
239
+ },
240
+ TASK_NOT_FOUND: {
241
+ prerequisite: "a task present in the target execution",
242
+ },
243
+ STALE_CALLBACK: {
244
+ prerequisite: "a callback matching the current in-flight attempt",
245
+ },
246
+ NOT_ACCEPTED: {
247
+ prerequisite: "a currently applicable acceptance",
248
+ },
249
+ PREREQUISITES_NOT_ACCEPTED: {
250
+ prerequisite: "accepted dependency tasks",
251
+ nextAction: "inspect dependency status with work_item_list",
252
+ },
253
+ };
254
+ /** Bounded prerequisite/next-action guidance for a known failure family. */
255
+ export function failureGuidance(errorCode) {
256
+ return FAILURE_GUIDANCE[errorCode];
257
+ }
258
+ // END_BLOCK_GUIDANCE
259
+ // START_BLOCK_FAILURE_DTO
260
+ function isRecord(value) {
261
+ return typeof value === "object" && value !== null && !Array.isArray(value);
262
+ }
263
+ function isCategory(value) {
264
+ return (typeof value === "string" && WORKFLOW_FAILURE_CATEGORIES.includes(value));
265
+ }
266
+ function isOutcome(value) {
267
+ return (typeof value === "string" && WORKFLOW_MUTATION_OUTCOMES.includes(value));
268
+ }
269
+ function asString(value) {
270
+ return typeof value === "string" && value ? value : undefined;
271
+ }
272
+ /** Bound one failure string to a finite documented length without throwing. */
273
+ function boundText(value, max) {
274
+ if (value.length <= max)
275
+ return value;
276
+ return `${value.slice(0, Math.max(0, max - 1))}…`;
277
+ }
278
+ /**
279
+ * Bound failure issues to the shared finite contract limits. Called by every
280
+ * failure constructor and by normalization so a caller cannot smuggle an
281
+ * unbounded issue list or oversized path/message into a public failure.
282
+ */
283
+ export function boundContractIssues(issues) {
284
+ return issues.slice(0, MAX_CONTRACT_ISSUES).map((issue) => ({
285
+ code: issue.code,
286
+ path: boundText(issue.path, MAX_ISSUE_PATH_CHARS),
287
+ message: boundText(issue.message, MAX_ISSUE_MESSAGE_CHARS),
288
+ ...(issue.expected !== undefined
289
+ ? { expected: boundText(issue.expected, MAX_ISSUE_MESSAGE_CHARS) }
290
+ : {}),
291
+ ...(issue.received !== undefined
292
+ ? { received: boundText(issue.received, MAX_ISSUE_VALUE_CHARS) }
293
+ : {}),
294
+ }));
295
+ }
296
+ function asNumber(value) {
297
+ return typeof value === "number" && Number.isFinite(value) ? value : undefined;
298
+ }
299
+ function asStringArray(value) {
300
+ if (!Array.isArray(value))
301
+ return undefined;
302
+ return value.filter((entry) => typeof entry === "string");
303
+ }
304
+ const CONTRACT_ISSUE_CODE_SET = new Set([
305
+ "unrecognized_keys",
306
+ "invalid_value",
307
+ "invalid_type",
308
+ "invalid_format",
309
+ "too_small",
310
+ "too_big",
311
+ "missing_value",
312
+ "invalid_union",
313
+ "custom",
314
+ ]);
315
+ function asIssues(value) {
316
+ if (!Array.isArray(value))
317
+ return undefined;
318
+ return boundContractIssues(value.filter(isRecord).map((entry) => ({
319
+ code: (typeof entry.code === "string" && CONTRACT_ISSUE_CODE_SET.has(entry.code)
320
+ ? entry.code
321
+ : "custom"),
322
+ path: typeof entry.path === "string" ? entry.path : "(root)",
323
+ message: typeof entry.message === "string" ? entry.message : "invalid value",
324
+ ...(typeof entry.expected === "string" ? { expected: entry.expected } : {}),
325
+ ...(typeof entry.received === "string" ? { received: entry.received } : {}),
326
+ })));
327
+ }
328
+ /**
329
+ * Truthful `applied` for a known outcome. `unknown` returns undefined so a
330
+ * caller can never read an unobserved mutation as not applied.
331
+ */
332
+ function appliedForOutcome(outcome) {
333
+ if (outcome === "committed")
334
+ return true;
335
+ if (outcome === "not_applied" || outcome === "rolled_back")
336
+ return false;
337
+ return undefined;
338
+ }
339
+ /**
340
+ * Add a stable category and bounded guidance to one failure object without
341
+ * mutating it or parsing its message text. Existing errorCode and message are
342
+ * preserved; explicit caller-supplied category/guidance/context wins.
343
+ */
344
+ export function normalizeWorkflowFailure(failure) {
345
+ const errorCode = boundText(asString(failure.errorCode) ?? "UNKNOWN_FAILURE", MAX_FAILURE_FIELD_CHARS);
346
+ const category = isCategory(failure.category)
347
+ ? failure.category
348
+ : categoryForWorkflowErrorCode(errorCode);
349
+ const guidance = failureGuidance(errorCode);
350
+ const message = boundText(asString(failure.message) ?? errorCode, MAX_FAILURE_MESSAGE_CHARS);
351
+ const outcome = isOutcome(failure.outcome) ? failure.outcome : undefined;
352
+ const applied = typeof failure.applied === "boolean" ? failure.applied : appliedForOutcome(outcome);
353
+ const tool = asString(failure.tool);
354
+ const sessionId = asString(failure.sessionId);
355
+ const issues = asIssues(failure.issues);
356
+ const state = asString(failure.state);
357
+ const attempt = asNumber(failure.attempt);
358
+ const attemptStatus = asString(failure.attemptStatus);
359
+ const resultStatus = asString(failure.resultStatus);
360
+ const attemptBudget = asNumber(failure.attemptBudget);
361
+ const remainingAttempts = asNumber(failure.remainingAttempts);
362
+ const recoveryCount = asNumber(failure.recoveryCount);
363
+ const pendingReviewers = asStringArray(failure.pendingReviewers);
364
+ const prerequisite = asString(failure.prerequisite);
365
+ const nextAction = asString(failure.nextAction);
366
+ const existingWorkItemId = asString(failure.existingWorkItemId);
367
+ const runId = asString(failure.runId);
368
+ return {
369
+ ok: false,
370
+ errorCode,
371
+ category,
372
+ message,
373
+ ...(tool !== undefined ? { tool: boundText(tool, MAX_FAILURE_FIELD_CHARS) } : {}),
374
+ ...(sessionId !== undefined
375
+ ? { sessionId: boundText(sessionId, MAX_FAILURE_FIELD_CHARS) }
376
+ : {}),
377
+ ...(issues !== undefined ? { issues } : {}),
378
+ ...(state !== undefined ? { state: boundText(state, MAX_FAILURE_FIELD_CHARS) } : {}),
379
+ ...(attempt !== undefined ? { attempt } : {}),
380
+ ...(attemptStatus !== undefined
381
+ ? { attemptStatus: boundText(attemptStatus, MAX_FAILURE_FIELD_CHARS) }
382
+ : {}),
383
+ ...(resultStatus !== undefined
384
+ ? { resultStatus: boundText(resultStatus, MAX_FAILURE_FIELD_CHARS) }
385
+ : {}),
386
+ ...(attemptBudget !== undefined ? { attemptBudget } : {}),
387
+ ...(remainingAttempts !== undefined ? { remainingAttempts } : {}),
388
+ ...(recoveryCount !== undefined ? { recoveryCount } : {}),
389
+ ...(pendingReviewers !== undefined
390
+ ? {
391
+ pendingReviewers: pendingReviewers.map((entry) => boundText(entry, MAX_FAILURE_FIELD_CHARS)),
392
+ }
393
+ : {}),
394
+ ...(prerequisite !== undefined
395
+ ? { prerequisite: boundText(prerequisite, MAX_FAILURE_FIELD_CHARS) }
396
+ : guidance?.prerequisite !== undefined
397
+ ? { prerequisite: guidance.prerequisite }
398
+ : {}),
399
+ ...(nextAction !== undefined
400
+ ? { nextAction: boundText(nextAction, MAX_FAILURE_FIELD_CHARS) }
401
+ : guidance?.nextAction !== undefined
402
+ ? { nextAction: guidance.nextAction }
403
+ : {}),
404
+ ...(outcome !== undefined ? { outcome } : {}),
405
+ ...(applied !== undefined ? { applied } : {}),
406
+ ...(typeof failure.retrySafe === "boolean" ? { retrySafe: failure.retrySafe } : {}),
407
+ ...(existingWorkItemId !== undefined
408
+ ? { existingWorkItemId: boundText(existingWorkItemId, MAX_FAILURE_FIELD_CHARS) }
409
+ : {}),
410
+ ...(runId !== undefined ? { runId: boundText(runId, MAX_FAILURE_FIELD_CHARS) } : {}),
411
+ };
412
+ }
413
+ /**
414
+ * Normalize a public workflow result for output: failure envelopes (top-level
415
+ * and batch items) gain category/guidance, everything else is returned as-is.
416
+ * Input is never mutated. This is the producer-side representation step;
417
+ * `validateWorkflowToolResult` validates without repair.
418
+ */
419
+ export function normalizeWorkflowResult(value) {
420
+ if (Array.isArray(value)) {
421
+ return value.map(normalizeWorkflowResult);
422
+ }
423
+ if (!isRecord(value)) {
424
+ return value;
425
+ }
426
+ if (value.ok === false && typeof value.errorCode === "string") {
427
+ return normalizeWorkflowFailure(value);
428
+ }
429
+ if (Array.isArray(value.items)) {
430
+ return { ...value, items: value.items.map(normalizeWorkflowResult) };
431
+ }
432
+ return value;
433
+ }
434
+ /** Producer-side finalization: typed identity wrapper around `normalizeWorkflowResult`. */
435
+ export function finalizeWorkflowResult(value) {
436
+ return normalizeWorkflowResult(value);
437
+ }
438
+ /** Bounded caller-input failure carrying the exact tokenized issues. */
439
+ export function workflowInputFailure(toolId, sessionId, issues) {
440
+ return {
441
+ tool: boundText(toolId, MAX_FAILURE_FIELD_CHARS),
442
+ sessionId: boundText(sessionId, MAX_FAILURE_FIELD_CHARS),
443
+ ok: false,
444
+ errorCode: "INVALID_INPUT",
445
+ category: "input",
446
+ message: formatContractIssues(issues),
447
+ issues: boundContractIssues(issues),
448
+ };
449
+ }
450
+ /**
451
+ * Trusted-host-context failure: a missing workspace root, plan loader, or
452
+ * authorization lookup is not a caller argument error.
453
+ */
454
+ export function workflowHostContextFailure(toolId, sessionId, message) {
455
+ return {
456
+ tool: boundText(toolId, MAX_FAILURE_FIELD_CHARS),
457
+ sessionId: boundText(sessionId, MAX_FAILURE_FIELD_CHARS),
458
+ ok: false,
459
+ errorCode: "HOST_CONTEXT_UNAVAILABLE",
460
+ category: "host_context",
461
+ message: boundText(message, MAX_FAILURE_MESSAGE_CHARS),
462
+ };
463
+ }
464
+ /**
465
+ * Internal result-contract/serialization failure with the observed outcome.
466
+ * Never reported as caller input and never invites an unqualified replay. An
467
+ * `unknown` outcome omits `applied` entirely.
468
+ */
469
+ export function workflowInternalResultFailure(options) {
470
+ const applied = appliedForOutcome(options.outcome);
471
+ return {
472
+ tool: boundText(options.tool, MAX_FAILURE_FIELD_CHARS),
473
+ ...(options.sessionId !== undefined
474
+ ? { sessionId: boundText(options.sessionId, MAX_FAILURE_FIELD_CHARS) }
475
+ : {}),
476
+ ok: false,
477
+ errorCode: "RESULT_CONTRACT_INVALID",
478
+ category: "internal",
479
+ message: boundText(options.message, MAX_FAILURE_MESSAGE_CHARS),
480
+ outcome: options.outcome,
481
+ ...(applied !== undefined ? { applied } : {}),
482
+ retrySafe: false,
483
+ nextAction: "inspect current workflow state with work_item_list before retrying; a reporting failure does not prove the mutation was skipped",
484
+ };
485
+ }
486
+ /**
487
+ * Thrown owned diagnostic carrying a stable code and category. The message is
488
+ * bounded for failure diagnostics while preserving its leading code/prefix.
489
+ */
490
+ export class WorkflowDiagnosticError extends Error {
491
+ code;
492
+ category;
493
+ outcome;
494
+ constructor(code, category, message, outcome) {
495
+ super(boundText(message, MAX_FAILURE_MESSAGE_CHARS));
496
+ this.name = "WorkflowDiagnosticError";
497
+ this.code = boundText(code, MAX_FAILURE_FIELD_CHARS);
498
+ this.category = category;
499
+ this.outcome = outcome;
500
+ }
501
+ }
502
+ // END_BLOCK_FAILURE_DTO
503
+ // START_BLOCK_SCHEMAS
504
+ const schema = tool.schema;
505
+ const WORK_ITEM_STATES = [
506
+ "open",
507
+ "awaiting_implementer",
508
+ "awaiting_reviews",
509
+ "awaiting_acceptance",
510
+ "needs_context",
511
+ "blocked",
512
+ "ready_to_close",
513
+ "closed",
514
+ ];
515
+ const DELEGATED_NEXT_ACTIONS = [
516
+ "launch_implementer",
517
+ "await_result",
518
+ "decide",
519
+ "recover",
520
+ "recover_with_user_authorization",
521
+ "launch_blocked",
522
+ "close",
523
+ "closed",
524
+ ];
525
+ const RECOVERY_KINDS = ["resume", "autonomous_grant", "user_grant", "advance_grant"];
526
+ const EXECUTION_SOURCE_KINDS = ["native-package", "conversation-scoped", "provided-plan"];
527
+ const EXECUTION_STATES = ["preparing", "active", "sealed"];
528
+ const TASK_STATUSES = ["pending", "launched", "accepted", "superseded"];
529
+ const REVIEWER_RESULT_STATUSES = ["PASS", "FAIL", "NEEDS_CONTEXT"];
530
+ const REVIEWER_AGENTS = ["vv-spec-reviewer", "vv-code-reviewer"];
531
+ const CHECKPOINT_KINDS = ["milestone", "final"];
532
+ const CHECKPOINT_STATUSES = ["pending", "in_review", "passed", "failed"];
533
+ const CHECKPOINT_OUTCOMES = ["passed", "failed", "stale", "stopped", "incomplete"];
534
+ const CHECKPOINT_HISTORY_OUTCOMES = ["passed", "failed", "stale", "stopped"];
535
+ const CHECKPOINT_NEXT_ACTIONS = [
536
+ "start",
537
+ "collect_and_verify",
538
+ "start_next_generation",
539
+ "recover",
540
+ "recover_with_user_authorization",
541
+ "passed",
542
+ "blocked",
543
+ ];
544
+ const CHECKPOINT_VERIFY_OUTCOMES = [
545
+ "passed",
546
+ "failed",
547
+ "stale",
548
+ "stopped",
549
+ "incomplete",
550
+ "already-passed",
551
+ ];
552
+ const GENERIC_REVIEW_OUTCOMES = ["in_progress", "passed", "failed", "stopped"];
553
+ const AUTHORITY_PROVENANCE = ["controller_delegated", "user_observed", "unspecified"];
554
+ const REVOCATION_KINDS = ["narrow", "revoke"];
555
+ const REVIEW_ROUND_STATUSES = ["active", "completed"];
556
+ const CONTRACT_ISSUE_CODES = [
557
+ "unrecognized_keys",
558
+ "invalid_value",
559
+ "invalid_type",
560
+ "invalid_format",
561
+ "too_small",
562
+ "too_big",
563
+ "missing_value",
564
+ "invalid_union",
565
+ "custom",
566
+ ];
567
+ const contractIssueSchema = schema.strictObject({
568
+ code: schema.enum(CONTRACT_ISSUE_CODES),
569
+ path: schema.string().max(MAX_ISSUE_PATH_CHARS),
570
+ message: schema.string().max(MAX_ISSUE_MESSAGE_CHARS),
571
+ expected: schema.string().max(MAX_ISSUE_MESSAGE_CHARS).optional(),
572
+ received: schema.string().max(MAX_ISSUE_VALUE_CHARS).optional(),
573
+ });
574
+ // START_BLOCK_FAILURE_SCHEMA
575
+ /** Enumerated failure tail shared by the generic and per-tool failure schemas. */
576
+ const failureTailShape = {
577
+ sessionId: schema.string().max(MAX_FAILURE_FIELD_CHARS).optional(),
578
+ ok: schema.literal(false),
579
+ errorCode: schema.string().max(MAX_FAILURE_FIELD_CHARS),
580
+ category: schema.enum(WORKFLOW_FAILURE_CATEGORIES),
581
+ message: schema.string().max(MAX_FAILURE_MESSAGE_CHARS),
582
+ issues: schema.array(contractIssueSchema).max(MAX_CONTRACT_ISSUES).optional(),
583
+ state: schema.string().max(MAX_FAILURE_FIELD_CHARS).optional(),
584
+ attempt: schema.number().optional(),
585
+ attemptStatus: schema.string().max(MAX_FAILURE_FIELD_CHARS).optional(),
586
+ resultStatus: schema.string().max(MAX_FAILURE_FIELD_CHARS).optional(),
587
+ attemptBudget: schema.number().optional(),
588
+ remainingAttempts: schema.number().optional(),
589
+ recoveryCount: schema.number().optional(),
590
+ pendingReviewers: schema.array(schema.string().max(MAX_FAILURE_FIELD_CHARS)).optional(),
591
+ prerequisite: schema.string().max(MAX_FAILURE_FIELD_CHARS).optional(),
592
+ nextAction: schema.string().max(MAX_FAILURE_FIELD_CHARS).optional(),
593
+ outcome: schema.enum(WORKFLOW_MUTATION_OUTCOMES).optional(),
594
+ applied: schema.boolean().optional(),
595
+ retrySafe: schema.boolean().optional(),
596
+ existingWorkItemId: schema.string().max(MAX_FAILURE_FIELD_CHARS).optional(),
597
+ runId: schema.string().max(MAX_FAILURE_FIELD_CHARS).optional(),
598
+ };
599
+ /** Generic failure schema; `tool` stays open so any owned tool can be validated. */
600
+ export const workflowFailureSchema = schema.strictObject({
601
+ tool: schema.string(),
602
+ ...failureTailShape,
603
+ });
604
+ /** Per-tool failure schema whose `tool` literal rejects a failure tagged for another tool. */
605
+ function failureSchemaFor(toolId) {
606
+ return schema.strictObject({ tool: schema.literal(toolId), ...failureTailShape });
607
+ }
608
+ // END_BLOCK_FAILURE_SCHEMA
609
+ const resultExcerptSchema = schema.strictObject({
610
+ source: schema.enum(["parsed_body", "normalized_output"]),
611
+ text: schema.string(),
612
+ truncated: schema.boolean(),
613
+ originalLength: schema.number(),
614
+ maxLength: schema.number(),
615
+ });
616
+ const reviewRoundResultSchema = schema.strictObject({
617
+ reviewer: schema.enum(REVIEWER_ROLES),
618
+ agent: schema.enum(REVIEWER_AGENTS),
619
+ status: schema.enum(REVIEWER_RESULT_STATUSES),
620
+ completedAt: schema.string(),
621
+ resultExcerpt: resultExcerptSchema.optional(),
622
+ });
623
+ const reviewRoundSchema = schema.strictObject({
624
+ round: schema.number(),
625
+ requiredReviewers: schema.array(schema.enum(REVIEWER_ROLES)),
626
+ pendingReviewers: schema.array(schema.enum(REVIEWER_ROLES)),
627
+ inFlightReviewers: schema.array(schema.enum(REVIEWER_ROLES)),
628
+ completedReviewers: schema.array(schema.enum(REVIEWER_ROLES)),
629
+ results: schema.strictObject({
630
+ spec: reviewRoundResultSchema.optional(),
631
+ code: reviewRoundResultSchema.optional(),
632
+ }),
633
+ status: schema.enum(REVIEW_ROUND_STATUSES),
634
+ createdAt: schema.string(),
635
+ completedAt: schema.string().optional(),
636
+ });
637
+ const delegatedAttemptViewSchema = schema.strictObject({
638
+ attempt: schema.number(),
639
+ status: schema.enum(["in_flight", "completed", "failed", "report_rejected"]),
640
+ resultStatus: schema.enum(["DONE", "DONE_WITH_CONCERNS", "NEEDS_CONTEXT", "BLOCKED"]).optional(),
641
+ completedAt: schema.string().optional(),
642
+ reportRejected: schema.boolean(),
643
+ });
644
+ const delegatedGuidanceViewSchema = schema.strictObject({
645
+ nextAction: schema.enum(DELEGATED_NEXT_ACTIONS),
646
+ launchEligible: schema.boolean(),
647
+ blockers: schema.array(schema.string()),
648
+ prerequisites: schema.array(schema.string()),
649
+ requiresConcernsDisposition: schema.boolean(),
650
+ });
651
+ const delegatedWorkItemViewSchema = schema.strictObject({
652
+ writeScope: schema.array(schema.string()),
653
+ planRunId: schema.string().optional(),
654
+ planTaskId: schema.string().optional(),
655
+ attempts: schema.number(),
656
+ inFlightAttempt: schema.boolean(),
657
+ decisions: schema.number(),
658
+ accepted: schema.boolean(),
659
+ acceptedAttempt: schema.number().optional(),
660
+ reworkCount: schema.number(),
661
+ attemptBudget: schema.number(),
662
+ remainingAttempts: schema.number(),
663
+ recoveryCount: schema.number(),
664
+ autonomousGrantConsumed: schema.boolean(),
665
+ reportRejectionCount: schema.number(),
666
+ nextAction: schema.enum(DELEGATED_NEXT_ACTIONS),
667
+ latestAttempt: delegatedAttemptViewSchema.optional(),
668
+ guidance: delegatedGuidanceViewSchema.optional(),
669
+ });
670
+ const workItemViewSchema = schema.strictObject({
671
+ workItemId: schema.string(),
672
+ header: schema.string(),
673
+ key: schema.string(),
674
+ title: schema.string(),
675
+ mode: schema.enum(WORK_ITEM_MODES),
676
+ requiredReviewers: schema.array(schema.enum(REVIEWER_ROLES)),
677
+ state: schema.enum(WORK_ITEM_STATES),
678
+ specReviewCount: schema.number(),
679
+ codeReviewCount: schema.number(),
680
+ reviewRound: schema.number(),
681
+ currentRound: reviewRoundSchema.optional(),
682
+ resultExcerpt: resultExcerptSchema.optional(),
683
+ completedReviewRoundCount: schema.number(),
684
+ createdAt: schema.string(),
685
+ updatedAt: schema.string(),
686
+ closedAt: schema.string().optional(),
687
+ delegated: delegatedWorkItemViewSchema.optional(),
688
+ });
689
+ const executionTaskViewSchema = schema.strictObject({
690
+ taskId: schema.string(),
691
+ workItemId: schema.string(),
692
+ status: schema.enum(TASK_STATUSES),
693
+ requiredReviewers: schema.array(schema.enum(REVIEWER_ROLES)),
694
+ dependsOn: schema.array(schema.string()),
695
+ blockedBy: schema.array(schema.string()),
696
+ latestAttempt: delegatedAttemptViewSchema.optional(),
697
+ });
698
+ const executionCheckpointReviewStateSchema = schema.strictObject({
699
+ reviewWorkItemId: schema.string(),
700
+ generation: schema.number(),
701
+ coveredAttemptIds: schema.array(schema.string()),
702
+ recordedReviewers: schema.array(schema.string()),
703
+ });
704
+ const executionCheckpointHistorySchema = schema.strictObject({
705
+ generation: schema.number(),
706
+ outcome: schema.enum(CHECKPOINT_HISTORY_OUTCOMES),
707
+ completedAt: schema.string(),
708
+ });
709
+ const executionCheckpointViewSchema = schema.strictObject({
710
+ checkpointId: schema.string(),
711
+ kind: schema.enum(CHECKPOINT_KINDS),
712
+ covers: schema.array(schema.string()),
713
+ requiredReviewers: schema.array(schema.enum(REVIEWER_ROLES)),
714
+ status: schema.string().optional(),
715
+ reviewWorkItemId: schema.string().optional(),
716
+ generation: schema.number().optional(),
717
+ recoveryCount: schema.number().optional(),
718
+ attempts: schema.number().optional(),
719
+ generationBudget: schema.number().optional(),
720
+ remainingGenerations: schema.number().optional(),
721
+ lastOutcome: schema.enum(CHECKPOINT_OUTCOMES).optional(),
722
+ nextAction: schema.enum(CHECKPOINT_NEXT_ACTIONS).optional(),
723
+ prerequisite: schema.string().optional(),
724
+ currentReview: executionCheckpointReviewStateSchema.optional(),
725
+ history: schema.array(executionCheckpointHistorySchema).optional(),
726
+ });
727
+ const executionAuthorityViewSchema = schema.strictObject({
728
+ authorityId: schema.string(),
729
+ availableUnits: schema.number(),
730
+ revoked: schema.boolean(),
731
+ stages: schema.array(schema.enum(AUTHORITY_STAGES)),
732
+ reservedStops: schema.array(schema.enum(AUTHORITY_STAGES)),
733
+ prerequisites: schema.array(schema.string()),
734
+ });
735
+ const executionViewSchema = schema.strictObject({
736
+ runId: schema.string(),
737
+ sessionId: schema.string(),
738
+ executionKey: schema.string(),
739
+ sourceKind: schema.enum(EXECUTION_SOURCE_KINDS),
740
+ goal: schema.string(),
741
+ state: schema.enum(EXECUTION_STATES),
742
+ revision: schema.number(),
743
+ tasks: schema.array(executionTaskViewSchema),
744
+ checkpoints: schema.array(executionCheckpointViewSchema),
745
+ authority: schema.array(executionAuthorityViewSchema).optional(),
746
+ });
747
+ const delegatedRunTaskViewSchema = schema.strictObject({
748
+ taskId: schema.string(),
749
+ workItemId: schema.string(),
750
+ });
751
+ const delegatedRunCheckpointViewSchema = schema.strictObject({
752
+ checkpointId: schema.string(),
753
+ kind: schema.enum(CHECKPOINT_KINDS),
754
+ afterWave: schema.string(),
755
+ covers: schema.array(schema.string()),
756
+ scope: schema.array(schema.string()),
757
+ reviewers: schema.array(schema.enum(REVIEWER_ROLES)),
758
+ status: schema.enum(CHECKPOINT_STATUSES),
759
+ attempts: schema.number(),
760
+ lastOutcome: schema.enum(CHECKPOINT_OUTCOMES).optional(),
761
+ generationBudget: schema.number(),
762
+ remainingGenerations: schema.number(),
763
+ recoveryCount: schema.number(),
764
+ nextAction: schema.enum(CHECKPOINT_NEXT_ACTIONS),
765
+ prerequisite: schema.string().optional(),
766
+ currentReview: schema
767
+ .strictObject({
768
+ reviewWorkItemId: schema.string(),
769
+ generation: schema.number(),
770
+ coveredAttemptIds: schema.array(schema.string()),
771
+ recordedReviewers: schema.array(schema.string()),
772
+ })
773
+ .optional(),
774
+ history: schema.array(schema.strictObject({
775
+ generation: schema.number(),
776
+ outcome: schema.enum(CHECKPOINT_HISTORY_OUTCOMES),
777
+ fingerprint: schema.string(),
778
+ completedAt: schema.string(),
779
+ })),
780
+ });
781
+ const delegatedRunViewSchema = schema.strictObject({
782
+ runId: schema.string(),
783
+ sessionId: schema.string(),
784
+ planPath: schema.string(),
785
+ specPath: schema.string(),
786
+ workspaceRoot: schema.string(),
787
+ status: schema.enum(["active", "sealed"]),
788
+ registeredAt: schema.string(),
789
+ sealedAt: schema.string().optional(),
790
+ finalCheckpointId: schema.string().optional(),
791
+ tasks: schema.array(delegatedRunTaskViewSchema),
792
+ checkpoints: schema.array(delegatedRunCheckpointViewSchema),
793
+ });
794
+ // END_BLOCK_SCHEMAS
795
+ // START_BLOCK_TOOL_RESULT_SCHEMAS
796
+ const openItemFailureSchema = schema.strictObject({
797
+ ok: schema.literal(false),
798
+ errorCode: schema.string().max(MAX_FAILURE_FIELD_CHARS),
799
+ category: schema.enum(WORKFLOW_FAILURE_CATEGORIES).optional(),
800
+ message: schema.string().max(MAX_FAILURE_MESSAGE_CHARS),
801
+ existingWorkItemId: schema.string().max(MAX_FAILURE_FIELD_CHARS).optional(),
802
+ state: schema.string().max(MAX_FAILURE_FIELD_CHARS).optional(),
803
+ prerequisite: schema.string().max(MAX_FAILURE_FIELD_CHARS).optional(),
804
+ nextAction: schema.string().max(MAX_FAILURE_FIELD_CHARS).optional(),
805
+ });
806
+ const openItemSuccessSchema = schema.strictObject({
807
+ ok: schema.literal(true),
808
+ reused: schema.boolean(),
809
+ workItemId: schema.string(),
810
+ header: schema.string(),
811
+ key: schema.string(),
812
+ title: schema.string(),
813
+ mode: schema.enum(WORK_ITEM_MODES),
814
+ requiredReviewers: schema.array(schema.enum(REVIEWER_ROLES)),
815
+ state: schema.enum(WORK_ITEM_STATES),
816
+ specReviewCount: schema.number(),
817
+ codeReviewCount: schema.number(),
818
+ reviewRound: schema.number(),
819
+ currentRound: reviewRoundSchema.optional(),
820
+ resultExcerpt: resultExcerptSchema.optional(),
821
+ completedReviewRoundCount: schema.number(),
822
+ createdAt: schema.string(),
823
+ updatedAt: schema.string(),
824
+ closedAt: schema.string().optional(),
825
+ delegated: delegatedWorkItemViewSchema.optional(),
826
+ });
827
+ const workItemOpenBatchSchema = schema.strictObject({
828
+ tool: schema.literal("work_item_open"),
829
+ sessionId: schema.string(),
830
+ items: schema.array(schema.union([openItemSuccessSchema, openItemFailureSchema])),
831
+ });
832
+ const workItemOpenRegisterSchema = schema.strictObject({
833
+ tool: schema.literal("work_item_open"),
834
+ sessionId: schema.string(),
835
+ ok: schema.literal(true),
836
+ action: schema.literal("register"),
837
+ runId: schema.string(),
838
+ reused: schema.boolean(),
839
+ execution: executionViewSchema,
840
+ });
841
+ const workItemOpenAmendSchema = schema.strictObject({
842
+ tool: schema.literal("work_item_open"),
843
+ sessionId: schema.string(),
844
+ ok: schema.literal(true),
845
+ action: schema.literal("amend"),
846
+ runId: schema.string(),
847
+ revision: schema.number(),
848
+ execution: executionViewSchema,
849
+ });
850
+ const workflowContractIdentitySchema = schema.strictObject({
851
+ packageName: schema.string(),
852
+ packageVersion: schema.string(),
853
+ toolContractRevision: schema.string(),
854
+ referencePath: schema.string(),
855
+ });
856
+ const workItemListViewSchema = schema.strictObject({
857
+ tool: schema.literal("work_item_list"),
858
+ sessionId: schema.string(),
859
+ includeClosed: schema.boolean(),
860
+ items: schema.array(workItemViewSchema),
861
+ planRuns: schema.array(delegatedRunViewSchema).optional(),
862
+ executions: schema.array(executionViewSchema).optional(),
863
+ contract: workflowContractIdentitySchema,
864
+ });
865
+ const workItemCloseSuccessSchema = schema.strictObject({
866
+ tool: schema.literal("work_item_close"),
867
+ sessionId: schema.string(),
868
+ ok: schema.literal(true),
869
+ workItemId: schema.string(),
870
+ header: schema.string(),
871
+ state: schema.enum(WORK_ITEM_STATES),
872
+ closedAt: schema.string(),
873
+ });
874
+ const decideReworkSuccessSchema = schema.strictObject({
875
+ tool: schema.literal("work_item_decide"),
876
+ sessionId: schema.string(),
877
+ ok: schema.literal(true),
878
+ action: schema.literal("rework"),
879
+ workItemId: schema.string(),
880
+ reworkId: schema.string(),
881
+ grantedAttempts: schema.number(),
882
+ state: schema.enum(WORK_ITEM_STATES).optional(),
883
+ });
884
+ const decideRecoverSuccessSchema = schema.strictObject({
885
+ tool: schema.literal("work_item_decide"),
886
+ sessionId: schema.string(),
887
+ ok: schema.literal(true),
888
+ action: schema.literal("recover"),
889
+ workItemId: schema.string(),
890
+ recoveryId: schema.string(),
891
+ kind: schema.enum(RECOVERY_KINDS),
892
+ attemptBudget: schema.number(),
893
+ remainingAttempts: schema.number(),
894
+ state: schema.enum(WORK_ITEM_STATES),
895
+ nextAction: schema.enum(DELEGATED_NEXT_ACTIONS),
896
+ });
897
+ const decideDecisionSuccessSchema = schema.strictObject({
898
+ tool: schema.literal("work_item_decide"),
899
+ sessionId: schema.string(),
900
+ ok: schema.literal(true),
901
+ action: schema.enum(["accept", "request_changes"]),
902
+ workItemId: schema.string(),
903
+ attempt: schema.number(),
904
+ decisionId: schema.string(),
905
+ state: schema.enum(WORK_ITEM_STATES),
906
+ });
907
+ const checkpointRegisterSuccessSchema = schema.strictObject({
908
+ tool: schema.literal("work_checkpoint"),
909
+ sessionId: schema.string(),
910
+ ok: schema.literal(true),
911
+ action: schema.literal("register"),
912
+ runId: schema.string(),
913
+ reused: schema.boolean(),
914
+ tasks: schema.number(),
915
+ checkpoints: schema.number(),
916
+ });
917
+ const checkpointStartSuccessSchema = schema.strictObject({
918
+ tool: schema.literal("work_checkpoint"),
919
+ sessionId: schema.string(),
920
+ ok: schema.literal(true),
921
+ action: schema.literal("start"),
922
+ runId: schema.string(),
923
+ checkpointId: schema.string(),
924
+ generation: schema.number().optional(),
925
+ reviewWorkItemId: schema.string(),
926
+ header: schema.string(),
927
+ reviewersToLaunch: schema.array(schema.enum(REVIEWER_ROLES)),
928
+ coveredAttemptIds: schema.array(schema.string()).optional(),
929
+ });
930
+ const checkpointVerifyNativeSuccessSchema = schema.strictObject({
931
+ tool: schema.literal("work_checkpoint"),
932
+ sessionId: schema.string(),
933
+ ok: schema.literal(true),
934
+ action: schema.literal("verify"),
935
+ runId: schema.string(),
936
+ checkpointId: schema.string(),
937
+ outcome: schema.enum(CHECKPOINT_VERIFY_OUTCOMES),
938
+ snapshotCurrent: schema.boolean(),
939
+ sealedRun: schema.boolean().optional(),
940
+ });
941
+ const checkpointReviewSuccessSchema = schema.strictObject({
942
+ tool: schema.literal("work_checkpoint"),
943
+ sessionId: schema.string(),
944
+ ok: schema.literal(true),
945
+ action: schema.enum(["review", "bind", "verify"]),
946
+ runId: schema.string(),
947
+ checkpointId: schema.string(),
948
+ reviewer: schema.enum(REVIEWER_ROLES).optional(),
949
+ outcome: schema.enum(GENERIC_REVIEW_OUTCOMES),
950
+ checkpointStatus: schema.enum(CHECKPOINT_STATUSES),
951
+ });
952
+ const checkpointRecoverSuccessSchema = schema.strictObject({
953
+ tool: schema.literal("work_checkpoint"),
954
+ sessionId: schema.string(),
955
+ ok: schema.literal(true),
956
+ action: schema.literal("recover"),
957
+ runId: schema.string(),
958
+ checkpointId: schema.string(),
959
+ recoveryId: schema.string(),
960
+ kind: schema.enum(RECOVERY_KINDS),
961
+ checkpointStatus: schema.enum(CHECKPOINT_STATUSES),
962
+ generationBudget: schema.number().optional(),
963
+ settledStoppedGeneration: schema.number().optional(),
964
+ lastOutcome: schema.enum(CHECKPOINT_OUTCOMES).optional(),
965
+ });
966
+ const checkpointAmendSuccessSchema = schema.strictObject({
967
+ tool: schema.literal("work_checkpoint"),
968
+ sessionId: schema.string(),
969
+ runId: schema.string(),
970
+ ok: schema.literal(true),
971
+ action: schema.literal("amend"),
972
+ revision: schema.number(),
973
+ execution: executionViewSchema,
974
+ });
975
+ const checkpointCompleteSuccessSchema = schema.strictObject({
976
+ tool: schema.literal("work_checkpoint"),
977
+ sessionId: schema.string(),
978
+ runId: schema.string(),
979
+ ok: schema.literal(true),
980
+ action: schema.literal("complete"),
981
+ reviewStatus: schema.enum(["controller_accepted", "independently_reviewed"]),
982
+ execution: executionViewSchema,
983
+ });
984
+ const checkpointAuthorizeSuccessSchema = schema.strictObject({
985
+ tool: schema.literal("work_checkpoint"),
986
+ sessionId: schema.string(),
987
+ runId: schema.string(),
988
+ ok: schema.literal(true),
989
+ action: schema.literal("authorize"),
990
+ authorityId: schema.string(),
991
+ reused: schema.boolean().optional(),
992
+ units: schema.number().optional(),
993
+ extensions: schema.number().optional(),
994
+ availableUnits: schema.number(),
995
+ });
996
+ const checkpointRecordApprovalSuccessSchema = schema.strictObject({
997
+ tool: schema.literal("work_checkpoint"),
998
+ sessionId: schema.string(),
999
+ runId: schema.string(),
1000
+ ok: schema.literal(true),
1001
+ action: schema.literal("record_approval"),
1002
+ approvalId: schema.string(),
1003
+ stage: schema.enum(AUTHORITY_STAGES),
1004
+ provenance: schema.enum(AUTHORITY_PROVENANCE),
1005
+ });
1006
+ const checkpointRevokeSuccessSchema = schema.strictObject({
1007
+ tool: schema.literal("work_checkpoint"),
1008
+ sessionId: schema.string(),
1009
+ runId: schema.string(),
1010
+ ok: schema.literal(true),
1011
+ action: schema.literal("revoke_authority"),
1012
+ authorityId: schema.string(),
1013
+ kind: schema.enum(REVOCATION_KINDS),
1014
+ availableUnits: schema.number(),
1015
+ stages: schema.array(schema.enum(AUTHORITY_STAGES)),
1016
+ });
1017
+ const workItemOpenResultSchema = schema.union([
1018
+ workItemOpenBatchSchema,
1019
+ workItemOpenRegisterSchema,
1020
+ workItemOpenAmendSchema,
1021
+ failureSchemaFor("work_item_open"),
1022
+ ]);
1023
+ const workItemListResultSchema = schema.union([
1024
+ workItemListViewSchema,
1025
+ failureSchemaFor("work_item_list"),
1026
+ ]);
1027
+ const workItemCloseResultSchema = schema.union([
1028
+ workItemCloseSuccessSchema,
1029
+ failureSchemaFor("work_item_close"),
1030
+ ]);
1031
+ const workItemDecideResultSchema = schema.union([
1032
+ decideReworkSuccessSchema,
1033
+ decideRecoverSuccessSchema,
1034
+ decideDecisionSuccessSchema,
1035
+ failureSchemaFor("work_item_decide"),
1036
+ ]);
1037
+ const workCheckpointResultSchema = schema.union([
1038
+ checkpointRegisterSuccessSchema,
1039
+ checkpointStartSuccessSchema,
1040
+ checkpointVerifyNativeSuccessSchema,
1041
+ checkpointReviewSuccessSchema,
1042
+ checkpointRecoverSuccessSchema,
1043
+ checkpointAmendSuccessSchema,
1044
+ checkpointCompleteSuccessSchema,
1045
+ checkpointAuthorizeSuccessSchema,
1046
+ checkpointRecordApprovalSuccessSchema,
1047
+ checkpointRevokeSuccessSchema,
1048
+ failureSchemaFor("work_checkpoint"),
1049
+ ]);
1050
+ /** Closed per-tool result schema map; exported for inspection/catalog consumers. */
1051
+ export const workflowToolResultSchemas = {
1052
+ work_item_open: workItemOpenResultSchema,
1053
+ work_item_list: workItemListResultSchema,
1054
+ work_item_close: workItemCloseResultSchema,
1055
+ work_item_decide: workItemDecideResultSchema,
1056
+ work_checkpoint: workCheckpointResultSchema,
1057
+ };
1058
+ /** Closed result schema for one owned workflow tool id, preserving its inferred type. */
1059
+ export function workflowToolResultSchema(toolId) {
1060
+ return workflowToolResultSchemas[toolId];
1061
+ }
1062
+ /**
1063
+ * Producer/test validation of one owned workflow tool result against its
1064
+ * closed schema. It does not normalize or repair failures first: a malformed
1065
+ * producer representation fails rather than being made compliant. Producers
1066
+ * apply `finalizeWorkflowResult` before this is used on their output.
1067
+ * Never call this as a throw-after-side-effect execute wrapper.
1068
+ */
1069
+ export function validateWorkflowToolResult(toolId, value) {
1070
+ const schemaForTool = workflowToolResultSchemas[toolId];
1071
+ const result = schemaForTool.safeParse(value);
1072
+ if (result.success) {
1073
+ return { ok: true };
1074
+ }
1075
+ // Reuse the bounded issue formatter from the shared contract primitives.
1076
+ return { ok: false, issues: toContractIssues(result.error) };
1077
+ }
1078
+ // END_BLOCK_TOOL_RESULT_SCHEMAS
1079
+ // START_BLOCK_SERIALIZATION
1080
+ /**
1081
+ * Serialize a public workflow result. Producers finalize category/guidance
1082
+ * before this point, so this is a pure presentation step (no silent repair). If
1083
+ * serialization itself fails after execution, produce a bounded truthful
1084
+ * internal error carrying the observed outcome instead of throwing raw or
1085
+ * inviting an unqualified replay. An `unknown` outcome omits `applied`.
1086
+ */
1087
+ export function serializeWorkflowResult(value, options) {
1088
+ try {
1089
+ const text = JSON.stringify(value, null, 2);
1090
+ return text === undefined ? "null" : text;
1091
+ }
1092
+ catch {
1093
+ const tool = typeof value.tool === "string" ? value.tool : "workflow";
1094
+ const outcome = options?.outcome ?? "unknown";
1095
+ const applied = appliedForOutcome(outcome);
1096
+ const fallback = {
1097
+ tool,
1098
+ ok: false,
1099
+ errorCode: "RESULT_SERIALIZATION_FAILED",
1100
+ category: "internal",
1101
+ message: "the workflow result could not be serialized after execution; the mutation outcome must be inspected, not assumed",
1102
+ outcome,
1103
+ ...(applied !== undefined ? { applied } : {}),
1104
+ retrySafe: false,
1105
+ nextAction: "inspect current workflow state with work_item_list before retrying; a reporting failure does not prove the mutation was skipped",
1106
+ };
1107
+ return JSON.stringify(fallback, null, 2);
1108
+ }
1109
+ }
1110
+ // END_BLOCK_SERIALIZATION
1111
+ //# sourceMappingURL=results.js.map