@zq-silk/yui 0.15.12 → 0.16.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 (236) hide show
  1. package/ARCHITECTURE.md +3 -2
  2. package/ARCHITECTURE.zh-CN.md +3 -2
  3. package/dist/artifacts/artifactCommitLock.js +16 -55
  4. package/dist/artifacts/taskArtifactRepository.js +3 -3
  5. package/dist/cli/agentConfigurationPicker.js +1 -1
  6. package/dist/cli/commandCatalog.js +57 -41
  7. package/dist/cli/completion.js +18 -18
  8. package/dist/cli/completionWizard.js +1 -1
  9. package/dist/cli/dynamicCompletion.js +1 -1
  10. package/dist/cli/interactionPolicy.js +7 -3
  11. package/dist/cli/invocationAuthority.js +64 -0
  12. package/dist/cli/managedDiagnostics.js +1 -1
  13. package/dist/cli/parseRepeatable.js +35 -0
  14. package/dist/cli/updatePorts.js +26 -5
  15. package/dist/cli.js +1280 -1243
  16. package/dist/commands/agentCommands.js +4 -4
  17. package/dist/commands/capabilityCommands.js +1 -1
  18. package/dist/commands/configCommands.js +6 -57
  19. package/dist/commands/configOverview.js +2 -2
  20. package/dist/commands/durableJobCommands.js +12 -6
  21. package/dist/commands/globalRoleCommands.js +53 -29
  22. package/dist/commands/grantCommands.js +0 -3
  23. package/dist/commands/jobCommands.js +1 -4
  24. package/dist/commands/operatorCommands.js +3 -3
  25. package/dist/commands/projectCommands.js +1 -10
  26. package/dist/commands/resourcesCommands.js +10 -40
  27. package/dist/commands/roleConfiguration.js +2 -6
  28. package/dist/commands/taskActivationCommands.js +4 -8
  29. package/dist/commands/taskAgentCapabilities.js +9 -0
  30. package/dist/commands/taskCommandSupport.js +155 -0
  31. package/dist/commands/taskCommandTypes.js +1 -0
  32. package/dist/commands/taskCommands.js +271 -710
  33. package/dist/commands/taskCompletionGate.js +1 -1
  34. package/dist/commands/taskExecutionCommands.js +1 -1
  35. package/dist/commands/taskFactCommands.js +325 -0
  36. package/dist/commands/taskInputCommands.js +12 -42
  37. package/dist/commands/taskIntegrationCommands.js +17 -57
  38. package/dist/commands/taskNextActionCommand.js +7 -6
  39. package/dist/commands/taskPublicationAdoptCommand.js +3 -3
  40. package/dist/commands/taskPublicationCommands.js +1 -1
  41. package/dist/commands/taskPublicationVerifyCommand.js +1 -1
  42. package/dist/commands/taskRemoteDeliveryCommand.js +2 -94
  43. package/dist/commands/taskUpstreamCommands.js +19 -8
  44. package/dist/commands/workflowCommands.js +6 -1
  45. package/dist/completion/completionInstaller.js +26 -26
  46. package/dist/completion/completionState.js +26 -26
  47. package/dist/completion/fileCompletionManager.js +6 -11
  48. package/dist/config/configCatalog.js +0 -2
  49. package/dist/config/timeZone.js +14 -0
  50. package/dist/config/yuiConfig.js +0 -29
  51. package/dist/context/runContextPack.js +2 -3
  52. package/dist/context/taskCatalog.js +3 -5
  53. package/dist/context/taskContext.js +139 -151
  54. package/dist/context/wakeRunReferences.js +11 -0
  55. package/dist/controller/agentCapabilities.js +58 -0
  56. package/dist/controller/agentRuntimeObserver.js +6 -11
  57. package/dist/controller/clientRuntime.js +22 -14
  58. package/dist/controller/controller.js +9 -22
  59. package/dist/controller/fileSchedulerStoreAdapter.js +117 -212
  60. package/dist/controller/jobClient.js +4 -4
  61. package/dist/controller/jobControl.js +60 -25
  62. package/dist/controller/jobSupervisor.js +4 -4
  63. package/dist/controller/providerRetryDelivery.js +7 -7
  64. package/dist/controller/runtime.js +51 -44
  65. package/dist/controller/runtimeEventProcessor.js +0 -5
  66. package/dist/controller/sessionOwnerReconciliation.js +5 -0
  67. package/dist/controller/taskAgentError.js +35 -0
  68. package/dist/coordination/workMailboxQueue.js +2 -24
  69. package/dist/core/controllerClient.js +2 -2
  70. package/dist/core/fileLockOwner.js +74 -0
  71. package/dist/decision/decision.js +17 -0
  72. package/dist/doctor/doctor.js +18 -9
  73. package/dist/event/taskEvent.js +12 -0
  74. package/dist/execution/workItemExecutionProjection.js +2 -2
  75. package/dist/executor/agentCapabilityConfig.js +43 -0
  76. package/dist/executor/agentConfigurationCatalog.js +49 -11
  77. package/dist/executor/agentExecutor.js +5 -8
  78. package/dist/executor/fileRoleLaunchPlanner.js +11 -53
  79. package/dist/executor/taskAgentCapabilities.js +64 -0
  80. package/dist/integration/deliveryObligation.js +1 -69
  81. package/dist/integration/gitIntegrationService.js +60 -84
  82. package/dist/integration/integrationAttempt.js +3 -14
  83. package/dist/integration/integrationSourceApplication.js +4 -12
  84. package/dist/integration/manifestTags.js +2 -2
  85. package/dist/job/durableJob.js +4 -25
  86. package/dist/job/jobAssignmentScope.js +22 -0
  87. package/dist/job/jobOperation.js +16 -0
  88. package/dist/job/jobRunner.js +2 -1
  89. package/dist/job/stepDirectory.js +15 -0
  90. package/dist/kernel/builtinCapabilities.js +15 -11
  91. package/dist/kernel/kernelPorts.js +1 -17
  92. package/dist/lifecycle/exactRunTerminalization.js +1 -9
  93. package/dist/message/inputControlResolution.js +3 -4
  94. package/dist/message/message.js +17 -20
  95. package/dist/message/messageContinuation.js +1 -1
  96. package/dist/milestone/milestone.js +11 -0
  97. package/dist/observability/orchestrationMetrics.js +24 -1
  98. package/dist/observability/runtimeIdentity.js +1 -17
  99. package/dist/output/agentConfigurationPresentation.js +2 -0
  100. package/dist/output/timePresentation.js +1 -14
  101. package/dist/plugins/pluginService.js +12 -4
  102. package/dist/release/releaseWorkflowPorts.js +45 -96
  103. package/dist/release/runtimeRelease.js +9 -15
  104. package/dist/repository/projectMaintenanceLock.js +23 -64
  105. package/dist/repository/taskBaseFreshness.js +1 -1
  106. package/dist/repository/taskWorkspaceCoordinator.js +13 -22
  107. package/dist/repository/taskWorkspacePreparer.js +29 -25
  108. package/dist/repository/workspaceCleanupInspection.js +5 -5
  109. package/dist/resources/autoResourceGc.js +4 -77
  110. package/dist/resources/liveReferences.js +7 -54
  111. package/dist/resources/resourceDiscovery.js +5 -118
  112. package/dist/resources/resourceGc.js +233 -181
  113. package/dist/resources/resourceRegistrar.js +5 -4
  114. package/dist/resources/resourceRegistry.js +3 -37
  115. package/dist/resources/resourceRegistryStore.js +13 -36
  116. package/dist/resources/sqliteResourceRegistry.js +43 -26
  117. package/dist/review/deltaRecheck.js +1 -1
  118. package/dist/review/reviewAcceptance.js +1 -1
  119. package/dist/review/reviewDecision.js +1 -1
  120. package/dist/review/reviewRound.js +8 -7
  121. package/dist/review/taskFinalReviewContractResolution.js +1 -1
  122. package/dist/runtime/acpProtocol.js +4 -51
  123. package/dist/runtime/acpSession.js +10 -75
  124. package/dist/runtime/acpSessionConfiguration.js +1 -8
  125. package/dist/runtime/agentEndpoint.js +3 -2
  126. package/dist/runtime/agentFailureContext.js +43 -0
  127. package/dist/runtime/agentHost.js +15 -15
  128. package/dist/runtime/codexInteractiveHost.js +2 -2
  129. package/dist/runtime/index.js +0 -1
  130. package/dist/runtime/managedCaller.js +21 -1
  131. package/dist/runtime/providerErrors.js +38 -0
  132. package/dist/runtime/runtimeObservation.js +0 -5
  133. package/dist/runtime/runtimeSessionCandidate.js +2 -3
  134. package/dist/runtime/sessionOwnerIdentity.js +1 -1
  135. package/dist/runtime/structuredProviderHost.js +4 -41
  136. package/dist/runtime/tmuxAdapters.js +6 -8
  137. package/dist/scheduler/activeRoleRunDelivery.js +7 -7
  138. package/dist/scheduler/leaderWakeupProcessor.js +19 -6
  139. package/dist/scheduler/operatorEvent.js +12 -20
  140. package/dist/scheduler/operatorInputNotificationProcessor.js +1 -1
  141. package/dist/scheduler/ports.js +5 -8
  142. package/dist/scheduler/roleRunLiveness.js +4 -4
  143. package/dist/scheduler/roleRunStall.js +16 -23
  144. package/dist/scheduler/taskExecutionProjection.js +25 -33
  145. package/dist/scheduler/taskObservabilityProjection.js +0 -1
  146. package/dist/scheduler/taskWake.js +5 -10
  147. package/dist/scheduler/wakeReason.js +2 -0
  148. package/dist/scheduler/wakeupQueue.js +2 -4
  149. package/dist/setup/setupCommand.js +1 -1
  150. package/dist/storage/contextRecords.js +106 -0
  151. package/dist/storage/currentTaskStore.js +1 -1
  152. package/dist/storage/homeLayout.js +13 -17
  153. package/dist/storage/migrations/agentFailureContext.js +22 -0
  154. package/dist/storage/migrations/currentInputContract.js +86 -0
  155. package/dist/storage/migrations/currentRuntimeContract.js +228 -0
  156. package/dist/storage/migrations/historicalVerificationPlan.js +35 -0
  157. package/dist/storage/migrations/integrationContinuation.js +5 -4
  158. package/dist/storage/migrations/narrowAgentFailureContext.js +65 -0
  159. package/dist/storage/migrations/notificationOnlyWakes.js +74 -0
  160. package/dist/storage/migrations/verificationPlanV1.js +162 -0
  161. package/dist/storage/migrations/verificationPolicy.js +74 -0
  162. package/dist/storage/migrations/workItemHistory.js +46 -0
  163. package/dist/storage/persistenceWorker.js +12 -4
  164. package/dist/storage/recordValidation.js +87 -0
  165. package/dist/storage/sqliteSchema.js +142 -1
  166. package/dist/storage/sqliteStore.js +156 -162
  167. package/dist/storage/storageSchema.js +2 -14
  168. package/dist/storage/storageVersions.js +1 -1
  169. package/dist/storage/storeRpc.js +15 -7
  170. package/dist/storage/taskStore.js +3 -7
  171. package/dist/storage/upgrade/upgradeOrchestrator.js +25 -37
  172. package/dist/task/completionReadiness.js +5 -24
  173. package/dist/task/draftPlan.js +0 -53
  174. package/dist/task/nextAction.js +7 -13
  175. package/dist/task/remoteDeliveryService.js +89 -0
  176. package/dist/task/taskActivation.js +2 -25
  177. package/dist/task/taskActivationService.js +0 -2
  178. package/dist/task/taskRecordReference.js +0 -1
  179. package/dist/task/taskSubmission.js +3 -14
  180. package/dist/telemetry/sqliteTelemetryBatch.js +29 -0
  181. package/dist/telemetry/sqliteTelemetryStore.js +49 -49
  182. package/dist/telemetry/telemetryWiring.js +4 -3
  183. package/dist/tmux/tmuxManager.js +29 -20
  184. package/dist/verification/gateArtifact.js +15 -24
  185. package/dist/verification/gateArtifactStore.js +14 -7
  186. package/dist/verification/verificationGateService.js +29 -88
  187. package/dist/verification/verificationPlan.js +27 -72
  188. package/dist/web/assets/client/app.js +1 -1
  189. package/dist/web/assets/client/taskSurface.js +4 -4
  190. package/dist/web/webServer.js +4 -6
  191. package/dist/web/webSnapshot.js +3 -83
  192. package/dist/web/webTaskSurface.js +17 -46
  193. package/dist/workItem/workItem.js +1 -12
  194. package/docs/architecture/capabilities-and-resources.md +14 -2
  195. package/docs/architecture/capabilities-and-resources.zh-CN.md +11 -1
  196. package/docs/managed-turn-and-session-runtime.md +83 -10
  197. package/docs/managed-turn-and-session-runtime.zh-CN.md +65 -11
  198. package/docs/plugin-sdk.md +4 -2
  199. package/docs/provider-runtime.md +3 -1
  200. package/docs/provider-runtime.zh-CN.md +4 -2
  201. package/docs/release-workflow.md +190 -7
  202. package/docs/release-workflow.zh-CN.md +139 -5
  203. package/docs/roles-and-configuration.md +29 -0
  204. package/docs/roles-and-configuration.zh-CN.md +21 -0
  205. package/docs/sqlite-control-plane-design.md +16 -3
  206. package/docs/sqlite-control-plane-design.zh-CN.md +13 -2
  207. package/docs/task-dag-semantics.md +3 -3
  208. package/docs/task-dag-semantics.zh-CN.md +2 -2
  209. package/docs/task-delivery.md +91 -5
  210. package/docs/task-delivery.zh-CN.md +72 -4
  211. package/docs/task-discovery.md +10 -11
  212. package/docs/task-discovery.zh-CN.md +8 -9
  213. package/docs/testing/verification-levels.md +145 -9
  214. package/docs/testing/verification-levels.zh-CN.md +103 -8
  215. package/package.json +1 -1
  216. package/skills/yui-leader/SKILL.md +5 -0
  217. package/skills/yui-leader/references/integration.md +9 -5
  218. package/skills/yui-leader/references/planning.md +9 -3
  219. package/skills/yui-operator/SKILL.md +15 -10
  220. package/skills/yui-runtime/SKILL.md +6 -6
  221. package/skills/yui-runtime/references/recovery.md +47 -0
  222. package/dist/agent/agentRegistry.js +0 -10
  223. package/dist/agentRun/runIdentity.js +0 -22
  224. package/dist/commands/deliveryGuardPreflight.js +0 -30
  225. package/dist/commands/taskIntegrationQueueCommands.js +0 -226
  226. package/dist/commands/taskOverviewCommand.js +0 -377
  227. package/dist/completion/completionWizard.js +0 -125
  228. package/dist/context/dispatchContext.js +0 -110
  229. package/dist/context/wakeNotification.js +0 -144
  230. package/dist/integration/integrationCheckEvidenceReuse.js +0 -53
  231. package/dist/integration/integrationQueueEntry.js +0 -191
  232. package/dist/integration/integrationQueueService.js +0 -810
  233. package/dist/release/fakeReleasePorts.js +0 -55
  234. package/dist/runtime/sessionOwnerRegistry.js +0 -137
  235. package/dist/task/deliveryGuard.js +0 -226
  236. /package/dist/{commands/taskActor.js → task/taskAuthority.js} +0 -0
