@hunsu/protocol 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/LICENSE +157 -0
  2. package/dist/command-decoder.d.ts +5 -0
  3. package/dist/command-decoder.js +329 -0
  4. package/dist/constants.d.ts +15 -0
  5. package/dist/constants.js +15 -0
  6. package/dist/decoder-helpers.d.ts +58 -0
  7. package/dist/decoder-helpers.js +1757 -0
  8. package/dist/errors.d.ts +14 -0
  9. package/dist/errors.js +28 -0
  10. package/dist/event-decoder.d.ts +5 -0
  11. package/dist/event-decoder.js +232 -0
  12. package/dist/index.d.ts +10 -0
  13. package/dist/index.js +10 -0
  14. package/dist/lifecycle.d.ts +67 -0
  15. package/dist/lifecycle.js +271 -0
  16. package/dist/model.d.ts +1058 -0
  17. package/dist/model.js +1 -0
  18. package/dist/primitives.d.ts +59 -0
  19. package/dist/primitives.js +135 -0
  20. package/dist/projection.d.ts +8 -0
  21. package/dist/projection.js +875 -0
  22. package/dist/prompt-template.d.ts +12 -0
  23. package/dist/prompt-template.js +83 -0
  24. package/dist/protocol-validation.d.ts +36 -0
  25. package/dist/protocol-validation.js +1139 -0
  26. package/dist/result.d.ts +20 -0
  27. package/dist/result.js +21 -0
  28. package/dist/workflow-helpers.d.ts +31 -0
  29. package/dist/workflow-helpers.js +193 -0
  30. package/dist/workflow.d.ts +10 -0
  31. package/dist/workflow.js +594 -0
  32. package/package.json +30 -0
  33. package/src/command-decoder.ts +292 -0
  34. package/src/constants.ts +27 -0
  35. package/src/decoder-helpers.ts +1672 -0
  36. package/src/errors.ts +38 -0
  37. package/src/event-decoder.ts +225 -0
  38. package/src/index.ts +43 -0
  39. package/src/lifecycle.ts +403 -0
  40. package/src/model.ts +1233 -0
  41. package/src/primitives.ts +196 -0
  42. package/src/projection.ts +1081 -0
  43. package/src/prompt-template.ts +97 -0
  44. package/src/protocol-validation.ts +1252 -0
  45. package/src/result.ts +35 -0
  46. package/src/workflow-helpers.ts +239 -0
  47. package/src/workflow.ts +753 -0
