@flowdesk/core 0.1.0 → 0.1.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 (142) hide show
  1. package/dist/authority-promotion.d.ts +70 -0
  2. package/dist/authority-promotion.d.ts.map +1 -0
  3. package/dist/authority-promotion.js +388 -0
  4. package/dist/authority-promotion.js.map +1 -0
  5. package/dist/chat-control-authority.d.ts +83 -0
  6. package/dist/chat-control-authority.d.ts.map +1 -0
  7. package/dist/chat-control-authority.js +238 -0
  8. package/dist/chat-control-authority.js.map +1 -0
  9. package/dist/chat-hook-authority-probe.d.ts +39 -0
  10. package/dist/chat-hook-authority-probe.d.ts.map +1 -0
  11. package/dist/chat-hook-authority-probe.js +153 -0
  12. package/dist/chat-hook-authority-probe.js.map +1 -0
  13. package/dist/chat-routing.d.ts.map +1 -1
  14. package/dist/chat-routing.js +18 -15
  15. package/dist/chat-routing.js.map +1 -1
  16. package/dist/connector-gateway.d.ts +34 -0
  17. package/dist/connector-gateway.d.ts.map +1 -0
  18. package/dist/connector-gateway.js +147 -0
  19. package/dist/connector-gateway.js.map +1 -0
  20. package/dist/connector-profile.d.ts +41 -0
  21. package/dist/connector-profile.d.ts.map +1 -0
  22. package/dist/connector-profile.js +125 -0
  23. package/dist/connector-profile.js.map +1 -0
  24. package/dist/controlled-conformance-doc-write.d.ts +44 -0
  25. package/dist/controlled-conformance-doc-write.d.ts.map +1 -0
  26. package/dist/controlled-conformance-doc-write.js +142 -0
  27. package/dist/controlled-conformance-doc-write.js.map +1 -0
  28. package/dist/controlled-redacted-audit-export-write.d.ts +45 -0
  29. package/dist/controlled-redacted-audit-export-write.d.ts.map +1 -0
  30. package/dist/controlled-redacted-audit-export-write.js +145 -0
  31. package/dist/controlled-redacted-audit-export-write.js.map +1 -0
  32. package/dist/core-completion-safety-contracts.d.ts +35 -0
  33. package/dist/core-completion-safety-contracts.d.ts.map +1 -0
  34. package/dist/core-completion-safety-contracts.js +98 -0
  35. package/dist/core-completion-safety-contracts.js.map +1 -0
  36. package/dist/dispatch-attempt-manifest.d.ts +59 -0
  37. package/dist/dispatch-attempt-manifest.d.ts.map +1 -0
  38. package/dist/dispatch-attempt-manifest.js +294 -0
  39. package/dist/dispatch-attempt-manifest.js.map +1 -0
  40. package/dist/dispatch-idempotency.d.ts +89 -0
  41. package/dist/dispatch-idempotency.d.ts.map +1 -0
  42. package/dist/dispatch-idempotency.js +275 -0
  43. package/dist/dispatch-idempotency.js.map +1 -0
  44. package/dist/external-auth-policy.d.ts +49 -0
  45. package/dist/external-auth-policy.d.ts.map +1 -0
  46. package/dist/external-auth-policy.js +166 -0
  47. package/dist/external-auth-policy.js.map +1 -0
  48. package/dist/fake-remote-connector-adapter.d.ts +37 -0
  49. package/dist/fake-remote-connector-adapter.d.ts.map +1 -0
  50. package/dist/fake-remote-connector-adapter.js +162 -0
  51. package/dist/fake-remote-connector-adapter.js.map +1 -0
  52. package/dist/fallback-decision.d.ts +29 -0
  53. package/dist/fallback-decision.d.ts.map +1 -0
  54. package/dist/fallback-decision.js +93 -0
  55. package/dist/fallback-decision.js.map +1 -0
  56. package/dist/fallback-regate-plan.d.ts +34 -0
  57. package/dist/fallback-regate-plan.d.ts.map +1 -0
  58. package/dist/fallback-regate-plan.js +122 -0
  59. package/dist/fallback-regate-plan.js.map +1 -0
  60. package/dist/fds1-schema-probe-result.d.ts +37 -0
  61. package/dist/fds1-schema-probe-result.d.ts.map +1 -0
  62. package/dist/fds1-schema-probe-result.js +115 -0
  63. package/dist/fds1-schema-probe-result.js.map +1 -0
  64. package/dist/index.d.ts +25 -0
  65. package/dist/index.d.ts.map +1 -1
  66. package/dist/index.js +25 -0
  67. package/dist/index.js.map +1 -1
  68. package/dist/lane-heartbeat.d.ts +49 -0
  69. package/dist/lane-heartbeat.d.ts.map +1 -0
  70. package/dist/lane-heartbeat.js +149 -0
  71. package/dist/lane-heartbeat.js.map +1 -0
  72. package/dist/lane-lifecycle-record.d.ts +32 -0
  73. package/dist/lane-lifecycle-record.d.ts.map +1 -0
  74. package/dist/lane-lifecycle-record.js +134 -0
  75. package/dist/lane-lifecycle-record.js.map +1 -0
  76. package/dist/lane-stall-projection.d.ts +43 -0
  77. package/dist/lane-stall-projection.d.ts.map +1 -0
  78. package/dist/lane-stall-projection.js +272 -0
  79. package/dist/lane-stall-projection.js.map +1 -0
  80. package/dist/model-availability-cache.d.ts +286 -0
  81. package/dist/model-availability-cache.d.ts.map +1 -0
  82. package/dist/model-availability-cache.js +1109 -0
  83. package/dist/model-availability-cache.js.map +1 -0
  84. package/dist/operational-intelligence.d.ts +38 -0
  85. package/dist/operational-intelligence.d.ts.map +1 -0
  86. package/dist/operational-intelligence.js +107 -0
  87. package/dist/operational-intelligence.js.map +1 -0
  88. package/dist/production-approval-source.d.ts +61 -0
  89. package/dist/production-approval-source.d.ts.map +1 -0
  90. package/dist/production-approval-source.js +226 -0
  91. package/dist/production-approval-source.js.map +1 -0
  92. package/dist/production-enablement.d.ts +92 -1
  93. package/dist/production-enablement.d.ts.map +1 -1
  94. package/dist/production-enablement.js +421 -8
  95. package/dist/production-enablement.js.map +1 -1
  96. package/dist/production-verification.d.ts +31 -0
  97. package/dist/production-verification.d.ts.map +1 -0
  98. package/dist/production-verification.js +126 -0
  99. package/dist/production-verification.js.map +1 -0
  100. package/dist/provider-usage-collector.d.ts +7 -0
  101. package/dist/provider-usage-collector.d.ts.map +1 -1
  102. package/dist/provider-usage-collector.js +64 -10
  103. package/dist/provider-usage-collector.js.map +1 -1
  104. package/dist/release1-contracts.d.ts +8 -2
  105. package/dist/release1-contracts.d.ts.map +1 -1
  106. package/dist/release1-contracts.js +2 -1
  107. package/dist/release1-contracts.js.map +1 -1
  108. package/dist/remote-write-connector-gate.d.ts +91 -0
  109. package/dist/remote-write-connector-gate.d.ts.map +1 -0
  110. package/dist/remote-write-connector-gate.js +291 -0
  111. package/dist/remote-write-connector-gate.js.map +1 -0
  112. package/dist/runtime-lane-productization.d.ts +78 -0
  113. package/dist/runtime-lane-productization.d.ts.map +1 -0
  114. package/dist/runtime-lane-productization.js +270 -0
  115. package/dist/runtime-lane-productization.js.map +1 -0
  116. package/dist/sanitized-auth-capture.d.ts +50 -0
  117. package/dist/sanitized-auth-capture.d.ts.map +1 -0
  118. package/dist/sanitized-auth-capture.js +164 -0
  119. package/dist/sanitized-auth-capture.js.map +1 -0
  120. package/dist/schema-artifacts.d.ts.map +1 -1
  121. package/dist/schema-artifacts.js +37 -6
  122. package/dist/schema-artifacts.js.map +1 -1
  123. package/dist/schema-registry.d.ts.map +1 -1
  124. package/dist/schema-registry.js +21 -0
  125. package/dist/schema-registry.js.map +1 -1
  126. package/dist/session-evidence.d.ts +117 -0
  127. package/dist/session-evidence.d.ts.map +1 -1
  128. package/dist/session-evidence.js +569 -1
  129. package/dist/session-evidence.js.map +1 -1
  130. package/dist/state-paths.d.ts +1 -1
  131. package/dist/state-paths.d.ts.map +1 -1
  132. package/dist/state-paths.js +56 -6
  133. package/dist/state-paths.js.map +1 -1
  134. package/dist/status.d.ts +7 -0
  135. package/dist/status.d.ts.map +1 -1
  136. package/dist/status.js +89 -1
  137. package/dist/status.js.map +1 -1
  138. package/dist/validators.d.ts +1 -0
  139. package/dist/validators.d.ts.map +1 -1
  140. package/dist/validators.js +30 -7
  141. package/dist/validators.js.map +1 -1
  142. package/package.json +1 -1
