@flowdesk/core 0.1.0 → 0.1.3
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.
- package/LICENSE +21 -0
- package/README.md +44 -0
- package/dist/authority-promotion.d.ts +70 -0
- package/dist/authority-promotion.d.ts.map +1 -0
- package/dist/authority-promotion.js +388 -0
- package/dist/authority-promotion.js.map +1 -0
- package/dist/chat-control-authority.d.ts +83 -0
- package/dist/chat-control-authority.d.ts.map +1 -0
- package/dist/chat-control-authority.js +238 -0
- package/dist/chat-control-authority.js.map +1 -0
- package/dist/chat-hook-authority-probe.d.ts +39 -0
- package/dist/chat-hook-authority-probe.d.ts.map +1 -0
- package/dist/chat-hook-authority-probe.js +153 -0
- package/dist/chat-hook-authority-probe.js.map +1 -0
- package/dist/chat-routing.d.ts.map +1 -1
- package/dist/chat-routing.js +18 -15
- package/dist/chat-routing.js.map +1 -1
- package/dist/connector-gateway.d.ts +34 -0
- package/dist/connector-gateway.d.ts.map +1 -0
- package/dist/connector-gateway.js +147 -0
- package/dist/connector-gateway.js.map +1 -0
- package/dist/connector-profile.d.ts +41 -0
- package/dist/connector-profile.d.ts.map +1 -0
- package/dist/connector-profile.js +125 -0
- package/dist/connector-profile.js.map +1 -0
- package/dist/controlled-conformance-doc-write.d.ts +44 -0
- package/dist/controlled-conformance-doc-write.d.ts.map +1 -0
- package/dist/controlled-conformance-doc-write.js +142 -0
- package/dist/controlled-conformance-doc-write.js.map +1 -0
- package/dist/controlled-redacted-audit-export-write.d.ts +45 -0
- package/dist/controlled-redacted-audit-export-write.d.ts.map +1 -0
- package/dist/controlled-redacted-audit-export-write.js +145 -0
- package/dist/controlled-redacted-audit-export-write.js.map +1 -0
- package/dist/core-completion-safety-contracts.d.ts +35 -0
- package/dist/core-completion-safety-contracts.d.ts.map +1 -0
- package/dist/core-completion-safety-contracts.js +98 -0
- package/dist/core-completion-safety-contracts.js.map +1 -0
- package/dist/dispatch-attempt-manifest.d.ts +59 -0
- package/dist/dispatch-attempt-manifest.d.ts.map +1 -0
- package/dist/dispatch-attempt-manifest.js +294 -0
- package/dist/dispatch-attempt-manifest.js.map +1 -0
- package/dist/dispatch-idempotency.d.ts +89 -0
- package/dist/dispatch-idempotency.d.ts.map +1 -0
- package/dist/dispatch-idempotency.js +275 -0
- package/dist/dispatch-idempotency.js.map +1 -0
- package/dist/external-auth-policy.d.ts +49 -0
- package/dist/external-auth-policy.d.ts.map +1 -0
- package/dist/external-auth-policy.js +166 -0
- package/dist/external-auth-policy.js.map +1 -0
- package/dist/fake-remote-connector-adapter.d.ts +37 -0
- package/dist/fake-remote-connector-adapter.d.ts.map +1 -0
- package/dist/fake-remote-connector-adapter.js +162 -0
- package/dist/fake-remote-connector-adapter.js.map +1 -0
- package/dist/fallback-decision.d.ts +29 -0
- package/dist/fallback-decision.d.ts.map +1 -0
- package/dist/fallback-decision.js +93 -0
- package/dist/fallback-decision.js.map +1 -0
- package/dist/fallback-regate-plan.d.ts +34 -0
- package/dist/fallback-regate-plan.d.ts.map +1 -0
- package/dist/fallback-regate-plan.js +122 -0
- package/dist/fallback-regate-plan.js.map +1 -0
- package/dist/fds1-schema-probe-result.d.ts +37 -0
- package/dist/fds1-schema-probe-result.d.ts.map +1 -0
- package/dist/fds1-schema-probe-result.js +115 -0
- package/dist/fds1-schema-probe-result.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -1
- package/dist/lane-heartbeat.d.ts +49 -0
- package/dist/lane-heartbeat.d.ts.map +1 -0
- package/dist/lane-heartbeat.js +149 -0
- package/dist/lane-heartbeat.js.map +1 -0
- package/dist/lane-lifecycle-record.d.ts +32 -0
- package/dist/lane-lifecycle-record.d.ts.map +1 -0
- package/dist/lane-lifecycle-record.js +134 -0
- package/dist/lane-lifecycle-record.js.map +1 -0
- package/dist/lane-stall-projection.d.ts +43 -0
- package/dist/lane-stall-projection.d.ts.map +1 -0
- package/dist/lane-stall-projection.js +272 -0
- package/dist/lane-stall-projection.js.map +1 -0
- package/dist/model-availability-cache.d.ts +286 -0
- package/dist/model-availability-cache.d.ts.map +1 -0
- package/dist/model-availability-cache.js +1109 -0
- package/dist/model-availability-cache.js.map +1 -0
- package/dist/operational-intelligence.d.ts +38 -0
- package/dist/operational-intelligence.d.ts.map +1 -0
- package/dist/operational-intelligence.js +107 -0
- package/dist/operational-intelligence.js.map +1 -0
- package/dist/production-approval-source.d.ts +61 -0
- package/dist/production-approval-source.d.ts.map +1 -0
- package/dist/production-approval-source.js +226 -0
- package/dist/production-approval-source.js.map +1 -0
- package/dist/production-enablement.d.ts +92 -1
- package/dist/production-enablement.d.ts.map +1 -1
- package/dist/production-enablement.js +421 -8
- package/dist/production-enablement.js.map +1 -1
- package/dist/production-verification.d.ts +31 -0
- package/dist/production-verification.d.ts.map +1 -0
- package/dist/production-verification.js +126 -0
- package/dist/production-verification.js.map +1 -0
- package/dist/provider-usage-collector.d.ts +7 -0
- package/dist/provider-usage-collector.d.ts.map +1 -1
- package/dist/provider-usage-collector.js +64 -10
- package/dist/provider-usage-collector.js.map +1 -1
- package/dist/release1-contracts.d.ts +8 -2
- package/dist/release1-contracts.d.ts.map +1 -1
- package/dist/release1-contracts.js +2 -1
- package/dist/release1-contracts.js.map +1 -1
- package/dist/remote-write-connector-gate.d.ts +91 -0
- package/dist/remote-write-connector-gate.d.ts.map +1 -0
- package/dist/remote-write-connector-gate.js +291 -0
- package/dist/remote-write-connector-gate.js.map +1 -0
- package/dist/runtime-lane-productization.d.ts +78 -0
- package/dist/runtime-lane-productization.d.ts.map +1 -0
- package/dist/runtime-lane-productization.js +270 -0
- package/dist/runtime-lane-productization.js.map +1 -0
- package/dist/sanitized-auth-capture.d.ts +50 -0
- package/dist/sanitized-auth-capture.d.ts.map +1 -0
- package/dist/sanitized-auth-capture.js +164 -0
- package/dist/sanitized-auth-capture.js.map +1 -0
- package/dist/schema-artifacts.d.ts.map +1 -1
- package/dist/schema-artifacts.js +37 -6
- package/dist/schema-artifacts.js.map +1 -1
- package/dist/schema-registry.d.ts.map +1 -1
- package/dist/schema-registry.js +21 -0
- package/dist/schema-registry.js.map +1 -1
- package/dist/session-evidence.d.ts +117 -0
- package/dist/session-evidence.d.ts.map +1 -1
- package/dist/session-evidence.js +581 -1
- package/dist/session-evidence.js.map +1 -1
- package/dist/state-paths.d.ts +1 -1
- package/dist/state-paths.d.ts.map +1 -1
- package/dist/state-paths.js +56 -6
- package/dist/state-paths.js.map +1 -1
- package/dist/status.d.ts +7 -0
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +89 -1
- package/dist/status.js.map +1 -1
- package/dist/validators.d.ts +1 -0
- package/dist/validators.d.ts.map +1 -1
- package/dist/validators.js +30 -7
- package/dist/validators.js.map +1 -1
- package/package.json +11 -2
|
@@ -0,0 +1,1109 @@
|
|
|
1
|
+
import { validateFlowDeskRuntimeLaneLaunchRequestV1 } from "./runtime-lane-productization.js";
|
|
2
|
+
import { invalid, valid, validateConcreteProviderQualifiedModelId, validateNoForbiddenRawPayloads, validateOpaqueId, validateOpaqueRef, validateProviderFamily, } from "./validators.js";
|
|
3
|
+
const FLOWDESK_EXACT_MODEL_PROVIDER_FAMILIES = ["claude", "openai", "gemini", "opencode_go", "z_ai"];
|
|
4
|
+
const perspectives = [
|
|
5
|
+
"policy_security",
|
|
6
|
+
"architecture",
|
|
7
|
+
"verification_implementation",
|
|
8
|
+
];
|
|
9
|
+
const disabledReviewerRuntimeAuthority = {
|
|
10
|
+
dispatch_authority_enabled: false,
|
|
11
|
+
providerCall: false,
|
|
12
|
+
actualLaneLaunch: false,
|
|
13
|
+
runtimeExecution: false,
|
|
14
|
+
};
|
|
15
|
+
function isRecord(value) {
|
|
16
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
17
|
+
}
|
|
18
|
+
function validateHash(value, label) {
|
|
19
|
+
const ref = validateOpaqueRef(value, label);
|
|
20
|
+
if (!ref.ok)
|
|
21
|
+
return ref;
|
|
22
|
+
return typeof value === "string" && /^(hash-|sha256-)/.test(value)
|
|
23
|
+
? valid()
|
|
24
|
+
: invalid(`${label} must be hash-bound`);
|
|
25
|
+
}
|
|
26
|
+
function validateLocalDate(value, label) {
|
|
27
|
+
return typeof value === "string" && /^\d{4}-\d{2}-\d{2}$/.test(value)
|
|
28
|
+
? valid()
|
|
29
|
+
: invalid(`${label} must be YYYY-MM-DD`);
|
|
30
|
+
}
|
|
31
|
+
function validateTimestamp(value, label) {
|
|
32
|
+
return typeof value === "string" && Number.isFinite(Date.parse(value))
|
|
33
|
+
? valid()
|
|
34
|
+
: invalid(`${label} must be a parseable timestamp`);
|
|
35
|
+
}
|
|
36
|
+
function unique(values) {
|
|
37
|
+
return [...new Set(values)];
|
|
38
|
+
}
|
|
39
|
+
function eligibleEntries(cache) {
|
|
40
|
+
return cache.entries
|
|
41
|
+
.filter((entry) => entry.registered && entry.available && entry.highest_tier_eligible)
|
|
42
|
+
.sort((left, right) => {
|
|
43
|
+
const leftKey = `${left.provider_family}/${left.provider_qualified_model_id}/${left.entry_id}`;
|
|
44
|
+
const rightKey = `${right.provider_family}/${right.provider_qualified_model_id}/${right.entry_id}`;
|
|
45
|
+
return leftKey.localeCompare(rightKey);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
function validatePerspectiveArray(value, label) {
|
|
49
|
+
if (!Array.isArray(value) || value.length === 0)
|
|
50
|
+
return invalid(`${label} must be a non-empty array`);
|
|
51
|
+
const errors = [];
|
|
52
|
+
for (const [index, perspective] of value.entries())
|
|
53
|
+
if (!perspectives.includes(perspective))
|
|
54
|
+
errors.push(`${label}[${index}] is invalid`);
|
|
55
|
+
if (new Set(value).size !== value.length)
|
|
56
|
+
errors.push(`${label} must not contain duplicate perspectives`);
|
|
57
|
+
return errors.length === 0 ? valid() : invalid(...errors);
|
|
58
|
+
}
|
|
59
|
+
function rejectUnknownProperties(record, allowed, label) {
|
|
60
|
+
const unknown = Object.keys(record).filter((key) => !allowed.includes(key));
|
|
61
|
+
return unknown.length === 0 ? valid() : invalid(`${label} unknown properties: ${unknown.join(",")}`);
|
|
62
|
+
}
|
|
63
|
+
export function validateFlowDeskExactModelAvailabilityCacheV1(value) {
|
|
64
|
+
if (!isRecord(value))
|
|
65
|
+
return invalid("exact model availability cache must be an object");
|
|
66
|
+
const record = value;
|
|
67
|
+
const errors = [];
|
|
68
|
+
errors.push(...rejectUnknownProperties(record, [
|
|
69
|
+
"schema_version",
|
|
70
|
+
"cache_id",
|
|
71
|
+
"local_date",
|
|
72
|
+
"active_profile_ref",
|
|
73
|
+
"opencode_version_ref",
|
|
74
|
+
"flowdesk_package_version_ref",
|
|
75
|
+
"registry_hash",
|
|
76
|
+
"policy_pack_hash",
|
|
77
|
+
"auth_account_boundary_ref",
|
|
78
|
+
"entries",
|
|
79
|
+
"dispatch_authority_enabled",
|
|
80
|
+
"providerCall",
|
|
81
|
+
"actualLaneLaunch",
|
|
82
|
+
"runtimeExecution",
|
|
83
|
+
], "availability cache").errors);
|
|
84
|
+
errors.push(...validateOpaqueId(record.cache_id, "cache_id").errors);
|
|
85
|
+
if (record.schema_version !== "flowdesk.exact_model_availability_cache.v1")
|
|
86
|
+
errors.push("availability cache schema_version is invalid");
|
|
87
|
+
if (typeof record.local_date !== "string" || !/^\d{4}-\d{2}-\d{2}$/.test(record.local_date))
|
|
88
|
+
errors.push("local_date must be YYYY-MM-DD");
|
|
89
|
+
for (const [value, label] of [
|
|
90
|
+
[record.active_profile_ref, "active_profile_ref"],
|
|
91
|
+
[record.opencode_version_ref, "opencode_version_ref"],
|
|
92
|
+
[record.flowdesk_package_version_ref, "flowdesk_package_version_ref"],
|
|
93
|
+
[record.auth_account_boundary_ref, "auth_account_boundary_ref"],
|
|
94
|
+
])
|
|
95
|
+
errors.push(...validateOpaqueRef(value, label).errors);
|
|
96
|
+
errors.push(...validateHash(record.registry_hash, "registry_hash").errors);
|
|
97
|
+
errors.push(...validateHash(record.policy_pack_hash, "policy_pack_hash").errors);
|
|
98
|
+
if (!Array.isArray(record.entries) || record.entries.length === 0)
|
|
99
|
+
errors.push("availability cache entries must be non-empty");
|
|
100
|
+
else
|
|
101
|
+
for (const [index, entry] of record.entries.entries()) {
|
|
102
|
+
if (!isRecord(entry)) {
|
|
103
|
+
errors.push(`entries[${index}] must be an object`);
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
errors.push(...rejectUnknownProperties(entry, [
|
|
107
|
+
"entry_id",
|
|
108
|
+
"provider_family",
|
|
109
|
+
"provider_identity_ref",
|
|
110
|
+
"provider_qualified_model_id",
|
|
111
|
+
"model_family",
|
|
112
|
+
"registered",
|
|
113
|
+
"available",
|
|
114
|
+
"highest_tier_eligible",
|
|
115
|
+
"availability_ref",
|
|
116
|
+
], `entries[${index}]`).errors);
|
|
117
|
+
errors.push(...validateOpaqueId(entry.entry_id, `entries[${index}].entry_id`).errors);
|
|
118
|
+
errors.push(...validateProviderFamily(entry.provider_family).errors.map((error) => `entries[${index}].${error}`));
|
|
119
|
+
if (!FLOWDESK_EXACT_MODEL_PROVIDER_FAMILIES.includes(entry.provider_family))
|
|
120
|
+
errors.push(`entries[${index}].provider_family is not exact-model eligible`);
|
|
121
|
+
errors.push(...validateOpaqueRef(entry.provider_identity_ref, `entries[${index}].provider_identity_ref`).errors);
|
|
122
|
+
errors.push(...validateConcreteProviderQualifiedModelId(entry.provider_qualified_model_id).errors);
|
|
123
|
+
if (typeof entry.provider_qualified_model_id === "string" && typeof entry.provider_family === "string") {
|
|
124
|
+
const provider = entry.provider_qualified_model_id.split("/")[0];
|
|
125
|
+
if (provider !== entry.provider_family)
|
|
126
|
+
errors.push(`entries[${index}].provider_family must match provider_qualified_model_id`);
|
|
127
|
+
}
|
|
128
|
+
errors.push(...validateOpaqueId(entry.model_family, `entries[${index}].model_family`).errors);
|
|
129
|
+
for (const key of ["registered", "available", "highest_tier_eligible"])
|
|
130
|
+
if (typeof entry[key] !== "boolean")
|
|
131
|
+
errors.push(`entries[${index}].${key} must be boolean`);
|
|
132
|
+
errors.push(...validateOpaqueRef(entry.availability_ref, `entries[${index}].availability_ref`).errors);
|
|
133
|
+
}
|
|
134
|
+
if (record.dispatch_authority_enabled !== false ||
|
|
135
|
+
record.providerCall !== false ||
|
|
136
|
+
record.actualLaneLaunch !== false ||
|
|
137
|
+
record.runtimeExecution !== false)
|
|
138
|
+
errors.push("availability cache cannot enable runtime authority");
|
|
139
|
+
errors.push(...validateNoForbiddenRawPayloads(record, "exact_model_availability_cache").errors);
|
|
140
|
+
return errors.length === 0 ? valid() : invalid(...errors);
|
|
141
|
+
}
|
|
142
|
+
export function planFlowDeskReviewerAssignmentsV1(input) {
|
|
143
|
+
const errors = validateFlowDeskExactModelAvailabilityCacheV1(input.cache).errors;
|
|
144
|
+
const blockedLabels = [];
|
|
145
|
+
if (errors.length > 0)
|
|
146
|
+
blockedLabels.push("cache_invalid");
|
|
147
|
+
if (input.cache.local_date !== input.localDate)
|
|
148
|
+
blockedLabels.push("cache_not_same_day");
|
|
149
|
+
const eligible = input.cache.entries.filter((entry) => entry.registered && entry.available && entry.highest_tier_eligible);
|
|
150
|
+
if (eligible.length === 0)
|
|
151
|
+
blockedLabels.push("no_registered_available_highest_tier_models");
|
|
152
|
+
const canBind = errors.length === 0 && blockedLabels.length === 0;
|
|
153
|
+
const lane_bindings = !canBind ? [] : perspectives.map((perspective, index) => {
|
|
154
|
+
const entry = eligible[index % eligible.length];
|
|
155
|
+
return { perspective, entry_id: entry.entry_id, provider_qualified_model_id: entry.provider_qualified_model_id };
|
|
156
|
+
});
|
|
157
|
+
const ready = canBind;
|
|
158
|
+
return {
|
|
159
|
+
schema_version: "flowdesk.reviewer_assignment_plan.v1",
|
|
160
|
+
cache_id: input.cache.cache_id,
|
|
161
|
+
ok: errors.length === 0,
|
|
162
|
+
errors,
|
|
163
|
+
state: ready ? "ready" : "blocked",
|
|
164
|
+
lane_bindings,
|
|
165
|
+
blocked_labels: blockedLabels,
|
|
166
|
+
dispatch_authority_enabled: false,
|
|
167
|
+
providerCall: false,
|
|
168
|
+
actualLaneLaunch: false,
|
|
169
|
+
runtimeExecution: false,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
export function planFlowDeskExactModelAvailabilityCacheRefreshV1(input) {
|
|
173
|
+
const errors = [];
|
|
174
|
+
const blockedLabels = [];
|
|
175
|
+
const refreshReasonLabels = [];
|
|
176
|
+
errors.push(...validateLocalDate(input.localDate, "expected_local_date").errors);
|
|
177
|
+
errors.push(...validateOpaqueRef(input.activeProfileRef, "expected_active_profile_ref").errors);
|
|
178
|
+
errors.push(...validateOpaqueRef(input.opencodeVersionRef, "expected_opencode_version_ref").errors);
|
|
179
|
+
errors.push(...validateOpaqueRef(input.flowdeskPackageVersionRef, "expected_flowdesk_package_version_ref").errors);
|
|
180
|
+
errors.push(...validateHash(input.registryHash, "expected_registry_hash").errors);
|
|
181
|
+
errors.push(...validateHash(input.policyPackHash, "expected_policy_pack_hash").errors);
|
|
182
|
+
errors.push(...validateOpaqueRef(input.authAccountBoundaryRef, "expected_auth_account_boundary_ref").errors);
|
|
183
|
+
const cacheResult = input.cache === undefined ? valid() : validateFlowDeskExactModelAvailabilityCacheV1(input.cache);
|
|
184
|
+
if (!cacheResult.ok) {
|
|
185
|
+
errors.push(...cacheResult.errors);
|
|
186
|
+
blockedLabels.push("cache_invalid");
|
|
187
|
+
}
|
|
188
|
+
else if (input.cache === undefined)
|
|
189
|
+
refreshReasonLabels.push("cache_missing");
|
|
190
|
+
else {
|
|
191
|
+
if (input.cache.local_date !== input.localDate)
|
|
192
|
+
refreshReasonLabels.push("cache_not_same_day");
|
|
193
|
+
if (input.cache.active_profile_ref !== input.activeProfileRef)
|
|
194
|
+
refreshReasonLabels.push("active_profile_changed");
|
|
195
|
+
if (input.cache.opencode_version_ref !== input.opencodeVersionRef)
|
|
196
|
+
refreshReasonLabels.push("opencode_version_changed");
|
|
197
|
+
if (input.cache.flowdesk_package_version_ref !== input.flowdeskPackageVersionRef)
|
|
198
|
+
refreshReasonLabels.push("flowdesk_package_version_changed");
|
|
199
|
+
if (input.cache.registry_hash !== input.registryHash)
|
|
200
|
+
refreshReasonLabels.push("registry_hash_changed");
|
|
201
|
+
if (input.cache.policy_pack_hash !== input.policyPackHash)
|
|
202
|
+
refreshReasonLabels.push("policy_pack_hash_changed");
|
|
203
|
+
if (input.cache.auth_account_boundary_ref !== input.authAccountBoundaryRef)
|
|
204
|
+
refreshReasonLabels.push("auth_account_boundary_changed");
|
|
205
|
+
}
|
|
206
|
+
if (errors.length > 0 && blockedLabels.length === 0)
|
|
207
|
+
blockedLabels.push("refresh_context_invalid");
|
|
208
|
+
const hasRefreshReasons = refreshReasonLabels.length > 0;
|
|
209
|
+
const state = blockedLabels.length > 0 ? "blocked" : hasRefreshReasons ? "refresh_required" : "cache_hit";
|
|
210
|
+
return {
|
|
211
|
+
schema_version: "flowdesk.exact_model_availability_cache_refresh_plan.v1",
|
|
212
|
+
ok: errors.length === 0,
|
|
213
|
+
errors,
|
|
214
|
+
state,
|
|
215
|
+
blocked_labels: unique(blockedLabels),
|
|
216
|
+
refresh_reason_labels: unique(refreshReasonLabels),
|
|
217
|
+
expected_local_date: input.localDate,
|
|
218
|
+
expected_active_profile_ref: input.activeProfileRef,
|
|
219
|
+
expected_opencode_version_ref: input.opencodeVersionRef,
|
|
220
|
+
expected_flowdesk_package_version_ref: input.flowdeskPackageVersionRef,
|
|
221
|
+
expected_registry_hash: input.registryHash,
|
|
222
|
+
expected_policy_pack_hash: input.policyPackHash,
|
|
223
|
+
expected_auth_account_boundary_ref: input.authAccountBoundaryRef,
|
|
224
|
+
cache_id: input.cache?.cache_id,
|
|
225
|
+
cache_local_date: input.cache?.local_date,
|
|
226
|
+
cache_active_profile_ref: input.cache?.active_profile_ref,
|
|
227
|
+
cache_opencode_version_ref: input.cache?.opencode_version_ref,
|
|
228
|
+
cache_flowdesk_package_version_ref: input.cache?.flowdesk_package_version_ref,
|
|
229
|
+
cache_registry_hash: input.cache?.registry_hash,
|
|
230
|
+
cache_policy_pack_hash: input.cache?.policy_pack_hash,
|
|
231
|
+
cache_auth_account_boundary_ref: input.cache?.auth_account_boundary_ref,
|
|
232
|
+
discovery_required: state !== "cache_hit",
|
|
233
|
+
refresh_required: state === "refresh_required",
|
|
234
|
+
cache_usable_for_assignment: state === "cache_hit",
|
|
235
|
+
discovery_attempted: false,
|
|
236
|
+
refresh_attempted: false,
|
|
237
|
+
...disabledReviewerRuntimeAuthority,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
export function validateFlowDeskExactModelAvailabilityCacheRefreshPlanV1(value) {
|
|
241
|
+
if (!isRecord(value))
|
|
242
|
+
return invalid("exact model availability cache refresh plan must be an object");
|
|
243
|
+
const record = value;
|
|
244
|
+
const errors = [];
|
|
245
|
+
errors.push(...rejectUnknownProperties(record, [
|
|
246
|
+
"schema_version",
|
|
247
|
+
"ok",
|
|
248
|
+
"errors",
|
|
249
|
+
"state",
|
|
250
|
+
"blocked_labels",
|
|
251
|
+
"refresh_reason_labels",
|
|
252
|
+
"expected_local_date",
|
|
253
|
+
"expected_active_profile_ref",
|
|
254
|
+
"expected_opencode_version_ref",
|
|
255
|
+
"expected_flowdesk_package_version_ref",
|
|
256
|
+
"expected_registry_hash",
|
|
257
|
+
"expected_policy_pack_hash",
|
|
258
|
+
"expected_auth_account_boundary_ref",
|
|
259
|
+
"cache_id",
|
|
260
|
+
"cache_local_date",
|
|
261
|
+
"cache_active_profile_ref",
|
|
262
|
+
"cache_opencode_version_ref",
|
|
263
|
+
"cache_flowdesk_package_version_ref",
|
|
264
|
+
"cache_registry_hash",
|
|
265
|
+
"cache_policy_pack_hash",
|
|
266
|
+
"cache_auth_account_boundary_ref",
|
|
267
|
+
"discovery_required",
|
|
268
|
+
"refresh_required",
|
|
269
|
+
"cache_usable_for_assignment",
|
|
270
|
+
"discovery_attempted",
|
|
271
|
+
"refresh_attempted",
|
|
272
|
+
"dispatch_authority_enabled",
|
|
273
|
+
"providerCall",
|
|
274
|
+
"actualLaneLaunch",
|
|
275
|
+
"runtimeExecution",
|
|
276
|
+
], "availability cache refresh plan").errors);
|
|
277
|
+
if (record.schema_version !== "flowdesk.exact_model_availability_cache_refresh_plan.v1")
|
|
278
|
+
errors.push("availability cache refresh plan schema_version is invalid");
|
|
279
|
+
if (record.state !== "cache_hit" && record.state !== "refresh_required" && record.state !== "blocked")
|
|
280
|
+
errors.push("availability cache refresh plan state is invalid");
|
|
281
|
+
for (const [value, label] of [[record.blocked_labels, "blocked_labels"], [record.refresh_reason_labels, "refresh_reason_labels"]]) {
|
|
282
|
+
if (!Array.isArray(value))
|
|
283
|
+
errors.push(`${label} must be an array`);
|
|
284
|
+
else
|
|
285
|
+
for (const [index, item] of value.entries())
|
|
286
|
+
errors.push(...validateOpaqueId(item, `${label}[${index}]`).errors);
|
|
287
|
+
}
|
|
288
|
+
if (record.state === "cache_hit" && ((record.blocked_labels?.length ?? 0) > 0 || (record.refresh_reason_labels?.length ?? 0) > 0))
|
|
289
|
+
errors.push("cache_hit refresh plan cannot carry blockers or refresh reasons");
|
|
290
|
+
if (record.state === "refresh_required" && (record.refresh_reason_labels?.length ?? 0) === 0)
|
|
291
|
+
errors.push("refresh_required plan requires refresh reasons");
|
|
292
|
+
if (record.state === "blocked" && (record.blocked_labels?.length ?? 0) === 0)
|
|
293
|
+
errors.push("blocked refresh plan requires blocked labels");
|
|
294
|
+
errors.push(...validateLocalDate(record.expected_local_date, "expected_local_date").errors);
|
|
295
|
+
errors.push(...validateHash(record.expected_registry_hash, "expected_registry_hash").errors);
|
|
296
|
+
errors.push(...validateHash(record.expected_policy_pack_hash, "expected_policy_pack_hash").errors);
|
|
297
|
+
for (const [field, label] of [
|
|
298
|
+
[record.expected_active_profile_ref, "expected_active_profile_ref"],
|
|
299
|
+
[record.expected_opencode_version_ref, "expected_opencode_version_ref"],
|
|
300
|
+
[record.expected_flowdesk_package_version_ref, "expected_flowdesk_package_version_ref"],
|
|
301
|
+
[record.expected_auth_account_boundary_ref, "expected_auth_account_boundary_ref"],
|
|
302
|
+
[record.cache_active_profile_ref, "cache_active_profile_ref"],
|
|
303
|
+
[record.cache_opencode_version_ref, "cache_opencode_version_ref"],
|
|
304
|
+
[record.cache_flowdesk_package_version_ref, "cache_flowdesk_package_version_ref"],
|
|
305
|
+
[record.cache_auth_account_boundary_ref, "cache_auth_account_boundary_ref"],
|
|
306
|
+
])
|
|
307
|
+
if (field !== undefined)
|
|
308
|
+
errors.push(...validateOpaqueRef(field, label).errors);
|
|
309
|
+
if (record.cache_id !== undefined)
|
|
310
|
+
errors.push(...validateOpaqueId(record.cache_id, "cache_id").errors);
|
|
311
|
+
if (record.cache_local_date !== undefined)
|
|
312
|
+
errors.push(...validateLocalDate(record.cache_local_date, "cache_local_date").errors);
|
|
313
|
+
if (record.cache_registry_hash !== undefined)
|
|
314
|
+
errors.push(...validateHash(record.cache_registry_hash, "cache_registry_hash").errors);
|
|
315
|
+
if (record.cache_policy_pack_hash !== undefined)
|
|
316
|
+
errors.push(...validateHash(record.cache_policy_pack_hash, "cache_policy_pack_hash").errors);
|
|
317
|
+
if (record.discovery_required !== (record.state !== "cache_hit"))
|
|
318
|
+
errors.push("discovery_required must match non-cache-hit state");
|
|
319
|
+
if (record.refresh_required !== (record.state === "refresh_required"))
|
|
320
|
+
errors.push("refresh_required must match refresh_required state");
|
|
321
|
+
if (record.cache_usable_for_assignment !== (record.state === "cache_hit"))
|
|
322
|
+
errors.push("cache_usable_for_assignment must match cache_hit state");
|
|
323
|
+
if (record.discovery_attempted !== false ||
|
|
324
|
+
record.refresh_attempted !== false ||
|
|
325
|
+
record.dispatch_authority_enabled !== false ||
|
|
326
|
+
record.providerCall !== false ||
|
|
327
|
+
record.actualLaneLaunch !== false ||
|
|
328
|
+
record.runtimeExecution !== false)
|
|
329
|
+
errors.push("availability cache refresh plan cannot attempt discovery, refresh, launch, provider call, or runtime authority");
|
|
330
|
+
errors.push(...validateNoForbiddenRawPayloads(record, "exact_model_availability_cache_refresh_plan").errors);
|
|
331
|
+
return errors.length === 0 ? valid() : invalid(...errors);
|
|
332
|
+
}
|
|
333
|
+
export function planFlowDeskExactModelAvailabilityCacheAcquisitionV1(input) {
|
|
334
|
+
const refreshResult = validateFlowDeskExactModelAvailabilityCacheRefreshPlanV1(input.refreshPlan);
|
|
335
|
+
const errors = refreshResult.errors.map((error) => `refresh_plan: ${error}`);
|
|
336
|
+
const blockedLabels = [];
|
|
337
|
+
const acquisitionReasonLabels = [];
|
|
338
|
+
let refreshPlanState = "invalid";
|
|
339
|
+
if (!refreshResult.ok)
|
|
340
|
+
blockedLabels.push("refresh_plan_invalid");
|
|
341
|
+
else {
|
|
342
|
+
refreshPlanState = input.refreshPlan.state;
|
|
343
|
+
if (input.refreshPlan.state === "blocked")
|
|
344
|
+
blockedLabels.push("refresh_plan_blocked");
|
|
345
|
+
if (input.refreshPlan.state === "refresh_required")
|
|
346
|
+
acquisitionReasonLabels.push(...input.refreshPlan.refresh_reason_labels);
|
|
347
|
+
}
|
|
348
|
+
const state = !refreshResult.ok || blockedLabels.length > 0
|
|
349
|
+
? "blocked"
|
|
350
|
+
: input.refreshPlan.state === "cache_hit"
|
|
351
|
+
? "acquisition_not_needed"
|
|
352
|
+
: "acquisition_planned";
|
|
353
|
+
return {
|
|
354
|
+
schema_version: "flowdesk.exact_model_availability_cache_acquisition_plan.v1",
|
|
355
|
+
ok: errors.length === 0,
|
|
356
|
+
errors,
|
|
357
|
+
state,
|
|
358
|
+
blocked_labels: unique(blockedLabels),
|
|
359
|
+
acquisition_reason_labels: state === "acquisition_planned" ? unique(acquisitionReasonLabels) : [],
|
|
360
|
+
refresh_plan_ok: refreshResult.ok,
|
|
361
|
+
refresh_plan_state: refreshPlanState,
|
|
362
|
+
refresh_plan_cache_id: refreshResult.ok ? input.refreshPlan.cache_id : undefined,
|
|
363
|
+
acquisition_required: state === "acquisition_planned",
|
|
364
|
+
cache_usable_for_assignment: state === "acquisition_not_needed",
|
|
365
|
+
acquisition_attempted: false,
|
|
366
|
+
discovery_attempted: false,
|
|
367
|
+
refresh_attempted: false,
|
|
368
|
+
...disabledReviewerRuntimeAuthority,
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
export function validateFlowDeskExactModelAvailabilityCacheAcquisitionPlanV1(value) {
|
|
372
|
+
if (!isRecord(value))
|
|
373
|
+
return invalid("exact model availability cache acquisition plan must be an object");
|
|
374
|
+
const record = value;
|
|
375
|
+
const errors = [];
|
|
376
|
+
errors.push(...rejectUnknownProperties(record, [
|
|
377
|
+
"schema_version",
|
|
378
|
+
"ok",
|
|
379
|
+
"errors",
|
|
380
|
+
"state",
|
|
381
|
+
"blocked_labels",
|
|
382
|
+
"acquisition_reason_labels",
|
|
383
|
+
"refresh_plan_ok",
|
|
384
|
+
"refresh_plan_state",
|
|
385
|
+
"refresh_plan_cache_id",
|
|
386
|
+
"acquisition_required",
|
|
387
|
+
"cache_usable_for_assignment",
|
|
388
|
+
"acquisition_attempted",
|
|
389
|
+
"discovery_attempted",
|
|
390
|
+
"refresh_attempted",
|
|
391
|
+
"dispatch_authority_enabled",
|
|
392
|
+
"providerCall",
|
|
393
|
+
"actualLaneLaunch",
|
|
394
|
+
"runtimeExecution",
|
|
395
|
+
], "availability cache acquisition plan").errors);
|
|
396
|
+
if (record.schema_version !== "flowdesk.exact_model_availability_cache_acquisition_plan.v1")
|
|
397
|
+
errors.push("availability cache acquisition plan schema_version is invalid");
|
|
398
|
+
if (record.state !== "acquisition_not_needed" && record.state !== "acquisition_planned" && record.state !== "blocked")
|
|
399
|
+
errors.push("availability cache acquisition plan state is invalid");
|
|
400
|
+
for (const [value, label] of [[record.blocked_labels, "blocked_labels"], [record.acquisition_reason_labels, "acquisition_reason_labels"]]) {
|
|
401
|
+
if (!Array.isArray(value))
|
|
402
|
+
errors.push(`${label} must be an array`);
|
|
403
|
+
else
|
|
404
|
+
for (const [index, item] of value.entries())
|
|
405
|
+
errors.push(...validateOpaqueId(item, `${label}[${index}]`).errors);
|
|
406
|
+
}
|
|
407
|
+
if (typeof record.refresh_plan_ok !== "boolean")
|
|
408
|
+
errors.push("refresh_plan_ok must be boolean");
|
|
409
|
+
if (record.refresh_plan_state !== "cache_hit" &&
|
|
410
|
+
record.refresh_plan_state !== "refresh_required" &&
|
|
411
|
+
record.refresh_plan_state !== "blocked" &&
|
|
412
|
+
record.refresh_plan_state !== "invalid")
|
|
413
|
+
errors.push("refresh_plan_state is invalid");
|
|
414
|
+
if (record.refresh_plan_cache_id !== undefined)
|
|
415
|
+
errors.push(...validateOpaqueId(record.refresh_plan_cache_id, "refresh_plan_cache_id").errors);
|
|
416
|
+
if (record.refresh_plan_state === "invalid" && record.refresh_plan_ok !== false)
|
|
417
|
+
errors.push("invalid refresh plan state must report refresh_plan_ok false");
|
|
418
|
+
if (record.refresh_plan_state !== "invalid" && record.refresh_plan_ok !== true)
|
|
419
|
+
errors.push("non-invalid refresh plan state must report refresh_plan_ok true");
|
|
420
|
+
if (record.state === "acquisition_not_needed" &&
|
|
421
|
+
((record.blocked_labels?.length ?? 0) > 0 || (record.acquisition_reason_labels?.length ?? 0) > 0))
|
|
422
|
+
errors.push("acquisition_not_needed plan cannot carry blockers or acquisition reasons");
|
|
423
|
+
if (record.state === "acquisition_not_needed" && record.refresh_plan_state !== "cache_hit")
|
|
424
|
+
errors.push("acquisition_not_needed plan requires cache_hit refresh plan state");
|
|
425
|
+
if (record.state === "acquisition_planned" && (record.acquisition_reason_labels?.length ?? 0) === 0)
|
|
426
|
+
errors.push("acquisition_planned plan requires acquisition reasons");
|
|
427
|
+
if (record.state === "acquisition_planned" && (record.blocked_labels?.length ?? 0) > 0)
|
|
428
|
+
errors.push("acquisition_planned plan cannot carry blockers");
|
|
429
|
+
if (record.state === "acquisition_planned" && record.refresh_plan_state !== "refresh_required")
|
|
430
|
+
errors.push("acquisition_planned plan requires refresh_required refresh plan state");
|
|
431
|
+
if (record.state === "blocked" && (record.blocked_labels?.length ?? 0) === 0)
|
|
432
|
+
errors.push("blocked acquisition plan requires blocked labels");
|
|
433
|
+
if (record.state === "blocked" && (record.acquisition_reason_labels?.length ?? 0) > 0)
|
|
434
|
+
errors.push("blocked acquisition plan cannot carry acquisition reasons");
|
|
435
|
+
if (record.state === "blocked" &&
|
|
436
|
+
record.refresh_plan_state !== "blocked" &&
|
|
437
|
+
record.refresh_plan_state !== "invalid")
|
|
438
|
+
errors.push("blocked acquisition plan requires blocked or invalid refresh plan state");
|
|
439
|
+
if (record.acquisition_required !== (record.state === "acquisition_planned"))
|
|
440
|
+
errors.push("acquisition_required must match acquisition_planned state");
|
|
441
|
+
if (record.cache_usable_for_assignment !== (record.state === "acquisition_not_needed"))
|
|
442
|
+
errors.push("cache_usable_for_assignment must match acquisition_not_needed state");
|
|
443
|
+
if (record.acquisition_attempted !== false ||
|
|
444
|
+
record.discovery_attempted !== false ||
|
|
445
|
+
record.refresh_attempted !== false ||
|
|
446
|
+
record.dispatch_authority_enabled !== false ||
|
|
447
|
+
record.providerCall !== false ||
|
|
448
|
+
record.actualLaneLaunch !== false ||
|
|
449
|
+
record.runtimeExecution !== false)
|
|
450
|
+
errors.push("availability cache acquisition plan cannot attempt discovery, refresh, acquisition, launch, provider call, or runtime authority");
|
|
451
|
+
errors.push(...validateNoForbiddenRawPayloads(record, "exact_model_availability_cache_acquisition_plan").errors);
|
|
452
|
+
return errors.length === 0 ? valid() : invalid(...errors);
|
|
453
|
+
}
|
|
454
|
+
export function recordFlowDeskExactModelAvailabilityCacheProviderAcquisitionResultV1(input) {
|
|
455
|
+
const planResult = validateFlowDeskExactModelAvailabilityCacheAcquisitionPlanV1(input.acquisitionPlan);
|
|
456
|
+
const inputErrors = [];
|
|
457
|
+
inputErrors.push(...validateOpaqueId(input.resultId, "result_id").errors);
|
|
458
|
+
inputErrors.push(...validateLocalDate(input.localDate, "local_date").errors);
|
|
459
|
+
inputErrors.push(...validateOpaqueRef(input.activeProfileRef, "active_profile_ref").errors);
|
|
460
|
+
inputErrors.push(...validateOpaqueRef(input.opencodeVersionRef, "opencode_version_ref").errors);
|
|
461
|
+
inputErrors.push(...validateOpaqueRef(input.flowdeskPackageVersionRef, "flowdesk_package_version_ref").errors);
|
|
462
|
+
inputErrors.push(...validateHash(input.registryHash, "registry_hash").errors);
|
|
463
|
+
inputErrors.push(...validateHash(input.policyPackHash, "policy_pack_hash").errors);
|
|
464
|
+
inputErrors.push(...validateOpaqueRef(input.authAccountBoundaryRef, "auth_account_boundary_ref").errors);
|
|
465
|
+
inputErrors.push(...validateProviderFamily(input.providerFamily).errors);
|
|
466
|
+
if (!FLOWDESK_EXACT_MODEL_PROVIDER_FAMILIES.includes(input.providerFamily))
|
|
467
|
+
inputErrors.push("provider_family is not exact-model eligible");
|
|
468
|
+
inputErrors.push(...validateOpaqueRef(input.providerIdentityRef, "provider_identity_ref").errors);
|
|
469
|
+
inputErrors.push(...validateConcreteProviderQualifiedModelId(input.providerQualifiedModelId).errors);
|
|
470
|
+
if (input.providerQualifiedModelId.split("/")[0] !== input.providerFamily)
|
|
471
|
+
inputErrors.push("provider_family must match provider_qualified_model_id");
|
|
472
|
+
inputErrors.push(...validateOpaqueId(input.modelFamily, "model_family").errors);
|
|
473
|
+
inputErrors.push(...validateOpaqueRef(input.availabilityRef, "availability_ref").errors);
|
|
474
|
+
inputErrors.push(...validateOpaqueRef(input.preCallAuditRef, "pre_call_audit_ref").errors);
|
|
475
|
+
inputErrors.push(...validateOpaqueRef(input.idempotencyRef, "idempotency_ref").errors);
|
|
476
|
+
inputErrors.push(...validateOpaqueRef(input.liveTestRunRef, "live_test_run_ref").errors);
|
|
477
|
+
inputErrors.push(...validateOpaqueRef(input.redactionProofRef, "redaction_proof_ref").errors);
|
|
478
|
+
if (input.sanitizedProviderResultRef !== undefined)
|
|
479
|
+
inputErrors.push(...validateOpaqueRef(input.sanitizedProviderResultRef, "sanitized_provider_result_ref").errors);
|
|
480
|
+
inputErrors.push(...validateTimestamp(input.observedAt, "observed_at").errors);
|
|
481
|
+
if (input.blockedLabels !== undefined)
|
|
482
|
+
for (const [index, label] of input.blockedLabels.entries())
|
|
483
|
+
inputErrors.push(...validateOpaqueId(label, `blocked_labels[${index}]`).errors);
|
|
484
|
+
const blockedLabels = [...(input.blockedLabels ?? [])];
|
|
485
|
+
let acquisitionPlanState = "invalid";
|
|
486
|
+
if (!planResult.ok)
|
|
487
|
+
blockedLabels.push("acquisition_plan_invalid");
|
|
488
|
+
else {
|
|
489
|
+
acquisitionPlanState = input.acquisitionPlan.state;
|
|
490
|
+
if (input.acquisitionPlan.state !== "acquisition_planned")
|
|
491
|
+
blockedLabels.push("acquisition_plan_not_planned");
|
|
492
|
+
}
|
|
493
|
+
if (inputErrors.length > 0)
|
|
494
|
+
blockedLabels.push("provider_acquisition_context_invalid");
|
|
495
|
+
if (input.outcome === "blocked" && blockedLabels.length === 0)
|
|
496
|
+
blockedLabels.push("provider_acquisition_blocked");
|
|
497
|
+
const planAllowsProviderCall = planResult.ok && input.acquisitionPlan.state === "acquisition_planned" && inputErrors.length === 0;
|
|
498
|
+
const acquisitionAttempted = planAllowsProviderCall && (input.outcome !== "blocked" || input.providerCall === true);
|
|
499
|
+
const providerCall = input.providerCall === false
|
|
500
|
+
? false
|
|
501
|
+
: acquisitionAttempted || (input.providerCall === true && planAllowsProviderCall);
|
|
502
|
+
const state = planAllowsProviderCall && input.outcome !== "blocked" ? "availability_acquired" : "blocked";
|
|
503
|
+
const available = state === "availability_acquired" && input.outcome === "available";
|
|
504
|
+
const record = {
|
|
505
|
+
schema_version: "flowdesk.exact_model_availability_cache_provider_acquisition_result.v1",
|
|
506
|
+
ok: planResult.ok && inputErrors.length === 0 && blockedLabels.length === 0,
|
|
507
|
+
errors: [...planResult.errors.map((error) => `acquisition_plan: ${error}`), ...inputErrors],
|
|
508
|
+
result_id: input.resultId,
|
|
509
|
+
state,
|
|
510
|
+
blocked_labels: unique(blockedLabels),
|
|
511
|
+
acquisition_plan_ok: planResult.ok,
|
|
512
|
+
acquisition_plan_state: acquisitionPlanState,
|
|
513
|
+
local_date: input.localDate,
|
|
514
|
+
active_profile_ref: input.activeProfileRef,
|
|
515
|
+
opencode_version_ref: input.opencodeVersionRef,
|
|
516
|
+
flowdesk_package_version_ref: input.flowdeskPackageVersionRef,
|
|
517
|
+
registry_hash: input.registryHash,
|
|
518
|
+
policy_pack_hash: input.policyPackHash,
|
|
519
|
+
auth_account_boundary_ref: input.authAccountBoundaryRef,
|
|
520
|
+
provider_family: input.providerFamily,
|
|
521
|
+
provider_identity_ref: input.providerIdentityRef,
|
|
522
|
+
provider_qualified_model_id: input.providerQualifiedModelId,
|
|
523
|
+
model_family: input.modelFamily,
|
|
524
|
+
availability_ref: input.availabilityRef,
|
|
525
|
+
pre_call_audit_ref: input.preCallAuditRef,
|
|
526
|
+
idempotency_ref: input.idempotencyRef,
|
|
527
|
+
live_test_run_ref: input.liveTestRunRef,
|
|
528
|
+
redaction_proof_ref: input.redactionProofRef,
|
|
529
|
+
sanitized_provider_result_ref: input.sanitizedProviderResultRef,
|
|
530
|
+
observed_at: input.observedAt,
|
|
531
|
+
available,
|
|
532
|
+
highest_tier_eligible: available && input.highestTierEligible === true,
|
|
533
|
+
acquisition_attempted: acquisitionAttempted,
|
|
534
|
+
discovery_attempted: acquisitionAttempted,
|
|
535
|
+
refresh_attempted: false,
|
|
536
|
+
dispatch_authority_enabled: false,
|
|
537
|
+
providerCall,
|
|
538
|
+
actualLaneLaunch: false,
|
|
539
|
+
runtimeExecution: false,
|
|
540
|
+
};
|
|
541
|
+
const validation = validateFlowDeskExactModelAvailabilityCacheProviderAcquisitionResultV1(record);
|
|
542
|
+
return validation.ok ? record : { ...record, ok: false, errors: unique([...record.errors, ...validation.errors]) };
|
|
543
|
+
}
|
|
544
|
+
export function validateFlowDeskExactModelAvailabilityCacheProviderAcquisitionResultV1(value) {
|
|
545
|
+
if (!isRecord(value))
|
|
546
|
+
return invalid("exact model availability cache provider acquisition result must be an object");
|
|
547
|
+
const record = value;
|
|
548
|
+
const errors = [];
|
|
549
|
+
errors.push(...rejectUnknownProperties(record, [
|
|
550
|
+
"schema_version",
|
|
551
|
+
"ok",
|
|
552
|
+
"errors",
|
|
553
|
+
"result_id",
|
|
554
|
+
"state",
|
|
555
|
+
"blocked_labels",
|
|
556
|
+
"acquisition_plan_ok",
|
|
557
|
+
"acquisition_plan_state",
|
|
558
|
+
"local_date",
|
|
559
|
+
"active_profile_ref",
|
|
560
|
+
"opencode_version_ref",
|
|
561
|
+
"flowdesk_package_version_ref",
|
|
562
|
+
"registry_hash",
|
|
563
|
+
"policy_pack_hash",
|
|
564
|
+
"auth_account_boundary_ref",
|
|
565
|
+
"provider_family",
|
|
566
|
+
"provider_identity_ref",
|
|
567
|
+
"provider_qualified_model_id",
|
|
568
|
+
"model_family",
|
|
569
|
+
"availability_ref",
|
|
570
|
+
"pre_call_audit_ref",
|
|
571
|
+
"idempotency_ref",
|
|
572
|
+
"live_test_run_ref",
|
|
573
|
+
"redaction_proof_ref",
|
|
574
|
+
"sanitized_provider_result_ref",
|
|
575
|
+
"observed_at",
|
|
576
|
+
"available",
|
|
577
|
+
"highest_tier_eligible",
|
|
578
|
+
"acquisition_attempted",
|
|
579
|
+
"discovery_attempted",
|
|
580
|
+
"refresh_attempted",
|
|
581
|
+
"dispatch_authority_enabled",
|
|
582
|
+
"providerCall",
|
|
583
|
+
"actualLaneLaunch",
|
|
584
|
+
"runtimeExecution",
|
|
585
|
+
], "availability cache provider acquisition result").errors);
|
|
586
|
+
if (record.schema_version !== "flowdesk.exact_model_availability_cache_provider_acquisition_result.v1")
|
|
587
|
+
errors.push("availability cache provider acquisition result schema_version is invalid");
|
|
588
|
+
if (record.state !== "availability_acquired" && record.state !== "blocked")
|
|
589
|
+
errors.push("availability cache provider acquisition result state is invalid");
|
|
590
|
+
if (!Array.isArray(record.blocked_labels))
|
|
591
|
+
errors.push("blocked_labels must be an array");
|
|
592
|
+
else
|
|
593
|
+
for (const [index, label] of record.blocked_labels.entries())
|
|
594
|
+
errors.push(...validateOpaqueId(label, `blocked_labels[${index}]`).errors);
|
|
595
|
+
if (typeof record.acquisition_plan_ok !== "boolean")
|
|
596
|
+
errors.push("acquisition_plan_ok must be boolean");
|
|
597
|
+
if (record.acquisition_plan_state !== "acquisition_not_needed" &&
|
|
598
|
+
record.acquisition_plan_state !== "acquisition_planned" &&
|
|
599
|
+
record.acquisition_plan_state !== "blocked" &&
|
|
600
|
+
record.acquisition_plan_state !== "invalid")
|
|
601
|
+
errors.push("acquisition_plan_state is invalid");
|
|
602
|
+
if (record.acquisition_plan_state === "invalid" && record.acquisition_plan_ok !== false)
|
|
603
|
+
errors.push("invalid acquisition plan state must report acquisition_plan_ok false");
|
|
604
|
+
if (record.acquisition_plan_state !== "invalid" && record.acquisition_plan_ok !== true)
|
|
605
|
+
errors.push("non-invalid acquisition plan state must report acquisition_plan_ok true");
|
|
606
|
+
for (const [value, label] of [
|
|
607
|
+
[record.result_id, "result_id"],
|
|
608
|
+
[record.model_family, "model_family"],
|
|
609
|
+
])
|
|
610
|
+
errors.push(...validateOpaqueId(value, label).errors);
|
|
611
|
+
errors.push(...validateLocalDate(record.local_date, "local_date").errors);
|
|
612
|
+
for (const [value, label] of [
|
|
613
|
+
[record.active_profile_ref, "active_profile_ref"],
|
|
614
|
+
[record.opencode_version_ref, "opencode_version_ref"],
|
|
615
|
+
[record.flowdesk_package_version_ref, "flowdesk_package_version_ref"],
|
|
616
|
+
[record.auth_account_boundary_ref, "auth_account_boundary_ref"],
|
|
617
|
+
[record.provider_identity_ref, "provider_identity_ref"],
|
|
618
|
+
[record.availability_ref, "availability_ref"],
|
|
619
|
+
[record.pre_call_audit_ref, "pre_call_audit_ref"],
|
|
620
|
+
[record.idempotency_ref, "idempotency_ref"],
|
|
621
|
+
[record.live_test_run_ref, "live_test_run_ref"],
|
|
622
|
+
[record.redaction_proof_ref, "redaction_proof_ref"],
|
|
623
|
+
[record.sanitized_provider_result_ref, "sanitized_provider_result_ref"],
|
|
624
|
+
])
|
|
625
|
+
if (value !== undefined)
|
|
626
|
+
errors.push(...validateOpaqueRef(value, label).errors);
|
|
627
|
+
errors.push(...validateHash(record.registry_hash, "registry_hash").errors);
|
|
628
|
+
errors.push(...validateHash(record.policy_pack_hash, "policy_pack_hash").errors);
|
|
629
|
+
errors.push(...validateProviderFamily(record.provider_family).errors);
|
|
630
|
+
if (typeof record.provider_family === "string" && !FLOWDESK_EXACT_MODEL_PROVIDER_FAMILIES.includes(record.provider_family))
|
|
631
|
+
errors.push("provider_family is not exact-model eligible");
|
|
632
|
+
errors.push(...validateConcreteProviderQualifiedModelId(record.provider_qualified_model_id).errors);
|
|
633
|
+
if (typeof record.provider_qualified_model_id === "string" && typeof record.provider_family === "string") {
|
|
634
|
+
const provider = record.provider_qualified_model_id.split("/")[0];
|
|
635
|
+
if (provider !== record.provider_family)
|
|
636
|
+
errors.push("provider_family must match provider_qualified_model_id");
|
|
637
|
+
}
|
|
638
|
+
errors.push(...validateTimestamp(record.observed_at, "observed_at").errors);
|
|
639
|
+
for (const key of ["available", "highest_tier_eligible", "acquisition_attempted", "discovery_attempted", "providerCall"])
|
|
640
|
+
if (typeof record[key] !== "boolean")
|
|
641
|
+
errors.push(`${key} must be boolean`);
|
|
642
|
+
if (record.state === "availability_acquired") {
|
|
643
|
+
if ((record.blocked_labels?.length ?? 0) > 0)
|
|
644
|
+
errors.push("availability_acquired result cannot carry blockers");
|
|
645
|
+
if (record.acquisition_plan_state !== "acquisition_planned")
|
|
646
|
+
errors.push("availability_acquired result requires acquisition_planned input");
|
|
647
|
+
if (record.acquisition_attempted !== true || record.discovery_attempted !== true)
|
|
648
|
+
errors.push("availability_acquired result must record provider acquisition attempt");
|
|
649
|
+
if (record.sanitized_provider_result_ref === undefined)
|
|
650
|
+
errors.push("availability_acquired result requires sanitized_provider_result_ref");
|
|
651
|
+
}
|
|
652
|
+
if (record.state === "blocked" && (record.blocked_labels?.length ?? 0) === 0)
|
|
653
|
+
errors.push("blocked provider acquisition result requires blocked labels");
|
|
654
|
+
if (record.providerCall === true && (record.acquisition_attempted !== true || record.discovery_attempted !== true))
|
|
655
|
+
errors.push("providerCall requires acquisition and discovery attempts");
|
|
656
|
+
if (record.highest_tier_eligible === true && record.available !== true)
|
|
657
|
+
errors.push("highest_tier_eligible requires available true");
|
|
658
|
+
if (record.refresh_attempted !== false ||
|
|
659
|
+
record.dispatch_authority_enabled !== false ||
|
|
660
|
+
record.actualLaneLaunch !== false ||
|
|
661
|
+
record.runtimeExecution !== false)
|
|
662
|
+
errors.push("provider acquisition result cannot refresh cache, launch lanes, authorize dispatch, or run runtime execution");
|
|
663
|
+
errors.push(...validateNoForbiddenRawPayloads(record, "exact_model_availability_cache_provider_acquisition_result").errors);
|
|
664
|
+
return errors.length === 0 ? valid() : invalid(...errors);
|
|
665
|
+
}
|
|
666
|
+
export function materializeFlowDeskExactModelAvailabilityCacheFromProviderAcquisitionResultV1(input) {
|
|
667
|
+
const errors = [];
|
|
668
|
+
const blockedLabels = [];
|
|
669
|
+
const validation = validateFlowDeskExactModelAvailabilityCacheProviderAcquisitionResultV1(input.providerAcquisitionResult);
|
|
670
|
+
if (!validation.ok) {
|
|
671
|
+
errors.push(...validation.errors.map((error) => `provider_acquisition_result: ${error}`));
|
|
672
|
+
blockedLabels.push("provider_acquisition_result_invalid");
|
|
673
|
+
}
|
|
674
|
+
const record = validation.ok
|
|
675
|
+
? input.providerAcquisitionResult
|
|
676
|
+
: undefined;
|
|
677
|
+
if (record !== undefined) {
|
|
678
|
+
if (record.ok !== true)
|
|
679
|
+
blockedLabels.push("provider_acquisition_result_not_ok");
|
|
680
|
+
if (record.state !== "availability_acquired")
|
|
681
|
+
blockedLabels.push("provider_acquisition_not_acquired");
|
|
682
|
+
if (record.available !== true)
|
|
683
|
+
blockedLabels.push("provider_model_unavailable");
|
|
684
|
+
if (record.highest_tier_eligible !== true)
|
|
685
|
+
blockedLabels.push("provider_model_not_highest_tier_eligible");
|
|
686
|
+
if (record.providerCall !== true)
|
|
687
|
+
blockedLabels.push("provider_acquisition_metadata_only");
|
|
688
|
+
if (record.acquisition_attempted !== true)
|
|
689
|
+
blockedLabels.push("provider_acquisition_not_attempted");
|
|
690
|
+
if (record.discovery_attempted !== true)
|
|
691
|
+
blockedLabels.push("provider_discovery_not_attempted");
|
|
692
|
+
if (record.sanitized_provider_result_ref === undefined)
|
|
693
|
+
blockedLabels.push("sanitized_provider_result_ref_missing");
|
|
694
|
+
for (const [actual, expected, label] of [
|
|
695
|
+
[record.local_date, input.expectedContext?.localDate, "local_date"],
|
|
696
|
+
[record.active_profile_ref, input.expectedContext?.activeProfileRef, "active_profile_ref"],
|
|
697
|
+
[record.opencode_version_ref, input.expectedContext?.opencodeVersionRef, "opencode_version_ref"],
|
|
698
|
+
[record.flowdesk_package_version_ref, input.expectedContext?.flowdeskPackageVersionRef, "flowdesk_package_version_ref"],
|
|
699
|
+
[record.registry_hash, input.expectedContext?.registryHash, "registry_hash"],
|
|
700
|
+
[record.policy_pack_hash, input.expectedContext?.policyPackHash, "policy_pack_hash"],
|
|
701
|
+
[record.auth_account_boundary_ref, input.expectedContext?.authAccountBoundaryRef, "auth_account_boundary_ref"],
|
|
702
|
+
])
|
|
703
|
+
if (expected !== undefined && actual !== expected)
|
|
704
|
+
blockedLabels.push(`${label}_drift`);
|
|
705
|
+
}
|
|
706
|
+
const cacheId = input.cacheId ?? (record === undefined ? undefined : `cache-${record.result_id}`);
|
|
707
|
+
const entryId = input.entryId ?? (record === undefined ? undefined : `entry-${record.result_id}`);
|
|
708
|
+
if (record !== undefined && cacheId !== undefined)
|
|
709
|
+
errors.push(...validateOpaqueId(cacheId, "cache_id").errors);
|
|
710
|
+
if (record !== undefined && entryId !== undefined)
|
|
711
|
+
errors.push(...validateOpaqueId(entryId, "entry_id").errors);
|
|
712
|
+
if (errors.length > 0 && !blockedLabels.includes("provider_acquisition_result_invalid"))
|
|
713
|
+
blockedLabels.push("cache_materialization_context_invalid");
|
|
714
|
+
const canMaterialize = record !== undefined && errors.length === 0 && blockedLabels.length === 0 && cacheId !== undefined && entryId !== undefined;
|
|
715
|
+
const cache = canMaterialize
|
|
716
|
+
? {
|
|
717
|
+
schema_version: "flowdesk.exact_model_availability_cache.v1",
|
|
718
|
+
cache_id: cacheId,
|
|
719
|
+
local_date: record.local_date,
|
|
720
|
+
active_profile_ref: record.active_profile_ref,
|
|
721
|
+
opencode_version_ref: record.opencode_version_ref,
|
|
722
|
+
flowdesk_package_version_ref: record.flowdesk_package_version_ref,
|
|
723
|
+
registry_hash: record.registry_hash,
|
|
724
|
+
policy_pack_hash: record.policy_pack_hash,
|
|
725
|
+
auth_account_boundary_ref: record.auth_account_boundary_ref,
|
|
726
|
+
entries: [{
|
|
727
|
+
entry_id: entryId,
|
|
728
|
+
provider_family: record.provider_family,
|
|
729
|
+
provider_identity_ref: record.provider_identity_ref,
|
|
730
|
+
provider_qualified_model_id: record.provider_qualified_model_id,
|
|
731
|
+
model_family: record.model_family,
|
|
732
|
+
registered: true,
|
|
733
|
+
available: true,
|
|
734
|
+
highest_tier_eligible: true,
|
|
735
|
+
availability_ref: record.availability_ref,
|
|
736
|
+
}],
|
|
737
|
+
dispatch_authority_enabled: false,
|
|
738
|
+
providerCall: false,
|
|
739
|
+
actualLaneLaunch: false,
|
|
740
|
+
runtimeExecution: false,
|
|
741
|
+
}
|
|
742
|
+
: undefined;
|
|
743
|
+
if (cache !== undefined) {
|
|
744
|
+
const cacheValidation = validateFlowDeskExactModelAvailabilityCacheV1(cache);
|
|
745
|
+
if (!cacheValidation.ok) {
|
|
746
|
+
errors.push(...cacheValidation.errors.map((error) => `materialized_cache: ${error}`));
|
|
747
|
+
blockedLabels.push("materialized_cache_invalid");
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
const ready = cache !== undefined && errors.length === 0 && blockedLabels.length === 0;
|
|
751
|
+
return {
|
|
752
|
+
ok: ready,
|
|
753
|
+
errors,
|
|
754
|
+
state: ready ? "cache_materialized" : "blocked",
|
|
755
|
+
blocked_labels: unique(blockedLabels),
|
|
756
|
+
...(ready ? { cache } : {}),
|
|
757
|
+
...disabledReviewerRuntimeAuthority,
|
|
758
|
+
};
|
|
759
|
+
}
|
|
760
|
+
export function revalidateFlowDeskReviewerAssignmentsV1(input) {
|
|
761
|
+
const cacheResult = validateFlowDeskExactModelAvailabilityCacheV1(input.cache);
|
|
762
|
+
const errors = [...cacheResult.errors];
|
|
763
|
+
const blockedLabels = [];
|
|
764
|
+
if (!cacheResult.ok)
|
|
765
|
+
blockedLabels.push("cache_invalid");
|
|
766
|
+
errors.push(...validateLocalDate(input.localDate, "expected_local_date").errors);
|
|
767
|
+
errors.push(...validateOpaqueRef(input.activeProfileRef, "expected_active_profile_ref").errors);
|
|
768
|
+
errors.push(...validateOpaqueRef(input.opencodeVersionRef, "expected_opencode_version_ref").errors);
|
|
769
|
+
errors.push(...validateOpaqueRef(input.flowdeskPackageVersionRef, "expected_flowdesk_package_version_ref").errors);
|
|
770
|
+
errors.push(...validateHash(input.registryHash, "expected_registry_hash").errors);
|
|
771
|
+
errors.push(...validateHash(input.policyPackHash, "expected_policy_pack_hash").errors);
|
|
772
|
+
errors.push(...validateOpaqueRef(input.authAccountBoundaryRef, "expected_auth_account_boundary_ref").errors);
|
|
773
|
+
if (input.cache.local_date !== input.localDate)
|
|
774
|
+
blockedLabels.push("cache_not_same_day");
|
|
775
|
+
if (input.cache.active_profile_ref !== input.activeProfileRef)
|
|
776
|
+
blockedLabels.push("active_profile_drift");
|
|
777
|
+
if (input.cache.opencode_version_ref !== input.opencodeVersionRef)
|
|
778
|
+
blockedLabels.push("opencode_version_drift");
|
|
779
|
+
if (input.cache.flowdesk_package_version_ref !== input.flowdeskPackageVersionRef)
|
|
780
|
+
blockedLabels.push("flowdesk_package_version_drift");
|
|
781
|
+
if (input.cache.registry_hash !== input.registryHash)
|
|
782
|
+
blockedLabels.push("registry_hash_drift");
|
|
783
|
+
if (input.cache.policy_pack_hash !== input.policyPackHash)
|
|
784
|
+
blockedLabels.push("policy_pack_hash_drift");
|
|
785
|
+
if (input.cache.auth_account_boundary_ref !== input.authAccountBoundaryRef)
|
|
786
|
+
blockedLabels.push("auth_account_boundary_drift");
|
|
787
|
+
const eligible = cacheResult.ok ? eligibleEntries(input.cache) : [];
|
|
788
|
+
if (cacheResult.ok && eligible.length === 0) {
|
|
789
|
+
const registeredAvailable = input.cache.entries.some((entry) => entry.registered && entry.available);
|
|
790
|
+
blockedLabels.push(registeredAvailable ? "registered_available_lower_tier_only" : "no_registered_available_highest_tier_models");
|
|
791
|
+
}
|
|
792
|
+
const ready = errors.length === 0 && blockedLabels.length === 0;
|
|
793
|
+
return {
|
|
794
|
+
schema_version: "flowdesk.reviewer_assignment_revalidation.v1",
|
|
795
|
+
ok: errors.length === 0,
|
|
796
|
+
errors,
|
|
797
|
+
cache_id: input.cache.cache_id,
|
|
798
|
+
state: ready ? "revalidated" : "blocked",
|
|
799
|
+
blocked_labels: unique(blockedLabels),
|
|
800
|
+
expected_local_date: input.localDate,
|
|
801
|
+
expected_active_profile_ref: input.activeProfileRef,
|
|
802
|
+
expected_opencode_version_ref: input.opencodeVersionRef,
|
|
803
|
+
expected_flowdesk_package_version_ref: input.flowdeskPackageVersionRef,
|
|
804
|
+
expected_registry_hash: input.registryHash,
|
|
805
|
+
expected_policy_pack_hash: input.policyPackHash,
|
|
806
|
+
expected_auth_account_boundary_ref: input.authAccountBoundaryRef,
|
|
807
|
+
cache_local_date: input.cache.local_date,
|
|
808
|
+
cache_active_profile_ref: input.cache.active_profile_ref,
|
|
809
|
+
cache_opencode_version_ref: input.cache.opencode_version_ref,
|
|
810
|
+
cache_flowdesk_package_version_ref: input.cache.flowdesk_package_version_ref,
|
|
811
|
+
cache_registry_hash: input.cache.registry_hash,
|
|
812
|
+
cache_policy_pack_hash: input.cache.policy_pack_hash,
|
|
813
|
+
cache_auth_account_boundary_ref: input.cache.auth_account_boundary_ref,
|
|
814
|
+
eligible_bindings: ready
|
|
815
|
+
? eligible.map((entry) => ({
|
|
816
|
+
entry_id: entry.entry_id,
|
|
817
|
+
provider_qualified_model_id: entry.provider_qualified_model_id,
|
|
818
|
+
}))
|
|
819
|
+
: [],
|
|
820
|
+
...disabledReviewerRuntimeAuthority,
|
|
821
|
+
};
|
|
822
|
+
}
|
|
823
|
+
export function revalidateFlowDeskReviewerAssignmentsFromCacheEvidenceV1(input) {
|
|
824
|
+
const base = revalidateFlowDeskReviewerAssignmentsV1(input);
|
|
825
|
+
const refreshResult = validateFlowDeskExactModelAvailabilityCacheRefreshPlanV1(input.cacheRefreshPlan);
|
|
826
|
+
const evidenceBlockedLabels = [];
|
|
827
|
+
if (!refreshResult.ok)
|
|
828
|
+
evidenceBlockedLabels.push("cache_refresh_plan_invalid");
|
|
829
|
+
if (input.cacheRefreshPlan.state !== "cache_hit")
|
|
830
|
+
evidenceBlockedLabels.push("cache_refresh_not_cache_hit");
|
|
831
|
+
if (input.cacheRefreshPlan.cache_usable_for_assignment !== true)
|
|
832
|
+
evidenceBlockedLabels.push("cache_refresh_not_usable_for_assignment");
|
|
833
|
+
if (input.cacheRefreshPlan.cache_id !== input.cache.cache_id)
|
|
834
|
+
evidenceBlockedLabels.push("cache_refresh_cache_id_mismatch");
|
|
835
|
+
for (const [planValue, expectedValue] of [
|
|
836
|
+
[input.cacheRefreshPlan.expected_local_date, input.localDate],
|
|
837
|
+
[input.cacheRefreshPlan.expected_active_profile_ref, input.activeProfileRef],
|
|
838
|
+
[input.cacheRefreshPlan.expected_opencode_version_ref, input.opencodeVersionRef],
|
|
839
|
+
[input.cacheRefreshPlan.expected_flowdesk_package_version_ref, input.flowdeskPackageVersionRef],
|
|
840
|
+
[input.cacheRefreshPlan.expected_registry_hash, input.registryHash],
|
|
841
|
+
[input.cacheRefreshPlan.expected_policy_pack_hash, input.policyPackHash],
|
|
842
|
+
[input.cacheRefreshPlan.expected_auth_account_boundary_ref, input.authAccountBoundaryRef],
|
|
843
|
+
])
|
|
844
|
+
if (planValue !== expectedValue)
|
|
845
|
+
evidenceBlockedLabels.push("cache_refresh_expected_context_drift");
|
|
846
|
+
for (const [planValue, cacheValue] of [
|
|
847
|
+
[input.cacheRefreshPlan.cache_local_date, input.cache.local_date],
|
|
848
|
+
[input.cacheRefreshPlan.cache_active_profile_ref, input.cache.active_profile_ref],
|
|
849
|
+
[input.cacheRefreshPlan.cache_opencode_version_ref, input.cache.opencode_version_ref],
|
|
850
|
+
[input.cacheRefreshPlan.cache_flowdesk_package_version_ref, input.cache.flowdesk_package_version_ref],
|
|
851
|
+
[input.cacheRefreshPlan.cache_registry_hash, input.cache.registry_hash],
|
|
852
|
+
[input.cacheRefreshPlan.cache_policy_pack_hash, input.cache.policy_pack_hash],
|
|
853
|
+
[input.cacheRefreshPlan.cache_auth_account_boundary_ref, input.cache.auth_account_boundary_ref],
|
|
854
|
+
])
|
|
855
|
+
if (planValue !== cacheValue)
|
|
856
|
+
evidenceBlockedLabels.push("cache_refresh_cache_context_drift");
|
|
857
|
+
const ready = base.state === "revalidated" && evidenceBlockedLabels.length === 0;
|
|
858
|
+
const errors = [...base.errors, ...refreshResult.errors.map((error) => `cache_refresh_plan: ${error}`)];
|
|
859
|
+
return {
|
|
860
|
+
...base,
|
|
861
|
+
ok: base.ok && refreshResult.ok,
|
|
862
|
+
errors,
|
|
863
|
+
state: ready ? "revalidated" : "blocked",
|
|
864
|
+
blocked_labels: unique([...base.blocked_labels, ...evidenceBlockedLabels]),
|
|
865
|
+
eligible_bindings: ready ? base.eligible_bindings : [],
|
|
866
|
+
};
|
|
867
|
+
}
|
|
868
|
+
export function validateFlowDeskReviewerAssignmentRevalidationV1(value) {
|
|
869
|
+
if (!isRecord(value))
|
|
870
|
+
return invalid("reviewer assignment revalidation must be an object");
|
|
871
|
+
const record = value;
|
|
872
|
+
const errors = [];
|
|
873
|
+
errors.push(...rejectUnknownProperties(record, [
|
|
874
|
+
"schema_version",
|
|
875
|
+
"ok",
|
|
876
|
+
"errors",
|
|
877
|
+
"cache_id",
|
|
878
|
+
"state",
|
|
879
|
+
"blocked_labels",
|
|
880
|
+
"expected_local_date",
|
|
881
|
+
"expected_active_profile_ref",
|
|
882
|
+
"expected_opencode_version_ref",
|
|
883
|
+
"expected_flowdesk_package_version_ref",
|
|
884
|
+
"expected_registry_hash",
|
|
885
|
+
"expected_policy_pack_hash",
|
|
886
|
+
"expected_auth_account_boundary_ref",
|
|
887
|
+
"cache_local_date",
|
|
888
|
+
"cache_active_profile_ref",
|
|
889
|
+
"cache_opencode_version_ref",
|
|
890
|
+
"cache_flowdesk_package_version_ref",
|
|
891
|
+
"cache_registry_hash",
|
|
892
|
+
"cache_policy_pack_hash",
|
|
893
|
+
"cache_auth_account_boundary_ref",
|
|
894
|
+
"eligible_bindings",
|
|
895
|
+
"dispatch_authority_enabled",
|
|
896
|
+
"providerCall",
|
|
897
|
+
"actualLaneLaunch",
|
|
898
|
+
"runtimeExecution",
|
|
899
|
+
], "reviewer assignment revalidation").errors);
|
|
900
|
+
if (record.schema_version !== "flowdesk.reviewer_assignment_revalidation.v1")
|
|
901
|
+
errors.push("reviewer assignment revalidation schema_version is invalid");
|
|
902
|
+
if (record.cache_id !== undefined)
|
|
903
|
+
errors.push(...validateOpaqueId(record.cache_id, "cache_id").errors);
|
|
904
|
+
if (record.state !== "revalidated" && record.state !== "blocked")
|
|
905
|
+
errors.push("reviewer assignment revalidation state is invalid");
|
|
906
|
+
if (!Array.isArray(record.blocked_labels))
|
|
907
|
+
errors.push("blocked_labels must be an array");
|
|
908
|
+
else
|
|
909
|
+
for (const [index, label] of record.blocked_labels.entries())
|
|
910
|
+
errors.push(...validateOpaqueId(label, `blocked_labels[${index}]`).errors);
|
|
911
|
+
if (record.state === "revalidated" && (record.blocked_labels?.length ?? 0) > 0)
|
|
912
|
+
errors.push("revalidated assignment cannot carry blocked labels");
|
|
913
|
+
if (record.state === "blocked" && (record.blocked_labels?.length ?? 0) === 0)
|
|
914
|
+
errors.push("blocked revalidation requires blocked labels");
|
|
915
|
+
errors.push(...validateLocalDate(record.expected_local_date, "expected_local_date").errors);
|
|
916
|
+
for (const [field, label] of [
|
|
917
|
+
[record.expected_active_profile_ref, "expected_active_profile_ref"],
|
|
918
|
+
[record.expected_opencode_version_ref, "expected_opencode_version_ref"],
|
|
919
|
+
[record.expected_flowdesk_package_version_ref, "expected_flowdesk_package_version_ref"],
|
|
920
|
+
[record.expected_auth_account_boundary_ref, "expected_auth_account_boundary_ref"],
|
|
921
|
+
[record.cache_active_profile_ref, "cache_active_profile_ref"],
|
|
922
|
+
[record.cache_opencode_version_ref, "cache_opencode_version_ref"],
|
|
923
|
+
[record.cache_flowdesk_package_version_ref, "cache_flowdesk_package_version_ref"],
|
|
924
|
+
[record.cache_auth_account_boundary_ref, "cache_auth_account_boundary_ref"],
|
|
925
|
+
])
|
|
926
|
+
if (field !== undefined)
|
|
927
|
+
errors.push(...validateOpaqueRef(field, label).errors);
|
|
928
|
+
errors.push(...validateHash(record.expected_registry_hash, "expected_registry_hash").errors);
|
|
929
|
+
errors.push(...validateHash(record.expected_policy_pack_hash, "expected_policy_pack_hash").errors);
|
|
930
|
+
if (record.cache_local_date !== undefined)
|
|
931
|
+
errors.push(...validateLocalDate(record.cache_local_date, "cache_local_date").errors);
|
|
932
|
+
if (record.cache_registry_hash !== undefined)
|
|
933
|
+
errors.push(...validateHash(record.cache_registry_hash, "cache_registry_hash").errors);
|
|
934
|
+
if (record.cache_policy_pack_hash !== undefined)
|
|
935
|
+
errors.push(...validateHash(record.cache_policy_pack_hash, "cache_policy_pack_hash").errors);
|
|
936
|
+
if (!Array.isArray(record.eligible_bindings))
|
|
937
|
+
errors.push("eligible_bindings must be an array");
|
|
938
|
+
else {
|
|
939
|
+
if (record.state === "revalidated" && record.eligible_bindings.length === 0)
|
|
940
|
+
errors.push("revalidated assignment requires eligible bindings");
|
|
941
|
+
for (const [index, binding] of record.eligible_bindings.entries()) {
|
|
942
|
+
if (!isRecord(binding)) {
|
|
943
|
+
errors.push(`eligible_bindings[${index}] must be an object`);
|
|
944
|
+
continue;
|
|
945
|
+
}
|
|
946
|
+
errors.push(...rejectUnknownProperties(binding, ["entry_id", "provider_qualified_model_id"], `eligible_bindings[${index}]`).errors);
|
|
947
|
+
errors.push(...validateOpaqueId(binding.entry_id, `eligible_bindings[${index}].entry_id`).errors);
|
|
948
|
+
errors.push(...validateConcreteProviderQualifiedModelId(binding.provider_qualified_model_id, `eligible_bindings[${index}].provider_qualified_model_id`).errors);
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
if (record.dispatch_authority_enabled !== false ||
|
|
952
|
+
record.providerCall !== false ||
|
|
953
|
+
record.actualLaneLaunch !== false ||
|
|
954
|
+
record.runtimeExecution !== false)
|
|
955
|
+
errors.push("reviewer assignment revalidation cannot enable runtime authority");
|
|
956
|
+
errors.push(...validateNoForbiddenRawPayloads(record, "reviewer_assignment_revalidation").errors);
|
|
957
|
+
return errors.length === 0 ? valid() : invalid(...errors);
|
|
958
|
+
}
|
|
959
|
+
export function planFlowDeskReviewerFanoutV1(input) {
|
|
960
|
+
const errors = validateFlowDeskReviewerAssignmentRevalidationV1(input.revalidation).errors;
|
|
961
|
+
const blockedLabels = [];
|
|
962
|
+
if (errors.length > 0 || input.revalidation.state !== "revalidated")
|
|
963
|
+
blockedLabels.push("assignment_revalidation_blocked", ...input.revalidation.blocked_labels);
|
|
964
|
+
errors.push(...validateOpaqueId(input.workflowId, "workflow_id").errors);
|
|
965
|
+
errors.push(...validateOpaqueId(input.attemptId, "attempt_id").errors);
|
|
966
|
+
errors.push(...validateOpaqueRef(input.parentSessionRef, "parent_session_ref").errors);
|
|
967
|
+
errors.push(...validateOpaqueRef(input.agentRef, "agent_ref").errors);
|
|
968
|
+
if (typeof input.requestedAt !== "string" || !Number.isFinite(Date.parse(input.requestedAt)))
|
|
969
|
+
errors.push("requested_at must be a parseable timestamp");
|
|
970
|
+
const requiredPerspectives = input.requestedPerspectives ?? perspectives;
|
|
971
|
+
errors.push(...validatePerspectiveArray(requiredPerspectives, "required_perspectives").errors);
|
|
972
|
+
const maxConcurrentLaneCount = input.maxConcurrentLaneCount ?? Math.min(3, requiredPerspectives.length);
|
|
973
|
+
if (!Number.isInteger(maxConcurrentLaneCount) || maxConcurrentLaneCount < 1 || maxConcurrentLaneCount > requiredPerspectives.length)
|
|
974
|
+
errors.push("max_concurrent_lane_count must be a bounded positive integer");
|
|
975
|
+
const timeoutMs = input.timeoutMs ?? 30000;
|
|
976
|
+
const orphanMaxAgeMs = input.orphanMaxAgeMs ?? 60000;
|
|
977
|
+
const retryBudget = input.retryBudget ?? 1;
|
|
978
|
+
if (!Number.isInteger(timeoutMs) || timeoutMs < 0 || timeoutMs > 600000)
|
|
979
|
+
errors.push("timeout_ms must be bounded");
|
|
980
|
+
if (!Number.isInteger(orphanMaxAgeMs) || orphanMaxAgeMs < 0 || orphanMaxAgeMs > 3600000)
|
|
981
|
+
errors.push("orphan_max_age_ms must be bounded");
|
|
982
|
+
if (!Number.isInteger(retryBudget) || retryBudget < 0 || retryBudget > 2)
|
|
983
|
+
errors.push("retry_budget must be bounded");
|
|
984
|
+
const ready = errors.length === 0 && blockedLabels.length === 0;
|
|
985
|
+
const bindings = input.revalidation.eligible_bindings;
|
|
986
|
+
const runtimeRequests = ready
|
|
987
|
+
? requiredPerspectives.map((perspective, index) => {
|
|
988
|
+
const binding = bindings[index % bindings.length];
|
|
989
|
+
return {
|
|
990
|
+
schema_version: "flowdesk.runtime_lane_launch_request.v1",
|
|
991
|
+
launch_request_id: `reviewer-launch-${input.attemptId}-${perspective}`,
|
|
992
|
+
workflow_id: input.workflowId,
|
|
993
|
+
attempt_id: input.attemptId,
|
|
994
|
+
lane_id: `reviewer-lane-${input.attemptId}-${perspective}`,
|
|
995
|
+
parent_session_ref: input.parentSessionRef,
|
|
996
|
+
agent_ref: input.agentRef,
|
|
997
|
+
provider_qualified_model_id: binding.provider_qualified_model_id,
|
|
998
|
+
launch_reason: "reviewer_fanout",
|
|
999
|
+
pre_launch_audit_ref: input.preLaunchAuditRef,
|
|
1000
|
+
lane_launch_approval_ref: input.laneLaunchApprovalRef,
|
|
1001
|
+
requested_at: input.requestedAt,
|
|
1002
|
+
timeout_ms: timeoutMs,
|
|
1003
|
+
orphan_max_age_ms: orphanMaxAgeMs,
|
|
1004
|
+
retry_budget: retryBudget,
|
|
1005
|
+
...disabledReviewerRuntimeAuthority,
|
|
1006
|
+
};
|
|
1007
|
+
})
|
|
1008
|
+
: [];
|
|
1009
|
+
for (const [index, request] of runtimeRequests.entries())
|
|
1010
|
+
errors.push(...validateFlowDeskRuntimeLaneLaunchRequestV1(request).errors.map((error) => `runtime_lane_launch_requests[${index}].${error}`));
|
|
1011
|
+
const finalReady = errors.length === 0 && blockedLabels.length === 0 && runtimeRequests.length === requiredPerspectives.length;
|
|
1012
|
+
return {
|
|
1013
|
+
schema_version: "flowdesk.reviewer_fanout_plan.v1",
|
|
1014
|
+
ok: errors.length === 0,
|
|
1015
|
+
errors,
|
|
1016
|
+
workflow_id: input.workflowId,
|
|
1017
|
+
attempt_id: input.attemptId,
|
|
1018
|
+
cache_id: input.revalidation.cache_id,
|
|
1019
|
+
state: finalReady ? "fanout_ready" : "blocked",
|
|
1020
|
+
blocked_labels: unique(blockedLabels),
|
|
1021
|
+
required_perspectives: [...requiredPerspectives],
|
|
1022
|
+
planned_perspectives: finalReady ? [...requiredPerspectives] : [],
|
|
1023
|
+
runtime_lane_launch_requests: runtimeRequests,
|
|
1024
|
+
max_concurrent_lane_count: maxConcurrentLaneCount,
|
|
1025
|
+
runtime_launch_plan_required: true,
|
|
1026
|
+
lane_launch_approval_required: true,
|
|
1027
|
+
launch_attempted: false,
|
|
1028
|
+
approval_inferred: false,
|
|
1029
|
+
...disabledReviewerRuntimeAuthority,
|
|
1030
|
+
};
|
|
1031
|
+
}
|
|
1032
|
+
export function validateFlowDeskReviewerFanoutPlanV1(value) {
|
|
1033
|
+
if (!isRecord(value))
|
|
1034
|
+
return invalid("reviewer fanout plan must be an object");
|
|
1035
|
+
const record = value;
|
|
1036
|
+
const errors = [];
|
|
1037
|
+
errors.push(...rejectUnknownProperties(record, [
|
|
1038
|
+
"schema_version",
|
|
1039
|
+
"ok",
|
|
1040
|
+
"errors",
|
|
1041
|
+
"workflow_id",
|
|
1042
|
+
"attempt_id",
|
|
1043
|
+
"cache_id",
|
|
1044
|
+
"state",
|
|
1045
|
+
"blocked_labels",
|
|
1046
|
+
"required_perspectives",
|
|
1047
|
+
"planned_perspectives",
|
|
1048
|
+
"runtime_lane_launch_requests",
|
|
1049
|
+
"max_concurrent_lane_count",
|
|
1050
|
+
"runtime_launch_plan_required",
|
|
1051
|
+
"lane_launch_approval_required",
|
|
1052
|
+
"launch_attempted",
|
|
1053
|
+
"approval_inferred",
|
|
1054
|
+
"dispatch_authority_enabled",
|
|
1055
|
+
"providerCall",
|
|
1056
|
+
"actualLaneLaunch",
|
|
1057
|
+
"runtimeExecution",
|
|
1058
|
+
], "reviewer fanout plan").errors);
|
|
1059
|
+
if (record.schema_version !== "flowdesk.reviewer_fanout_plan.v1")
|
|
1060
|
+
errors.push("reviewer fanout plan schema_version is invalid");
|
|
1061
|
+
errors.push(...validateOpaqueId(record.workflow_id, "workflow_id").errors);
|
|
1062
|
+
errors.push(...validateOpaqueId(record.attempt_id, "attempt_id").errors);
|
|
1063
|
+
if (record.cache_id !== undefined)
|
|
1064
|
+
errors.push(...validateOpaqueId(record.cache_id, "cache_id").errors);
|
|
1065
|
+
if (record.state !== "fanout_ready" && record.state !== "blocked")
|
|
1066
|
+
errors.push("reviewer fanout plan state is invalid");
|
|
1067
|
+
if (!Array.isArray(record.blocked_labels))
|
|
1068
|
+
errors.push("blocked_labels must be an array");
|
|
1069
|
+
else
|
|
1070
|
+
for (const [index, label] of record.blocked_labels.entries())
|
|
1071
|
+
errors.push(...validateOpaqueId(label, `blocked_labels[${index}]`).errors);
|
|
1072
|
+
if (record.state === "fanout_ready" && (record.blocked_labels?.length ?? 0) > 0)
|
|
1073
|
+
errors.push("fanout_ready plan cannot carry blocked labels");
|
|
1074
|
+
if (record.state === "blocked" && (record.blocked_labels?.length ?? 0) === 0)
|
|
1075
|
+
errors.push("blocked fanout plan requires blocked labels");
|
|
1076
|
+
errors.push(...validatePerspectiveArray(record.required_perspectives, "required_perspectives").errors);
|
|
1077
|
+
if (!Array.isArray(record.planned_perspectives))
|
|
1078
|
+
errors.push("planned_perspectives must be an array");
|
|
1079
|
+
else if (record.state === "fanout_ready" || record.planned_perspectives.length > 0)
|
|
1080
|
+
errors.push(...validatePerspectiveArray(record.planned_perspectives, "planned_perspectives").errors);
|
|
1081
|
+
if (record.state === "fanout_ready") {
|
|
1082
|
+
const required = record.required_perspectives ?? [];
|
|
1083
|
+
const planned = record.planned_perspectives ?? [];
|
|
1084
|
+
if (required.length !== planned.length || required.some((perspective) => !planned.includes(perspective)))
|
|
1085
|
+
errors.push("fanout_ready plan must cover every required perspective");
|
|
1086
|
+
}
|
|
1087
|
+
if (!Array.isArray(record.runtime_lane_launch_requests))
|
|
1088
|
+
errors.push("runtime_lane_launch_requests must be an array");
|
|
1089
|
+
else {
|
|
1090
|
+
if (record.state === "fanout_ready" && record.runtime_lane_launch_requests.length !== (record.required_perspectives?.length ?? 0))
|
|
1091
|
+
errors.push("fanout_ready plan requires one launch request per perspective");
|
|
1092
|
+
for (const [index, request] of record.runtime_lane_launch_requests.entries())
|
|
1093
|
+
errors.push(...validateFlowDeskRuntimeLaneLaunchRequestV1(request).errors.map((error) => `runtime_lane_launch_requests[${index}].${error}`));
|
|
1094
|
+
}
|
|
1095
|
+
if (typeof record.max_concurrent_lane_count !== "number" || !Number.isInteger(record.max_concurrent_lane_count) || record.max_concurrent_lane_count < 1)
|
|
1096
|
+
errors.push("max_concurrent_lane_count must be a positive integer");
|
|
1097
|
+
if (record.runtime_launch_plan_required !== true || record.lane_launch_approval_required !== true)
|
|
1098
|
+
errors.push("reviewer fanout plan must require runtime launch plan and lane launch approval");
|
|
1099
|
+
if (record.launch_attempted !== false ||
|
|
1100
|
+
record.approval_inferred !== false ||
|
|
1101
|
+
record.dispatch_authority_enabled !== false ||
|
|
1102
|
+
record.providerCall !== false ||
|
|
1103
|
+
record.actualLaneLaunch !== false ||
|
|
1104
|
+
record.runtimeExecution !== false)
|
|
1105
|
+
errors.push("reviewer fanout plan cannot launch lanes, infer approval, or enable authority");
|
|
1106
|
+
errors.push(...validateNoForbiddenRawPayloads(record, "reviewer_fanout_plan").errors);
|
|
1107
|
+
return errors.length === 0 ? valid() : invalid(...errors);
|
|
1108
|
+
}
|
|
1109
|
+
//# sourceMappingURL=model-availability-cache.js.map
|