@@ -0,0 +1,228 @@
1
+ import { readdirSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { createHash } from "node:crypto";
4
+ import { requireIdentity, requirePositiveInteger, requireText, requireTimestamp } from "../../domain/validation.js";
5
+ import { readHistoricalVerificationPlan } from "./verificationPlanV1.js";
6
+ /** Immutable audit only: no current executor or cache lookup reads this table.
7
+ * Raw payloads and binary logs survive retiring their executable contracts. */
8
+ export const CURRENT_RUNTIME_CONTRACT_SQL = `
9
+ CREATE TABLE storage_migration_archive (
10
+ migration_version INTEGER NOT NULL,
11
+ family TEXT NOT NULL,
12
+ record_key TEXT NOT NULL,
13
+ payload TEXT NOT NULL,
14
+ content BLOB,
15
+ PRIMARY KEY (migration_version, family, record_key)
16
+ );
17
+ `;
18
+ /** Read-only blockers, shared by preflight and the actual 34→35 transaction.
19
+ * An old admitted execution/physical owner must settle through its original
20
+ * contract. Neither an upgrade nor a directory name proves it is safe to drop. */
21
+ export function preflightCurrentRuntimeContract(db) {
22
+ const gate = db.prepare(`SELECT task_id,integration_id FROM integration_attempts
23
+ WHERE status NOT IN ('committed','superseded','failed')
24
+ AND json_type(payload,'$.gatePlanDigest') IS NOT NULL LIMIT 1`)
25
+ .get();
26
+ if (gate)
27
+ throw new Error(`Current verification cutover requires the admitted gate to settle: ${gate.task_id}/${gate.integration_id}. Continue or abort it with the old release.`);
28
+ const owner = db.prepare(`SELECT process_key FROM session_owners
29
+ WHERE json_extract(payload,'$.providerRoot.attribution')='launch-env' LIMIT 1`)
30
+ .get();
31
+ if (owner)
32
+ throw new Error(`Retired Session owner attribution requires explicit old-release reconciliation: ${owner.process_key}. Preserve its process evidence; no owner was rewritten.`);
33
+ if (db.name !== ":memory:" && db.name !== "") {
34
+ const directory = join(dirname(db.name), "runtime", "session-owners");
35
+ let files;
36
+ try {
37
+ files = readdirSync(directory).filter(name => name.endsWith(".json"));
38
+ }
39
+ catch (error) {
40
+ if (error.code !== "ENOENT")
41
+ throw error;
42
+ files = [];
43
+ }
44
+ if (files.length)
45
+ throw new Error(`Retired file Session owners remain at ${directory}. Inspect and release their exact resources with the old release, then archive the files outside the active Home before upgrading.`);
46
+ }
47
+ for (const row of historicalIntegrations(db)) {
48
+ const value = JSON.parse(row.payload);
49
+ if (!["committed", "superseded", "failed"].includes(value.status)) {
50
+ throw new Error(`Historical Integration must be explicitly settled before retirement: ${row.task_id}/${row.integration_id}.`);
51
+ }
52
+ const workspace = db.prepare(`SELECT 1 FROM managed_workspaces WHERE task_id=?
53
+ AND json_extract(payload,'$.owner.integrationAttemptId')=? LIMIT 1`).get(row.task_id, row.integration_id);
54
+ const job = db.prepare(`SELECT 1 FROM durable_jobs WHERE task_id=?
55
+ AND json_extract(payload,'$.owner.integrationAttemptId')=?
56
+ AND (status IN ('queued','running') OR (status='unknown-needs-attention'
57
+ AND json_type(payload,'$.acknowledgedAt') IS NULL)) LIMIT 1`).get(row.task_id, row.integration_id);
58
+ const adoption = db.prepare(`SELECT 1 FROM events WHERE task_id=?
59
+ AND type='publication.candidate-adopted'
60
+ AND json_extract(payload,'$.payload.integrationId')=? LIMIT 1`).get(row.task_id, row.integration_id);
61
+ if (workspace || job || adoption) {
62
+ throw new Error(`Historical Integration still has execution or delivery references: ${row.task_id}/${row.integration_id}. Preserve the Home and resolve/export its original evidence before upgrading.`);
63
+ }
64
+ }
65
+ }
66
+ /** Frozen v34 contract retirement. Existing migration definitions are unchanged.
67
+ * Only active verification configuration changes; old proof is never relabelled
68
+ * as current success. Historical Integration bytes become ordinary Task Events. */
69
+ export function migrateCurrentRuntimeContract(db) {
70
+ preflightCurrentRuntimeContract(db);
71
+ const archive = db.prepare(`INSERT INTO storage_migration_archive
72
+ (migration_version,family,record_key,payload,content) VALUES(35,?,?,?,?)`);
73
+ for (const row of db.prepare("SELECT id,payload FROM projects").all()) {
74
+ const project = JSON.parse(row.payload);
75
+ let changed = false;
76
+ let plans = 0;
77
+ const knowledge = project.knowledge.map((entry) => {
78
+ if (entry.status !== "active")
79
+ return entry;
80
+ let value;
81
+ try {
82
+ value = JSON.parse(entry.body);
83
+ }
84
+ catch {
85
+ return entry;
86
+ }
87
+ if (!value || typeof value !== "object" || value.kind !== "verification-plan")
88
+ return entry;
89
+ if (++plans > 1 || value.schemaVersion !== 1 || Object.hasOwn(value, "mode")) {
90
+ throw new Error(`Invalid v34 verification configuration: ${row.id}. Preserve it for diagnosis.`);
91
+ }
92
+ readHistoricalVerificationPlan(value);
93
+ const { l1: _retired, ...current } = value;
94
+ changed = true;
95
+ return { ...entry, body: JSON.stringify({ ...current, schemaVersion: 2 }) };
96
+ });
97
+ if (changed) {
98
+ archive.run("project", row.id, row.payload, null);
99
+ db.prepare("UPDATE projects SET payload=? WHERE id=?").run(JSON.stringify({ ...project, knowledge }), row.id);
100
+ }
101
+ }
102
+ const artifacts = db.prepare("SELECT key,project_id,payload FROM gate_artifacts WHERE level='L1'")
103
+ .all();
104
+ for (const row of artifacts) {
105
+ const value = JSON.parse(row.payload);
106
+ if (value.schemaVersion !== 1 || value.level !== "L1" || value.key !== row.key
107
+ || value.projectId !== row.project_id || !/^[a-f0-9]{64}$/.test(row.key)
108
+ || !["complete", "incomplete"].includes(value.status)
109
+ || !["unknown", "succeeded", "failed"].includes(value.outcome)
110
+ || (value.status === "incomplete") !== (value.outcome === "unknown")
111
+ || !Array.isArray(value.steps)) {
112
+ throw new Error(`Invalid retired L1 evidence: ${row.key}. Preserve it for diagnosis.`);
113
+ }
114
+ validateRetiredGate(value);
115
+ archive.run("gate-artifact", row.key, row.payload, null);
116
+ const logs = db.prepare("SELECT step_name,log_content,log_digest,log_bytes FROM gate_artifact_logs WHERE artifact_key=?")
117
+ .all(row.key);
118
+ for (const { log_content, ...metadata } of logs) {
119
+ if (metadata.log_bytes !== log_content.length
120
+ || metadata.log_digest !== createHash("sha256").update(log_content).digest("hex")) {
121
+ throw new Error(`Corrupted retired L1 log: ${row.key}/${metadata.step_name}. Preserve it for diagnosis.`);
122
+ }
123
+ archive.run("gate-artifact-log", JSON.stringify([row.key, metadata.step_name]), JSON.stringify(metadata), log_content);
124
+ }
125
+ db.prepare("DELETE FROM gate_artifact_logs WHERE artifact_key=?").run(row.key);
126
+ db.prepare("DELETE FROM gate_artifacts WHERE key=?").run(row.key);
127
+ }
128
+ const at = new Date().toISOString();
129
+ for (const row of historicalIntegrations(db)) {
130
+ const value = JSON.parse(row.payload);
131
+ if (value.schemaVersion !== 6 || value.taskId !== row.task_id || value.id !== row.integration_id
132
+ || !/^integration-[1-9][0-9]*$/.test(value.id)
133
+ || !Array.isArray(value.source.changeSetIds) || value.source.changeSetIds.length === 0
134
+ || value.source.changeSetIds.some((id) => typeof id !== "string" || !/^change-set-[1-9][0-9]*$/.test(id))
135
+ || new Set(value.source.changeSetIds).size !== value.source.changeSetIds.length
136
+ || !Array.isArray(value.checkCommands) || value.checkCommands.some((command) => !text(command))
137
+ || typeof value.rerunChecks !== "boolean" || !text(value.projectId) || !text(value.targetRef)
138
+ || !/^[a-f0-9]{40}(?:[a-f0-9]{24})?$/.test(value.beforeCommit)
139
+ || !Number.isFinite(Date.parse(value.createdAt)) || !Number.isFinite(Date.parse(value.updatedAt))
140
+ || !Number.isFinite(Date.parse(value.endedAt))
141
+ || (value.status === "committed" && (value.afterCommit !== value.candidateCommit
142
+ || !/^[a-f0-9]{40}(?:[a-f0-9]{24})?$/.test(value.afterCommit) || !text(value.summary)))) {
143
+ throw new Error(`Invalid historical Integration: ${row.task_id}/${row.integration_id}. Preserve it for diagnosis.`);
144
+ }
145
+ const sequence = db.prepare(`SELECT max(
146
+ coalesce((SELECT high_water FROM id_sequences WHERE task_id=? AND kind='event'),0),
147
+ coalesce((SELECT max(CAST(substr(event_id,7) AS INTEGER)) FROM events WHERE task_id=?),0)
148
+ ) AS value`).get(row.task_id, row.task_id).value + 1;
149
+ const event = { schemaVersion: 2, id: `event-${sequence}`, taskId: row.task_id,
150
+ type: "integration.source-retired", createdAt: at,
151
+ payload: { integrationId: row.integration_id, record: row.payload, disposition: "audit-only" } };
152
+ db.prepare("INSERT INTO events(task_id,event_id,type,occurred_at,payload) VALUES(?,?,?,?,?)")
153
+ .run(row.task_id, event.id, event.type, at, JSON.stringify(event));
154
+ db.prepare(`INSERT INTO id_sequences(task_id,kind,high_water) VALUES(?,'event',?)
155
+ ON CONFLICT(task_id,kind) DO UPDATE SET high_water=max(high_water,excluded.high_water)`).run(row.task_id, sequence);
156
+ // Preserve the retired identity's high-water mark so it cannot be reused.
157
+ const integrationSequence = Number(row.integration_id.slice("integration-".length));
158
+ if (!Number.isSafeInteger(integrationSequence) || integrationSequence < 1)
159
+ throw new Error("Invalid retired Integration identity.");
160
+ db.prepare(`INSERT INTO id_sequences(task_id,kind,high_water) VALUES(?,'integrationAttempt',?)
161
+ ON CONFLICT(task_id,kind) DO UPDATE SET high_water=max(high_water,excluded.high_water)`).run(row.task_id, integrationSequence);
162
+ db.prepare("DELETE FROM integration_attempts WHERE task_id=? AND integration_id=?").run(row.task_id, row.integration_id);
163
+ }
164
+ }
165
+ function historicalIntegrations(db) {
166
+ return db.prepare(`SELECT task_id,integration_id,payload FROM integration_attempts
167
+ WHERE json_extract(payload,'$.source.kind')='historical-change-sets' ORDER BY task_id,integration_id`)
168
+ .all();
169
+ }
170
+ function text(value) {
171
+ return typeof value === "string" && value.trim().length > 0 && !value.includes("\0");
172
+ }
173
+ /** The retired v34 artifact shape, frozen here rather than accepted by current
174
+ * GateArtifact readers. It is archived verbatim, never made into an L2 proof. */
175
+ function validateRetiredGate(value) {
176
+ requireIdentity(value.projectId, "Retired GateArtifact projectId");
177
+ for (const field of ["key", "planDigest", "toolchainDigest"]) {
178
+ if (typeof value[field] !== "string" || !/^[a-f0-9]{64}$/.test(value[field])) {
179
+ throw new Error(`Invalid retired GateArtifact ${field}.`);
180
+ }
181
+ }
182
+ const commit = (sha) => {
183
+ if (typeof sha !== "string" || !/^[a-f0-9]{40}(?:[a-f0-9]{24})?$/.test(sha))
184
+ throw new Error("Invalid retired gate commit.");
185
+ };
186
+ commit(value.commit);
187
+ if (value.boundary !== undefined) {
188
+ requireText(value.boundary.targetRef, "Retired gate target");
189
+ commit(value.boundary.baseHead);
190
+ }
191
+ const identity = { projectId: value.projectId, level: value.level, commit: value.commit,
192
+ planDigest: value.planDigest, toolchainDigest: value.toolchainDigest,
193
+ ...(value.boundary === undefined ? {} : { boundary: value.boundary }) };
194
+ if (createHash("sha256").update(canonical(identity)).digest("hex") !== value.key) {
195
+ throw new Error("Retired gate key does not match its original identity.");
196
+ }
197
+ const names = new Set();
198
+ for (const step of value.steps) {
199
+ requireIdentity(step.name, "Retired gate step name");
200
+ if (names.has(step.name))
201
+ throw new Error("Duplicate retired gate step.");
202
+ names.add(step.name);
203
+ requireText(step.command, "Retired gate command");
204
+ if (!["passed", "failed", "skipped"].includes(step.outcome))
205
+ throw new Error("Invalid retired gate step outcome.");
206
+ requirePositiveInteger(step.durationMs + 1, "Retired gate duration");
207
+ requireText(step.logPath, "Retired gate log path");
208
+ if (step.logPath.startsWith("/") || step.logPath.includes("..")
209
+ || typeof step.logDigest !== "string" || !/^[a-f0-9]{64}$/.test(step.logDigest)
210
+ || !Number.isSafeInteger(step.logBytes) || step.logBytes < 0)
211
+ throw new Error("Invalid retired gate log metadata.");
212
+ }
213
+ if (Object.hasOwn(value, "potentialReuseCount"))
214
+ throw new Error("Invalid v34 retired gate counter.");
215
+ requirePositiveInteger(value.reuseCount + 1, "Retired gate reuse count");
216
+ requireTimestamp(value.createdAt, "Retired gate createdAt");
217
+ requireTimestamp(value.lastUsedAt, "Retired gate lastUsedAt");
218
+ if (value.completedAt !== undefined)
219
+ requireTimestamp(value.completedAt, "Retired gate completedAt");
220
+ }
221
+ function canonical(value) {
222
+ if (Array.isArray(value))
223
+ return `[${value.map(canonical).join(",")}]`;
224
+ if (value !== null && typeof value === "object")
225
+ return `{${Object.keys(value).sort()
226
+ .map(key => `${JSON.stringify(key)}:${canonical(value[key])}`).join(",")}}`;
227
+ return JSON.stringify(value);
228
+ }
@@ -0,0 +1,35 @@
1
+ import { createHash } from "node:crypto";
2
+ /** Frozen pre-fix encoding used only to recognize valid historical Jobs.
3
+ * Never use corrected execution semantics to reinterpret a released migration,
4
+ * and never use these helpers to create executable work or reusable evidence.
5
+ */
6
+ export function historicalVerificationPlanDigest(plan) {
7
+ return createHash("sha256").update(JSON.stringify(canonicalize({
8
+ id: plan.id, version: plan.version, toolchain: plan.toolchain,
9
+ bootstrap: plan.bootstrap, l1: plan.l1, l2: plan.l2,
10
+ ...(plan.l3 === undefined ? {} : { l3: plan.l3 })
11
+ }))).digest("hex");
12
+ }
13
+ export function historicalGateJobSteps(plan) {
14
+ const encode = (step, name) => ({
15
+ name,
16
+ command: step.shell === true ? step.argv.join(" ") : step.argv.map(value => /^[A-Za-z0-9_./:=@+-]+$/u.test(value) ? value : `'${value.replaceAll("'", "'\\''")}'`).join(" "),
17
+ argv: step.argv,
18
+ ...(step.cwd === undefined ? {} : { cwd: step.cwd }),
19
+ ...(step.env === undefined ? {} : { env: step.env })
20
+ });
21
+ return [
22
+ ...plan.bootstrap.map((step, index) => encode(step, `bootstrap-${index + 1}`)),
23
+ ...plan.l2.steps.map((step, index) => encode(step, `gate-${index + 1}`))
24
+ ];
25
+ }
26
+ function canonicalize(value) {
27
+ if (Array.isArray(value))
28
+ return value.map(canonicalize);
29
+ if (typeof value === "object" && value !== null) {
30
+ return Object.fromEntries(Object.entries(value)
31
+ .sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0)
32
+ .map(([key, entry]) => [key, canonicalize(entry)]));
33
+ }
34
+ return value;
35
+ }
@@ -1,7 +1,8 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { resolve } from "node:path";
3
3
  import { durableJobIdempotencyKey } from "../../job/durableJob.js";
