@zq-silk/yui 0.2.0 → 0.4.2

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 (208) hide show
  1. package/ARCHITECTURE.md +603 -133
  2. package/README.md +806 -31
  3. package/dist/agent/agent.js +2 -1
  4. package/dist/agent/argumentPolicy.js +3 -1
  5. package/dist/agent/launchEnvironment.js +106 -0
  6. package/dist/agent/managedRuntimeEnvironment.js +34 -0
  7. package/dist/brief/taskBrief.js +11 -1
  8. package/dist/cli/agentConfigurationPicker.js +287 -0
  9. package/dist/cli/commandCatalog.js +488 -60
  10. package/dist/cli/completion.js +146 -22
  11. package/dist/cli/helpRenderer.js +3 -1
  12. package/dist/cli/interactionCandidates.js +53 -15
  13. package/dist/cli/interactionPolicy.js +267 -30
  14. package/dist/cli/interactiveSelection.js +6 -2
  15. package/dist/cli/invocationRouter.js +5 -1
  16. package/dist/cli/operatorWizard.js +87 -0
  17. package/dist/cli/roleOptionCatalog.js +1 -0
  18. package/dist/cli/roleWizard.js +185 -21
  19. package/dist/cli/updateCommand.js +62 -19
  20. package/dist/cli/updateOrchestrator.js +539 -0
  21. package/dist/cli/updatePorts.js +1119 -0
  22. package/dist/cli/upgradeCommand.js +112 -0
  23. package/dist/cli.js +1420 -86
  24. package/dist/commands/agentCommands.js +146 -3
  25. package/dist/commands/configCommands.js +126 -0
  26. package/dist/commands/controllerCommands.js +365 -0
  27. package/dist/commands/globalRoleCommands.js +168 -126
  28. package/dist/commands/jobCommands.js +18 -8
  29. package/dist/commands/operatorCommands.js +159 -9
  30. package/dist/commands/profileCommands.js +203 -0
  31. package/dist/commands/projectCommands.js +650 -0
  32. package/dist/commands/roleConfiguration.js +85 -24
  33. package/dist/commands/roleRuntimeGuard.js +12 -0
  34. package/dist/commands/roleSkillValidation.js +47 -0
  35. package/dist/commands/taskActor.js +127 -0
  36. package/dist/commands/taskCommands.js +4201 -313
  37. package/dist/commands/taskCompletionGate.js +131 -0
  38. package/dist/commands/taskContextCommand.js +244 -30
  39. package/dist/commands/taskInputCommands.js +177 -59
  40. package/dist/commands/taskIntegrationCommands.js +303 -0
  41. package/dist/commands/taskOverviewCommand.js +363 -0
  42. package/dist/commands/taskRoleRuntimeStatus.js +125 -19
  43. package/dist/commands/textInput.js +15 -0
  44. package/dist/completion/completionInstaller.js +26 -22
  45. package/dist/config/yuiConfig.js +4 -3
  46. package/dist/context/dispatchContext.js +90 -38
  47. package/dist/context/roleSessionContext.js +119 -0
  48. package/dist/controller/claudeLifecycleHook.js +203 -0
  49. package/dist/controller/clientRuntime.js +408 -56
  50. package/dist/controller/codexLifecycleHook.js +108 -0
  51. package/dist/controller/controller.js +1089 -32
  52. package/dist/controller/domainIdentity.js +505 -0
  53. package/dist/controller/ephemeralResourceReaper.js +131 -0
  54. package/dist/controller/fileSchedulerStoreAdapter.js +2153 -103
  55. package/dist/controller/providerHookRunFence.js +127 -0
  56. package/dist/controller/resourceCleanupLinux.js +286 -0
  57. package/dist/controller/resourceInventory.js +531 -0
  58. package/dist/controller/resourceInventoryLinux.js +610 -0
  59. package/dist/controller/runtime.js +629 -10
  60. package/dist/controller/runtimeEventInbox.js +564 -0
  61. package/dist/controller/runtimeEventProcessor.js +248 -0
  62. package/dist/controller/runtimeLaunchCoordinator.js +477 -0
  63. package/dist/controller/sessionNotify.js +121 -78
  64. package/dist/coordination/deadlineScheduler.js +15 -0
  65. package/dist/coordination/mailboxScheduler.js +108 -0
  66. package/dist/coordination/workMailbox.js +329 -0
  67. package/dist/coordination/workMailboxQueue.js +86 -0
  68. package/dist/core/controllerClient.js +19 -5
  69. package/dist/core/controllerEndpoint.js +37 -0
  70. package/dist/core/controllerServer.js +218 -10
  71. package/dist/core/protocol.js +6 -2
  72. package/dist/decision/decision.js +2 -1
  73. package/dist/doctor/doctor.js +681 -32
  74. package/dist/domain/validation.js +53 -0
  75. package/dist/errors/cliError.js +5 -3
  76. package/dist/event/taskEvent.js +7 -3
  77. package/dist/execution/codexThreadNaming.js +160 -0
  78. package/dist/execution/executionGroup.js +579 -0
  79. package/dist/executor/agentAdapter.js +255 -40
  80. package/dist/executor/agentConfigurationCatalog.js +326 -0
  81. package/dist/executor/agentConfigurationProbe.js +506 -0
  82. package/dist/executor/agentExecutor.js +625 -10
  83. package/dist/executor/codexConfigConflict.js +290 -0
  84. package/dist/executor/effectiveLaunch.js +340 -0
  85. package/dist/executor/executorRegistry.js +238 -36
  86. package/dist/executor/fileRoleLaunchPlanner.js +550 -40
  87. package/dist/executor/turnCompletion.js +126 -0
  88. package/dist/input/inputRequest.js +30 -9
  89. package/dist/integration/changeSet.js +36 -0
  90. package/dist/integration/checkResult.js +24 -0
  91. package/dist/integration/gitIntegrationService.js +695 -0
  92. package/dist/integration/integrationAttempt.js +142 -0
  93. package/dist/interaction/operatorPresentation.js +96 -0
  94. package/dist/lifecycle/canonicalLifecycleEvent.js +342 -0
  95. package/dist/lifecycle/exactRunTerminalization.js +572 -0
  96. package/dist/lifecycle/providerLifecycleMapping.js +190 -0
  97. package/dist/lifecycle/taskRoleSessionReset.js +124 -0
  98. package/dist/message/message.js +23 -7
  99. package/dist/milestone/milestone.js +2 -1
  100. package/dist/operator/operatorSessionHistory.js +124 -0
  101. package/dist/output/agentConfigurationPresentation.js +43 -0
  102. package/dist/output/rolePresentation.js +34 -10
  103. package/dist/output/terminal.js +8 -0
  104. package/dist/output/timePresentation.js +55 -0
  105. package/dist/profile/agentProfile.js +128 -0
  106. package/dist/repository/gitWorkspace.js +578 -24
  107. package/dist/repository/project.js +213 -0
  108. package/dist/repository/taskWorkspaceCoordinator.js +392 -0
  109. package/dist/repository/taskWorkspacePreparer.js +1688 -191
  110. package/dist/review/reviewConfig.js +11 -0
  111. package/dist/review/reviewRound.js +399 -0
  112. package/dist/review/taskFinalReviewContract.js +90 -0
  113. package/dist/role/role.js +124 -23
  114. package/dist/run/agentRun.js +155 -12
  115. package/dist/run/runIdentity.js +82 -0
  116. package/dist/runtime/exactControlPlane.js +472 -0
  117. package/dist/runtime/index.js +8 -0
  118. package/dist/runtime/lifecycleReservation.js +38 -0
  119. package/dist/runtime/ports.js +11 -0
  120. package/dist/runtime/preallocatedNativeSession.js +13 -0
  121. package/dist/runtime/promptEnvelope.js +30 -0
  122. package/dist/runtime/runtimeBinding.js +31 -0
  123. package/dist/runtime/runtimeOwner.js +14 -0
  124. package/dist/runtime/sessionLaunchRequest.js +62 -0
  125. package/dist/runtime/sessionTitle.js +54 -0
  126. package/dist/runtime/taskRuntimeIsolation.js +643 -0
  127. package/dist/runtime/tmuxAdapters.js +315 -0
  128. package/dist/runtime/turnCompletion.js +3 -0
  129. package/dist/runtime/validation.js +23 -0
  130. package/dist/scheduler/activeRoleRunDelivery.js +342 -32
  131. package/dist/scheduler/activeTaskProgress.js +63 -0
  132. package/dist/scheduler/leaderFailure.js +2 -1
  133. package/dist/scheduler/leaderWakeupProcessor.js +307 -66
  134. package/dist/scheduler/operatorInputNotificationProcessor.js +109 -46
  135. package/dist/scheduler/operatorNotification.js +44 -2
  136. package/dist/scheduler/ports.js +28 -1
  137. package/dist/scheduler/roleRunLiveness.js +131 -25
  138. package/dist/scheduler/roleRunStall.js +951 -0
  139. package/dist/scheduler/taskExecutionProjection.js +544 -0
  140. package/dist/scheduler/wakeupQueue.js +3 -0
  141. package/dist/setup/setupCommand.js +302 -52
  142. package/dist/storage/compatibleTaskStore.js +102 -0
  143. package/dist/storage/migration/baseline.js +78 -0
  144. package/dist/storage/migration/classifier.js +51 -0
  145. package/dist/storage/migration/compatibleCodec.js +53 -0
  146. package/dist/storage/migration/engine.js +147 -0
  147. package/dist/storage/migration/index.js +33 -0
  148. package/dist/storage/migration/planner.js +154 -0
  149. package/dist/storage/migration/productionRegistry.js +486 -0
  150. package/dist/storage/migration/registry.js +169 -0
  151. package/dist/storage/migration/report.js +54 -0
  152. package/dist/storage/migration/types.js +31 -0
  153. package/dist/storage/storageSchema.js +147 -123
  154. package/dist/storage/storageVersions.js +11 -0
  155. package/dist/storage/taskStore.js +1793 -197
  156. package/dist/storage/upgrade/homeClassification.js +156 -0
  157. package/dist/storage/upgrade/homeMigrationTarget.js +595 -0
  158. package/dist/storage/upgrade/offlineUpgradeInventory.js +315 -0
  159. package/dist/storage/upgrade/productionMigrationRegistry.js +6 -0
  160. package/dist/storage/upgrade/recordVersionScan.js +176 -0
  161. package/dist/storage/upgrade/recordVersions.js +159 -0
  162. package/dist/storage/upgrade/switchProgress.js +80 -0
  163. package/dist/storage/upgrade/upgradeOrchestrator.js +948 -0
  164. package/dist/storage/upgrade/upgradeReceipt.js +161 -0
  165. package/dist/storage/upgradeCoordination.js +186 -0
  166. package/dist/storage/upgradeFence.js +366 -0
  167. package/dist/task/task.js +132 -26
  168. package/dist/task/taskRecordReference.js +66 -0
  169. package/dist/tmux/commandExecutor.js +75 -2
  170. package/dist/tmux/tmuxManager.js +747 -49
  171. package/dist/version.js +23 -0
  172. package/dist/web/assets/assetManifest.js +62 -0
  173. package/dist/web/assets/client/app.js +631 -0
  174. package/dist/web/assets/client/components.js +605 -0
  175. package/dist/web/assets/client/dom.js +14 -0
  176. package/dist/web/assets/client/format.js +28 -0
  177. package/dist/web/assets/client/i18n.js +494 -0
  178. package/dist/web/assets/client/markdown.js +114 -0
  179. package/dist/web/assets/client/theme.js +32 -0
  180. package/dist/web/assets/client/view.js +458 -0
  181. package/dist/web/assets/fontData.js +12 -0
  182. package/dist/web/assets/fonts.js +12 -0
  183. package/dist/web/assets/shell.js +114 -0
  184. package/dist/web/assets/styles/cards.js +135 -0
  185. package/dist/web/assets/styles/layout.js +47 -0
  186. package/dist/web/assets/styles/markdown.js +29 -0
  187. package/dist/web/assets/styles/responsive.js +39 -0
  188. package/dist/web/assets/styles/tokens.js +101 -0
  189. package/dist/web/assets/styles/widgets.js +147 -0
  190. package/dist/web/tmuxWebTerminal.js +158 -0
  191. package/dist/web/webServer.js +463 -0
  192. package/dist/web/webSnapshot.js +148 -0
  193. package/dist/workItem/workItem.js +642 -23
  194. package/dist/workspace/gitChangeSetCapture.js +86 -0
  195. package/dist/workspace/workItemChangeSetManager.js +445 -0
  196. package/dist/worktree/managedWorkspace.js +202 -0
  197. package/docs/task-local-identity.md +62 -0
  198. package/i18n/README.zh-CN.md +406 -31
  199. package/package.json +10 -2
  200. package/skills/yui-leader/SKILL.md +601 -39
  201. package/skills/yui-operator/SKILL.md +255 -34
  202. package/skills/yui-reviewer/SKILL.md +57 -0
  203. package/skills/yui-worker/SKILL.md +214 -17
  204. package/dist/commands/repositoryCommands.js +0 -86
  205. package/dist/operator/operatorContext.js +0 -66
  206. package/dist/repository/repository.js +0 -55
  207. package/dist/scheduler/archivedTaskRuntime.js +0 -12
  208. package/dist/worktree/roleWorkspace.js +0 -62