@@ -1,11 +1,41 @@
1
1
  import { existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, renameSync, writeFileSync, } from "node:fs";
2
2
  import { dirname, resolve, sep } from "node:path";
3
+ import { validateFlowDeskControlledConformanceDocWriteRecordV1 } from "./controlled-conformance-doc-write.js";
4
+ import { validateFlowDeskControlledRedactedAuditExportWriteRecordV1 } from "./controlled-redacted-audit-export-write.js";
5
+ import { validateFlowDeskDispatchIdempotencySnapshotV1 } from "./dispatch-idempotency.js";
6
+ import { validateFlowDeskFallbackRegatePlanV1 } from "./fallback-regate-plan.js";
7
+ import { validateFlowDeskLaneHeartbeatRecordV1 } from "./lane-heartbeat.js";
8
+ import { validateFlowDeskLaneLifecycleRecordV1 } from "./lane-lifecycle-record.js";
9
+ import { materializeFlowDeskExactModelAvailabilityCacheFromProviderAcquisitionResultV1, planFlowDeskExactModelAvailabilityCacheRefreshV1, planFlowDeskReviewerFanoutV1, revalidateFlowDeskReviewerAssignmentsFromCacheEvidenceV1, validateFlowDeskExactModelAvailabilityCacheAcquisitionPlanV1, validateFlowDeskExactModelAvailabilityCacheProviderAcquisitionResultV1, validateFlowDeskExactModelAvailabilityCacheRefreshPlanV1, validateFlowDeskExactModelAvailabilityCacheV1, validateFlowDeskReviewerFanoutPlanV1, } from "./model-availability-cache.js";
10
+ import { validateFlowDeskProductionApprovalSourceV1 } from "./production-approval-source.js";
11
+ import { validateFlowDeskReviewerLaneConformanceObservationV1 } from "./reviewer-lane-conformance.js";
12
+ import { planFlowDeskRuntimeLaneLaunchV1, validateFlowDeskRuntimeLaneLaunchPlanV1, } from "./runtime-lane-productization.js";
3
13
  import { FLOWDESK_SESSION_EVIDENCE_CLASSES, sessionEvidenceDirectoryPath, sessionEvidenceRecordPath, } from "./state-paths.js";