@@ -0,0 +1,1757 @@
1
+ import { ARTIFACT_ACTION_KINDS, ARTIFACT_ACTION_SOURCE_SCOPES, ARTIFACT_KINDS, DESTINATION_SOURCES, DESTINATION_STATUSES, DOMAIN_ROLES, HUNSU_DRAFT_STATUSES, LINE_STATUSES, MOVE_OUTCOMES, SKILL_DRAFT_STATUSES } from "./constants.js";
2
+ import { workflowError } from "./errors.js";
3
+ import { makeAgentConversationHash, makeArtifactActionId, makeArtifactId, makeDestinationAcceptanceCriterion, makeDestinationConstraint, makeDestinationId, makeDestinationNotes, makeDestinationTitle, makeExecuteId, makeTeamName, makeEvidenceText, makeFailureReason, makeHunsuDraftId, makeHunsuId, makeLineId, makeMoveCommit, makeMoveId, makeNodeId, makeNonEmptyArray, makeNonEmptyText, makePositiveInteger, makeRequestGoal, makeRequestId, makeRequestTitle, makeRiskText, makeSingleItemArray, makeSkillDraftId, makeSummary, makeWorktreeHash } from "./primitives.js";
4
+ import { harnessEntityFromSnapshot, validateHarnessEntity } from "./protocol-validation.js";
5
+ import { ok } from "./result.js";
6
+ export function decodeRecord(value, path) {
7
+ return value && typeof value === "object" && !Array.isArray(value)
8
+ ? ok(value)
9
+ : workflowError(`${path} must be an object`);
10
+ }
11
+ export function decodeArray(value, path) {
12
+ return Array.isArray(value) ? ok(value) : workflowError(`${path} must be an array`);
13
+ }
14
+ export function decodeAllowed(path, value, allowed) {
15
+ return typeof value === "string" && allowed.includes(value)
16
+ ? ok(value)
17
+ : workflowError(`${path} must be one of: ${allowed.join(", ")}`);
18
+ }
19
+ function decodeDomainRole(value, path) {
20
+ return decodeAllowed(path, value, DOMAIN_ROLES);
21
+ }
22
+ function decodeDestinationSource(value, path) {
23
+ return decodeAllowed(path, value, DESTINATION_SOURCES);
24
+ }
25
+ export function decodeNumber(value, path) {
26
+ return typeof value === "number" && Number.isFinite(value)
27
+ ? ok(value)
28
+ : workflowError(`${path} must be a finite number`);
29
+ }
30
+ export function decodeOptionalBoolean(value, path) {
31
+ if (value === undefined) {
32
+ return ok(undefined);
33
+ }
34
+ return typeof value === "boolean" ? ok(value) : workflowError(`${path} must be a boolean`);
35
+ }
36
+ export function decodeNonNegativeInteger(value, path) {
37
+ return Number.isInteger(value) && Number(value) >= 0
38
+ ? ok(Number(value))
39
+ : workflowError(`${path} must be a non-negative integer`);
40
+ }
41
+ export function decodeOptionalString(value, path) {
42
+ if (value === undefined) {
43
+ return ok(undefined);
44
+ }
45
+ return typeof value === "string" ? ok(value) : workflowError(`${path} must be a string`);
46
+ }
47
+ export function decodeString(value, path) {
48
+ return typeof value === "string" ? ok(value) : workflowError(`${path} must be a string`);
49
+ }
50
+ export function decodeOptionalNonEmptyText(value, path) {
51
+ if (value === undefined) {
52
+ return ok(undefined);
53
+ }
54
+ const text = makeNonEmptyText(value, path);
55
+ return text.ok ? ok(text.value) : workflowError(text.error.message);
56
+ }
57
+ export function decodeNonEmptyText(value, path) {
58
+ const text = makeNonEmptyText(value, path);
59
+ return text.ok ? ok(text.value) : workflowError(text.error.message);
60
+ }
61
+ export function decodeOptionalAt(value, path = "at") {
62
+ return decodeOptionalString(value, path);
63
+ }
64
+ export function decodeStringArray(value, path) {
65
+ const array = decodeArray(value, path);
66
+ if (!array.ok) {
67
+ return array;
68
+ }
69
+ const items = [];
70
+ for (let index = 0; index < array.value.length; index += 1) {
71
+ const item = array.value[index];
72
+ if (typeof item !== "string") {
73
+ return workflowError(`${path}[${index}] must be a string`);
74
+ }
75
+ items.push(item);
76
+ }
77
+ return ok(items);
78
+ }
79
+ export function decodeOptionalStringArray(value, path) {
80
+ return value === undefined ? ok(undefined) : decodeStringArray(value, path);
81
+ }
82
+ function decodeOptionalNonEmptyTextArray(value, path) {
83
+ if (value === undefined) {
84
+ return ok(undefined);
85
+ }
86
+ const array = decodeArray(value, path);
87
+ if (!array.ok) {
88
+ return array;
89
+ }
90
+ const items = [];
91
+ for (let index = 0; index < array.value.length; index += 1) {
92
+ const item = primitive(makeNonEmptyText(array.value[index], `${path}[${index}]`));
93
+ if (!item.ok) {
94
+ return item;
95
+ }
96
+ items.push(item.value);
97
+ }
98
+ return ok(items);
99
+ }
100
+ export function decodeOptionalNumber(value, path) {
101
+ return value === undefined ? ok(undefined) : decodeNumber(value, path);
102
+ }
103
+ export function decodeRequestRecord(value, path) {
104
+ const record = decodeRecord(value, path);
105
+ if (!record.ok) {
106
+ return record;
107
+ }
108
+ const id = primitive(makeRequestId(record.value.id));
109
+ if (!id.ok)
110
+ return id;
111
+ const title = primitive(makeRequestTitle(record.value.title));
112
+ if (!title.ok)
113
+ return title;
114
+ const goal = primitive(makeRequestGoal(record.value.goal));
115
+ if (!goal.ok)
116
+ return goal;
117
+ const createdBy = decodeDomainRole(record.value.createdBy, `${path}.createdBy`);
118
+ if (!createdBy.ok)
119
+ return createdBy;
120
+ const createdAt = decodeOptionalString(record.value.createdAt, `${path}.createdAt`);
121
+ if (!createdAt.ok)
122
+ return createdAt;
123
+ return ok({ id: id.value, title: title.value, goal: goal.value, createdBy: createdBy.value, createdAt: createdAt.value });
124
+ }
125
+ export function decodeDestinationSeed(value, path) {
126
+ const record = decodeRecord(value, path);
127
+ if (!record.ok) {
128
+ return record;
129
+ }
130
+ const id = primitive(makeDestinationId(record.value.id));
131
+ if (!id.ok)
132
+ return id;
133
+ const title = primitive(makeDestinationTitle(record.value.title, `${path}.title`));
134
+ if (!title.ok)
135
+ return title;
136
+ const acceptanceCriteria = decodeOptionalDestinationAcceptanceCriteria(record.value.acceptanceCriteria, `${path}.acceptanceCriteria`);
137
+ if (!acceptanceCriteria.ok)
138
+ return acceptanceCriteria;
139
+ const constraints = decodeOptionalDestinationConstraints(record.value.constraints, `${path}.constraints`);
140
+ if (!constraints.ok)
141
+ return constraints;
142
+ const priority = decodeOptionalNumber(record.value.priority, `${path}.priority`);
143
+ if (!priority.ok)
144
+ return priority;
145
+ const notes = record.value.notes === undefined ? ok(undefined) : primitive(makeDestinationNotes(record.value.notes, `${path}.notes`));
146
+ if (!notes.ok)
147
+ return notes;
148
+ return ok({
149
+ id: id.value,
150
+ title: title.value,
151
+ acceptanceCriteria: acceptanceCriteria.value,
152
+ constraints: constraints.value,
153
+ priority: priority.value,
154
+ notes: notes.value
155
+ });
156
+ }
157
+ export function decodeDestinationSeedArray(value, path, requireNonEmpty = true) {
158
+ const array = decodeArray(value, path);
159
+ if (!array.ok) {
160
+ return array;
161
+ }
162
+ if (requireNonEmpty) {
163
+ const nonEmpty = primitive(makeNonEmptyArray(array.value, path));
164
+ if (!nonEmpty.ok) {
165
+ return nonEmpty;
166
+ }
167
+ }
168
+ const destinations = [];
169
+ for (let index = 0; index < array.value.length; index += 1) {
170
+ const destination = decodeDestinationSeed(array.value[index], `${path}[${index}]`);
171
+ if (!destination.ok) {
172
+ return destination;
173
+ }
174
+ destinations.push(destination.value);
175
+ }
176
+ const uniqueIds = rejectDuplicateIds(destinations.map(destination => destination.id), path, "DestinationId");
177
+ if (!uniqueIds.ok) {
178
+ return uniqueIds;
179
+ }
180
+ return ok(destinations);
181
+ }
182
+ function decodeOptionalDestinationAcceptanceCriteria(value, path) {
183
+ if (value === undefined) {
184
+ return ok(undefined);
185
+ }
186
+ const array = decodeArray(value, path);
187
+ if (!array.ok) {
188
+ return array;
189
+ }
190
+ const items = [];
191
+ for (let index = 0; index < array.value.length; index += 1) {
192
+ const item = primitive(makeDestinationAcceptanceCriterion(array.value[index], `${path}[${index}]`));
193
+ if (!item.ok) {
194
+ return item;
195
+ }
196
+ items.push(item.value);
197
+ }
198
+ return ok(items);
199
+ }
200
+ function decodeOptionalDestinationConstraints(value, path) {
201
+ if (value === undefined) {
202
+ return ok(undefined);
203
+ }
204
+ const array = decodeArray(value, path);
205
+ if (!array.ok) {
206
+ return array;
207
+ }
208
+ const items = [];
209
+ for (let index = 0; index < array.value.length; index += 1) {
210
+ const item = primitive(makeDestinationConstraint(array.value[index], `${path}[${index}]`));
211
+ if (!item.ok) {
212
+ return item;
213
+ }
214
+ items.push(item.value);
215
+ }
216
+ return ok(items);
217
+ }
218
+ export function decodeDestinationPatch(value, path) {
219
+ const record = decodeRecord(value, path);
220
+ if (!record.ok) {
221
+ return record;
222
+ }
223
+ const title = record.value.title === undefined ? ok(undefined) : primitive(makeDestinationTitle(record.value.title, `${path}.title`));
224
+ if (!title.ok)
225
+ return title;
226
+ const acceptanceCriteria = decodeOptionalDestinationAcceptanceCriteria(record.value.acceptanceCriteria, `${path}.acceptanceCriteria`);
227
+ if (!acceptanceCriteria.ok)
228
+ return acceptanceCriteria;
229
+ const constraints = decodeOptionalDestinationConstraints(record.value.constraints, `${path}.constraints`);
230
+ if (!constraints.ok)
231
+ return constraints;
232
+ const priority = decodeOptionalNumber(record.value.priority, `${path}.priority`);
233
+ if (!priority.ok)
234
+ return priority;
235
+ const notes = record.value.notes === undefined ? ok(undefined) : primitive(makeDestinationNotes(record.value.notes, `${path}.notes`));
236
+ if (!notes.ok)
237
+ return notes;
238
+ return ok({
239
+ title: title.value,
240
+ acceptanceCriteria: acceptanceCriteria.value,
241
+ constraints: constraints.value,
242
+ priority: priority.value,
243
+ notes: notes.value
244
+ });
245
+ }
246
+ export function decodeDestinationRecord(value, path) {
247
+ const record = decodeRecord(value, path);
248
+ if (!record.ok) {
249
+ return record;
250
+ }
251
+ const seed = decodeDestinationSeed(record.value, path);
252
+ if (!seed.ok)
253
+ return seed;
254
+ const requestId = primitive(makeRequestId(record.value.requestId));
255
+ if (!requestId.ok)
256
+ return requestId;
257
+ const source = decodeDestinationSource(record.value.source, `${path}.source`);
258
+ if (!source.ok)
259
+ return source;
260
+ const createdBy = decodeDomainRole(record.value.createdBy, `${path}.createdBy`);
261
+ if (!createdBy.ok)
262
+ return createdBy;
263
+ const updatedBy = decodeDomainRole(record.value.updatedBy, `${path}.updatedBy`);
264
+ if (!updatedBy.ok)
265
+ return updatedBy;
266
+ const status = decodeAllowed(`${path}.status`, record.value.status, DESTINATION_STATUSES);
267
+ if (!status.ok)
268
+ return status;
269
+ const createdAt = decodeOptionalString(record.value.createdAt, `${path}.createdAt`);
270
+ if (!createdAt.ok)
271
+ return createdAt;
272
+ const updatedAt = decodeOptionalString(record.value.updatedAt, `${path}.updatedAt`);
273
+ if (!updatedAt.ok)
274
+ return updatedAt;
275
+ const base = {
276
+ ...seed.value,
277
+ requestId: requestId.value,
278
+ source: source.value,
279
+ createdBy: createdBy.value,
280
+ updatedBy: updatedBy.value,
281
+ createdAt: createdAt.value,
282
+ updatedAt: updatedAt.value
283
+ };
284
+ switch (status.value) {
285
+ case "pending":
286
+ return rejectDestinationLifecycleFields(record.value, path, ["claimedBy", "reachedByMoveId", "blockedReason", "canceledReason", "supersededByDestinationId"], base);
287
+ case "claimed":
288
+ case "in_progress": {
289
+ const claimedBy = primitive(makeNonEmptyText(record.value.claimedBy, `${path}.claimedBy`));
290
+ if (!claimedBy.ok)
291
+ return claimedBy;
292
+ const invalid = rejectPresent(record.value, path, ["reachedByMoveId", "blockedReason", "canceledReason", "supersededByDestinationId"]);
293
+ if (!invalid.ok)
294
+ return invalid;
295
+ return ok({ ...base, status: status.value, claimedBy: claimedBy.value });
296
+ }
297
+ case "reached": {
298
+ const reachedByMoveId = primitive(makeMoveId(record.value.reachedByMoveId));
299
+ if (!reachedByMoveId.ok)
300
+ return reachedByMoveId;
301
+ const claimedBy = decodeOptionalNonEmptyText(record.value.claimedBy, `${path}.claimedBy`);
302
+ if (!claimedBy.ok)
303
+ return claimedBy;
304
+ const invalid = rejectPresent(record.value, path, ["blockedReason", "canceledReason", "supersededByDestinationId"]);
305
+ if (!invalid.ok)
306
+ return invalid;
307
+ return ok({ ...base, status: "reached", reachedByMoveId: reachedByMoveId.value, claimedBy: claimedBy.value });
308
+ }
309
+ case "blocked": {
310
+ const blockedReason = primitive(makeFailureReason(record.value.blockedReason, `${path}.blockedReason`));
311
+ if (!blockedReason.ok)
312
+ return blockedReason;
313
+ const claimedBy = decodeOptionalNonEmptyText(record.value.claimedBy, `${path}.claimedBy`);
314
+ if (!claimedBy.ok)
315
+ return claimedBy;
316
+ const invalid = rejectPresent(record.value, path, ["reachedByMoveId", "canceledReason", "supersededByDestinationId"]);
317
+ if (!invalid.ok)
318
+ return invalid;
319
+ return ok({ ...base, status: "blocked", blockedReason: blockedReason.value, claimedBy: claimedBy.value });
320
+ }
321
+ case "superseded": {
322
+ const supersededByDestinationId = primitive(makeDestinationId(record.value.supersededByDestinationId));
323
+ if (!supersededByDestinationId.ok)
324
+ return supersededByDestinationId;
325
+ const claimedBy = decodeOptionalNonEmptyText(record.value.claimedBy, `${path}.claimedBy`);
326
+ if (!claimedBy.ok)
327
+ return claimedBy;
328
+ const invalid = rejectPresent(record.value, path, ["reachedByMoveId", "blockedReason", "canceledReason"]);
329
+ if (!invalid.ok)
330
+ return invalid;
331
+ return ok({ ...base, status: "superseded", supersededByDestinationId: supersededByDestinationId.value, claimedBy: claimedBy.value });
332
+ }
333
+ case "canceled": {
334
+ const canceledReason = primitive(makeFailureReason(record.value.canceledReason, `${path}.canceledReason`));
335
+ if (!canceledReason.ok)
336
+ return canceledReason;
337
+ const claimedBy = decodeOptionalNonEmptyText(record.value.claimedBy, `${path}.claimedBy`);
338
+ if (!claimedBy.ok)
339
+ return claimedBy;
340
+ const invalid = rejectPresent(record.value, path, ["reachedByMoveId", "blockedReason", "supersededByDestinationId"]);
341
+ if (!invalid.ok)
342
+ return invalid;
343
+ return ok({ ...base, status: "canceled", canceledReason: canceledReason.value, claimedBy: claimedBy.value });
344
+ }
345
+ }
346
+ }
347
+ export function decodeDestinationRecordArray(value, path) {
348
+ const array = decodeArray(value, path);
349
+ if (!array.ok) {
350
+ return array;
351
+ }
352
+ const destinations = [];
353
+ for (let index = 0; index < array.value.length; index += 1) {
354
+ const destination = decodeDestinationRecord(array.value[index], `${path}[${index}]`);
355
+ if (!destination.ok) {
356
+ return destination;
357
+ }
358
+ destinations.push(destination.value);
359
+ }
360
+ return ok(destinations);
361
+ }
362
+ export function decodeDestinationIdArray(value, path, requireNonEmpty) {
363
+ const array = decodeArray(value, path);
364
+ if (!array.ok) {
365
+ return array;
366
+ }
367
+ if (requireNonEmpty) {
368
+ const nonEmpty = primitive(makeNonEmptyArray(array.value, path));
369
+ if (!nonEmpty.ok)
370
+ return nonEmpty;
371
+ }
372
+ const ids = [];
373
+ for (let index = 0; index < array.value.length; index += 1) {
374
+ const id = primitive(makeDestinationId(array.value[index]));
375
+ if (!id.ok)
376
+ return id;
377
+ ids.push(id.value);
378
+ }
379
+ const uniqueIds = rejectDuplicateIds(ids, path, "DestinationId");
380
+ if (!uniqueIds.ok) {
381
+ return uniqueIds;
382
+ }
383
+ return ok(ids);
384
+ }
385
+ export function decodeMoveIdArray(value, path, requireNonEmpty) {
386
+ const array = decodeArray(value, path);
387
+ if (!array.ok) {
388
+ return array;
389
+ }
390
+ if (requireNonEmpty) {
391
+ const nonEmpty = primitive(makeNonEmptyArray(array.value, path));
392
+ if (!nonEmpty.ok)
393
+ return nonEmpty;
394
+ }
395
+ const ids = [];
396
+ for (let index = 0; index < array.value.length; index += 1) {
397
+ const id = primitive(makeMoveId(array.value[index]));
398
+ if (!id.ok)
399
+ return id;
400
+ ids.push(id.value);
401
+ }
402
+ const uniqueIds = rejectDuplicateIds(ids, path, "MoveId");
403
+ if (!uniqueIds.ok) {
404
+ return uniqueIds;
405
+ }
406
+ return ok(ids);
407
+ }
408
+ export function decodeNodeIdArray(value, path, requireNonEmpty) {
409
+ const array = decodeArray(value, path);
410
+ if (!array.ok) {
411
+ return array;
412
+ }
413
+ if (requireNonEmpty) {
414
+ const nonEmpty = primitive(makeNonEmptyArray(array.value, path));
415
+ if (!nonEmpty.ok)
416
+ return nonEmpty;
417
+ }
418
+ const ids = [];
419
+ for (let index = 0; index < array.value.length; index += 1) {
420
+ const id = primitive(makeNodeId(array.value[index]));
421
+ if (!id.ok)
422
+ return id;
423
+ ids.push(id.value);
424
+ }
425
+ const uniqueIds = rejectDuplicateIds(ids, path, "NodeId");
426
+ if (!uniqueIds.ok) {
427
+ return uniqueIds;
428
+ }
429
+ return requireNonEmpty ? primitive(makeNonEmptyArray(ids, path)) : ok(ids);
430
+ }
431
+ export function decodeEvidenceArray(value, path, requireNonEmpty) {
432
+ const array = decodeArray(value, path);
433
+ if (!array.ok) {
434
+ return array;
435
+ }
436
+ if (requireNonEmpty) {
437
+ const nonEmpty = primitive(makeNonEmptyArray(array.value, path));
438
+ if (!nonEmpty.ok)
439
+ return nonEmpty;
440
+ }
441
+ const evidence = [];
442
+ for (let index = 0; index < array.value.length; index += 1) {
443
+ const item = primitive(makeEvidenceText(array.value[index], `${path}[${index}]`));
444
+ if (!item.ok)
445
+ return item;
446
+ evidence.push(item.value);
447
+ }
448
+ return ok(evidence);
449
+ }
450
+ export function decodeRiskArray(value, path) {
451
+ const array = decodeArray(value, path);
452
+ if (!array.ok) {
453
+ return array;
454
+ }
455
+ const risks = [];
456
+ for (let index = 0; index < array.value.length; index += 1) {
457
+ const item = primitive(makeRiskText(array.value[index], `${path}[${index}]`));
458
+ if (!item.ok) {
459
+ return item;
460
+ }
461
+ risks.push(item.value);
462
+ }
463
+ return ok(risks);
464
+ }
465
+ export function decodeOptionalRiskArray(value, path) {
466
+ return value === undefined ? ok(undefined) : decodeRiskArray(value, path);
467
+ }
468
+ export function decodeHunsuTarget(value, path) {
469
+ const record = decodeRecord(value, path);
470
+ if (!record.ok) {
471
+ return record;
472
+ }
473
+ switch (record.value.type) {
474
+ case "destination": {
475
+ const id = primitive(makeDestinationId(record.value.id));
476
+ return id.ok ? ok({ type: "destination", id: id.value }) : id;
477
+ }
478
+ case "move": {
479
+ const id = primitive(makeMoveId(record.value.id));
480
+ return id.ok ? ok({ type: "move", id: id.value }) : id;
481
+ }
482
+ case "line": {
483
+ const id = primitive(makeLineId(record.value.id));
484
+ return id.ok ? ok({ type: "line", id: id.value }) : id;
485
+ }
486
+ case "node": {
487
+ const id = primitive(makeNodeId(record.value.id));
488
+ return id.ok ? ok({ type: "node", id: id.value }) : id;
489
+ }
490
+ default:
491
+ return workflowError(`${path}.type must be destination, move, line, or node`);
492
+ }
493
+ }
494
+ export function decodeAgentConversationRef(value, path) {
495
+ if (value === undefined) {
496
+ return ok(undefined);
497
+ }
498
+ const record = decodeRecord(value, path);
499
+ if (!record.ok)
500
+ return record;
501
+ const provider = decodeAllowed(`${path}.provider`, record.value.provider, ["codex", "local"]);
502
+ if (!provider.ok)
503
+ return provider;
504
+ const conversationHash = primitive(makeAgentConversationHash(record.value.conversationHash));
505
+ if (!conversationHash.ok)
506
+ return conversationHash;
507
+ const threadId = decodeOptionalNonEmptyText(record.value.threadId, `${path}.threadId`);
508
+ if (!threadId.ok)
509
+ return threadId;
510
+ const contextHash = primitive(makeNonEmptyText(record.value.contextHash, `${path}.contextHash`));
511
+ if (!contextHash.ok)
512
+ return contextHash;
513
+ const worktreeHash = record.value.worktreeHash === undefined ? ok(undefined) : primitive(makeWorktreeHash(record.value.worktreeHash));
514
+ if (!worktreeHash.ok)
515
+ return worktreeHash;
516
+ const startedAt = primitive(makeNonEmptyText(record.value.startedAt, `${path}.startedAt`));
517
+ if (!startedAt.ok)
518
+ return startedAt;
519
+ const endedAt = decodeOptionalNonEmptyText(record.value.endedAt, `${path}.endedAt`);
520
+ if (!endedAt.ok)
521
+ return endedAt;
522
+ const base = {
523
+ conversationHash: conversationHash.value,
524
+ contextHash: contextHash.value,
525
+ worktreeHash: worktreeHash.value,
526
+ startedAt: startedAt.value
527
+ };
528
+ if (provider.value === "local") {
529
+ if (threadId.value !== undefined) {
530
+ return workflowError(`${path}.threadId is only valid for codex conversations`);
531
+ }
532
+ return endedAt.value === undefined
533
+ ? ok({ provider: "local", ...base })
534
+ : ok({ provider: "local", ...base, endedAt: endedAt.value });
535
+ }
536
+ return endedAt.value === undefined
537
+ ? ok({ provider: "codex", ...base, threadId: threadId.value })
538
+ : ok({ provider: "codex", ...base, threadId: threadId.value, endedAt: endedAt.value });
539
+ }
540
+ export function decodeWorktreeRef(value, path) {
541
+ if (value === undefined) {
542
+ return ok(undefined);
543
+ }
544
+ const record = decodeRecord(value, path);
545
+ if (!record.ok)
546
+ return record;
547
+ const worktreeHash = primitive(makeWorktreeHash(record.value.worktreeHash));
548
+ if (!worktreeHash.ok)
549
+ return worktreeHash;
550
+ const pathValue = primitive(makeNonEmptyText(record.value.path, `${path}.path`));
551
+ if (!pathValue.ok)
552
+ return pathValue;
553
+ const branch = primitive(makeNonEmptyText(record.value.branch, `${path}.branch`));
554
+ if (!branch.ok)
555
+ return branch;
556
+ const baseRef = primitive(makeNonEmptyText(record.value.baseRef, `${path}.baseRef`));
557
+ if (!baseRef.ok)
558
+ return baseRef;
559
+ const createdAt = primitive(makeNonEmptyText(record.value.createdAt, `${path}.createdAt`));
560
+ if (!createdAt.ok)
561
+ return createdAt;
562
+ const removedAt = decodeOptionalNonEmptyText(record.value.removedAt, `${path}.removedAt`);
563
+ if (!removedAt.ok)
564
+ return removedAt;
565
+ const base = {
566
+ worktreeHash: worktreeHash.value,
567
+ path: pathValue.value,
568
+ branch: branch.value,
569
+ baseRef: baseRef.value,
570
+ createdAt: createdAt.value
571
+ };
572
+ return removedAt.value === undefined ? ok(base) : ok({ ...base, removedAt: removedAt.value });
573
+ }
574
+ export function decodeSkillBinding(value, path) {
575
+ const record = decodeRecord(value, path);
576
+ if (!record.ok)
577
+ return record;
578
+ const kind = decodeAllowed(`${path}.kind`, record.value.kind, ["local-snapshot", "local-root-installed", "registry-package", "skillMeta"]);
579
+ if (!kind.ok)
580
+ return kind;
581
+ const name = primitive(makeNonEmptyText(record.value.name, `${path}.name`));
582
+ if (!name.ok)
583
+ return name;
584
+ if (kind.value === "skillMeta") {
585
+ const source = primitive(makeNonEmptyText(record.value.source, `${path}.source`));
586
+ if (!source.ok)
587
+ return source;
588
+ const agent = decodeAllowed(`${path}.agent`, record.value.agent, ["codex"]);
589
+ if (!agent.ok)
590
+ return agent;
591
+ return ok({
592
+ kind: "skillMeta",
593
+ name: name.value,
594
+ source: source.value,
595
+ agent: agent.value
596
+ });
597
+ }
598
+ if (kind.value === "local-root-installed") {
599
+ const sourcePath = decodeOptionalNonEmptyText(record.value.sourcePath, `${path}.sourcePath`);
600
+ if (!sourcePath.ok)
601
+ return sourcePath;
602
+ return ok({
603
+ kind: "local-root-installed",
604
+ name: name.value,
605
+ sourcePath: sourcePath.value
606
+ });
607
+ }
608
+ if (kind.value === "local-snapshot") {
609
+ const sourcePath = primitive(makeNonEmptyText(record.value.sourcePath, `${path}.sourcePath`));
610
+ if (!sourcePath.ok)
611
+ return sourcePath;
612
+ const contentHash = primitive(makeNonEmptyText(record.value.contentHash, `${path}.contentHash`));
613
+ if (!contentHash.ok)
614
+ return contentHash;
615
+ const snapshotRef = primitive(makeNonEmptyText(record.value.snapshotRef, `${path}.snapshotRef`));
616
+ if (!snapshotRef.ok)
617
+ return snapshotRef;
618
+ const snapshotFiles = record.value.snapshotFiles === undefined ? ok(undefined) : decodeSnapshotFiles(record.value.snapshotFiles, `${path}.snapshotFiles`);
619
+ if (!snapshotFiles.ok)
620
+ return snapshotFiles;
621
+ return ok({
622
+ kind: "local-snapshot",
623
+ name: name.value,
624
+ sourcePath: sourcePath.value,
625
+ contentHash: contentHash.value,
626
+ snapshotRef: snapshotRef.value,
627
+ snapshotFiles: snapshotFiles.value
628
+ });
629
+ }
630
+ const registryKind = decodeAllowed(`${path}.registryKind`, record.value.registryKind, ["apm"]);
631
+ if (!registryKind.ok)
632
+ return registryKind;
633
+ const registry = primitive(makeNonEmptyText(record.value.registry, `${path}.registry`));
634
+ if (!registry.ok)
635
+ return registry;
636
+ const packageName = primitive(makeNonEmptyText(record.value.package, `${path}.package`));
637
+ if (!packageName.ok)
638
+ return packageName;
639
+ const version = primitive(makeNonEmptyText(record.value.version, `${path}.version`));
640
+ if (!version.ok)
641
+ return version;
642
+ if (!isExactSemver(String(version.value))) {
643
+ return workflowError(`${path}.version must be an exact semver version`);
644
+ }
645
+ const integrity = primitive(makeNonEmptyText(record.value.integrity, `${path}.integrity`));
646
+ if (!integrity.ok)
647
+ return integrity;
648
+ const contentHash = primitive(makeNonEmptyText(record.value.contentHash, `${path}.contentHash`));
649
+ if (!contentHash.ok)
650
+ return contentHash;
651
+ if (record.value.snapshotFiles !== undefined) {
652
+ return workflowError(`${path}.snapshotFiles is not supported for registry-package skills`);
653
+ }
654
+ return ok({
655
+ kind: "registry-package",
656
+ registryKind: registryKind.value,
657
+ name: name.value,
658
+ registry: registry.value,
659
+ package: packageName.value,
660
+ version: version.value,
661
+ integrity: integrity.value,
662
+ contentHash: contentHash.value
663
+ });
664
+ }
665
+ export function decodeMemberPluginBinding(value, path) {
666
+ const record = decodeRecord(value, path);
667
+ if (!record.ok)
668
+ return record;
669
+ const kind = decodeAllowed(`${path}.kind`, record.value.kind, ["local-root-installed"]);
670
+ if (!kind.ok)
671
+ return kind;
672
+ const id = primitive(makeNonEmptyText(record.value.id, `${path}.id`));
673
+ if (!id.ok)
674
+ return id;
675
+ return ok({
676
+ kind: kind.value,
677
+ id: id.value
678
+ });
679
+ }
680
+ export function decodeHubPackageLock(value, path, allowedKinds) {
681
+ if (value === undefined) {
682
+ return ok(undefined);
683
+ }
684
+ const record = decodeRecord(value, path);
685
+ if (!record.ok)
686
+ return record;
687
+ const origin = primitive(makeNonEmptyText(record.value.origin, `${path}.origin`));
688
+ if (!origin.ok)
689
+ return origin;
690
+ const kind = decodeAllowed(`${path}.kind`, record.value.kind, ["team", "member", "manager", "skill"]);
691
+ if (!kind.ok)
692
+ return kind;
693
+ if (allowedKinds && !allowedKinds.includes(kind.value)) {
694
+ return workflowError(`${path}.kind must be ${allowedKinds.join(" or ")}`);
695
+ }
696
+ const key = primitive(makeNonEmptyText(record.value.key, `${path}.key`));
697
+ if (!key.ok)
698
+ return key;
699
+ const version = primitive(makeNonEmptyText(record.value.version, `${path}.version`));
700
+ if (!version.ok)
701
+ return version;
702
+ const integrity = primitive(makeNonEmptyText(record.value.integrity, `${path}.integrity`));
703
+ if (!integrity.ok)
704
+ return integrity;
705
+ return ok({
706
+ origin: origin.value,
707
+ kind: kind.value,
708
+ key: key.value,
709
+ version: version.value,
710
+ integrity: integrity.value
711
+ });
712
+ }
713
+ export function decodeExecutorPackageBindings(value, path) {
714
+ if (value === undefined)
715
+ return ok([]);
716
+ const array = decodeArray(value, path);
717
+ if (!array.ok)
718
+ return array;
719
+ const bindings = [];
720
+ for (let index = 0; index < array.value.length; index += 1) {
721
+ const record = decodeRecord(array.value[index], `${path}[${index}]`);
722
+ if (!record.ok)
723
+ return record;
724
+ const executorId = primitive(makeNonEmptyText(record.value.executorId, `${path}[${index}].executorId`));
725
+ if (!executorId.ok)
726
+ return executorId;
727
+ const lock = decodeHubPackageLock(record.value.lock, `${path}[${index}].lock`, ["member"]);
728
+ if (!lock.ok)
729
+ return lock;
730
+ if (!lock.value)
731
+ return workflowError(`${path}[${index}].lock is required`);
732
+ bindings.push({ executorId: executorId.value, lock: lock.value });
733
+ }
734
+ return ok(bindings);
735
+ }
736
+ export function decodeResourcePackageBindings(value, path) {
737
+ if (value === undefined)
738
+ return ok([]);
739
+ const array = decodeArray(value, path);
740
+ if (!array.ok)
741
+ return array;
742
+ const bindings = [];
743
+ for (let index = 0; index < array.value.length; index += 1) {
744
+ const record = decodeRecord(array.value[index], `${path}[${index}]`);
745
+ if (!record.ok)
746
+ return record;
747
+ const name = primitive(makeNonEmptyText(record.value.name, `${path}[${index}].name`));
748
+ if (!name.ok)
749
+ return name;
750
+ const lock = decodeHubPackageLock(record.value.lock, `${path}[${index}].lock`, ["skill"]);
751
+ if (!lock.ok)
752
+ return lock;
753
+ if (!lock.value)
754
+ return workflowError(`${path}[${index}].lock is required`);
755
+ bindings.push({ name: name.value, lock: lock.value });
756
+ }
757
+ return ok(bindings);
758
+ }
759
+ export function decodeHunsuOrigin(value, path) {
760
+ const record = decodeRecord(value, path);
761
+ if (!record.ok)
762
+ return record;
763
+ const name = primitive(makeNonEmptyText(record.value.name, `${path}.name`));
764
+ if (!name.ok)
765
+ return name;
766
+ const url = primitive(makeNonEmptyText(record.value.url, `${path}.url`));
767
+ if (!url.ok)
768
+ return url;
769
+ const transport = decodeAllowed(`${path}.transport`, record.value.transport, ["http", "ssh"]);
770
+ if (!transport.ok)
771
+ return transport;
772
+ const identity = decodeOptionalNonEmptyText(record.value.identity, `${path}.identity`);
773
+ if (!identity.ok)
774
+ return identity;
775
+ return ok({
776
+ name: name.value,
777
+ url: url.value,
778
+ transport: transport.value,
779
+ identity: identity.value
780
+ });
781
+ }
782
+ export function decodeArtifactRecord(value, path) {
783
+ const record = decodeRecord(value, path);
784
+ if (!record.ok)
785
+ return record;
786
+ const id = primitive(makeArtifactId(record.value.id));
787
+ if (!id.ok)
788
+ return id;
789
+ const owner = decodeArtifactOwner(record.value.owner, `${path}.owner`);
790
+ if (!owner.ok)
791
+ return owner;
792
+ const kind = decodeAllowed(`${path}.kind`, record.value.kind, ARTIFACT_KINDS);
793
+ if (!kind.ok)
794
+ return kind;
795
+ const artifactPath = decodeOptionalNonEmptyText(record.value.path, `${path}.path`);
796
+ if (!artifactPath.ok)
797
+ return artifactPath;
798
+ const text = decodeOptionalNonEmptyText(record.value.text, `${path}.text`);
799
+ if (!text.ok)
800
+ return text;
801
+ if (artifactPath.value === undefined && text.value === undefined) {
802
+ return workflowError(`${path} must include path, text, or both`);
803
+ }
804
+ if (artifactPath.value !== undefined && text.value !== undefined) {
805
+ return ok({ id: id.value, owner: owner.value, kind: kind.value, path: artifactPath.value, text: text.value });
806
+ }
807
+ return artifactPath.value !== undefined
808
+ ? ok({ id: id.value, owner: owner.value, kind: kind.value, path: artifactPath.value })
809
+ : ok({ id: id.value, owner: owner.value, kind: kind.value, text: text.value });
810
+ }
811
+ export function decodeArtifactActionDefinition(value, path) {
812
+ const record = decodeRecord(value, path);
813
+ if (!record.ok)
814
+ return record;
815
+ const id = primitive(makeArtifactActionId(record.value.id));
816
+ if (!id.ok)
817
+ return id;
818
+ const title = primitive(makeNonEmptyText(record.value.title, `${path}.title`));
819
+ if (!title.ok)
820
+ return title;
821
+ const kind = decodeAllowed(`${path}.kind`, record.value.kind, ARTIFACT_ACTION_KINDS);
822
+ if (!kind.ok)
823
+ return kind;
824
+ const sourceScope = decodeAllowed(`${path}.sourceScope`, record.value.sourceScope, ARTIFACT_ACTION_SOURCE_SCOPES);
825
+ if (!sourceScope.ok)
826
+ return sourceScope;
827
+ const env = decodeArtifactActionEnv(record.value.env, `${path}.env`);
828
+ if (!env.ok)
829
+ return env;
830
+ const runner = decodeArtifactActionRunner(record.value.runner, `${path}.runner`);
831
+ if (!runner.ok)
832
+ return runner;
833
+ const aliases = decodeArtifactActionAliases(record.value.aliases, `${path}.aliases`);
834
+ if (!aliases.ok)
835
+ return aliases;
836
+ const envAliases = validateArtifactActionEnvAliases(env.value, aliases.value, `${path}.env`);
837
+ if (!envAliases.ok)
838
+ return envAliases;
839
+ const evidence = decodeArtifactActionEvidence(record.value.evidence, `${path}.evidence`);
840
+ if (!evidence.ok)
841
+ return evidence;
842
+ const displayOrder = decodeNonNegativeInteger(record.value.displayOrder, `${path}.displayOrder`);
843
+ if (!displayOrder.ok)
844
+ return displayOrder;
845
+ if (kind.value === "check" && runner.value.type !== "command") {
846
+ return workflowError(`${path}.runner.type must be command for check actions`);
847
+ }
848
+ return ok({
849
+ id: id.value,
850
+ title: title.value,
851
+ kind: kind.value,
852
+ sourceScope: sourceScope.value,
853
+ env: env.value,
854
+ runner: runner.value,
855
+ aliases: aliases.value,
856
+ evidence: evidence.value,
857
+ displayOrder: displayOrder.value
858
+ });
859
+ }
860
+ export function decodeArtifactActionPatch(value, path) {
861
+ const record = decodeRecord(value, path);
862
+ if (!record.ok)
863
+ return record;
864
+ const patch = {};
865
+ if ("title" in record.value) {
866
+ const title = primitive(makeNonEmptyText(record.value.title, `${path}.title`));
867
+ if (!title.ok)
868
+ return title;
869
+ patch.title = title.value;
870
+ }
871
+ if ("kind" in record.value) {
872
+ const kind = decodeAllowed(`${path}.kind`, record.value.kind, ARTIFACT_ACTION_KINDS);
873
+ if (!kind.ok)
874
+ return kind;
875
+ patch.kind = kind.value;
876
+ }
877
+ if ("sourceScope" in record.value) {
878
+ const sourceScope = decodeAllowed(`${path}.sourceScope`, record.value.sourceScope, ARTIFACT_ACTION_SOURCE_SCOPES);
879
+ if (!sourceScope.ok)
880
+ return sourceScope;
881
+ patch.sourceScope = sourceScope.value;
882
+ }
883
+ if ("env" in record.value) {
884
+ const env = decodeArtifactActionEnv(record.value.env, `${path}.env`);
885
+ if (!env.ok)
886
+ return env;
887
+ patch.env = env.value;
888
+ }
889
+ if ("runner" in record.value) {
890
+ const runner = decodeArtifactActionRunner(record.value.runner, `${path}.runner`);
891
+ if (!runner.ok)
892
+ return runner;
893
+ patch.runner = runner.value;
894
+ }
895
+ if ("aliases" in record.value) {
896
+ const aliases = decodeArtifactActionAliases(record.value.aliases, `${path}.aliases`);
897
+ if (!aliases.ok)
898
+ return aliases;
899
+ patch.aliases = aliases.value;
900
+ }
901
+ if ("evidence" in record.value) {
902
+ const evidence = decodeArtifactActionEvidence(record.value.evidence, `${path}.evidence`);
903
+ if (!evidence.ok)
904
+ return evidence;
905
+ patch.evidence = evidence.value;
906
+ }
907
+ if ("displayOrder" in record.value) {
908
+ const displayOrder = decodeNonNegativeInteger(record.value.displayOrder, `${path}.displayOrder`);
909
+ if (!displayOrder.ok)
910
+ return displayOrder;
911
+ patch.displayOrder = displayOrder.value;
912
+ }
913
+ if (patch.kind === "check" && patch.runner && patch.runner.type !== "command") {
914
+ return workflowError(`${path}.runner.type must be command for check actions`);
915
+ }
916
+ return ok(patch);
917
+ }
918
+ function validateArtifactActionEnvAliases(env, aliases, path) {
919
+ const aliasNames = new Set(Object.keys(aliases ?? {}));
920
+ for (const [name, spec] of Object.entries(env ?? {})) {
921
+ if ("alias" in spec && !aliasNames.has(String(spec.alias))) {
922
+ return workflowError(`${path}.${name}.alias references unknown alias: ${spec.alias}`);
923
+ }
924
+ if ("fromAliasUrl" in spec && !aliasNames.has(String(spec.fromAliasUrl))) {
925
+ return workflowError(`${path}.${name}.fromAliasUrl references unknown alias: ${spec.fromAliasUrl}`);
926
+ }
927
+ }
928
+ return ok(undefined);
929
+ }
930
+ export function decodeLineRecord(value, path) {
931
+ const record = decodeRecord(value, path);
932
+ if (!record.ok)
933
+ return record;
934
+ const id = primitive(makeLineId(record.value.id));
935
+ if (!id.ok)
936
+ return id;
937
+ const requestId = primitive(makeRequestId(record.value.requestId));
938
+ if (!requestId.ok)
939
+ return requestId;
940
+ const teamName = record.value.teamName === undefined
941
+ ? ok(undefined)
942
+ : primitive(makeTeamName(record.value.teamName, `${path}.teamName`));
943
+ if (!teamName.ok)
944
+ return teamName;
945
+ const status = decodeAllowed(`${path}.status`, record.value.status, LINE_STATUSES);
946
+ if (!status.ok)
947
+ return status;
948
+ const moveIds = decodeMoveIdArray(record.value.moveIds, `${path}.moveIds`, false);
949
+ if (!moveIds.ok)
950
+ return moveIds;
951
+ const rootNodeId = primitive(makeNodeId(record.value.rootNodeId));
952
+ if (!rootNodeId.ok)
953
+ return rootNodeId;
954
+ const currentNodeId = primitive(makeNodeId(record.value.currentNodeId));
955
+ if (!currentNodeId.ok)
956
+ return currentNodeId;
957
+ const nodeIds = decodeNodeIdArray(record.value.nodeIds, `${path}.nodeIds`, true);
958
+ if (!nodeIds.ok)
959
+ return nodeIds;
960
+ const parentLineId = decodeOptionalLineId(record.value.parentLineId);
961
+ if (!parentLineId.ok)
962
+ return parentLineId;
963
+ const forkedFromMoveId = decodeOptionalMoveId(record.value.forkedFromMoveId);
964
+ if (!forkedFromMoveId.ok)
965
+ return forkedFromMoveId;
966
+ if (forkedFromMoveId.value !== undefined && parentLineId.value === undefined) {
967
+ return workflowError(`${path}.parentLineId is required when forkedFromMoveId is present`);
968
+ }
969
+ const route = {
970
+ id: id.value,
971
+ requestId: requestId.value,
972
+ teamName: teamName.value,
973
+ moveIds: moveIds.value,
974
+ rootNodeId: rootNodeId.value,
975
+ currentNodeId: currentNodeId.value,
976
+ nodeIds: nodeIds.value
977
+ };
978
+ if (parentLineId.value !== undefined) {
979
+ const forkedBase = { ...route, parentLineId: parentLineId.value, forkedFromMoveId: forkedFromMoveId.value };
980
+ return lineWithStatus(forkedBase, status.value);
981
+ }
982
+ const rootBase = route;
983
+ return lineWithStatus(rootBase, status.value);
984
+ }
985
+ export function decodeSkillDraftRecord(value, path) {
986
+ const record = decodeRecord(value, path);
987
+ if (!record.ok)
988
+ return record;
989
+ const id = primitive(makeSkillDraftId(record.value.id));
990
+ if (!id.ok)
991
+ return id;
992
+ const name = primitive(makeNonEmptyText(record.value.name, `${path}.name`));
993
+ if (!name.ok)
994
+ return name;
995
+ const sourcePath = decodeOptionalNonEmptyText(record.value.sourcePath, `${path}.sourcePath`);
996
+ if (!sourcePath.ok)
997
+ return sourcePath;
998
+ const draftPath = primitive(makeNonEmptyText(record.value.draftPath, `${path}.draftPath`));
999
+ if (!draftPath.ok)
1000
+ return draftPath;
1001
+ const createdFromNodeId = decodeOptionalNodeId(record.value.createdFromNodeId);
1002
+ if (!createdFromNodeId.ok)
1003
+ return createdFromNodeId;
1004
+ const createdAt = decodeOptionalString(record.value.createdAt, `${path}.createdAt`);
1005
+ if (!createdAt.ok)
1006
+ return createdAt;
1007
+ const status = decodeAllowed(`${path}.status`, record.value.status, SKILL_DRAFT_STATUSES);
1008
+ if (!status.ok)
1009
+ return status;
1010
+ const base = {
1011
+ id: id.value,
1012
+ name: name.value,
1013
+ sourcePath: sourcePath.value,
1014
+ draftPath: draftPath.value,
1015
+ createdFromNodeId: createdFromNodeId.value,
1016
+ createdAt: createdAt.value
1017
+ };
1018
+ switch (status.value) {
1019
+ case "accepted": {
1020
+ const acceptedSnapshot = decodeSkillBinding(record.value.acceptedSnapshot, `${path}.acceptedSnapshot`);
1021
+ return acceptedSnapshot.ok ? ok({ ...base, status: "accepted", acceptedSnapshot: acceptedSnapshot.value }) : acceptedSnapshot;
1022
+ }
1023
+ case "draft":
1024
+ if (record.value.acceptedSnapshot !== undefined) {
1025
+ return workflowError(`${path}.acceptedSnapshot is only valid for accepted Skill Drafts`);
1026
+ }
1027
+ return ok({ ...base, status: "draft" });
1028
+ case "discarded":
1029
+ if (record.value.acceptedSnapshot !== undefined) {
1030
+ return workflowError(`${path}.acceptedSnapshot is only valid for accepted Skill Drafts`);
1031
+ }
1032
+ return ok({ ...base, status: "discarded" });
1033
+ }
1034
+ }
1035
+ export function decodeHunsuDraftRecord(value, path, decodeHarness) {
1036
+ const record = decodeRecord(value, path);
1037
+ if (!record.ok)
1038
+ return record;
1039
+ const id = primitive(makeHunsuDraftId(record.value.id));
1040
+ if (!id.ok)
1041
+ return id;
1042
+ const sourceLineId = primitive(makeLineId(record.value.sourceLineId));
1043
+ if (!sourceLineId.ok)
1044
+ return sourceLineId;
1045
+ const sourceNodeId = primitive(makeNodeId(record.value.sourceNodeId));
1046
+ if (!sourceNodeId.ok)
1047
+ return sourceNodeId;
1048
+ const sourceMoveId = decodeOptionalMoveId(record.value.sourceMoveId);
1049
+ if (!sourceMoveId.ok)
1050
+ return sourceMoveId;
1051
+ const target = decodeHunsuTarget(record.value.target, `${path}.target`);
1052
+ if (!target.ok)
1053
+ return target;
1054
+ const newTeamName = primitive(makeTeamName(record.value.newTeamName, `${path}.newTeamName`));
1055
+ if (!newTeamName.ok)
1056
+ return newTeamName;
1057
+ const summary = primitive(makeSummary(record.value.summary, `${path}.summary`));
1058
+ if (!summary.ok)
1059
+ return summary;
1060
+ const teamSnapshot = decodeTeamSnapshot(record.value.teamSnapshot, `${path}.teamSnapshot`, decodeHarness);
1061
+ if (!teamSnapshot.ok)
1062
+ return teamSnapshot;
1063
+ const changedFiles = decodeHunsuChangedFileArray(record.value.changedFiles, `${path}.changedFiles`);
1064
+ if (!changedFiles.ok)
1065
+ return changedFiles;
1066
+ const createdAt = decodeOptionalString(record.value.createdAt, `${path}.createdAt`);
1067
+ if (!createdAt.ok)
1068
+ return createdAt;
1069
+ const updatedAt = decodeOptionalString(record.value.updatedAt, `${path}.updatedAt`);
1070
+ if (!updatedAt.ok)
1071
+ return updatedAt;
1072
+ const status = decodeAllowed(`${path}.status`, record.value.status, HUNSU_DRAFT_STATUSES);
1073
+ if (!status.ok)
1074
+ return status;
1075
+ const base = {
1076
+ id: id.value,
1077
+ sourceLineId: sourceLineId.value,
1078
+ sourceNodeId: sourceNodeId.value,
1079
+ sourceMoveId: sourceMoveId.value,
1080
+ target: target.value,
1081
+ newTeamName: newTeamName.value,
1082
+ summary: summary.value,
1083
+ teamSnapshot: teamSnapshot.value,
1084
+ changedFiles: changedFiles.value,
1085
+ createdAt: createdAt.value,
1086
+ updatedAt: updatedAt.value
1087
+ };
1088
+ switch (status.value) {
1089
+ case "ready": {
1090
+ const hunsuId = primitive(makeHunsuId(record.value.hunsuId));
1091
+ if (!hunsuId.ok)
1092
+ return hunsuId;
1093
+ const newLineId = primitive(makeLineId(record.value.newLineId));
1094
+ if (!newLineId.ok)
1095
+ return newLineId;
1096
+ if (record.value.conversationRef === undefined) {
1097
+ return workflowError(`${path}.conversationRef is required for ready HUNSU Drafts`);
1098
+ }
1099
+ const conversationRef = decodeAgentConversationRef(record.value.conversationRef, `${path}.conversationRef`);
1100
+ if (!conversationRef.ok)
1101
+ return conversationRef;
1102
+ if (!conversationRef.value) {
1103
+ return workflowError(`${path}.conversationRef is required for ready HUNSU Drafts`);
1104
+ }
1105
+ return ok({ ...base, status: "ready", hunsuId: hunsuId.value, newLineId: newLineId.value, conversationRef: conversationRef.value });
1106
+ }
1107
+ case "confirmed": {
1108
+ const hunsuId = primitive(makeHunsuId(record.value.hunsuId));
1109
+ if (!hunsuId.ok)
1110
+ return hunsuId;
1111
+ const newLineId = primitive(makeLineId(record.value.newLineId));
1112
+ if (!newLineId.ok)
1113
+ return newLineId;
1114
+ if (record.value.conversationRef === undefined) {
1115
+ return workflowError(`${path}.conversationRef is required for confirmed HUNSU Drafts`);
1116
+ }
1117
+ const conversationRef = decodeAgentConversationRef(record.value.conversationRef, `${path}.conversationRef`);
1118
+ if (!conversationRef.ok)
1119
+ return conversationRef;
1120
+ if (!conversationRef.value) {
1121
+ return workflowError(`${path}.conversationRef is required for confirmed HUNSU Drafts`);
1122
+ }
1123
+ return ok({ ...base, status: "confirmed", hunsuId: hunsuId.value, newLineId: newLineId.value, conversationRef: conversationRef.value });
1124
+ }
1125
+ case "draft":
1126
+ if (record.value.hunsuId !== undefined || record.value.newLineId !== undefined || record.value.conversationRef !== undefined) {
1127
+ return workflowError(`${path} draft status cannot carry hunsuId, newLineId, or conversationRef`);
1128
+ }
1129
+ return ok({ ...base, status: "draft" });
1130
+ case "discarded":
1131
+ if (record.value.hunsuId !== undefined || record.value.newLineId !== undefined) {
1132
+ return workflowError(`${path} discarded status cannot carry hunsuId or newLineId`);
1133
+ }
1134
+ if (record.value.conversationRef === undefined) {
1135
+ return ok({ ...base, status: "discarded" });
1136
+ }
1137
+ const conversationRef = decodeAgentConversationRef(record.value.conversationRef, `${path}.conversationRef`);
1138
+ return conversationRef.ok ? ok({ ...base, status: "discarded", conversationRef: conversationRef.value }) : conversationRef;
1139
+ }
1140
+ }
1141
+ export function decodeMoveRecord(value, path, decodeHarness) {
1142
+ const record = decodeRecord(value, path);
1143
+ if (!record.ok)
1144
+ return record;
1145
+ const id = primitive(makeMoveId(record.value.id));
1146
+ if (!id.ok)
1147
+ return id;
1148
+ const lineId = primitive(makeLineId(record.value.lineId));
1149
+ if (!lineId.ok)
1150
+ return lineId;
1151
+ const fromNodeId = primitive(makeNodeId(record.value.fromNodeId));
1152
+ if (!fromNodeId.ok)
1153
+ return fromNodeId;
1154
+ const toNodeId = primitive(makeNodeId(record.value.toNodeId));
1155
+ if (!toNodeId.ok)
1156
+ return toNodeId;
1157
+ const teamName = record.value.teamName === undefined
1158
+ ? ok(undefined)
1159
+ : primitive(makeTeamName(record.value.teamName, `${path}.teamName`));
1160
+ if (!teamName.ok)
1161
+ return teamName;
1162
+ const ordinal = record.value.ordinal === undefined ? ok(undefined) : decodeNonNegativeInteger(record.value.ordinal, `${path}.ordinal`);
1163
+ if (!ordinal.ok)
1164
+ return ordinal;
1165
+ const snapshot = record.value.snapshot === undefined ? ok(undefined) : decodeTeamSnapshot(record.value.snapshot, `${path}.snapshot`, decodeHarness);
1166
+ if (!snapshot.ok)
1167
+ return snapshot;
1168
+ const sourceHunsuId = decodeOptionalHunsuId(record.value.sourceHunsuId);
1169
+ if (!sourceHunsuId.ok)
1170
+ return sourceHunsuId;
1171
+ const executeId = decodeOptionalExecuteId(record.value.executeId);
1172
+ if (!executeId.ok)
1173
+ return executeId;
1174
+ const conversationRef = decodeAgentConversationRef(record.value.conversationRef, `${path}.conversationRef`);
1175
+ if (!conversationRef.ok)
1176
+ return conversationRef;
1177
+ const worktree = decodeWorktreeRef(record.value.worktree, `${path}.worktree`);
1178
+ if (!worktree.ok)
1179
+ return worktree;
1180
+ const summary = primitive(makeSummary(record.value.summary, `${path}.summary`));
1181
+ if (!summary.ok)
1182
+ return summary;
1183
+ const commit = primitive(makeMoveCommit(record.value.commit, `${path}.commit`));
1184
+ if (!commit.ok)
1185
+ return commit;
1186
+ const evidence = decodeEvidenceArray(record.value.evidence, `${path}.evidence`, true);
1187
+ if (!evidence.ok)
1188
+ return evidence;
1189
+ const risks = decodeOptionalRiskArray(record.value.risks, `${path}.risks`);
1190
+ if (!risks.ok)
1191
+ return risks;
1192
+ const recordedBy = decodeAllowed(`${path}.recordedBy`, record.value.recordedBy, ["SYSTEM"]);
1193
+ if (!recordedBy.ok)
1194
+ return recordedBy;
1195
+ const recordedAt = decodeOptionalString(record.value.recordedAt, `${path}.recordedAt`);
1196
+ if (!recordedAt.ok)
1197
+ return recordedAt;
1198
+ const outcome = decodeAllowed(`${path}.outcome`, record.value.outcome, MOVE_OUTCOMES);
1199
+ if (!outcome.ok)
1200
+ return outcome;
1201
+ const base = {
1202
+ id: id.value,
1203
+ lineId: lineId.value,
1204
+ fromNodeId: fromNodeId.value,
1205
+ toNodeId: toNodeId.value,
1206
+ teamName: teamName.value,
1207
+ ordinal: ordinal.value,
1208
+ snapshot: snapshot.value,
1209
+ sourceHunsuId: sourceHunsuId.value,
1210
+ executeId: executeId.value,
1211
+ conversationRef: conversationRef.value,
1212
+ worktree: worktree.value,
1213
+ summary: summary.value,
1214
+ commit: commit.value,
1215
+ evidence: evidence.value,
1216
+ risks: risks.value,
1217
+ recordedBy: recordedBy.value,
1218
+ recordedAt: recordedAt.value
1219
+ };
1220
+ if (outcome.value === "arrived") {
1221
+ const reachedDestinationIds = decodeDestinationIdArray(record.value.reachedDestinationIds, `${path}.reachedDestinationIds`, true);
1222
+ if (!reachedDestinationIds.ok)
1223
+ return reachedDestinationIds;
1224
+ const single = primitive(makeSingleItemArray(reachedDestinationIds.value, `${path}.reachedDestinationIds`));
1225
+ if (!single.ok)
1226
+ return single;
1227
+ if (record.value.failureReason !== undefined) {
1228
+ return workflowError(`${path}.failureReason is only valid for accident MOVEs`);
1229
+ }
1230
+ return ok({ ...base, outcome: "arrived", reachedDestinationIds: single.value });
1231
+ }
1232
+ const reachedDestinationIds = decodeDestinationIdArray(record.value.reachedDestinationIds, `${path}.reachedDestinationIds`, false);
1233
+ if (!reachedDestinationIds.ok)
1234
+ return reachedDestinationIds;
1235
+ if (reachedDestinationIds.value.length !== 0) {
1236
+ return workflowError(`${path}.reachedDestinationIds must be empty for accident MOVEs`);
1237
+ }
1238
+ const failureReason = primitive(makeFailureReason(record.value.failureReason, `${path}.failureReason`));
1239
+ return failureReason.ok ? ok({ ...base, outcome: "accident", reachedDestinationIds: [], failureReason: failureReason.value }) : failureReason;
1240
+ }
1241
+ export function decodeHunsuRecord(value, path, decodeHarness) {
1242
+ const record = decodeRecord(value, path);
1243
+ if (!record.ok)
1244
+ return record;
1245
+ const id = primitive(makeHunsuId(record.value.id));
1246
+ if (!id.ok)
1247
+ return id;
1248
+ const hunsuDraftId = decodeOptionalHunsuDraftId(record.value.hunsuDraftId);
1249
+ if (!hunsuDraftId.ok)
1250
+ return hunsuDraftId;
1251
+ const conversationRef = decodeAgentConversationRef(record.value.conversationRef, `${path}.conversationRef`);
1252
+ if (!conversationRef.ok)
1253
+ return conversationRef;
1254
+ const lineId = primitive(makeLineId(record.value.lineId));
1255
+ if (!lineId.ok)
1256
+ return lineId;
1257
+ const fromNodeId = primitive(makeNodeId(record.value.fromNodeId));
1258
+ if (!fromNodeId.ok)
1259
+ return fromNodeId;
1260
+ const toNodeId = primitive(makeNodeId(record.value.toNodeId));
1261
+ if (!toNodeId.ok)
1262
+ return toNodeId;
1263
+ const target = decodeHunsuTarget(record.value.target, `${path}.target`);
1264
+ if (!target.ok)
1265
+ return target;
1266
+ const summary = primitive(makeSummary(record.value.summary, `${path}.summary`));
1267
+ if (!summary.ok)
1268
+ return summary;
1269
+ const newLineId = record.value.newLineId === undefined
1270
+ ? ok(undefined)
1271
+ : primitive(makeLineId(record.value.newLineId));
1272
+ if (!newLineId.ok)
1273
+ return newLineId;
1274
+ const sourceMoveId = decodeOptionalMoveId(record.value.sourceMoveId);
1275
+ if (!sourceMoveId.ok)
1276
+ return sourceMoveId;
1277
+ const newTeamName = record.value.newTeamName === undefined
1278
+ ? ok(undefined)
1279
+ : primitive(makeTeamName(record.value.newTeamName, `${path}.newTeamName`));
1280
+ if (!newTeamName.ok)
1281
+ return newTeamName;
1282
+ const teamSnapshot = decodeTeamSnapshot(record.value.teamSnapshot, `${path}.teamSnapshot`, decodeHarness);
1283
+ if (!teamSnapshot.ok)
1284
+ return teamSnapshot;
1285
+ const changedFiles = decodeHunsuChangedFileArray(record.value.changedFiles, `${path}.changedFiles`);
1286
+ if (!changedFiles.ok)
1287
+ return changedFiles;
1288
+ const recordedBy = decodeAllowed(`${path}.recordedBy`, record.value.recordedBy, ["DIRECTOR"]);
1289
+ if (!recordedBy.ok)
1290
+ return recordedBy;
1291
+ const recordedAt = decodeOptionalString(record.value.recordedAt, `${path}.recordedAt`);
1292
+ if (!recordedAt.ok)
1293
+ return recordedAt;
1294
+ return ok({
1295
+ id: id.value,
1296
+ hunsuDraftId: hunsuDraftId.value,
1297
+ conversationRef: conversationRef.value,
1298
+ lineId: lineId.value,
1299
+ fromNodeId: fromNodeId.value,
1300
+ toNodeId: toNodeId.value,
1301
+ target: target.value,
1302
+ summary: summary.value,
1303
+ newLineId: newLineId.value,
1304
+ sourceMoveId: sourceMoveId.value,
1305
+ newTeamName: newTeamName.value,
1306
+ teamSnapshot: teamSnapshot.value,
1307
+ changedFiles: changedFiles.value,
1308
+ recordedBy: recordedBy.value,
1309
+ recordedAt: recordedAt.value
1310
+ });
1311
+ }
1312
+ function decodeHunsuChangedFileArray(value, path) {
1313
+ const array = decodeArray(value, path);
1314
+ if (!array.ok)
1315
+ return array;
1316
+ if (array.value.length === 0) {
1317
+ return workflowError(`${path} must contain at least one changed file`);
1318
+ }
1319
+ const files = [];
1320
+ for (let index = 0; index < array.value.length; index += 1) {
1321
+ const file = decodeHunsuChangedFile(array.value[index], `${path}[${index}]`);
1322
+ if (!file.ok)
1323
+ return file;
1324
+ files.push(file.value);
1325
+ }
1326
+ return ok(files);
1327
+ }
1328
+ function decodeHunsuChangedFile(value, path) {
1329
+ const record = decodeRecord(value, path);
1330
+ if (!record.ok)
1331
+ return record;
1332
+ const filePath = primitive(makeNonEmptyText(record.value.path, `${path}.path`));
1333
+ if (!filePath.ok)
1334
+ return filePath;
1335
+ const kind = decodeAllowed(`${path}.kind`, record.value.kind, ["added", "updated", "removed"]);
1336
+ if (!kind.ok)
1337
+ return kind;
1338
+ const summary = primitive(makeSummary(record.value.summary, `${path}.summary`));
1339
+ return summary.ok ? ok({ path: filePath.value, kind: kind.value, summary: summary.value }) : summary;
1340
+ }
1341
+ export function decodeNodeRecord(value, path, decodeHarness) {
1342
+ const record = decodeRecord(value, path);
1343
+ if (!record.ok)
1344
+ return record;
1345
+ const id = primitive(makeNodeId(record.value.id));
1346
+ if (!id.ok)
1347
+ return id;
1348
+ const requestId = primitive(makeRequestId(record.value.requestId));
1349
+ if (!requestId.ok)
1350
+ return requestId;
1351
+ const lineId = decodeOptionalLineId(record.value.lineId);
1352
+ if (!lineId.ok)
1353
+ return lineId;
1354
+ const teamName = record.value.teamName === undefined
1355
+ ? ok(undefined)
1356
+ : primitive(makeTeamName(record.value.teamName, `${path}.teamName`));
1357
+ if (!teamName.ok)
1358
+ return teamName;
1359
+ const ordinal = decodeNonNegativeInteger(record.value.ordinal, `${path}.ordinal`);
1360
+ if (!ordinal.ok)
1361
+ return ordinal;
1362
+ const destinations = decodeDestinationRecordArray(record.value.destinations, `${path}.destinations`);
1363
+ if (!destinations.ok)
1364
+ return destinations;
1365
+ const harness = decodeHarness(record.value.harness);
1366
+ if (!harness.ok)
1367
+ return harness;
1368
+ const harnessGraph = decodeHarnessGraph(record.value.harnessGraph, harness.value, `${path}.harnessGraph`);
1369
+ if (!harnessGraph.ok)
1370
+ return harnessGraph;
1371
+ const harnessLock = decodeHubPackageLock(record.value.harnessLock, `${path}.harnessLock`, ["team"]);
1372
+ if (!harnessLock.ok)
1373
+ return harnessLock;
1374
+ const executorPackageBindings = decodeExecutorPackageBindings(record.value.executorPackageBindings, `${path}.executorPackageBindings`);
1375
+ if (!executorPackageBindings.ok)
1376
+ return executorPackageBindings;
1377
+ const resourcePackageBindings = decodeResourcePackageBindings(record.value.resourcePackageBindings, `${path}.resourcePackageBindings`);
1378
+ if (!resourcePackageBindings.ok)
1379
+ return resourcePackageBindings;
1380
+ const artifactActions = decodeArtifactActionDefinitionArray(record.value.artifactActions ?? [], `${path}.artifactActions`);
1381
+ if (!artifactActions.ok)
1382
+ return artifactActions;
1383
+ const source = decodeNodeSource(record.value.source, `${path}.source`);
1384
+ if (!source.ok)
1385
+ return source;
1386
+ const createdAt = decodeOptionalString(record.value.createdAt, `${path}.createdAt`);
1387
+ if (!createdAt.ok)
1388
+ return createdAt;
1389
+ return ok({
1390
+ id: id.value,
1391
+ requestId: requestId.value,
1392
+ lineId: lineId.value,
1393
+ teamName: teamName.value,
1394
+ ordinal: ordinal.value,
1395
+ destinations: destinations.value,
1396
+ harness: harness.value,
1397
+ harnessGraph: harnessGraph.value,
1398
+ harnessLock: harnessLock.value,
1399
+ executorPackageBindings: executorPackageBindings.value,
1400
+ resourcePackageBindings: resourcePackageBindings.value,
1401
+ artifactActions: artifactActions.value,
1402
+ source: source.value,
1403
+ createdAt: createdAt.value
1404
+ });
1405
+ }
1406
+ export function decodeOptionalHunsuDraftId(value) {
1407
+ return value === undefined ? ok(undefined) : primitive(makeHunsuDraftId(value));
1408
+ }
1409
+ export function decodeOptionalHunsuId(value) {
1410
+ return value === undefined ? ok(undefined) : primitive(makeHunsuId(value));
1411
+ }
1412
+ export function decodeOptionalMoveId(value) {
1413
+ return value === undefined ? ok(undefined) : primitive(makeMoveId(value));
1414
+ }
1415
+ export function decodeArtifactActionIdArray(value, path) {
1416
+ const array = decodeArray(value, path);
1417
+ if (!array.ok)
1418
+ return array;
1419
+ const ids = [];
1420
+ for (let index = 0; index < array.value.length; index += 1) {
1421
+ const id = primitive(makeArtifactActionId(array.value[index]));
1422
+ if (!id.ok)
1423
+ return id;
1424
+ ids.push(id.value);
1425
+ }
1426
+ return ok(ids);
1427
+ }
1428
+ export function decodeOptionalLineId(value) {
1429
+ return value === undefined ? ok(undefined) : primitive(makeLineId(value));
1430
+ }
1431
+ export function decodeOptionalNodeId(value) {
1432
+ return value === undefined ? ok(undefined) : primitive(makeNodeId(value));
1433
+ }
1434
+ export function decodeOptionalExecuteId(value) {
1435
+ return value === undefined ? ok(undefined) : primitive(makeExecuteId(value));
1436
+ }
1437
+ function decodeTeamSnapshot(value, path, decodeHarness) {
1438
+ const record = decodeRecord(value, path);
1439
+ if (!record.ok)
1440
+ return record;
1441
+ const teamName = primitive(makeTeamName(record.value.teamName, `${path}.teamName`));
1442
+ if (!teamName.ok)
1443
+ return teamName;
1444
+ const moveOrdinal = decodeNonNegativeInteger(record.value.moveOrdinal, `${path}.moveOrdinal`);
1445
+ if (!moveOrdinal.ok)
1446
+ return moveOrdinal;
1447
+ const destinations = decodeDestinationRecordArray(record.value.destinations, `${path}.destinations`);
1448
+ if (!destinations.ok)
1449
+ return destinations;
1450
+ const harness = decodeHarness(record.value.harness);
1451
+ if (!harness.ok)
1452
+ return harness;
1453
+ const harnessGraph = decodeHarnessGraph(record.value.harnessGraph, harness.value, `${path}.harnessGraph`);
1454
+ if (!harnessGraph.ok)
1455
+ return harnessGraph;
1456
+ const harnessLock = decodeHubPackageLock(record.value.harnessLock, `${path}.harnessLock`, ["team"]);
1457
+ if (!harnessLock.ok)
1458
+ return harnessLock;
1459
+ const executorPackageBindings = decodeExecutorPackageBindings(record.value.executorPackageBindings, `${path}.executorPackageBindings`);
1460
+ if (!executorPackageBindings.ok)
1461
+ return executorPackageBindings;
1462
+ const resourcePackageBindings = decodeResourcePackageBindings(record.value.resourcePackageBindings, `${path}.resourcePackageBindings`);
1463
+ if (!resourcePackageBindings.ok)
1464
+ return resourcePackageBindings;
1465
+ const artifactActions = decodeArtifactActionDefinitionArray(record.value.artifactActions ?? [], `${path}.artifactActions`);
1466
+ if (!artifactActions.ok)
1467
+ return artifactActions;
1468
+ return ok({
1469
+ teamName: teamName.value,
1470
+ moveOrdinal: moveOrdinal.value,
1471
+ destinations: destinations.value,
1472
+ harness: harness.value,
1473
+ harnessGraph: harnessGraph.value,
1474
+ harnessLock: harnessLock.value,
1475
+ executorPackageBindings: executorPackageBindings.value,
1476
+ resourcePackageBindings: resourcePackageBindings.value,
1477
+ artifactActions: artifactActions.value
1478
+ });
1479
+ }
1480
+ function decodeHarnessGraph(value, fallbackHarness, path) {
1481
+ if (value === undefined) {
1482
+ return ok(harnessEntityFromSnapshot(fallbackHarness));
1483
+ }
1484
+ return validateHarnessEntity(value, path);
1485
+ }
1486
+ export function decodeArtifactActionDefinitionArray(value, path) {
1487
+ const array = decodeArray(value, path);
1488
+ if (!array.ok)
1489
+ return array;
1490
+ const actions = [];
1491
+ for (let index = 0; index < array.value.length; index += 1) {
1492
+ const action = decodeArtifactActionDefinition(array.value[index], `${path}[${index}]`);
1493
+ if (!action.ok)
1494
+ return action;
1495
+ if (actions.some(existing => existing.id === action.value.id)) {
1496
+ return workflowError(`${path} contains duplicate ArtifactActionId: ${action.value.id}`);
1497
+ }
1498
+ actions.push(action.value);
1499
+ }
1500
+ return ok(actions);
1501
+ }
1502
+ function decodeSnapshotFiles(value, path) {
1503
+ const array = decodeArray(value, path);
1504
+ if (!array.ok)
1505
+ return array;
1506
+ const files = [];
1507
+ for (let index = 0; index < array.value.length; index += 1) {
1508
+ const record = decodeRecord(array.value[index], `${path}[${index}]`);
1509
+ if (!record.ok)
1510
+ return record;
1511
+ const filePath = primitive(makeNonEmptyText(record.value.path, `${path}[${index}].path`));
1512
+ if (!filePath.ok)
1513
+ return filePath;
1514
+ const text = record.value.text;
1515
+ if (typeof text !== "string") {
1516
+ return workflowError(`${path}[${index}].text must be a string`);
1517
+ }
1518
+ files.push({ path: filePath.value, text });
1519
+ }
1520
+ return ok(files);
1521
+ }
1522
+ function isExactSemver(value) {
1523
+ return /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(value);
1524
+ }
1525
+ function decodeArtifactOwner(value, path) {
1526
+ const record = decodeRecord(value, path);
1527
+ if (!record.ok)
1528
+ return record;
1529
+ switch (record.value.type) {
1530
+ case "move": {
1531
+ const id = primitive(makeMoveId(record.value.id));
1532
+ return id.ok ? ok({ type: "move", id: id.value }) : id;
1533
+ }
1534
+ case "hunsu": {
1535
+ const id = primitive(makeHunsuId(record.value.id));
1536
+ return id.ok ? ok({ type: "hunsu", id: id.value }) : id;
1537
+ }
1538
+ case "line": {
1539
+ const id = primitive(makeLineId(record.value.id));
1540
+ return id.ok ? ok({ type: "line", id: id.value }) : id;
1541
+ }
1542
+ default:
1543
+ return workflowError(`${path}.type must be move, hunsu, or line`);
1544
+ }
1545
+ }
1546
+ function decodeArtifactActionEnv(value, path) {
1547
+ if (value === undefined) {
1548
+ return ok(undefined);
1549
+ }
1550
+ const record = decodeRecord(value, path);
1551
+ if (!record.ok)
1552
+ return record;
1553
+ const env = {};
1554
+ for (const [name, raw] of Object.entries(record.value)) {
1555
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) {
1556
+ return workflowError(`${path}.${name} must be a valid environment variable name`);
1557
+ }
1558
+ const spec = decodeRecord(raw, `${path}.${name}`);
1559
+ if (!spec.ok)
1560
+ return spec;
1561
+ const keys = ["default", "value", "required", "alias", "fromAliasUrl"].filter(key => key in spec.value);
1562
+ if (keys.length !== 1) {
1563
+ return workflowError(`${path}.${name} must declare exactly one of default, value, required, alias, or fromAliasUrl`);
1564
+ }
1565
+ if ("default" in spec.value) {
1566
+ const scalar = decodeScalar(spec.value.default, `${path}.${name}.default`);
1567
+ if (!scalar.ok)
1568
+ return scalar;
1569
+ env[name] = { default: scalar.value };
1570
+ }
1571
+ else if ("value" in spec.value) {
1572
+ const scalar = decodeScalar(spec.value.value, `${path}.${name}.value`);
1573
+ if (!scalar.ok)
1574
+ return scalar;
1575
+ env[name] = { value: scalar.value };
1576
+ }
1577
+ else if ("alias" in spec.value) {
1578
+ const alias = primitive(makeNonEmptyText(spec.value.alias, `${path}.${name}.alias`));
1579
+ if (!alias.ok)
1580
+ return alias;
1581
+ env[name] = { alias: alias.value };
1582
+ }
1583
+ else if ("fromAliasUrl" in spec.value) {
1584
+ const alias = primitive(makeNonEmptyText(spec.value.fromAliasUrl, `${path}.${name}.fromAliasUrl`));
1585
+ if (!alias.ok)
1586
+ return alias;
1587
+ env[name] = { fromAliasUrl: alias.value };
1588
+ }
1589
+ else {
1590
+ if (spec.value.required !== true) {
1591
+ return workflowError(`${path}.${name}.required must be true`);
1592
+ }
1593
+ const secret = decodeOptionalBoolean(spec.value.secret, `${path}.${name}.secret`);
1594
+ if (!secret.ok)
1595
+ return secret;
1596
+ env[name] = { required: true, secret: secret.value };
1597
+ }
1598
+ }
1599
+ return ok(env);
1600
+ }
1601
+ function decodeArtifactActionRunner(value, path) {
1602
+ const record = decodeRecord(value, path);
1603
+ if (!record.ok)
1604
+ return record;
1605
+ switch (record.value.type) {
1606
+ case "docker_compose": {
1607
+ const file = primitive(makeNonEmptyText(record.value.file, `${path}.file`));
1608
+ if (!file.ok)
1609
+ return file;
1610
+ const projectName = decodeOptionalNonEmptyText(record.value.projectName, `${path}.projectName`);
1611
+ return projectName.ok ? ok({ type: "docker_compose", file: file.value, projectName: projectName.value }) : projectName;
1612
+ }
1613
+ case "command": {
1614
+ const command = primitive(makeNonEmptyText(record.value.command, `${path}.command`));
1615
+ if (!command.ok)
1616
+ return command;
1617
+ const stopCommand = decodeOptionalNonEmptyText(record.value.stopCommand, `${path}.stopCommand`);
1618
+ return stopCommand.ok ? ok({ type: "command", command: command.value, stopCommand: stopCommand.value }) : stopCommand;
1619
+ }
1620
+ default:
1621
+ return workflowError(`${path}.type must be docker_compose or command`);
1622
+ }
1623
+ }
1624
+ function decodeArtifactActionAliases(value, path) {
1625
+ if (value === undefined) {
1626
+ return ok(undefined);
1627
+ }
1628
+ const record = decodeRecord(value, path);
1629
+ if (!record.ok)
1630
+ return record;
1631
+ const aliases = {};
1632
+ for (const [alias, raw] of Object.entries(record.value)) {
1633
+ if (!/^[A-Za-z][A-Za-z0-9_-]*$/.test(alias)) {
1634
+ return workflowError(`${path}.${alias} must start with a letter and contain only letters, numbers, "_" or "-"`);
1635
+ }
1636
+ const aliasRecord = decodeRecord(raw, `${path}.${alias}`);
1637
+ if (!aliasRecord.ok)
1638
+ return aliasRecord;
1639
+ const hasTarget = "target" in aliasRecord.value;
1640
+ const hasService = "service" in aliasRecord.value;
1641
+ const hasContainerPort = "containerPort" in aliasRecord.value;
1642
+ if (hasTarget && (hasService || hasContainerPort || "healthPath" in aliasRecord.value)) {
1643
+ return workflowError(`${path}.${alias} must not mix target with docker service fields`);
1644
+ }
1645
+ if (hasTarget) {
1646
+ const target = primitive(makeNonEmptyText(aliasRecord.value.target, `${path}.${alias}.target`));
1647
+ if (!target.ok)
1648
+ return target;
1649
+ aliases[alias] = { target: target.value };
1650
+ continue;
1651
+ }
1652
+ if (!hasService || !hasContainerPort) {
1653
+ return workflowError(`${path}.${alias} must declare either target or both service and containerPort`);
1654
+ }
1655
+ const service = primitive(makeNonEmptyText(aliasRecord.value.service, `${path}.${alias}.service`));
1656
+ if (!service.ok)
1657
+ return service;
1658
+ const containerPort = primitive(makePositiveInteger(aliasRecord.value.containerPort, `${path}.${alias}.containerPort`));
1659
+ if (!containerPort.ok)
1660
+ return containerPort;
1661
+ const healthPath = decodeOptionalNonEmptyText(aliasRecord.value.healthPath, `${path}.${alias}.healthPath`);
1662
+ if (!healthPath.ok)
1663
+ return healthPath;
1664
+ aliases[alias] = {
1665
+ service: service.value,
1666
+ containerPort: containerPort.value,
1667
+ healthPath: healthPath.value
1668
+ };
1669
+ }
1670
+ return ok(aliases);
1671
+ }
1672
+ function decodeArtifactActionEvidence(value, path) {
1673
+ if (value === undefined) {
1674
+ return ok(undefined);
1675
+ }
1676
+ const record = decodeRecord(value, path);
1677
+ if (!record.ok)
1678
+ return record;
1679
+ const paths = decodeOptionalNonEmptyTextArray(record.value.paths, `${path}.paths`);
1680
+ if (!paths.ok)
1681
+ return paths;
1682
+ const attach = decodeOptionalBoolean(record.value.attach, `${path}.attach`);
1683
+ if (!attach.ok)
1684
+ return attach;
1685
+ return ok({
1686
+ attach: attach.value,
1687
+ paths: paths.value
1688
+ });
1689
+ }
1690
+ function decodeScalar(value, path) {
1691
+ return typeof value === "string" || typeof value === "number" || typeof value === "boolean"
1692
+ ? ok(value)
1693
+ : workflowError(`${path} must be a string, number, or boolean`);
1694
+ }
1695
+ function rejectDuplicateIds(ids, path, label) {
1696
+ const seen = new Set();
1697
+ for (const id of ids) {
1698
+ if (seen.has(id)) {
1699
+ return workflowError(`${path} contains duplicate ${label}: ${id}`);
1700
+ }
1701
+ seen.add(id);
1702
+ }
1703
+ return ok(undefined);
1704
+ }
1705
+ function decodeNodeSource(value, path) {
1706
+ const record = decodeRecord(value, path);
1707
+ if (!record.ok)
1708
+ return record;
1709
+ switch (record.value.type) {
1710
+ case "initial-execute-team":
1711
+ case "request": {
1712
+ const requestId = primitive(makeRequestId(record.value.requestId));
1713
+ return requestId.ok ? ok({ type: record.value.type, requestId: requestId.value }) : requestId;
1714
+ }
1715
+ case "move": {
1716
+ const moveId = primitive(makeMoveId(record.value.moveId));
1717
+ if (!moveId.ok)
1718
+ return moveId;
1719
+ const fromNodeId = primitive(makeNodeId(record.value.fromNodeId));
1720
+ return fromNodeId.ok ? ok({ type: "move", moveId: moveId.value, fromNodeId: fromNodeId.value }) : fromNodeId;
1721
+ }
1722
+ case "hunsu": {
1723
+ const hunsuId = primitive(makeHunsuId(record.value.hunsuId));
1724
+ if (!hunsuId.ok)
1725
+ return hunsuId;
1726
+ const fromNodeId = primitive(makeNodeId(record.value.fromNodeId));
1727
+ return fromNodeId.ok ? ok({ type: "hunsu", hunsuId: hunsuId.value, fromNodeId: fromNodeId.value }) : fromNodeId;
1728
+ }
1729
+ default:
1730
+ return workflowError(`${path}.type must be a supported node source type`);
1731
+ }
1732
+ }
1733
+ function lineWithStatus(base, status) {
1734
+ switch (status) {
1735
+ case "active":
1736
+ return ok({ ...base, status: "active" });
1737
+ case "paused":
1738
+ return ok({ ...base, status: "paused" });
1739
+ case "complete":
1740
+ return ok({ ...base, status: "complete" });
1741
+ case "failed":
1742
+ return ok({ ...base, status: "failed" });
1743
+ case "abandoned":
1744
+ return ok({ ...base, status: "abandoned" });
1745
+ }
1746
+ }
1747
+ function rejectDestinationLifecycleFields(record, path, fields, base) {
1748
+ const invalid = rejectPresent(record, path, fields);
1749
+ return invalid.ok ? ok({ ...base, status: "pending" }) : invalid;
1750
+ }
1751
+ function rejectPresent(record, path, fields) {
1752
+ const field = fields.find(candidate => record[candidate] !== undefined);
1753
+ return field ? workflowError(`${path}.${field} is not valid for this lifecycle status`) : ok(undefined);
1754
+ }
1755
+ function primitive(result) {
1756
+ return result.ok ? ok(result.value) : workflowError(result.error.message);
1757
+ }