@@ -0,0 +1,126 @@
1
+ import { requireSafeIdentity, requireText, requireTimestamp } from "../runtime/validation.js";
2
+ export const DEFAULT_RECENT_TURN_ID_LIMIT = 32;
3
+ const PENDING_TURN_COMPLETION_FIELDS = [
4
+ "schemaVersion",
5
+ "taskId",
6
+ "roleName",
7
+ "agentId",
8
+ "nativeSessionId",
9
+ "turnId",
10
+ "runId",
11
+ "summary",
12
+ "observedAt",
13
+ "dueAt"
14
+ ];
15
+ export function createPendingTurnCompletion(input) {
16
+ const observedAt = requireTimestamp(input.observedAt, "Turn observedAt");
17
+ const dueAt = requireTimestamp(input.dueAt, "Turn dueAt");
18
+ requireOrderedTimestamps(observedAt, dueAt);
19
+ return {
20
+ schemaVersion: 1,
21
+ taskId: requireSafeIdentity(input.taskId, "Task id"),
22
+ roleName: requireSafeIdentity(input.roleName, "Role name"),
23
+ agentId: requireSafeIdentity(input.agentId, "Agent id"),
24
+ nativeSessionId: requireText(input.nativeSessionId, "Native session id"),
25
+ turnId: requireSafeIdentity(input.turnId, "Turn id"),
26
+ runId: requireSafeIdentity(input.runId, "Run id"),
27
+ summary: requireText(input.summary, "Turn summary"),
28
+ observedAt,
29
+ dueAt
30
+ };
31
+ }
32
+ export function validatePendingTurnCompletion(value) {
33
+ const input = requireRecord(value, "PendingTurnCompletion");
34
+ requireExactFields(input, PENDING_TURN_COMPLETION_FIELDS, "PendingTurnCompletion");
35
+ if (input.schemaVersion !== 1) {
36
+ throw new Error("PendingTurnCompletion must use schemaVersion 1.");
37
+ }
38
+ const observedAt = requirePersistedTimestamp(input.observedAt, "PendingTurnCompletion observedAt");
39
+ const dueAt = requirePersistedTimestamp(input.dueAt, "PendingTurnCompletion dueAt");
40
+ requireOrderedTimestamps(observedAt, dueAt);
41
+ return {
42
+ schemaVersion: 1,
43
+ taskId: requireSafeIdentityValue(input.taskId, "Task id"),
44
+ roleName: requireSafeIdentityValue(input.roleName, "Role name"),
45
+ agentId: requireSafeIdentityValue(input.agentId, "Agent id"),
46
+ nativeSessionId: requireTextValue(input.nativeSessionId, "Native session id"),
47
+ turnId: requireSafeIdentityValue(input.turnId, "Turn id"),
48
+ runId: requireSafeIdentityValue(input.runId, "Run id"),
49
+ summary: requireTextValue(input.summary, "Turn summary"),
50
+ observedAt,
51
+ dueAt
52
+ };
53
+ }
54
+ export function validateRecentTurnIds(value, limit = DEFAULT_RECENT_TURN_ID_LIMIT) {
55
+ const normalizedLimit = requireLimit(limit);
56
+ if (!Array.isArray(value))
57
+ throw new Error("Recent Turn ids must be an array.");
58
+ if (value.length > normalizedLimit) {
59
+ throw new Error(`Recent Turn ids must not contain more than ${normalizedLimit} entries.`);
60
+ }
61
+ const result = value.map((turnId) => requireSafeIdentityValue(turnId, "Recent Turn id"));
62
+ if (new Set(result).size !== result.length) {
63
+ throw new Error("Recent Turn ids must not contain duplicates.");
64
+ }
65
+ return result;
66
+ }
67
+ export function rememberRecentTurnId(recentTurnIds, turnId, limit = DEFAULT_RECENT_TURN_ID_LIMIT) {
68
+ const normalizedLimit = requireLimit(limit);
69
+ const existing = validateRecentTurnIds(recentTurnIds, normalizedLimit);
70
+ const normalizedTurnId = requireSafeIdentity(turnId, "Recent Turn id");
71
+ return [
72
+ ...existing.filter((candidate) => candidate !== normalizedTurnId),
73
+ normalizedTurnId
74
+ ].slice(-normalizedLimit);
75
+ }
76
+ export function hasRecentTurnId(recentTurnIds, turnId) {
77
+ const existing = validateRecentTurnIds(recentTurnIds);
78
+ return existing.includes(requireSafeIdentity(turnId, "Recent Turn id"));
79
+ }
80
+ function requireOrderedTimestamps(observedAt, dueAt) {
81
+ if (Date.parse(dueAt) < Date.parse(observedAt)) {
82
+ throw new Error("PendingTurnCompletion dueAt must not be earlier than observedAt.");
83
+ }
84
+ }
85
+ function requirePersistedTimestamp(value, label) {
86
+ if (typeof value !== "string")
87
+ throw new Error(`${label} must be a valid timestamp.`);
88
+ const timestamp = Date.parse(value);
89
+ if (!Number.isFinite(timestamp))
90
+ throw new Error(`${label} must be a valid timestamp.`);
91
+ const canonical = new Date(timestamp).toISOString();
92
+ if (value !== canonical)
93
+ throw new Error(`${label} must be a canonical timestamp.`);
94
+ return canonical;
95
+ }
96
+ function requireRecord(value, label) {
97
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
98
+ throw new Error(`${label} must be an object.`);
99
+ }
100
+ return value;
101
+ }
102
+ function requireExactFields(value, fields, label) {
103
+ const expected = new Set(fields);
104
+ const unknown = Object.keys(value).find((field) => !expected.has(field));
105
+ if (unknown !== undefined)
106
+ throw new Error(`${label} has unknown field: ${unknown}.`);
107
+ const missing = fields.find((field) => !Object.hasOwn(value, field));
108
+ if (missing !== undefined)
109
+ throw new Error(`${label} is missing field: ${missing}.`);
110
+ }
111
+ function requireSafeIdentityValue(value, label) {
112
+ if (typeof value !== "string")
113
+ throw new Error(`${label} is invalid.`);
114
+ return requireSafeIdentity(value, label);
115
+ }
116
+ function requireTextValue(value, label) {
117
+ if (typeof value !== "string")
118
+ throw new Error(`${label} is invalid.`);
119
+ return requireText(value, label);
120
+ }
121
+ function requireLimit(value) {
122
+ if (!Number.isInteger(value) || value < 1) {
123
+ throw new Error("Recent Turn id limit must be a positive integer.");
124
+ }
125
+ return value;
126
+ }
@@ -1,3 +1,4 @@
1
+ import { validateTaskRecordReference } from "../task/taskRecordReference.js";
1
2
  export class InputRequestStateError extends Error {
2
3
  requestId;
3
4
  status;
@@ -13,7 +14,7 @@ export function createInputRequest(id, taskId, requester, input, now) {
13
14
  const timestamp = requireDate(now, "Input request creation time");
14
15
  const choices = normalizeChoices(input.choices);
15
16
  return validateInputRequest({
16
- schemaVersion: 1,
17
+ schemaVersion: 2,
17
18
  id: requireIdentity(id, "Input request id"),
18
19
  taskId: requireIdentity(taskId, "Input request Task id"),
19
20
  requester: normalizeRequester(requester),
@@ -71,12 +72,12 @@ export function validateInputRequest(value) {
71
72
  "schemaVersion", "id", "taskId", "requester", "question", "choices",
72
73
  "blockedRefs", "policy", "status", "createdAt", "updatedAt", ...terminalField
73
74
  ], "Input request");
74
- if (request.schemaVersion !== 1)
75
- throw new Error("Input request must use schemaVersion 1.");
75
+ if (request.schemaVersion !== 2)
76
+ throw new Error("Input request must use schemaVersion 2.");
76
77
  const choices = validateChoices(request.choices);
77
78
  const createdAt = requireTimestamp(request.createdAt, "Input request createdAt");
78
79
  const base = {
79
- schemaVersion: 1,
80
+ schemaVersion: 2,
80
81
  id: requireIdentity(request.id, "Input request id"),
81
82
  taskId: requireIdentity(request.taskId, "Input request Task id"),
82
83
  requester: normalizeRequester(request.requester),
@@ -87,6 +88,18 @@ export function validateInputRequest(value) {
87
88
  createdAt,
88
89
  updatedAt: requireTimestamp(request.updatedAt, "Input request updatedAt")
89
90
  };
91
+ validateTaskRecordReference({ taskId: base.taskId, localId: base.id }, "inputRequest");
92
+ validateTaskRecordReference({
93
+ taskId: base.requester.taskId,
94
+ localId: base.requester.runId
95
+ }, "agentRun");
96
+ for (const reference of base.blockedRefs) {
97
+ validateTaskRecordReference({ taskId: reference.taskId, localId: reference.id }, reference.type === "run" ? "agentRun" : "workItem");
98
+ }
99
+ if (base.requester.taskId !== base.taskId
100
+ || base.blockedRefs.some(({ taskId }) => taskId !== base.taskId)) {
101
+ throw new Error("Input request references must belong to its Task.");
102
+ }
90
103
  if (Date.parse(base.updatedAt) < Date.parse(base.createdAt)) {
91
104
  throw new Error("Input request updatedAt cannot precede createdAt.");
92
105
  }
@@ -161,13 +174,17 @@ function normalizeBlockedRefs(value) {
161
174
  throw new Error("Input blocked references must be an array.");
162
175
  const references = value.map((reference) => {
163
176
  const item = record(reference, "Input blocked reference");
164
- exact(item, ["type", "id"], "Input blocked reference");
177
+ exact(item, ["type", "taskId", "id"], "Input blocked reference");
165
178
  if (item.type !== "work-item" && item.type !== "run") {
166
179
  throw new Error("Input blocked reference type must be work-item or run.");
167
180
  }
168
- return { type: item.type, id: requireIdentity(item.id, "Input blocked reference id") };
181
+ return {
182
+ type: item.type,
183
+ taskId: requireIdentity(item.taskId, "Input blocked reference Task id"),
184
+ id: requireIdentity(item.id, "Input blocked reference id")
185
+ };
169
186
  });
170
- const keys = references.map(({ type, id }) => `${type}:${id}`);
187
+ const keys = references.map(({ type, taskId, id }) => `${type}:${taskId}/${id}`);
171
188
  if (new Set(keys).size !== keys.length)
172
189
  throw new Error("Input blocked references must be unique.");
173
190
  return references;
@@ -207,11 +224,12 @@ function validateBlockedRefs(value) {
207
224
  function normalizeRequester(value) {
208
225
  const requester = record(value, "Input requester");
209
226
  exact(requester, requester.nativeSessionId === undefined
210
- ? ["roleName", "agentId", "runId"]
211
- : ["roleName", "agentId", "runId", "nativeSessionId"], "Input requester");
227
+ ? ["taskId", "roleName", "agentId", "runId"]
228
+ : ["taskId", "roleName", "agentId", "runId", "nativeSessionId"], "Input requester");
212
229
  if (requester.roleName !== "leader")
213
230
  throw new Error("Input requester must be the Task Leader.");
214
231
  return {
232
+ taskId: requireIdentity(requester.taskId, "Input requester Task id"),
215
233
  roleName: "leader",
216
234
  agentId: requireIdentity(requester.agentId, "Input requester Agent id"),
217
235
  runId: requireIdentity(requester.runId, "Input requester Run id"),
@@ -228,6 +246,9 @@ function normalizeAnswer(request, answer) {
228
246
  exact(value, ["text"], "Input answer");
229
247
  return { text: requireText(value.text, "Input answer") };
230
248
  }
249
+ if (value.text !== undefined) {
250
+ throw new Error("Choice input request requires a choice answer.");
251
+ }
231
252
  exact(value, ["choiceKey"], "Input answer");
232
253
  const key = requireChoiceKey(value.choiceKey);
233
254
  const choice = request.choices.find((candidate) => candidate.key === key);
@@ -0,0 +1,36 @@
1
+ import { normalizedUniqueText, requireIdentity, requireText, requireTimestamp } from "../domain/validation.js";
2
+ import { validateTaskRecordReference } from "../task/taskRecordReference.js";
3
+ export function createWorkItemChangeSet(input, now) {
4
+ return validateChangeSet({
5
+ schemaVersion: 2,
6
+ ...input,
7
+ changedPaths: [...input.changedPaths],
8
+ createdAt: now.toISOString()
9
+ });
10
+ }
11
+ export function validateChangeSet(changeSet) {
12
+ if (changeSet.schemaVersion !== 2)
13
+ throw new Error("ChangeSet must use schemaVersion 2.");
14
+ validateTaskRecordReference({ taskId: changeSet.taskId, localId: changeSet.id }, "changeSet");
15
+ validateTaskRecordReference({
16
+ taskId: changeSet.taskId,
17
+ localId: changeSet.workItemId
18
+ }, "workItem");
19
+ requireIdentity(changeSet.projectId, "Project id");
20
+ requireCommit(changeSet.baseCommit, "ChangeSet base commit");
21
+ requireCommit(changeSet.headCommit, "ChangeSet head commit");
22
+ if (changeSet.baseCommit === changeSet.headCommit) {
23
+ throw new Error("ChangeSet must contain at least one commit.");
24
+ }
25
+ requireText(changeSet.branch, "ChangeSet branch");
26
+ normalizedUniqueText(changeSet.changedPaths, "ChangeSet path");
27
+ requireTimestamp(changeSet.createdAt, "ChangeSet createdAt");
28
+ return changeSet;
29
+ }
30
+ function requireCommit(value, label) {
31
+ const normalized = requireText(value, label).toLowerCase();
32
+ if (!/^[a-f0-9]{40}(?:[a-f0-9]{24})?$/u.test(normalized)) {
33
+ throw new Error(`${label} is invalid.`);
34
+ }
35
+ return normalized;
36
+ }
@@ -0,0 +1,24 @@
1
+ import { requireText } from "../domain/validation.js";
2
+ export function normalizeCheckResult(check) {
3
+ if (!["passed", "failed", "skipped"].includes(check.outcome)) {
4
+ throw new Error(`Check outcome is invalid: ${String(check.outcome)}.`);
5
+ }
6
+ return {
7
+ name: requireText(check.name, "Check name"),
8
+ outcome: check.outcome,
9
+ ...(check.details === undefined
10
+ ? {}
11
+ : { details: requireText(check.details, "Check details") }),
12
+ ...(check.logPath === undefined
13
+ ? {}
14
+ : { logPath: requireRelativePath(check.logPath, "Check log path") })
15
+ };
16
+ }
17
+ function requireRelativePath(value, label) {
18
+ const normalized = requireText(value, label);
19
+ if (/^(?:[A-Za-z]:[\\/]|[\\/])/u.test(normalized)
20
+ || normalized.split(/[\\/]/u).includes("..")) {
21
+ throw new Error(`${label} must be relative.`);
22
+ }
23
+ return normalized.replaceAll("\\", "/");
24
+ }