4
- import { resolveProjectVerificationPlan, verificationPlanDigest, planBootstrapJobSteps, planL2JobSteps } from "../../verification/verificationPlan.js";
4
+ import { resolveHistoricalVerificationPlan } from "./verificationPlanV1.js";
5
+ import { historicalGateJobSteps, historicalVerificationPlanDigest } from "./historicalVerificationPlan.js";
5
6
  /** Classify only known v19 Git conflicts. Manual strategy and CAS blockers
6
7
  * keep their meaning. Never synthesize candidate/Job success or edit history. */
7
8
  export function migrateIntegrationContinuation(db) {
@@ -74,10 +75,10 @@ function migrateBoundFastForward(db, attempt) {
74
75
  name: `check-${index + 1}`, command, timeoutMs: 30 * 60_000
75
76
  }));
76
77
  if (attempt.gatePlanDigest !== undefined) {
77
- const plan = resolveProjectVerificationPlan(project);
78
- if (plan === undefined || verificationPlanDigest(plan) !== attempt.gatePlanDigest)
78
+ const plan = resolveHistoricalVerificationPlan(project);
79
+ if (plan === undefined || historicalVerificationPlanDigest(plan) !== attempt.gatePlanDigest)
79
80
  return;
80
- steps = [...planBootstrapJobSteps(plan), ...planL2JobSteps(plan)].map(step => ({
81
+ steps = historicalGateJobSteps(plan).map(step => ({
81
82
  ...step, timeoutMs: 30 * 60_000
82
83
  }));
83
84
  }
@@ -0,0 +1,65 @@
1
+ import { isAbsolute } from "node:path";
2
+ /** Frozen v36 projection. Execution/Review/protocol data remains in audit only;
3
+ * a metadata query must not require those unrelated contracts to stay current. */
4
+ export function migrateNarrowAgentFailureContext(db) {
5
+ const rows = db.prepare("SELECT task_id,event_id,payload FROM events WHERE type='runtime.agent-error'").all();
6
+ const archive = db.prepare(`INSERT INTO storage_migration_archive
7
+ (migration_version,family,record_key,payload,content) VALUES(37,?,?,?,NULL)`);
8
+ const update = db.prepare("UPDATE events SET payload=? WHERE task_id=? AND event_id=?");
9
+ for (const row of rows) {
10
+ const event = JSON.parse(row.payload);
11
+ const raw = event?.payload?.capabilityContext;
12
+ if (typeof raw !== "string")
13
+ throw new Error(`Missing v36 failure context: ${row.task_id}/${row.event_id}.`);
14
+ const context = JSON.parse(raw);
15
+ const key = JSON.stringify([row.task_id, row.event_id]);
16
+ const emptyIdentities = {};
17
+ for (const field of ["runId", "nativeSessionId", "nativeTurnId"]) {
18
+ if (event.payload[field] !== "")
19
+ continue;
20
+ emptyIdentities[field] = "";
21
+ delete event.payload[field];
22
+ }
23
+ if (Object.keys(emptyIdentities).length) {
24
+ archive.run("agent-error-empty-identities", key, JSON.stringify(emptyIdentities));
25
+ }
26
+ if (context?.status === "unavailable" && text(context.reason)) {
27
+ if (Object.keys(emptyIdentities).length)
28
+ update.run(JSON.stringify(event), row.task_id, row.event_id);
29
+ continue;
30
+ }
31
+ const old = context?.effective;
32
+ if (context?.status !== "recorded" || !/^[a-f0-9]{64}$/.test(context.agentFingerprint)
33
+ || old?.schemaVersion !== 4 || !text(old.agentId)
34
+ || !["codex", "claude", "acp"].includes(old.adapterId)
35
+ || !text(old.workspace?.root) || !isAbsolute(old.workspace.root)) {
36
+ throw new Error(`Invalid v36 failure context: ${row.task_id}/${row.event_id}.`);
37
+ }
38
+ const config = { adapterId: old.adapterId };
39
+ for (const key of ["model", "effort", ...(old.adapterId === "codex" ? ["profile"] : []),
40
+ ...(old.adapterId === "claude" ? ["settingsFile"] : [])]) {
41
+ if (old[key] === undefined)
42
+ continue;
43
+ if (!text(old[key]) || key === "settingsFile" && !isAbsolute(old[key])) {
44
+ throw new Error(`Invalid v36 failure ${key}: ${row.task_id}/${row.event_id}.`);
45
+ }
46
+ config[key] = old[key];
47
+ }
48
+ if (old.adapterId === "claude" && old.settingsSources !== undefined) {
49
+ if (!Array.isArray(old.settingsSources) || old.settingsSources.some((source) => !text(source))
50
+ || new Set(old.settingsSources).size !== old.settingsSources.length) {
51
+ throw new Error(`Invalid v36 settings sources: ${row.task_id}/${row.event_id}.`);
52
+ }
53
+ config.settingsSources = old.settingsSources;
54
+ }
55
+ archive.run("agent-error-context", key, raw);
56
+ event.payload.capabilityContext = JSON.stringify({
57
+ status: "recorded", agentId: old.agentId, cwd: old.workspace.root, config,
58
+ agentFingerprint: context.agentFingerprint
59
+ });
60
+ update.run(JSON.stringify(event), row.task_id, row.event_id);
61
+ }
62
+ }
63
+ function text(value) {
64
+ return typeof value === "string" && value.trim().length > 0 && !value.includes("\0");
65
+ }
@@ -0,0 +1,74 @@
1
+ function runLinkedWakes(db) {
2
+ return db.prepare(`SELECT task_id, wake_id, seq, turn_id, payload FROM task_wakes
3
+ WHERE turn_id IS NOT NULL OR json_type(payload, '$.runId') IS NOT NULL
4
+ ORDER BY task_id, seq`).all();
5
+ }
6
+ /** Read only stable SQL identities, including prefixes before payload key renames. */
7
+ export function preflightNotificationOnlyWakes(db) {
8
+ assertQuiescent(db, runLinkedWakes(db));
9
+ }
10
+ /** Frozen 29→30 transform. Old execution-linked wakes become audit events;
11
+ * accepted/pending notifications keep their exact identity and delivery state. */
12
+ export function migrateNotificationOnlyWakes(db) {
13
+ const rows = runLinkedWakes(db);
14
+ // Check every retiring reference before changing any data. The outer
15
+ // migration transaction also rolls back on any later failure.
16
+ for (const row of rows) {
17
+ const value = JSON.parse(row.payload);
18
+ if (typeof value.runId !== "string" || value.runId !== row.turn_id) {
19
+ throw new Error(`Run-linked wake identity is inconsistent: ${row.task_id}/${row.wake_id}.`);
20
+ }
21
+ }
22
+ assertQuiescent(db, rows);
23
+ const counters = new Map();
24
+ const readCounter = db.prepare(`SELECT max(
25
+ coalesce((SELECT high_water FROM id_sequences WHERE task_id = ? AND kind = 'event'), 0),
26
+ coalesce((SELECT max(CAST(substr(event_id, 7) AS INTEGER)) FROM events WHERE task_id = ?), 0)
27
+ ) AS value`);
28
+ const saveCounter = db.prepare(`INSERT INTO id_sequences(task_id, kind, high_water) VALUES (?, ?, ?)
29
+ ON CONFLICT(task_id, kind) DO UPDATE SET high_water = max(high_water, excluded.high_water)`);
30
+ const saveEvent = db.prepare("INSERT INTO events(task_id, event_id, type, occurred_at, payload) VALUES (?, ?, ?, ?, ?)");
31
+ const removeWake = db.prepare("DELETE FROM task_wakes WHERE task_id = ? AND wake_id = ?");
32
+ const at = new Date().toISOString();
33
+ for (const row of rows) {
34
+ const sequence = (counters.get(row.task_id)
35
+ ?? readCounter.get(row.task_id, row.task_id).value) + 1;
36
+ counters.set(row.task_id, sequence);
37
+ const event = {
38
+ schemaVersion: 2, id: `event-${sequence}`, taskId: row.task_id,
39
+ type: "wake.run-link-retired", createdAt: at,
40
+ payload: { wakeId: row.wake_id, runId: row.turn_id,
41
+ record: row.payload, disposition: "retired-without-replay" }
42
+ };
43
+ saveEvent.run(row.task_id, event.id, event.type, at, JSON.stringify(event));
44
+ saveCounter.run(row.task_id, "event", sequence);
45
+ saveCounter.run(row.task_id, "taskWake", row.seq);
46
+ removeWake.run(row.task_id, row.wake_id);
47
+ }
48
+ db.exec(`
49
+ ALTER TABLE task_wakes DROP COLUMN turn_id;
50
+ UPDATE task_wakes SET payload = json_set(payload, '$.schemaVersion', 2);
51
+ UPDATE global_role_session_sets
52
+ SET payload = json_set(payload, '$.providerBinding', json('null'))
53
+ WHERE json_type(payload, '$.providerBinding') IS NULL;
54
+ `);
55
+ }
56
+ function assertQuiescent(db, rows) {
57
+ const activeRun = db.prepare("SELECT 1 FROM turns WHERE task_id = ? AND turn_id = ? AND status = 'active'");
58
+ const claimedWake = db.prepare(`SELECT 1 FROM mailboxes WHERE task_id = ?
59
+ AND json_extract(processing, '$.owner') = ? LIMIT 1`);
60
+ const pendingInput = db.prepare(`SELECT 1 FROM role_session_sets WHERE task_id = ? AND (
61
+ (json_extract(payload, '$.providerBinding.run.status') IN ('submitting', 'accepted', 'delivery-unknown')
62
+ AND (json_extract(payload, '$.providerBinding.run.runId') = ?
63
+ OR json_extract(payload, '$.providerBinding.run.input.wakeId') = ?))
64
+ OR (json_extract(payload, '$.providerBinding.retry.status') IN ('waiting', 'in-flight')
65
+ AND (json_extract(payload, '$.providerBinding.retry.input.runId') = ?
66
+ OR json_extract(payload, '$.providerBinding.retry.input.wakeId') = ?))) LIMIT 1`);
67
+ for (const row of rows) {
68
+ if (activeRun.get(row.task_id, row.turn_id)
69
+ || claimedWake.get(row.task_id, `leader-notification:${row.wake_id}`)
70
+ || pendingInput.get(row.task_id, row.turn_id, row.wake_id, row.turn_id, row.wake_id)) {
71
+ throw new Error(`Run-linked wake still owns unsettled input: ${row.task_id}/${row.wake_id}, Run ${row.turn_id}. Settle or stop that exact execution before upgrading.`);
72
+ }
73
+ }
74
+ }
@@ -0,0 +1,162 @@
1
+ export function readHistoricalVerificationPlan(raw) {
2
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
3
+ throw new Error("VerificationPlan must be an object.");
4
+ }
5
+ const record = raw;
6
+ if (record.kind !== "verification-plan")
7
+ throw new Error('VerificationPlan kind must be "verification-plan".');
8
+ if ((record.schemaVersion ?? 1) !== 1)
9
+ throw new Error("VerificationPlan schemaVersion must be 1.");
10
+ const mode = record.mode ?? "record";
11
+ if (mode !== "record" && mode !== "reuse" && mode !== "enforce") {
12
+ throw new Error(`VerificationPlan mode is invalid: ${String(mode)}.`);
13
+ }
14
+ const plan = {
15
+ schemaVersion: 1, kind: "verification-plan",
16
+ id: identity(record.id, "VerificationPlan id"),
17
+ version: text(record.version, "VerificationPlan version"),
18
+ mode,
19
+ toolchain: toolchain(record.toolchain),
20
+ bootstrap: steps(record.bootstrap, "bootstrap"),
21
+ l1: { categories: categories(record.l1) },
22
+ l2: { steps: steps(record.l2?.steps, "l2") },
23
+ ...(record.l3 === undefined ? {} : {
24
+ l3: { steps: steps(record.l3?.steps, "l3") }
25
+ }),
26
+ ...(record.excludedRealResourceChecks === undefined ? {} : {
27
+ excludedRealResourceChecks: textList(record.excludedRealResourceChecks, "VerificationPlan excludedRealResourceChecks")
28
+ }),
29
+ ...(record.artifactTtlDays === undefined ? {} : {
30
+ artifactTtlDays: positive(Number(record.artifactTtlDays), "VerificationPlan artifactTtlDays")
31
+ })
32
+ };
33
+ if (plan.l2.steps.length === 0)
34
+ throw new Error("VerificationPlan l2 requires at least one step.");
35
+ return plan;
36
+ }
37
+ export function resolveHistoricalVerificationPlan(project) {
38
+ let found;
39
+ for (const entry of project.knowledge) {
40
+ if (entry.status !== "active")
41
+ continue;
42
+ let parsed;
43
+ try {
44
+ parsed = JSON.parse(entry.body);
45
+ }
46
+ catch {
47
+ continue;
48
+ }
49
+ if (typeof parsed !== "object" || parsed === null
50
+ || parsed.kind !== "verification-plan")
51
+ continue;
52
+ const plan = readHistoricalVerificationPlan(parsed);
53
+ if (found !== undefined)
54
+ throw new Error(`Project ${project.id} declares multiple VerificationPlans; only one is allowed.`);
55
+ found = plan;
56
+ }
57
+ return found;
58
+ }
59
+ function toolchain(raw) {
60
+ if (raw === undefined)
61
+ return Object.freeze({});
62
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw))
63
+ throw new Error("VerificationPlan toolchain must be an object.");
64
+ const record = raw;
65
+ return {
66
+ ...(record.node === undefined ? {} : { node: text(record.node, "VerificationPlan toolchain node") }),
67
+ ...(record.npm === undefined ? {} : { npm: text(record.npm, "VerificationPlan toolchain npm") }),
68
+ ...(record.platform === undefined ? {} : { platform: text(record.platform, "VerificationPlan toolchain platform") })
69
+ };
70
+ }
71
+ function steps(raw, label) {
72
+ if (!Array.isArray(raw))
73
+ throw new Error(`VerificationPlan ${label} steps must be an array.`);
74
+ const names = new Set();
75
+ return Object.freeze(raw.map(entry => {
76
+ if (typeof entry !== "object" || entry === null || Array.isArray(entry))
77
+ throw new Error(`VerificationPlan ${label} step must be an object.`);
78
+ const record = entry;
79
+ const name = identity(record.name, `VerificationPlan ${label} step name`);
80
+ if (names.has(name))
81
+ throw new Error(`VerificationPlan ${label} step names must be unique: ${name}.`);
82
+ names.add(name);
83
+ if (!Array.isArray(record.argv) || record.argv.length === 0) {
84
+ throw new Error(`VerificationPlan ${label} step ${name} requires a non-empty argv.`);
85
+ }
86
+ const step = {
87
+ name, argv: Object.freeze(record.argv.map(value => text(value, `VerificationPlan ${label} step ${name} argv`))),
88
+ ...(record.cwd === undefined ? {} : { cwd: text(record.cwd, `VerificationPlan ${label} step ${name} cwd`) }),
89
+ ...(record.env === undefined ? {} : { env: stepEnv(record.env, `${label} step ${name}`) }),
90
+ ...(record.shell === undefined ? {} : { shell: record.shell === true })
91
+ };
92
+ if (step.cwd?.startsWith("/"))
93
+ throw new Error(`VerificationPlan ${label} step ${name} cwd must be workspace-relative.`);
94
+ return step;
95
+ }));
96
+ }
97
+ function stepEnv(raw, label) {
98
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw))
99
+ throw new Error(`VerificationPlan ${label} env must be a map.`);
100
+ const env = {};
101
+ for (const [key, value] of Object.entries(raw)) {
102
+ if (typeof value !== "string")
103
+ throw new Error(`VerificationPlan ${label} env values must be strings: ${key}.`);
104
+ env[identity(key, `VerificationPlan ${label} env key`)] = value;
105
+ }
106
+ return Object.freeze(env);
107
+ }
108
+ function categories(raw) {
109
+ if (raw === undefined)
110
+ return Object.freeze([]);
111
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw))
112
+ throw new Error("VerificationPlan l1 must be an object.");
113
+ const values = raw.categories;
114
+ if (!Array.isArray(values))
115
+ throw new Error("VerificationPlan l1 categories must be an array.");
116
+ const ids = new Set();
117
+ return Object.freeze(values.map(entry => {
118
+ if (typeof entry !== "object" || entry === null || Array.isArray(entry))
119
+ throw new Error("VerificationPlan l1 category must be an object.");
120
+ const record = entry;
121
+ const id = identity(record.id, "VerificationPlan l1 category id");
122
+ if (ids.has(id))
123
+ throw new Error(`VerificationPlan l1 category ids must be unique: ${id}.`);
124
+ ids.add(id);
125
+ return Object.freeze({
126
+ id, paths: textList(record.paths, `VerificationPlan l1 category ${id} paths`),
127
+ checks: steps(record.checks, `l1 ${id}`)
128
+ });
129
+ }));
130
+ }
131
+ function textList(raw, label) {
132
+ if (!Array.isArray(raw))
133
+ throw new Error(`${label} must be an array.`);
134
+ const seen = new Set();
135
+ return Object.freeze(raw.map(value => {
136
+ const next = text(value, label);
137
+ if (seen.has(next))
138
+ throw new Error(`${label} must be unique: ${next}.`);
139
+ seen.add(next);
140
+ return next;
141
+ }));
142
+ }
143
+ function text(value, label) {
144
+ if (typeof value !== "string" || value.includes("\0"))
145
+ throw new Error(`${label} is invalid.`);
146
+ const normalized = value.trim();
147
+ if (normalized.length === 0)
148
+ throw new Error(`${label} is required.`);
149
+ return normalized;
150
+ }
151
+ function identity(value, label) {
152
+ const normalized = text(value, label);
153
+ if ([".", "..", "__proto__", "prototype", "constructor"].includes(normalized) || /[\/\\\0]/u.test(normalized)) {
154
+ throw new Error(`${label} is invalid.`);
155
+ }
156
+ return normalized;
157
+ }
158
+ function positive(value, label) {
159
+ if (!Number.isSafeInteger(value) || value < 1)
160
+ throw new Error(`${label} must be a positive integer.`);
161
+ return value;
162
+ }