4
- import { invalid, valid, validateNoForbiddenRawPayloads, validateOpaqueId, validateOpaqueRef, } from "./validators.js";
14
+ import { invalid, valid, validateNoForbiddenRawPayloads, validateOpaqueId, validateOpaqueRef, validateTopTierReviewVerdictV1, } from "./validators.js";
5
15
  const EVIDENCE_SCHEMA_BY_CLASS = {
6
16
  usage_authority: "flowdesk.managed_dispatch_beta.usage_authority_evidence.v1",
7
17
  runtime_echo: "flowdesk.managed_dispatch_beta.runtime_echo_evidence.v1",
8
18
  telemetry_correlation: "flowdesk.managed_dispatch_beta.telemetry_correlation.v1",
19
+ configured_verification: "flowdesk.configured_verification_result.v1",
20
+ sanitized_auth_capture: "flowdesk.sanitized_auth_capture_result.v1",
21
+ external_auth_provider_policy: "flowdesk.external_auth_provider_policy_result.v1",
22
+ production_approval: "flowdesk.production_approval_decision.v1",
23
+ production_approval_source: "flowdesk.production_approval_source.v1",
24
+ dispatch_idempotency: "flowdesk.dispatch_idempotency_snapshot.v1",
25
+ pre_dispatch_audit: "flowdesk.pre_dispatch_audit_record.v1",
26
+ exact_model_availability_cache: "flowdesk.exact_model_availability_cache.v1",
27
+ exact_model_availability_cache_refresh_plan: "flowdesk.exact_model_availability_cache_refresh_plan.v1",
28
+ exact_model_availability_cache_acquisition_plan: "flowdesk.exact_model_availability_cache_acquisition_plan.v1",
29
+ exact_model_availability_cache_provider_acquisition_result: "flowdesk.exact_model_availability_cache_provider_acquisition_result.v1",
30
+ reviewer_verdict: "flowdesk.top_tier_review_verdict.v1",
31
+ reviewer_fanout_plan: "flowdesk.reviewer_fanout_plan.v1",
32
+ runtime_lane_launch_plan: "flowdesk.runtime_lane_launch_plan.v1",
33
+ lane_lifecycle: "flowdesk.lane_lifecycle_record.v1",
34
+ reviewer_lane_conformance: "flowdesk.top_tier_reviewer_lane_conformance_observation.v1",
35
+ controlled_conformance_doc_write: "flowdesk.controlled_conformance_doc_write.v1",
36
+ controlled_redacted_audit_export_write: "flowdesk.controlled_redacted_audit_export_write.v1",
37
+ fallback_regate_plan: "flowdesk.fallback_regate_plan.v1",
38
+ lane_heartbeat: "flowdesk.lane_heartbeat.v1",
9
39
  };
10
40
  const CLASS_BY_SCHEMA = Object.fromEntries(Object.entries(EVIDENCE_SCHEMA_BY_CLASS).map(([cls, schema]) => [schema, cls]));
11
41
  const disabledEvidenceAuthority = {
@@ -17,6 +47,426 @@ const disabledEvidenceAuthority = {
17
47
  function isRecordObject(value) {
18
48
  return typeof value === "object" && value !== null && !Array.isArray(value);
19
49
  }
50
+ function exactModelCacheRefreshPlanMatchesContext(plan, input) {
51
+ return plan.state === "cache_hit" &&
52
+ plan.cache_usable_for_assignment === true &&
53
+ plan.expected_local_date === input.localDate &&
54
+ plan.expected_active_profile_ref === input.activeProfileRef &&
55
+ plan.expected_opencode_version_ref === input.opencodeVersionRef &&
56
+ plan.expected_flowdesk_package_version_ref === input.flowdeskPackageVersionRef &&
57
+ plan.expected_registry_hash === input.registryHash &&
58
+ plan.expected_policy_pack_hash === input.policyPackHash &&
59
+ plan.expected_auth_account_boundary_ref === input.authAccountBoundaryRef;
60
+ }
61
+ function exactModelCacheMatchesRefreshPlan(cache, plan) {
62
+ return plan.cache_id === cache.cache_id &&
63
+ plan.cache_local_date === cache.local_date &&
64
+ plan.cache_active_profile_ref === cache.active_profile_ref &&
65
+ plan.cache_opencode_version_ref === cache.opencode_version_ref &&
66
+ plan.cache_flowdesk_package_version_ref === cache.flowdesk_package_version_ref &&
67
+ plan.cache_registry_hash === cache.registry_hash &&
68
+ plan.cache_policy_pack_hash === cache.policy_pack_hash &&
69
+ plan.cache_auth_account_boundary_ref === cache.auth_account_boundary_ref;
70
+ }
71
+ export function selectFlowDeskExactModelCacheEvidencePairV1(input) {
72
+ const errors = [...input.reloadedEvidence.errors];
73
+ const blockedLabels = [];
74
+ if (!input.reloadedEvidence.ok)
75
+ blockedLabels.push("session_evidence_reload_invalid");
76
+ const refreshPlans = input.reloadedEvidence.entries
77
+ .filter((entry) => entry.evidenceClass === "exact_model_availability_cache_refresh_plan")
78
+ .map((entry) => entry.record)
79
+ .filter((record) => validateFlowDeskExactModelAvailabilityCacheRefreshPlanV1(record).ok)
80
+ .map((record) => record)
81
+ .filter((plan) => exactModelCacheRefreshPlanMatchesContext(plan, input));
82
+ if (refreshPlans.length === 0)
83
+ blockedLabels.push("cache_refresh_pair_missing");
84
+ if (refreshPlans.length > 1)
85
+ blockedLabels.push("cache_refresh_pair_ambiguous");
86
+ const cacheRefreshPlan = refreshPlans.length === 1 ? refreshPlans[0] : undefined;
87
+ const caches = cacheRefreshPlan === undefined ? [] : input.reloadedEvidence.entries
88
+ .filter((entry) => entry.evidenceClass === "exact_model_availability_cache")
89
+ .map((entry) => entry.record)
90
+ .filter((record) => validateFlowDeskExactModelAvailabilityCacheV1(record).ok)
91
+ .map((record) => record)
92
+ .filter((cache) => exactModelCacheMatchesRefreshPlan(cache, cacheRefreshPlan));
93
+ if (cacheRefreshPlan !== undefined && caches.length === 0)
94
+ blockedLabels.push("cache_pair_missing");
95
+ if (caches.length > 1)
96
+ blockedLabels.push("cache_pair_ambiguous");
97
+ const cache = caches.length === 1 ? caches[0] : undefined;
98
+ const ready = blockedLabels.length === 0 && cache !== undefined && cacheRefreshPlan !== undefined;
99
+ return {
100
+ ok: ready && errors.length === 0,
101
+ errors,
102
+ state: ready ? "pair_ready" : "blocked",
103
+ blocked_labels: [...new Set(blockedLabels)],
104
+ ...(cache === undefined ? {} : { cache }),
105
+ ...(cacheRefreshPlan === undefined ? {} : { cacheRefreshPlan }),
106
+ ...disabledEvidenceAuthority,
107
+ };
108
+ }
109
+ function blockedReviewerAssignmentRevalidationFromSelection(input, selection) {
110
+ return {
111
+ schema_version: "flowdesk.reviewer_assignment_revalidation.v1",
112
+ ok: false,
113
+ errors: selection.errors,
114
+ state: "blocked",
115
+ blocked_labels: [...new Set(["cache_evidence_pair_selection_blocked", ...selection.blocked_labels])],
116
+ expected_local_date: input.localDate,
117
+ expected_active_profile_ref: input.activeProfileRef,
118
+ expected_opencode_version_ref: input.opencodeVersionRef,
119
+ expected_flowdesk_package_version_ref: input.flowdeskPackageVersionRef,
120
+ expected_registry_hash: input.registryHash,
121
+ expected_policy_pack_hash: input.policyPackHash,
122
+ expected_auth_account_boundary_ref: input.authAccountBoundaryRef,
123
+ eligible_bindings: [],
124
+ dispatch_authority_enabled: false,
125
+ providerCall: false,
126
+ actualLaneLaunch: false,
127
+ runtimeExecution: false,
128
+ };
129
+ }
130
+ export function planFlowDeskReviewerFanoutFromReloadedCacheEvidenceV1(input) {
131
+ const selection = selectFlowDeskExactModelCacheEvidencePairV1(input);
132
+ const revalidation = selection.state === "pair_ready" && selection.cache !== undefined && selection.cacheRefreshPlan !== undefined
133
+ ? revalidateFlowDeskReviewerAssignmentsFromCacheEvidenceV1({
134
+ cache: selection.cache,
135
+ cacheRefreshPlan: selection.cacheRefreshPlan,
136
+ localDate: input.localDate,
137
+ activeProfileRef: input.activeProfileRef,
138
+ opencodeVersionRef: input.opencodeVersionRef,
139
+ flowdeskPackageVersionRef: input.flowdeskPackageVersionRef,
140
+ registryHash: input.registryHash,
141
+ policyPackHash: input.policyPackHash,
142
+ authAccountBoundaryRef: input.authAccountBoundaryRef,
143
+ })
144
+ : blockedReviewerAssignmentRevalidationFromSelection(input, selection);
145
+ const fanoutPlan = planFlowDeskReviewerFanoutV1({
146
+ revalidation,
147
+ workflowId: input.workflowId,
148
+ attemptId: input.attemptId,
149
+ parentSessionRef: input.parentSessionRef,
150
+ agentRef: input.agentRef,
151
+ requestedAt: input.requestedAt,
152
+ requestedPerspectives: input.requestedPerspectives,
153
+ maxConcurrentLaneCount: input.maxConcurrentLaneCount,
154
+ timeoutMs: input.timeoutMs,
155
+ orphanMaxAgeMs: input.orphanMaxAgeMs,
156
+ retryBudget: input.retryBudget,
157
+ preLaunchAuditRef: input.preLaunchAuditRef,
158
+ laneLaunchApprovalRef: input.laneLaunchApprovalRef,
159
+ });
160
+ const ready = selection.state === "pair_ready" && revalidation.state === "revalidated" && fanoutPlan.state === "fanout_ready";
161
+ return {
162
+ ok: ready && selection.ok && revalidation.ok && fanoutPlan.ok,
163
+ errors: [...selection.errors, ...revalidation.errors, ...fanoutPlan.errors],
164
+ state: ready ? "fanout_ready" : "blocked",
165
+ blocked_labels: [...new Set([...selection.blocked_labels, ...revalidation.blocked_labels, ...fanoutPlan.blocked_labels])],
166
+ selection,
167
+ revalidation,
168
+ fanoutPlan,
169
+ dispatch_authority_enabled: false,
170
+ ...disabledEvidenceAuthority,
171
+ };
172
+ }
173
+ function sessionEvidenceAlreadyContainsId(reloadedEvidence, evidenceClass, evidenceId) {
174
+ return reloadedEvidence.entries.some((entry) => entry.evidenceClass === evidenceClass && entry.evidenceId === evidenceId) ||
175
+ reloadedEvidence.blocked.some((entry) => entry.evidenceClass === evidenceClass && entry.evidenceId === evidenceId);
176
+ }
177
+ function providerAcquisitionResultMatchesStrictContext(result, input) {
178
+ return result.local_date === input.localDate &&
179
+ result.active_profile_ref === input.activeProfileRef &&
180
+ result.opencode_version_ref === input.opencodeVersionRef &&
181
+ result.flowdesk_package_version_ref === input.flowdeskPackageVersionRef &&
182
+ result.registry_hash === input.registryHash &&
183
+ result.policy_pack_hash === input.policyPackHash &&
184
+ result.auth_account_boundary_ref === input.authAccountBoundaryRef;
185
+ }
186
+ function exactModelMaterializationExpectedContext(input) {
187
+ return {
188
+ localDate: input.localDate,
189
+ activeProfileRef: input.activeProfileRef,
190
+ opencodeVersionRef: input.opencodeVersionRef,
191
+ flowdeskPackageVersionRef: input.flowdeskPackageVersionRef,
192
+ registryHash: input.registryHash,
193
+ policyPackHash: input.policyPackHash,
194
+ authAccountBoundaryRef: input.authAccountBoundaryRef,
195
+ };
196
+ }
197
+ export function materializeFlowDeskExactModelCacheEvidenceFromProviderAcquisitionEvidenceV1(input) {
198
+ const errors = [...input.reloadedEvidence.errors];
199
+ const blockedLabels = [];
200
+ if (!input.reloadedEvidence.ok)
201
+ blockedLabels.push("session_evidence_reload_invalid");
202
+ if (sessionEvidenceAlreadyContainsId(input.reloadedEvidence, "exact_model_availability_cache", input.targetCacheEvidenceId))
203
+ blockedLabels.push("target_cache_evidence_duplicate");
204
+ if (sessionEvidenceAlreadyContainsId(input.reloadedEvidence, "exact_model_availability_cache_refresh_plan", input.targetCacheRefreshPlanEvidenceId))
205
+ blockedLabels.push("target_cache_refresh_evidence_duplicate");
206
+ const acquisitionEntries = input.reloadedEvidence.entries
207
+ .filter((entry) => entry.evidenceClass === "exact_model_availability_cache_provider_acquisition_result")
208
+ .filter((entry) => input.providerAcquisitionEvidenceId === undefined || entry.evidenceId === input.providerAcquisitionEvidenceId)
209
+ .map((entry) => ({
210
+ ...entry,
211
+ validation: validateFlowDeskExactModelAvailabilityCacheProviderAcquisitionResultV1(entry.record),
212
+ }))
213
+ .filter((entry) => entry.validation.ok)
214
+ .map((entry) => ({
215
+ ...entry,
216
+ record: entry.record,
217
+ }))
218
+ .filter((entry) => input.providerAcquisitionEvidenceId !== undefined || providerAcquisitionResultMatchesStrictContext(entry.record, input));
219
+ if (acquisitionEntries.length === 0)
220
+ blockedLabels.push("provider_acquisition_evidence_missing");
221
+ if (acquisitionEntries.length > 1)
222
+ blockedLabels.push("provider_acquisition_evidence_ambiguous");
223
+ const acquisition = acquisitionEntries.length === 1 ? acquisitionEntries[0].record : undefined;
224
+ const materialized = materializeFlowDeskExactModelAvailabilityCacheFromProviderAcquisitionResultV1({
225
+ providerAcquisitionResult: acquisition,
226
+ cacheId: input.cacheId,
227
+ entryId: input.entryId,
228
+ expectedContext: exactModelMaterializationExpectedContext(input),
229
+ });
230
+ if (!materialized.ok) {
231
+ errors.push(...materialized.errors);
232
+ blockedLabels.push(...materialized.blocked_labels);
233
+ }
234
+ const cache = materialized.cache;
235
+ const cacheRefreshPlan = cache === undefined ? undefined : planFlowDeskExactModelAvailabilityCacheRefreshV1({
236
+ cache,
237
+ localDate: input.localDate,
238
+ activeProfileRef: input.activeProfileRef,
239
+ opencodeVersionRef: input.opencodeVersionRef,
240
+ flowdeskPackageVersionRef: input.flowdeskPackageVersionRef,
241
+ registryHash: input.registryHash,
242
+ policyPackHash: input.policyPackHash,
243
+ authAccountBoundaryRef: input.authAccountBoundaryRef,
244
+ });
245
+ if (cacheRefreshPlan !== undefined) {
246
+ const refreshValidation = validateFlowDeskExactModelAvailabilityCacheRefreshPlanV1(cacheRefreshPlan);
247
+ if (!refreshValidation.ok) {
248
+ errors.push(...refreshValidation.errors.map((error) => `cache_refresh_plan: ${error}`));
249
+ blockedLabels.push("materialized_cache_refresh_plan_invalid");
250
+ }
251
+ if (cacheRefreshPlan.state !== "cache_hit")
252
+ blockedLabels.push("materialized_cache_refresh_not_cache_hit");
253
+ }
254
+ if (cache !== undefined) {
255
+ const existingRefreshPlans = input.reloadedEvidence.entries
256
+ .filter((entry) => entry.evidenceClass === "exact_model_availability_cache_refresh_plan")
257
+ .map((entry) => entry.record)
258
+ .filter((record) => validateFlowDeskExactModelAvailabilityCacheRefreshPlanV1(record).ok)
259
+ .map((record) => record)
260
+ .filter((plan) => exactModelCacheRefreshPlanMatchesContext(plan, input));
261
+ if (existingRefreshPlans.length > 0)
262
+ blockedLabels.push("cache_refresh_context_already_exists");
263
+ const existingCachesForPlannedRefresh = cacheRefreshPlan === undefined ? [] : input.reloadedEvidence.entries
264
+ .filter((entry) => entry.evidenceClass === "exact_model_availability_cache")
265
+ .map((entry) => entry.record)
266
+ .filter((record) => validateFlowDeskExactModelAvailabilityCacheV1(record).ok)
267
+ .map((record) => record)
268
+ .filter((existingCache) => exactModelCacheMatchesRefreshPlan(existingCache, cacheRefreshPlan));
269
+ if (existingCachesForPlannedRefresh.length > 0)
270
+ blockedLabels.push("cache_context_already_exists");
271
+ }
272
+ if (blockedLabels.length > 0 || cache === undefined || cacheRefreshPlan === undefined)
273
+ return {
274
+ ok: false,
275
+ errors,
276
+ state: "blocked",
277
+ blocked_labels: [...new Set(blockedLabels)],
278
+ ...(cache === undefined ? {} : { cache }),
279
+ ...(cacheRefreshPlan === undefined ? {} : { cacheRefreshPlan }),
280
+ writeIntents: [],
281
+ ...disabledEvidenceAuthority,
282
+ };
283
+ const cacheIntent = prepareFlowDeskSessionEvidenceWriteIntentV1({
284
+ workflowId: input.workflowId,
285
+ evidenceId: input.targetCacheEvidenceId,
286
+ record: cache,
287
+ });
288
+ const refreshIntent = prepareFlowDeskSessionEvidenceWriteIntentV1({
289
+ workflowId: input.workflowId,
290
+ evidenceId: input.targetCacheRefreshPlanEvidenceId,
291
+ record: cacheRefreshPlan,
292
+ });
293
+ if (!cacheIntent.ok || cacheIntent.writeIntent === undefined || !refreshIntent.ok || refreshIntent.writeIntent === undefined)
294
+ return {
295
+ ok: false,
296
+ errors: [...errors, ...cacheIntent.errors, ...refreshIntent.errors],
297
+ state: "blocked",
298
+ blocked_labels: [...new Set([...blockedLabels, "cache_materialization_write_intent_invalid"])],
299
+ cache,
300
+ cacheRefreshPlan,
301
+ writeIntents: [],
302
+ ...disabledEvidenceAuthority,
303
+ };
304
+ const writeIntents = [cacheIntent.writeIntent, refreshIntent.writeIntent];
305
+ if (input.rootDir === undefined)
306
+ return {
307
+ ok: true,
308
+ errors,
309
+ state: "materialized",
310
+ blocked_labels: [],
311
+ cache,
312
+ cacheRefreshPlan,
313
+ writeIntents,
314
+ ...disabledEvidenceAuthority,
315
+ };
316
+ const applyResult = applyFlowDeskSessionEvidenceWriteIntentsV1(input.rootDir, writeIntents);
317
+ if (!applyResult.ok)
318
+ return {
319
+ ok: false,
320
+ errors: [...errors, ...applyResult.errors],
321
+ state: "blocked",
322
+ blocked_labels: [...new Set([...blockedLabels, "cache_materialization_apply_failed"])],
323
+ cache,
324
+ cacheRefreshPlan,
325
+ writeIntents,
326
+ applyResult,
327
+ ...disabledEvidenceAuthority,
328
+ };
329
+ const reloadedEvidence = reloadFlowDeskSessionEvidenceV1({ workflowId: input.workflowId, rootDir: input.rootDir });
330
+ const selection = selectFlowDeskExactModelCacheEvidencePairV1({
331
+ reloadedEvidence,
332
+ localDate: input.localDate,
333
+ activeProfileRef: input.activeProfileRef,
334
+ opencodeVersionRef: input.opencodeVersionRef,
335
+ flowdeskPackageVersionRef: input.flowdeskPackageVersionRef,
336
+ registryHash: input.registryHash,
337
+ policyPackHash: input.policyPackHash,
338
+ authAccountBoundaryRef: input.authAccountBoundaryRef,
339
+ });
340
+ const ready = reloadedEvidence.ok && selection.state === "pair_ready";
341
+ return {
342
+ ok: ready,
343
+ errors: [...errors, ...reloadedEvidence.errors, ...selection.errors],
344
+ state: ready ? "materialized" : "blocked",
345
+ blocked_labels: ready ? [] : [...new Set([...blockedLabels, "cache_materialization_reload_verification_failed", ...selection.blocked_labels])],
346
+ cache,
347
+ cacheRefreshPlan,
348
+ writeIntents,
349
+ applyResult,
350
+ reloadedEvidence,
351
+ selection,
352
+ ...disabledEvidenceAuthority,
353
+ };
354
+ }
355
+ export function materializeFlowDeskRuntimeLaneLaunchPlansFromReviewerFanoutEvidenceV1(input) {
356
+ const errors = [...input.reloadedEvidence.errors];
357
+ const blockedLabels = [];
358
+ if (!input.reloadedEvidence.ok)
359
+ blockedLabels.push("session_evidence_reload_invalid");
360
+ const targetIds = input.targetLaunchPlanEvidenceIds;
361
+ if (targetIds.length === 0)
362
+ blockedLabels.push("target_launch_plan_ids_missing");
363
+ if (new Set(targetIds).size !== targetIds.length)
364
+ blockedLabels.push("target_launch_plan_ids_duplicate");
365
+ for (const targetId of targetIds) {
366
+ if (sessionEvidenceAlreadyContainsId(input.reloadedEvidence, "runtime_lane_launch_plan", targetId))
367
+ blockedLabels.push("target_runtime_launch_plan_evidence_duplicate");
368
+ }
369
+ const fanoutEntries = input.reloadedEvidence.entries
370
+ .filter((entry) => entry.evidenceClass === "reviewer_fanout_plan")
371
+ .filter((entry) => input.reviewerFanoutEvidenceId === undefined || entry.evidenceId === input.reviewerFanoutEvidenceId)
372
+ .map((entry) => ({ ...entry, validation: validateFlowDeskReviewerFanoutPlanV1(entry.record) }))
373
+ .filter((entry) => entry.validation.ok)
374
+ .map((entry) => ({ ...entry, record: entry.record }))
375
+ .filter((entry) => entry.record.workflow_id === input.workflowId);
376
+ if (fanoutEntries.length === 0)
377
+ blockedLabels.push("reviewer_fanout_evidence_missing");
378
+ if (fanoutEntries.length > 1)
379
+ blockedLabels.push("reviewer_fanout_evidence_ambiguous");
380
+ const fanoutPlan = fanoutEntries.length === 1 ? fanoutEntries[0].record : undefined;
381
+ if (fanoutPlan !== undefined && (fanoutPlan.state !== "fanout_ready" || !fanoutPlan.ok))
382
+ blockedLabels.push("reviewer_fanout_not_ready");
383
+ const launchRequests = fanoutPlan?.runtime_lane_launch_requests ?? [];
384
+ if (fanoutPlan !== undefined && targetIds.length !== launchRequests.length)
385
+ blockedLabels.push("target_launch_plan_count_mismatch");
386
+ const launchPlans = launchRequests.map((request) => planFlowDeskRuntimeLaneLaunchV1({
387
+ request,
388
+ sdkClientAvailable: input.sdkClientAvailable,
389
+ durableEvidenceRootRef: input.durableEvidenceRootRef,
390
+ }));
391
+ for (const [index, plan] of launchPlans.entries()) {
392
+ errors.push(...plan.errors.map((error) => `launch_plans[${index}]: ${error}`));
393
+ if (plan.state !== "launch_ready")
394
+ blockedLabels.push(...(plan.blocked_labels.length > 0 ? plan.blocked_labels : ["runtime_launch_plan_blocked"]));
395
+ }
396
+ if (blockedLabels.length > 0 || fanoutPlan === undefined || launchPlans.length === 0)
397
+ return {
398
+ ok: false,
399
+ errors,
400
+ state: "blocked",
401
+ blocked_labels: [...new Set(blockedLabels)],
402
+ ...(fanoutPlan === undefined ? {} : { fanoutPlan }),
403
+ launchPlans,
404
+ writeIntents: [],
405
+ ...disabledEvidenceAuthority,
406
+ };
407
+ const prepared = launchPlans.map((plan, index) => prepareFlowDeskSessionEvidenceWriteIntentV1({
408
+ workflowId: input.workflowId,
409
+ evidenceId: targetIds[index],
410
+ record: plan,
411
+ }));
412
+ const prepareErrors = prepared.flatMap((result) => result.errors);
413
+ const writeIntents = prepared
414
+ .map((result) => result.writeIntent)
415
+ .filter((intent) => intent !== undefined);
416
+ if (prepareErrors.length > 0 || writeIntents.length !== launchPlans.length)
417
+ return {
418
+ ok: false,
419
+ errors: [...errors, ...prepareErrors],
420
+ state: "blocked",
421
+ blocked_labels: ["runtime_launch_plan_write_intent_invalid"],
422
+ fanoutPlan,
423
+ launchPlans,
424
+ writeIntents: [],
425
+ ...disabledEvidenceAuthority,
426
+ };
427
+ if (input.rootDir === undefined)
428
+ return {
429
+ ok: true,
430
+ errors,
431
+ state: "materialized",
432
+ blocked_labels: [],
433
+ fanoutPlan,
434
+ launchPlans,
435
+ writeIntents,
436
+ ...disabledEvidenceAuthority,
437
+ };
438
+ const applyResult = applyFlowDeskSessionEvidenceWriteIntentsV1(input.rootDir, writeIntents);
439
+ if (!applyResult.ok)
440
+ return {
441
+ ok: false,
442
+ errors: [...errors, ...applyResult.errors],
443
+ state: "blocked",
444
+ blocked_labels: ["runtime_launch_plan_apply_failed"],
445
+ fanoutPlan,
446
+ launchPlans,
447
+ writeIntents,
448
+ applyResult,
449
+ ...disabledEvidenceAuthority,
450
+ };
451
+ const reloadedEvidence = reloadFlowDeskSessionEvidenceV1({ workflowId: input.workflowId, rootDir: input.rootDir });
452
+ const reloadedIds = new Set(reloadedEvidence.entries
453
+ .filter((entry) => entry.evidenceClass === "runtime_lane_launch_plan")
454
+ .map((entry) => entry.evidenceId));
455
+ const allReloaded = targetIds.every((targetId) => reloadedIds.has(targetId));
456
+ const ready = reloadedEvidence.ok && allReloaded;
457
+ return {
458
+ ok: ready,
459
+ errors: [...errors, ...reloadedEvidence.errors],
460
+ state: ready ? "materialized" : "blocked",
461
+ blocked_labels: ready ? [] : ["runtime_launch_plan_reload_verification_failed"],
462
+ fanoutPlan,
463
+ launchPlans,
464
+ writeIntents,
465
+ applyResult,
466
+ reloadedEvidence,
467
+ ...disabledEvidenceAuthority,
468
+ };
469
+ }
20
470
  function validateSchemaVersionForClass(record, evidenceClass) {
21
471
  const expected = EVIDENCE_SCHEMA_BY_CLASS[evidenceClass];
22
472
  return record.schema_version === expected
@@ -27,12 +477,68 @@ function validateEvidenceShape(record, evidenceClass) {
27
477
  const schemaCheck = validateSchemaVersionForClass(record, evidenceClass);
28
478
  if (!schemaCheck.ok)
29
479
  return schemaCheck;
480
+ if (evidenceClass === "dispatch_idempotency")
481
+ return validateFlowDeskDispatchIdempotencySnapshotV1(record);
482
+ if (evidenceClass === "production_approval_source")
483
+ return validateFlowDeskProductionApprovalSourceV1(record);
484
+ if (evidenceClass === "reviewer_verdict")
485
+ return validateTopTierReviewVerdictV1(record);
486
+ if (evidenceClass === "exact_model_availability_cache")
487
+ return validateFlowDeskExactModelAvailabilityCacheV1(record);
488
+ if (evidenceClass === "exact_model_availability_cache_refresh_plan")
489
+ return validateFlowDeskExactModelAvailabilityCacheRefreshPlanV1(record);
490
+ if (evidenceClass === "exact_model_availability_cache_acquisition_plan")
491
+ return validateFlowDeskExactModelAvailabilityCacheAcquisitionPlanV1(record);
492
+ if (evidenceClass === "exact_model_availability_cache_provider_acquisition_result")
493
+ return validateFlowDeskExactModelAvailabilityCacheProviderAcquisitionResultV1(record);
494
+ if (evidenceClass === "reviewer_fanout_plan")
495
+ return validateFlowDeskReviewerFanoutPlanV1(record);
496
+ if (evidenceClass === "runtime_lane_launch_plan")
497
+ return validateFlowDeskRuntimeLaneLaunchPlanV1(record);
498
+ if (evidenceClass === "lane_lifecycle")
499
+ return validateFlowDeskLaneLifecycleRecordV1(record);
500
+ if (evidenceClass === "lane_heartbeat")
501
+ return validateFlowDeskLaneHeartbeatRecordV1(record);
502
+ if (evidenceClass === "reviewer_lane_conformance")
503
+ return validateFlowDeskReviewerLaneConformanceObservationV1(record);
504
+ if (evidenceClass === "controlled_conformance_doc_write")
505
+ return validateFlowDeskControlledConformanceDocWriteRecordV1(record);
506
+ if (evidenceClass === "controlled_redacted_audit_export_write")
507
+ return validateFlowDeskControlledRedactedAuditExportWriteRecordV1(record);
508
+ if (evidenceClass === "fallback_regate_plan")
509
+ return validateFlowDeskFallbackRegatePlanV1(record);
30
510
  const requiredCommon = ["schema_version"];
31
511
  for (const key of requiredCommon)
32
512
  if (!(key in record))
33
513
  return invalid(`evidence is missing required field: ${key}`);
34
514
  return validateNoForbiddenRawPayloads(record, "session_evidence_record");
35
515
  }
516
+ function validateOptionalTimestampFreshness(record, rejectStaleAt) {
517
+ if (rejectStaleAt === undefined || !("expires_at" in record))
518
+ return valid();
519
+ const checkedAt = Date.parse(rejectStaleAt);
520
+ if (!Number.isFinite(checkedAt))
521
+ return invalid("rejectStaleAt must be a parseable timestamp");
522
+ if (typeof record.expires_at !== "string")
523
+ return invalid("evidence expires_at must be a timestamp string");
524
+ const expiresAt = Date.parse(record.expires_at);
525
+ if (!Number.isFinite(expiresAt))
526
+ return invalid("evidence expires_at must be parseable");
527
+ return expiresAt > checkedAt ? valid() : invalid("evidence is stale");
528
+ }
529
+ function validateOptionalProfileAlignment(record, expectedProfileRef) {
530
+ if (expectedProfileRef === undefined)
531
+ return valid();
532
+ const expected = validateOpaqueRef(expectedProfileRef, "expected_profile_ref");
533
+ if (!expected.ok)
534
+ return expected;
535
+ const profileRef = record.profile_ref ?? record.auth_profile_ref;
536
+ if (profileRef === undefined)
537
+ return valid();
538
+ return profileRef === expectedProfileRef
539
+ ? valid()
540
+ : invalid("evidence profile_ref mismatch");
541
+ }
36
542
  function ensureWorkflowAlignment(record, workflowId) {
37
543
  if (!("workflow_id" in record))
38
544
  return valid();
@@ -147,6 +653,8 @@ export function applyFlowDeskSessionEvidenceWriteIntentsV1(rootDir, intents) {
147
653
  const writtenPaths = [];
148
654
  const root = resolve(rootDir);
149
655
  try {
656
+ const seenTargets = new Set();
657
+ const seenTemps = new Set();
150
658
  for (const intent of intents) {
151
659
  const intentResult = validateSessionEvidenceWriteIntent(intent);
152
660
  if (!intentResult.ok)
@@ -158,6 +666,20 @@ export function applyFlowDeskSessionEvidenceWriteIntentsV1(rootDir, intents) {
158
666
  };
159
667
  const target = safeJoin(root, intent.path);
160
668
  const temp = safeJoin(root, intent.tempPath);
669
+ if (seenTargets.has(target))
670
+ return {
671
+ ...invalid("session evidence batch contains duplicate target path"),
672
+ rootDir: root,
673
+ writtenPaths,
674
+ ...disabledEvidenceAuthority,
675
+ };
676
+ if (seenTemps.has(temp))
677
+ return {
678
+ ...invalid("session evidence batch contains duplicate temp path"),
679
+ rootDir: root,
680
+ writtenPaths,
681
+ ...disabledEvidenceAuthority,
682
+ };
161
683
  if (dirname(target) !== dirname(temp))
162
684
  return {
163
685
  ...invalid("session evidence tempPath must stay beside target path"),
@@ -165,6 +687,12 @@ export function applyFlowDeskSessionEvidenceWriteIntentsV1(rootDir, intents) {
165
687
  writtenPaths,
166
688
  ...disabledEvidenceAuthority,
167
689
  };
690
+ seenTargets.add(target);
691
+ seenTemps.add(temp);
692
+ }
693
+ for (const intent of intents) {
694
+ const target = safeJoin(root, intent.path);
695
+ const temp = safeJoin(root, intent.tempPath);
168
696
  mkdirSync(dirname(target), { recursive: true });
169
697
  writeFileSync(temp, JSON.stringify(intent.record), "utf8");
170
698
  renameSync(temp, target);
@@ -316,6 +844,26 @@ export function reloadFlowDeskSessionEvidenceV1(options) {
316
844
  });
317
845
  continue;
318
846
  }
847
+ const profileAlignment = validateOptionalProfileAlignment(parsed, options.expectedProfileRef);
848
+ if (!profileAlignment.ok) {
849
+ blocked.push({
850
+ evidenceClass,
851
+ evidenceId,
852
+ reason: profileAlignment.errors.join("; "),
853
+ path: expectedRelative,
854
+ });
855
+ continue;
856
+ }
857
+ const freshness = validateOptionalTimestampFreshness(parsed, options.rejectStaleAt);
858
+ if (!freshness.ok) {
859
+ blocked.push({
860
+ evidenceClass,
861
+ evidenceId,
862
+ reason: freshness.errors.join("; "),
863
+ path: expectedRelative,
864
+ });
865
+ continue;
866
+ }
319
867
  entries.push({
320
868
  evidenceClass,
321
869
  evidenceId,
@@ -333,4 +881,24 @@ export function reloadFlowDeskSessionEvidenceV1(options) {
333
881
  };
334
882
  return reloadResult;
335
883
  }
884
+ export function summarizeFlowDeskSessionEvidenceInventoryV1(workflowId, reload) {
885
+ return {
886
+ schema_version: "flowdesk.session_evidence_inventory.v1",
887
+ workflow_id: workflowId,
888
+ total_entries: reload.entries.length,
889
+ total_blocked: reload.blocked.length,
890
+ classes: FLOWDESK_SESSION_EVIDENCE_CLASSES.map((evidenceClass) => {
891
+ const blocked = reload.blocked.filter((entry) => entry.evidenceClass === evidenceClass);
892
+ return {
893
+ evidenceClass,
894
+ valid: reload.entries.filter((entry) => entry.evidenceClass === evidenceClass).length,
895
+ blocked: blocked.length,
896
+ ...(blocked.length === 0
897
+ ? {}
898
+ : { lastBlockedReason: blocked[blocked.length - 1].reason }),
899
+ };
900
+ }),
901
+ ...disabledEvidenceAuthority,
902
+ };
903
+ }
336
904
  //# sourceMappingURL=session-evidence.js.map