@flowdesk/opencode-plugin 0.1.1 → 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 (41) hide show
  1. package/dist/command-handlers.d.ts +5 -1
  2. package/dist/command-handlers.d.ts.map +1 -1
  3. package/dist/command-handlers.js +74 -1
  4. package/dist/command-handlers.js.map +1 -1
  5. package/dist/lane-heartbeat-writer.d.ts +43 -0
  6. package/dist/lane-heartbeat-writer.d.ts.map +1 -0
  7. package/dist/lane-heartbeat-writer.js +133 -0
  8. package/dist/lane-heartbeat-writer.js.map +1 -0
  9. package/dist/local-adapter.d.ts +8 -1
  10. package/dist/local-adapter.d.ts.map +1 -1
  11. package/dist/local-adapter.js +62 -5
  12. package/dist/local-adapter.js.map +1 -1
  13. package/dist/managed-dispatch-adapter.d.ts +506 -1
  14. package/dist/managed-dispatch-adapter.d.ts.map +1 -1
  15. package/dist/managed-dispatch-adapter.js +2624 -38
  16. package/dist/managed-dispatch-adapter.js.map +1 -1
  17. package/dist/provider-usage-live-tool.d.ts +59 -0
  18. package/dist/provider-usage-live-tool.d.ts.map +1 -0
  19. package/dist/provider-usage-live-tool.js +259 -0
  20. package/dist/provider-usage-live-tool.js.map +1 -0
  21. package/dist/quick-fallback-run.d.ts +54 -0
  22. package/dist/quick-fallback-run.d.ts.map +1 -0
  23. package/dist/quick-fallback-run.js +230 -0
  24. package/dist/quick-fallback-run.js.map +1 -0
  25. package/dist/quick-reviewer-run.d.ts +61 -0
  26. package/dist/quick-reviewer-run.d.ts.map +1 -0
  27. package/dist/quick-reviewer-run.js +397 -0
  28. package/dist/quick-reviewer-run.js.map +1 -0
  29. package/dist/runtime-reviewer-execution-bridge.d.ts +43 -0
  30. package/dist/runtime-reviewer-execution-bridge.d.ts.map +1 -0
  31. package/dist/runtime-reviewer-execution-bridge.js +313 -0
  32. package/dist/runtime-reviewer-execution-bridge.js.map +1 -0
  33. package/dist/server.d.ts +104 -6
  34. package/dist/server.d.ts.map +1 -1
  35. package/dist/server.js +1990 -104
  36. package/dist/server.js.map +1 -1
  37. package/dist/status-live-tool.d.ts +55 -0
  38. package/dist/status-live-tool.d.ts.map +1 -0
  39. package/dist/status-live-tool.js +215 -0
  40. package/dist/status-live-tool.js.map +1 -0
  41. package/package.json +2 -2
@@ -1,4 +1,7 @@
1
- import { evaluateManagedDispatchBetaGuardBoundaryV1 } from "@flowdesk/core";
1
+ import { createHash } from "node:crypto";
2
+ import { existsSync, lstatSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync, } from "node:fs";
3
+ import { dirname, resolve, sep } from "node:path";
4
+ import { applyFlowDeskSessionEvidenceWriteIntentsV1, evaluateFlowDeskDispatchAttemptDurablePrecallV1, evaluateManagedDispatchBetaGuardBoundaryV1, planFlowDeskFallbackRegateV1, prepareFlowDeskDispatchIdempotencyReservationV1, prepareFlowDeskDispatchIdempotencyStateUpdateV1, prepareFlowDeskSessionEvidenceWriteIntentV1, promoteFlowDeskExternalWriteAuthorityV1, promoteFlowDeskFallbackReselectionRegateV1, promoteFlowDeskManagedDispatchBetaAuthorityV1, promoteFlowDeskReviewerTypedVerdictsV1, recordFlowDeskExactModelAvailabilityCacheProviderAcquisitionResultV1, reloadFlowDeskSessionEvidenceV1, validateFlowDeskExactModelAvailabilityCacheAcquisitionPlanV1, validateFlowDeskFallbackRegatePlanV1, validateFlowDeskPermissionAskDecisionV1, validateFlowDeskPromptNoReplyDecisionV1, validateFlowDeskRuntimeLaneLaunchPlanV1, validateFlowDeskSessionAbortDecisionV1, validateNoForbiddenRawPayloads, validateTopTierReviewVerdictV1, } from "@flowdesk/core";
2
5
  export const flowdeskManagedDispatchBetaAdapterProfile = "managed_dispatch_beta_real_opencode_dispatch_adapter";
3
6
  function disabledAuthority() {
4
7
  return {
@@ -8,7 +11,1890 @@ function disabledAuthority() {
8
11
  actualLaneLaunch: false,
9
12
  fallbackAuthority: false,
10
13
  toolAuthority: false,
11
- hardCancelOrNoReplyAuthority: false
14
+ hardCancelOrNoReplyAuthority: false,
15
+ };
16
+ }
17
+ function disabledFallbackAuthority() {
18
+ return { ...disabledAuthority(), automaticFallbackAuthorized: false };
19
+ }
20
+ function managedFallbackRegateAuthority(prepared) {
21
+ return {
22
+ ...disabledAuthority(),
23
+ freshRegatePlanPrepared: prepared,
24
+ automaticFallbackAuthorized: false,
25
+ };
26
+ }
27
+ function controlledExternalWriteAuthority(authorized) {
28
+ return {
29
+ ...disabledAuthority(),
30
+ controlledExternalWriteAuthorized: authorized,
31
+ };
32
+ }
33
+ function exactModelProviderAcquisitionAuthority(recorded) {
34
+ return {
35
+ ...disabledAuthority(),
36
+ exactModelProviderAcquisitionRecorded: recorded,
37
+ reviewerLaunchAuthorized: false,
38
+ dispatchAuthorityEnabled: false,
39
+ };
40
+ }
41
+ function runtimeLaneLaunchAuthority(authorized) {
42
+ return {
43
+ ...disabledAuthority(),
44
+ providerCall: authorized,
45
+ runtimeExecution: authorized,
46
+ actualLaneLaunch: authorized,
47
+ runtimeLaneLaunchAuthorized: authorized,
48
+ defaultRelease1ServerBehaviorUnchanged: true,
49
+ };
50
+ }
51
+ function blockedRuntimeLaneLaunch(redactedBlockReason, plan) {
52
+ return {
53
+ adapterProfile: "injected_sdk_runtime_lane_launch_adapter",
54
+ status: "blocked_before_lane_launch",
55
+ createAttempted: false,
56
+ promptAttempted: false,
57
+ ...(plan?.workflow_id === undefined ? {} : { workflowId: plan.workflow_id }),
58
+ ...(plan?.attempt_id === undefined ? {} : { attemptId: plan.attempt_id }),
59
+ ...(plan?.lane_id === undefined ? {} : { laneId: plan.lane_id }),
60
+ ...(plan?.parent_session_ref === undefined
61
+ ? {}
62
+ : { parentSessionRef: plan.parent_session_ref }),
63
+ redactedBlockReason,
64
+ safeNextActions: ["/flowdesk-status", "/flowdesk-export-debug"],
65
+ authority: runtimeLaneLaunchAuthority(false),
66
+ };
67
+ }
68
+ function runtimeLaneLaunchLifecycleAuthority(persisted) {
69
+ return {
70
+ ...disabledAuthority(),
71
+ runtimeLaneLifecyclePersisted: persisted,
72
+ defaultRelease1ServerBehaviorUnchanged: true,
73
+ };
74
+ }
75
+ function blockRuntimeLaneLaunchLifecycle(input) {
76
+ return {
77
+ adapterProfile: "runtime_lane_launch_lifecycle_materializer",
78
+ status: "blocked_before_lane_lifecycle",
79
+ writeAttempted: false,
80
+ evidenceReloaded: input.evidenceReloaded ?? false,
81
+ ...(input.plan?.workflow_id === undefined
82
+ ? {}
83
+ : { workflowId: input.plan.workflow_id }),
84
+ ...(input.plan?.attempt_id === undefined
85
+ ? {}
86
+ : { attemptId: input.plan.attempt_id }),
87
+ ...(input.plan?.lane_id === undefined ? {} : { laneId: input.plan.lane_id }),
88
+ ...(input.evidenceId === undefined ? {} : { evidenceId: input.evidenceId }),
89
+ redactedBlockReason: input.reason,
90
+ safeNextActions: ["/flowdesk-status", "/flowdesk-export-debug"],
91
+ authority: runtimeLaneLaunchLifecycleAuthority(false),
92
+ };
93
+ }
94
+ function blockedExactModelProviderAcquisition(reason, input) {
95
+ return {
96
+ adapterProfile: "exact_model_provider_acquisition_live_test_adapter",
97
+ status: "blocked_before_provider_acquisition",
98
+ providerCallAttempted: false,
99
+ writeAttempted: false,
100
+ evidenceReloaded: false,
101
+ workflowId: input?.workflowId,
102
+ evidenceId: input?.evidenceId,
103
+ resultId: input?.resultId,
104
+ providerQualifiedModelId: input?.providerQualifiedModelId,
105
+ redactedBlockReason: reason,
106
+ safeNextActions: ["/flowdesk-status"],
107
+ authority: exactModelProviderAcquisitionAuthority(false),
108
+ };
109
+ }
110
+ function opencodeMetadataCallParameters(input) {
111
+ const parameters = {
112
+ ...(typeof input.directory === "string" && input.directory.trim().length > 0
113
+ ? { directory: input.directory }
114
+ : {}),
115
+ ...(typeof input.workspace === "string" && input.workspace.trim().length > 0
116
+ ? { workspace: input.workspace }
117
+ : {}),
118
+ };
119
+ return Object.keys(parameters).length > 0 ? parameters : undefined;
120
+ }
121
+ function opencodeProvidersFromConfig(value) {
122
+ const record = asRecord(responseData(value));
123
+ return Array.isArray(record?.providers) ? record.providers : undefined;
124
+ }
125
+ function opencodeProvidersFromList(value) {
126
+ const record = asRecord(responseData(value));
127
+ return Array.isArray(record?.all) ? record.all : undefined;
128
+ }
129
+ function opencodeConnectedProviderIds(value) {
130
+ const record = asRecord(responseData(value));
131
+ if (!Array.isArray(record?.connected))
132
+ return undefined;
133
+ return record.connected.every((item) => typeof item === "string")
134
+ ? record.connected
135
+ : undefined;
136
+ }
137
+ function opencodeProviderAuthMethodTypes(value, providerID) {
138
+ const methods = asRecord(responseData(value))?.[providerID];
139
+ if (!Array.isArray(methods) || methods.length === 0)
140
+ return undefined;
141
+ const types = methods.map((method) => asRecord(method)?.type);
142
+ return types.every((type) => type === "api" || type === "oauth")
143
+ ? types
144
+ : undefined;
145
+ }
146
+ function opencodeProviderById(providers, providerID) {
147
+ return providers
148
+ .map(asRecord)
149
+ .find((provider) => provider?.id === providerID);
150
+ }
151
+ function opencodeProviderHasModel(provider, modelID) {
152
+ const models = asRecord(provider?.models);
153
+ return models !== undefined && asRecord(models[modelID]) !== undefined;
154
+ }
155
+ function unavailableMetadataResult(labels) {
156
+ return {
157
+ outcome: "blocked",
158
+ blocked_labels: labels,
159
+ provider_call_confirmed: false,
160
+ };
161
+ }
162
+ const flowdeskExactModelProviderAcquisitionSentinelPromptV1 = "FlowDesk exact-model provider acquisition sentinel. Return a short acknowledgement only.";
163
+ function providerAcquisitionPromptOptions(input) {
164
+ const sessionId = input.sessionId?.trim() || input.request.live_test_run_ref;
165
+ return {
166
+ path: { id: sessionId },
167
+ ...(input.directory === undefined
168
+ ? {}
169
+ : { query: { directory: input.directory } }),
170
+ body: {
171
+ model: input.model,
172
+ agent: input.agent?.trim() || "general",
173
+ parts: [{ type: "text", text: flowdeskExactModelProviderAcquisitionSentinelPromptV1 }],
174
+ },
175
+ };
176
+ }
177
+ function providerAcquisitionDuplicateLabels(input) {
178
+ const reloaded = reloadFlowDeskSessionEvidenceV1({
179
+ workflowId: input.request.workflowId,
180
+ rootDir: input.rootDir,
181
+ });
182
+ if (!reloaded.ok || reloaded.blocked.length > 0) {
183
+ return {
184
+ ok: false,
185
+ reason: "Exact-model provider acquisition live-test requires reloadable durable evidence before any provider call.",
186
+ };
187
+ }
188
+ for (const entry of reloaded.entries) {
189
+ if (entry.evidenceClass !==
190
+ "exact_model_availability_cache_provider_acquisition_result") {
191
+ continue;
192
+ }
193
+ const record = entry.record;
194
+ if (record.live_test_run_ref === input.request.liveTestRunRef ||
195
+ record.idempotency_ref === input.request.idempotencyRef) {
196
+ return {
197
+ ok: false,
198
+ reason: "Exact-model provider acquisition live-test duplicate idempotency evidence blocks before any provider call.",
199
+ };
200
+ }
201
+ }
202
+ return { ok: true };
203
+ }
204
+ export function createFlowDeskOpenCodeMetadataProviderAcquisitionClientV1(options) {
205
+ const client = asRecord(options.client);
206
+ if (client === undefined)
207
+ return undefined;
208
+ return {
209
+ async checkExactModelAvailability(request) {
210
+ const model = parseProviderQualifiedModelId(request.provider_qualified_model_id);
211
+ const providerID = opencodeRuntimeProviderIDForFlowDeskProviderFamily(request.provider_family);
212
+ if (model === undefined ||
213
+ providerID === undefined ||
214
+ model.providerID !== request.provider_family) {
215
+ return unavailableMetadataResult([
216
+ "opencode_provider_mapping_missing",
217
+ ]);
218
+ }
219
+ if (typeof client.config?.providers !== "function" ||
220
+ typeof client.provider?.list !== "function" ||
221
+ typeof client.provider?.auth !== "function") {
222
+ return unavailableMetadataResult([
223
+ "opencode_metadata_surface_missing",
224
+ ]);
225
+ }
226
+ const callParameters = opencodeMetadataCallParameters(options);
227
+ let configProvidersResponse;
228
+ let providerListResponse;
229
+ let providerAuthResponse;
230
+ try {
231
+ [configProvidersResponse, providerListResponse, providerAuthResponse] = await Promise.all([
232
+ client.config.providers.call(client.config, callParameters),
233
+ client.provider.list.call(client.provider, callParameters),
234
+ client.provider.auth.call(client.provider, callParameters),
235
+ ]);
236
+ }
237
+ catch {
238
+ return unavailableMetadataResult([
239
+ "opencode_metadata_query_failed",
240
+ ]);
241
+ }
242
+ const configProviders = opencodeProvidersFromConfig(configProvidersResponse);
243
+ const listedProviders = opencodeProvidersFromList(providerListResponse);
244
+ const connectedProviderIds = opencodeConnectedProviderIds(providerListResponse);
245
+ const authMethodTypes = opencodeProviderAuthMethodTypes(providerAuthResponse, providerID);
246
+ if (configProviders === undefined)
247
+ return unavailableMetadataResult([
248
+ "opencode_config_providers_missing",
249
+ ]);
250
+ if (listedProviders === undefined)
251
+ return unavailableMetadataResult([
252
+ "opencode_provider_list_missing",
253
+ ]);
254
+ if (connectedProviderIds === undefined || authMethodTypes === undefined)
255
+ return unavailableMetadataResult([
256
+ "opencode_provider_auth_missing",
257
+ ]);
258
+ const configProvider = opencodeProviderById(configProviders, providerID);
259
+ const listedProvider = opencodeProviderById(listedProviders, providerID);
260
+ if (configProvider === undefined || listedProvider === undefined)
261
+ return unavailableMetadataResult([
262
+ "opencode_provider_metadata_missing",
263
+ ]);
264
+ if (!opencodeProviderHasModel(configProvider, model.modelID) ||
265
+ !opencodeProviderHasModel(listedProvider, model.modelID)) {
266
+ return unavailableMetadataResult([
267
+ "opencode_provider_model_missing",
268
+ ]);
269
+ }
270
+ if (!connectedProviderIds.includes(providerID))
271
+ return unavailableMetadataResult([
272
+ "opencode_provider_auth_missing",
273
+ ]);
274
+ const sanitizedFacts = {
275
+ providerID,
276
+ modelID: model.modelID,
277
+ authMethodTypes,
278
+ };
279
+ const sanitized = validateNoForbiddenRawPayloads(sanitizedFacts, "opencode_metadata_provider_acquisition_facts");
280
+ if (!sanitized.ok)
281
+ return unavailableMetadataResult([
282
+ "opencode_provider_metadata_not_sanitized",
283
+ ]);
284
+ return {
285
+ outcome: "available",
286
+ sanitized_provider_result_ref: refFrom("provider-result", `${providerID}-${model.modelID}-metadata`),
287
+ availability_ref: refFrom("availability", `${providerID}-${model.modelID}-metadata`),
288
+ highest_tier_eligible: true,
289
+ provider_call_confirmed: false,
290
+ };
291
+ },
292
+ };
293
+ }
294
+ export function createFlowDeskOpenCodePromptBackedProviderAcquisitionClientV1(options) {
295
+ const client = asRecord(options.client);
296
+ const metadataClient = createFlowDeskOpenCodeMetadataProviderAcquisitionClientV1(options);
297
+ if (client === undefined || metadataClient === undefined)
298
+ return undefined;
299
+ return {
300
+ async checkExactModelAvailability(request) {
301
+ const metadataResult = await metadataClient.checkExactModelAvailability(request);
302
+ if (metadataResult.outcome !== "available") {
303
+ return { ...metadataResult, provider_call_confirmed: false };
304
+ }
305
+ if (options.allowProviderCall !== true) {
306
+ return unavailableMetadataResult([
307
+ "opencode_sdk_provider_call_not_allowed",
308
+ ]);
309
+ }
310
+ if (!options.allowedProviderQualifiedModelIds.includes(request.provider_qualified_model_id)) {
311
+ return unavailableMetadataResult([
312
+ "opencode_sdk_provider_model_not_allowed",
313
+ ]);
314
+ }
315
+ const parsedModel = parseProviderQualifiedModelId(request.provider_qualified_model_id);
316
+ const runtimeModel = parsedModel === undefined || parsedModel.providerID !== request.provider_family
317
+ ? undefined
318
+ : opencodeRuntimeModelForFlowDeskModel(parsedModel);
319
+ if (runtimeModel === undefined) {
320
+ return unavailableMetadataResult([
321
+ "opencode_provider_mapping_missing",
322
+ ]);
323
+ }
324
+ const prompt = typeof client.session?.promptAsync === "function"
325
+ ? client.session.promptAsync
326
+ : client.session?.prompt;
327
+ if (prompt === undefined) {
328
+ return unavailableMetadataResult([
329
+ "opencode_sdk_surface_missing",
330
+ ]);
331
+ }
332
+ const promptOptions = providerAcquisitionPromptOptions({
333
+ request,
334
+ model: runtimeModel,
335
+ ...(typeof options.sessionId === "string" ? { sessionId: options.sessionId } : {}),
336
+ ...(typeof options.agent === "string" ? { agent: options.agent } : {}),
337
+ ...(typeof options.directory === "string" ? { directory: options.directory } : {}),
338
+ });
339
+ try {
340
+ await prompt.call(client.session, promptOptions);
341
+ }
342
+ catch {
343
+ return {
344
+ outcome: "blocked",
345
+ blocked_labels: ["opencode_sdk_provider_call_failed"],
346
+ provider_call_confirmed: true,
347
+ };
348
+ }
349
+ return {
350
+ outcome: "available",
351
+ sanitized_provider_result_ref: refFrom("provider-result", `${runtimeModel.providerID}-${runtimeModel.modelID}-sdk-sentinel`),
352
+ availability_ref: refFrom("availability", `${runtimeModel.providerID}-${runtimeModel.modelID}-sdk-sentinel`),
353
+ highest_tier_eligible: metadataResult.highest_tier_eligible === true,
354
+ provider_call_confirmed: true,
355
+ };
356
+ },
357
+ };
358
+ }
359
+ export async function runFlowDeskExactModelProviderAcquisitionLiveTestV1(input) {
360
+ if (typeof input.rootDir !== "string" || input.rootDir.trim().length === 0)
361
+ return blockedExactModelProviderAcquisition("Exact-model provider acquisition live-test requires a durable state root before any provider call.", input.request);
362
+ const planValidation = validateFlowDeskExactModelAvailabilityCacheAcquisitionPlanV1(input.request.acquisitionPlan);
363
+ if (!planValidation.ok || input.request.acquisitionPlan.state !== "acquisition_planned") {
364
+ return blockedExactModelProviderAcquisition("Exact-model provider acquisition live-test requires valid acquisition_planned evidence before any provider call.", input.request);
365
+ }
366
+ const duplicateCheck = providerAcquisitionDuplicateLabels(input);
367
+ if (!duplicateCheck.ok) {
368
+ return blockedExactModelProviderAcquisition(duplicateCheck.reason, input.request);
369
+ }
370
+ let clientResult;
371
+ let providerCallAttempted = false;
372
+ try {
373
+ clientResult = await input.client.checkExactModelAvailability({
374
+ provider_family: input.request.providerFamily,
375
+ provider_identity_ref: input.request.providerIdentityRef,
376
+ provider_qualified_model_id: input.request.providerQualifiedModelId,
377
+ model_family: input.request.modelFamily,
378
+ active_profile_ref: input.request.activeProfileRef,
379
+ auth_account_boundary_ref: input.request.authAccountBoundaryRef,
380
+ live_test_run_ref: input.request.liveTestRunRef,
381
+ redaction_proof_ref: input.request.redactionProofRef,
382
+ });
383
+ providerCallAttempted = clientResult.provider_call_confirmed !== false;
384
+ }
385
+ catch {
386
+ providerCallAttempted = true;
387
+ clientResult = {
388
+ outcome: "blocked",
389
+ blocked_labels: ["provider_acquisition_client_error"],
390
+ provider_call_confirmed: true,
391
+ };
392
+ }
393
+ const rawCheck = validateNoForbiddenRawPayloads(clientResult, "provider_acquisition_client_result");
394
+ const blockedLabels = [
395
+ ...(clientResult.blocked_labels ?? []),
396
+ ...(rawCheck.ok ? [] : ["provider_acquisition_result_not_sanitized"]),
397
+ ];
398
+ const result = recordFlowDeskExactModelAvailabilityCacheProviderAcquisitionResultV1({
399
+ acquisitionPlan: input.request.acquisitionPlan,
400
+ resultId: input.request.resultId,
401
+ localDate: input.request.localDate,
402
+ activeProfileRef: input.request.activeProfileRef,
403
+ opencodeVersionRef: input.request.opencodeVersionRef,
404
+ flowdeskPackageVersionRef: input.request.flowdeskPackageVersionRef,
405
+ registryHash: input.request.registryHash,
406
+ policyPackHash: input.request.policyPackHash,
407
+ authAccountBoundaryRef: input.request.authAccountBoundaryRef,
408
+ providerFamily: input.request.providerFamily,
409
+ providerIdentityRef: input.request.providerIdentityRef,
410
+ providerQualifiedModelId: input.request.providerQualifiedModelId,
411
+ modelFamily: input.request.modelFamily,
412
+ availabilityRef: clientResult.availability_ref ?? input.request.availabilityRef,
413
+ preCallAuditRef: input.request.preCallAuditRef,
414
+ idempotencyRef: input.request.idempotencyRef,
415
+ liveTestRunRef: input.request.liveTestRunRef,
416
+ redactionProofRef: input.request.redactionProofRef,
417
+ sanitizedProviderResultRef: clientResult.sanitized_provider_result_ref,
418
+ observedAt: input.request.observedAt,
419
+ outcome: rawCheck.ok ? clientResult.outcome : "blocked",
420
+ highestTierEligible: clientResult.highest_tier_eligible,
421
+ blockedLabels,
422
+ providerCall: providerCallAttempted,
423
+ });
424
+ const prepared = prepareFlowDeskSessionEvidenceWriteIntentV1({
425
+ workflowId: input.request.workflowId,
426
+ evidenceId: input.request.evidenceId,
427
+ record: result,
428
+ });
429
+ if (!prepared.ok || prepared.writeIntent === undefined) {
430
+ return {
431
+ adapterProfile: "exact_model_provider_acquisition_live_test_adapter",
432
+ status: "provider_acquisition_blocked",
433
+ providerCallAttempted,
434
+ writeAttempted: false,
435
+ evidenceReloaded: false,
436
+ workflowId: input.request.workflowId,
437
+ evidenceId: input.request.evidenceId,
438
+ resultId: input.request.resultId,
439
+ providerQualifiedModelId: input.request.providerQualifiedModelId,
440
+ redactedBlockReason: "Provider acquisition result could not be prepared as durable session evidence.",
441
+ result,
442
+ safeNextActions: ["/flowdesk-status"],
443
+ authority: exactModelProviderAcquisitionAuthority(false),
444
+ };
445
+ }
446
+ const applied = applyFlowDeskSessionEvidenceWriteIntentsV1(input.rootDir, [prepared.writeIntent]);
447
+ const reloaded = applied.ok
448
+ ? reloadFlowDeskSessionEvidenceV1({ workflowId: input.request.workflowId, rootDir: input.rootDir })
449
+ : undefined;
450
+ const evidenceReloaded = reloaded?.entries.some((entry) => entry.evidenceClass === "exact_model_availability_cache_provider_acquisition_result" && entry.evidenceId === input.request.evidenceId) === true;
451
+ const recorded = applied.ok && evidenceReloaded && result.state === "availability_acquired" && result.ok;
452
+ return {
453
+ adapterProfile: "exact_model_provider_acquisition_live_test_adapter",
454
+ status: recorded ? "provider_acquisition_recorded" : "provider_acquisition_blocked",
455
+ providerCallAttempted,
456
+ writeAttempted: true,
457
+ evidenceReloaded,
458
+ workflowId: input.request.workflowId,
459
+ evidenceId: input.request.evidenceId,
460
+ resultId: input.request.resultId,
461
+ providerQualifiedModelId: input.request.providerQualifiedModelId,
462
+ ...(recorded ? {} : { redactedBlockReason: "Provider acquisition live-test did not produce reloadable availability evidence." }),
463
+ result,
464
+ safeNextActions: ["/flowdesk-status"],
465
+ authority: exactModelProviderAcquisitionAuthority(recorded),
466
+ };
467
+ }
468
+ function reviewerTypedVerdictAuthority(accepted) {
469
+ return { ...disabledAuthority(), typedReviewerVerdictsAccepted: accepted };
470
+ }
471
+ function durableReviewerVerdictAuthority(accepted) {
472
+ return {
473
+ ...disabledAuthority(),
474
+ typedReviewerVerdictsAccepted: accepted,
475
+ durableReviewerVerdictEvidenceLinked: accepted,
476
+ };
477
+ }
478
+ function observedReviewerVerdictEvidenceAuthority(persisted) {
479
+ return {
480
+ ...disabledAuthority(),
481
+ typedReviewerVerdictPersisted: persisted,
482
+ typedReviewerVerdictsAccepted: false,
483
+ durableReviewerVerdictEvidenceLinked: false,
484
+ };
485
+ }
486
+ function controlledConformanceDocWriteAuthority(recorded) {
487
+ return {
488
+ ...disabledAuthority(),
489
+ controlledExternalWriteAuthorized: recorded,
490
+ localConformanceDocWriteRecorded: recorded,
491
+ remoteWriteAttempted: false,
492
+ githubWriteAttempted: false,
493
+ connectorWriteAttempted: false,
494
+ storageWriteAttempted: false,
495
+ databaseWriteAttempted: false,
496
+ urlWriteAttempted: false,
497
+ rawPathWriteAttempted: false,
498
+ };
499
+ }
500
+ function controlledRedactedAuditExportWriteAuthority(recorded) {
501
+ return {
502
+ ...disabledAuthority(),
503
+ controlledExternalWriteAuthorized: recorded,
504
+ localRedactedAuditExportWriteRecorded: recorded,
505
+ remoteWriteAttempted: false,
506
+ githubWriteAttempted: false,
507
+ connectorWriteAttempted: false,
508
+ storageWriteAttempted: false,
509
+ databaseWriteAttempted: false,
510
+ urlWriteAttempted: false,
511
+ rawPathWriteAttempted: false,
512
+ };
513
+ }
514
+ function permissionAskControlAuthority(authorized) {
515
+ return {
516
+ ...disabledAuthority(),
517
+ permissionAskStatusControlAuthorized: authorized,
518
+ };
519
+ }
520
+ function sessionAbortControlAuthority(authorized) {
521
+ return { ...disabledAuthority(), sessionAbortAuthorized: authorized };
522
+ }
523
+ function promptNoReplyControlAuthority(authorized) {
524
+ return {
525
+ ...disabledAuthority(),
526
+ providerCall: authorized,
527
+ runtimeExecution: authorized,
528
+ promptNoReplyAuthorized: authorized,
529
+ };
530
+ }
531
+ function blockControlledConformanceDocWrite(input) {
532
+ return {
533
+ adapterProfile: "controlled_conformance_doc_local_writer",
534
+ status: "blocked_before_local_write",
535
+ writeAttempted: false,
536
+ workflowId: input.request?.workflow_id,
537
+ attemptId: input.request?.attempt_id,
538
+ targetKind: input.request?.target_kind === "release_conformance_doc"
539
+ ? "release_conformance_doc"
540
+ : undefined,
541
+ targetRef: input.request?.target_ref,
542
+ ledgerEvidenceReloaded: false,
543
+ redactedBlockReason: input.reason,
544
+ safeNextActions: ["/flowdesk-status"],
545
+ authority: controlledConformanceDocWriteAuthority(false),
546
+ };
547
+ }
548
+ function blockControlledRedactedAuditExportWrite(input) {
549
+ return {
550
+ adapterProfile: "controlled_redacted_audit_export_local_writer",
551
+ status: "blocked_before_local_write",
552
+ writeAttempted: false,
553
+ workflowId: input.request?.workflow_id,
554
+ attemptId: input.request?.attempt_id,
555
+ targetKind: input.request?.target_kind === "redacted_audit_export"
556
+ ? "redacted_audit_export"
557
+ : undefined,
558
+ targetRef: input.request?.target_ref,
559
+ ledgerEvidenceReloaded: false,
560
+ redactedBlockReason: input.reason,
561
+ safeNextActions: ["/flowdesk-status"],
562
+ authority: controlledRedactedAuditExportWriteAuthority(false),
563
+ };
564
+ }
565
+ function blockObservedReviewerVerdictEvidence(input) {
566
+ return {
567
+ adapterProfile: "observed_reviewer_verdict_evidence_materializer",
568
+ status: "blocked_before_verdict_evidence",
569
+ writeAttempted: false,
570
+ workflowId: input.observation.workflowId,
571
+ sessionRef: input.observation.sessionRef,
572
+ lanePlanRef: input.observation.lanePlanRef,
573
+ bindingRef: input.observation.bindingRef,
574
+ perspective: input.observation.perspective,
575
+ verdictId: input.observation.verdictId,
576
+ evidenceId: input.evidenceId,
577
+ evidenceReloaded: input.evidenceReloaded ?? false,
578
+ redactedBlockReason: input.reason,
579
+ safeNextActions: ["/flowdesk-status"],
580
+ authority: observedReviewerVerdictEvidenceAuthority(false),
581
+ };
582
+ }
583
+ export function applyFlowDeskPermissionAskControlV1(input) {
584
+ const validation = validateFlowDeskPermissionAskDecisionV1(input.decision);
585
+ if (!validation.ok) {
586
+ return {
587
+ adapterProfile: "permission_ask_control_adapter",
588
+ status: "blocked_before_permission_status",
589
+ permissionStatusApplied: false,
590
+ workflowId: input.decision.workflow_id,
591
+ attemptId: input.decision.attempt_id,
592
+ redactedBlockReason: validation.errors.join(",") || "invalid permission decision",
593
+ authority: permissionAskControlAuthority(false),
594
+ };
595
+ }
596
+ input.output.status = input.decision.status;
597
+ return {
598
+ adapterProfile: "permission_ask_control_adapter",
599
+ status: "permission_status_applied",
600
+ permissionStatusApplied: true,
601
+ permissionStatus: input.decision.status,
602
+ workflowId: input.decision.workflow_id,
603
+ attemptId: input.decision.attempt_id,
604
+ authority: permissionAskControlAuthority(true),
605
+ };
606
+ }
607
+ export async function abortFlowDeskSessionWithDecisionV1(input) {
608
+ const validation = validateFlowDeskSessionAbortDecisionV1(input.decision);
609
+ if (!validation.ok) {
610
+ return {
611
+ adapterProfile: "session_abort_control_adapter",
612
+ status: "blocked_before_session_abort",
613
+ abortAttempted: false,
614
+ workflowId: input.decision.workflow_id,
615
+ attemptId: input.decision.attempt_id,
616
+ sessionRef: input.decision.session_ref,
617
+ redactedBlockReason: validation.errors.join(",") || "invalid abort decision",
618
+ authority: sessionAbortControlAuthority(false),
619
+ };
620
+ }
621
+ const abort = input.client.session.abort;
622
+ if (abort === undefined) {
623
+ return {
624
+ adapterProfile: "session_abort_control_adapter",
625
+ status: "blocked_before_session_abort",
626
+ abortAttempted: false,
627
+ workflowId: input.decision.workflow_id,
628
+ attemptId: input.decision.attempt_id,
629
+ sessionRef: input.decision.session_ref,
630
+ redactedBlockReason: "Injected OpenCode client is missing session.abort.",
631
+ authority: sessionAbortControlAuthority(false),
632
+ };
633
+ }
634
+ const response = await abort.call(input.client.session, {
635
+ path: { id: input.decision.session_ref },
636
+ ...(input.directory === undefined
637
+ ? {}
638
+ : { query: { directory: input.directory } }),
639
+ });
640
+ return {
641
+ adapterProfile: "session_abort_control_adapter",
642
+ status: "session_abort_sent",
643
+ abortAttempted: true,
644
+ workflowId: input.decision.workflow_id,
645
+ attemptId: input.decision.attempt_id,
646
+ sessionRef: input.decision.session_ref,
647
+ response,
648
+ authority: sessionAbortControlAuthority(true),
649
+ };
650
+ }
651
+ export async function dispatchFlowDeskPromptNoReplyWithDecisionV1(input) {
652
+ const validation = validateFlowDeskPromptNoReplyDecisionV1(input.decision);
653
+ if (!validation.ok) {
654
+ return {
655
+ adapterProfile: "prompt_no_reply_control_adapter",
656
+ status: "blocked_before_no_reply_prompt",
657
+ promptAttempted: false,
658
+ workflowId: input.decision.workflow_id,
659
+ attemptId: input.decision.attempt_id,
660
+ sessionRef: input.decision.session_ref,
661
+ redactedBlockReason: validation.errors.join(",") || "invalid no-reply decision",
662
+ authority: promptNoReplyControlAuthority(false),
663
+ };
664
+ }
665
+ if (input.request.sessionId !== input.decision.session_ref) {
666
+ return {
667
+ adapterProfile: "prompt_no_reply_control_adapter",
668
+ status: "blocked_before_no_reply_prompt",
669
+ promptAttempted: false,
670
+ workflowId: input.decision.workflow_id,
671
+ attemptId: input.decision.attempt_id,
672
+ sessionRef: input.decision.session_ref,
673
+ redactedBlockReason: "No-reply decision session_ref must match request sessionId.",
674
+ authority: promptNoReplyControlAuthority(false),
675
+ };
676
+ }
677
+ if (refFrom("agent", input.request.agent) !== input.decision.agent_ref) {
678
+ return {
679
+ adapterProfile: "prompt_no_reply_control_adapter",
680
+ status: "blocked_before_no_reply_prompt",
681
+ promptAttempted: false,
682
+ workflowId: input.decision.workflow_id,
683
+ attemptId: input.decision.attempt_id,
684
+ sessionRef: input.decision.session_ref,
685
+ redactedBlockReason: "No-reply decision agent_ref must match request agent.",
686
+ authority: promptNoReplyControlAuthority(false),
687
+ };
688
+ }
689
+ if (input.request.provider_qualified_model_id !==
690
+ input.decision.provider_qualified_model_id) {
691
+ return {
692
+ adapterProfile: "prompt_no_reply_control_adapter",
693
+ status: "blocked_before_no_reply_prompt",
694
+ promptAttempted: false,
695
+ workflowId: input.decision.workflow_id,
696
+ attemptId: input.decision.attempt_id,
697
+ sessionRef: input.decision.session_ref,
698
+ redactedBlockReason: "No-reply decision provider_qualified_model_id must match request model.",
699
+ authority: promptNoReplyControlAuthority(false),
700
+ };
701
+ }
702
+ const text = promptTextFrom(input.request);
703
+ const model = parseProviderQualifiedModelId(input.request.provider_qualified_model_id);
704
+ const runtimeModel = model === undefined
705
+ ? undefined
706
+ : opencodeRuntimeModelForFlowDeskModel(model);
707
+ if (text === undefined ||
708
+ runtimeModel === undefined ||
709
+ input.request.agent.trim().length === 0) {
710
+ return {
711
+ adapterProfile: "prompt_no_reply_control_adapter",
712
+ status: "blocked_before_no_reply_prompt",
713
+ promptAttempted: false,
714
+ workflowId: input.decision.workflow_id,
715
+ attemptId: input.decision.attempt_id,
716
+ sessionRef: input.decision.session_ref,
717
+ redactedBlockReason: "No-reply prompt request is missing agent, model, or bounded text.",
718
+ authority: promptNoReplyControlAuthority(false),
719
+ };
720
+ }
721
+ const dispatchMethod = input.request.dispatchMethod ?? "prompt";
722
+ const dispatch = input.client.session[dispatchMethod];
723
+ if (dispatch === undefined) {
724
+ return {
725
+ adapterProfile: "prompt_no_reply_control_adapter",
726
+ status: "blocked_before_no_reply_prompt",
727
+ promptAttempted: false,
728
+ workflowId: input.decision.workflow_id,
729
+ attemptId: input.decision.attempt_id,
730
+ sessionRef: input.decision.session_ref,
731
+ redactedBlockReason: "Injected OpenCode client is missing the requested prompt method.",
732
+ authority: promptNoReplyControlAuthority(false),
733
+ };
734
+ }
735
+ const options = dispatchOptions(input.request, runtimeModel, text);
736
+ const response = await dispatch.call(input.client.session, {
737
+ ...options,
738
+ body: { ...options.body, noReply: true },
739
+ });
740
+ return {
741
+ adapterProfile: "prompt_no_reply_control_adapter",
742
+ status: "no_reply_prompt_sent",
743
+ promptAttempted: true,
744
+ workflowId: input.decision.workflow_id,
745
+ attemptId: input.decision.attempt_id,
746
+ sessionRef: input.decision.session_ref,
747
+ agent: input.request.agent,
748
+ model: runtimeModel,
749
+ response,
750
+ authority: promptNoReplyControlAuthority(true),
751
+ };
752
+ }
753
+ function sha256Ref(content) {
754
+ return `sha256-${createHash("sha256").update(content, "utf8").digest("hex")}`;
755
+ }
756
+ function safeJoinUnderRoot(rootDir, relativePath) {
757
+ const root = resolve(rootDir);
758
+ const target = resolve(root, relativePath);
759
+ const rootPrefix = root.endsWith(sep) ? root : `${root}${sep}`;
760
+ if (target !== root && !target.startsWith(rootPrefix))
761
+ throw new Error("controlled write target escapes root directory");
762
+ return target;
763
+ }
764
+ function ensureNoSymlinkedDirectory(rootDir, relativePath) {
765
+ const root = resolve(rootDir);
766
+ if (existsSync(root) && lstatSync(root).isSymbolicLink())
767
+ throw new Error("controlled write root must not be a symlink");
768
+ const parts = dirname(relativePath)
769
+ .split("/")
770
+ .filter((part) => part.length > 0);
771
+ let current = root;
772
+ for (const part of parts) {
773
+ current = resolve(current, part);
774
+ if (existsSync(current) && lstatSync(current).isSymbolicLink())
775
+ throw new Error("controlled write directory must not be a symlink");
776
+ }
777
+ }
778
+ function controlledDocPathFor(targetRef) {
779
+ return `docs/conformance/${targetRef}.md`;
780
+ }
781
+ function controlledRedactedAuditExportPathFor(workflowId, targetRef) {
782
+ return `.flowdesk/sessions/${workflowId}/redacted-audit/${targetRef}.json`;
783
+ }
784
+ function validateControlledDocMarkdown(value) {
785
+ if (typeof value !== "string" || value.trim().length === 0)
786
+ return ["documentMarkdown must be a non-empty string"];
787
+ if (value.length > 200_000)
788
+ return ["documentMarkdown exceeds controlled conformance doc limit"];
789
+ return [];
790
+ }
791
+ function validateRedactedAuditExportJson(value) {
792
+ if (typeof value !== "string" || value.trim().length === 0)
793
+ return ["exportJson must be a non-empty string"];
794
+ if (value.length > 200_000)
795
+ return ["exportJson exceeds controlled redacted audit export limit"];
796
+ let parsed;
797
+ try {
798
+ parsed = JSON.parse(value);
799
+ }
800
+ catch {
801
+ return ["exportJson must be parseable JSON"];
802
+ }
803
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
804
+ return ["exportJson must be a JSON object"];
805
+ const redaction = validateNoForbiddenRawPayloads(parsed, "redacted_audit_export");
806
+ return redaction.ok ? [] : redaction.errors;
807
+ }
808
+ export function materializeFlowDeskControlledConformanceDocLocalWriteV1(input) {
809
+ const errors = [];
810
+ if (typeof input.rootDir !== "string" || input.rootDir.trim().length === 0)
811
+ errors.push("rootDir is required");
812
+ errors.push(...validateControlledDocMarkdown(input.documentMarkdown));
813
+ if (input.readiness.status !== "write_ready")
814
+ errors.push("controlled external write readiness must be write_ready");
815
+ if (input.readiness.authority.controlledExternalWriteAuthorized !== true)
816
+ errors.push("controlled external write readiness must authorize the controlled target");
817
+ if (input.request.target_kind !== "release_conformance_doc")
818
+ errors.push("local writer only supports release_conformance_doc targets");
819
+ const recheckedReadiness = prepareFlowDeskControlledExternalWriteAdapterV1({
820
+ request: input.request,
821
+ consumedApproval: input.consumedApproval,
822
+ });
823
+ if (recheckedReadiness.status !== "write_ready")
824
+ errors.push(recheckedReadiness.redactedBlockReason ??
825
+ "controlled external write readiness recheck failed");
826
+ if (input.readiness.workflowId !== input.request.workflow_id ||
827
+ input.readiness.attemptId !== input.request.attempt_id ||
828
+ input.readiness.targetKind !== input.request.target_kind ||
829
+ input.readiness.targetRef !== input.request.target_ref)
830
+ errors.push("readiness result does not match request target");
831
+ if (input.consumedApproval.consumed_at === undefined)
832
+ errors.push("external_write approval must be consumed before local write");
833
+ if (input.consumedApproval.consumption_audit_ref === undefined)
834
+ errors.push("external_write approval must include consumption audit ref");
835
+ const artifactSha256Ref = sha256Ref(input.documentMarkdown);
836
+ if (input.request.content_hash_ref !== artifactSha256Ref)
837
+ errors.push("request content_hash_ref must match document sha256");
838
+ const materializedAt = input.materializedAt ?? new Date().toISOString();
839
+ if (!Number.isFinite(Date.parse(materializedAt)))
840
+ errors.push("materializedAt must be a parseable timestamp");
841
+ if (errors.length > 0)
842
+ return blockControlledConformanceDocWrite({
843
+ request: input.request,
844
+ reason: errors.join(", "),
845
+ });
846
+ const documentPath = controlledDocPathFor(input.request.target_ref);
847
+ const ledgerRecord = {
848
+ schema_version: "flowdesk.controlled_conformance_doc_write.v1",
849
+ ledger_entry_id: input.ledgerEntryId,
850
+ request_id: input.request.request_id,
851
+ workflow_id: input.request.workflow_id,
852
+ attempt_id: input.request.attempt_id,
853
+ target_kind: "release_conformance_doc",
854
+ target_ref: input.request.target_ref,
855
+ approval_id: input.consumedApproval.approval_id,
856
+ actor_ref: input.consumedApproval.actor_ref,
857
+ profile_ref: input.consumedApproval.profile_ref,
858
+ evidence_bundle_hash: input.consumedApproval.evidence_bundle_hash,
859
+ guard_decision_ref: input.consumedApproval.guard_decision_ref,
860
+ issuance_audit_ref: input.consumedApproval.issuance_audit_ref,
861
+ consumption_audit_ref: input.consumedApproval
862
+ .consumption_audit_ref,
863
+ redaction_policy_ref: input.request.redaction_policy_ref,
864
+ content_hash_ref: input.request.content_hash_ref,
865
+ pre_write_audit_ref: input.request.pre_write_audit_ref,
866
+ dry_run_ref: input.request.dry_run_ref,
867
+ artifact_ref: `artifact-${input.request.target_ref}`,
868
+ artifact_path: documentPath,
869
+ artifact_sha256_ref: artifactSha256Ref,
870
+ materialized_at: materializedAt,
871
+ local_only: true,
872
+ writeAttempted: true,
873
+ remoteWriteAttempted: false,
874
+ githubWriteAttempted: false,
875
+ connectorWriteAttempted: false,
876
+ storageWriteAttempted: false,
877
+ databaseWriteAttempted: false,
878
+ urlWriteAttempted: false,
879
+ rawPathWriteAttempted: false,
880
+ dispatch_authority_enabled: false,
881
+ realOpenCodeDispatch: false,
882
+ providerCall: false,
883
+ actualLaneLaunch: false,
884
+ runtimeExecution: false,
885
+ fallbackAuthority: false,
886
+ toolAuthority: false,
887
+ hardCancelOrNoReplyAuthority: false,
888
+ };
889
+ const preparedLedger = prepareFlowDeskSessionEvidenceWriteIntentV1({
890
+ workflowId: input.request.workflow_id,
891
+ evidenceId: input.ledgerEntryId,
892
+ record: ledgerRecord,
893
+ });
894
+ if (!preparedLedger.ok || preparedLedger.writeIntent === undefined)
895
+ return blockControlledConformanceDocWrite({
896
+ request: input.request,
897
+ reason: preparedLedger.errors.join(", ") || "ledger write intent invalid",
898
+ });
899
+ const preWriteReload = reloadFlowDeskSessionEvidenceV1({
900
+ workflowId: input.request.workflow_id,
901
+ rootDir: input.rootDir,
902
+ });
903
+ if (!preWriteReload.ok || preWriteReload.blocked.length > 0)
904
+ return blockControlledConformanceDocWrite({
905
+ request: input.request,
906
+ reason: "controlled conformance doc pre-write evidence reload failed",
907
+ });
908
+ let documentRenamed = false;
909
+ let ledgerRenamed = false;
910
+ try {
911
+ ensureNoSymlinkedDirectory(input.rootDir, documentPath);
912
+ ensureNoSymlinkedDirectory(input.rootDir, preparedLedger.writeIntent.path);
913
+ const documentTarget = safeJoinUnderRoot(input.rootDir, documentPath);
914
+ const documentTemp = safeJoinUnderRoot(input.rootDir, `docs/conformance/.${input.request.target_ref}.tmp-controlled-conformance-doc-write`);
915
+ const ledgerTarget = safeJoinUnderRoot(input.rootDir, preparedLedger.writeIntent.path);
916
+ const ledgerTemp = safeJoinUnderRoot(input.rootDir, preparedLedger.writeIntent.tempPath);
917
+ if (existsSync(documentTarget) || existsSync(ledgerTarget))
918
+ return blockControlledConformanceDocWrite({
919
+ request: input.request,
920
+ reason: "controlled conformance doc or ledger target already exists",
921
+ });
922
+ if (dirname(documentTarget) !== dirname(documentTemp))
923
+ return blockControlledConformanceDocWrite({
924
+ request: input.request,
925
+ reason: "controlled conformance doc temp path must stay beside target",
926
+ });
927
+ if (dirname(ledgerTarget) !== dirname(ledgerTemp))
928
+ return blockControlledConformanceDocWrite({
929
+ request: input.request,
930
+ reason: "controlled conformance doc ledger temp path must stay beside target",
931
+ });
932
+ mkdirSync(dirname(documentTarget), { recursive: true });
933
+ mkdirSync(dirname(ledgerTarget), { recursive: true });
934
+ writeFileSync(documentTemp, input.documentMarkdown, "utf8");
935
+ writeFileSync(ledgerTemp, JSON.stringify(ledgerRecord), "utf8");
936
+ renameSync(documentTemp, documentTarget);
937
+ documentRenamed = true;
938
+ renameSync(ledgerTemp, ledgerTarget);
939
+ ledgerRenamed = true;
940
+ const writtenHash = sha256Ref(readFileSync(documentTarget, "utf8"));
941
+ if (writtenHash !== artifactSha256Ref) {
942
+ try {
943
+ rmSync(ledgerTarget, { force: true });
944
+ rmSync(documentTarget, { force: true });
945
+ }
946
+ catch {
947
+ // Best-effort cleanup only; result remains blocked.
948
+ }
949
+ finally {
950
+ documentRenamed = false;
951
+ ledgerRenamed = false;
952
+ }
953
+ return blockControlledConformanceDocWrite({
954
+ request: input.request,
955
+ reason: "controlled conformance doc hash verification failed",
956
+ });
957
+ }
958
+ const reloaded = reloadFlowDeskSessionEvidenceV1({
959
+ workflowId: input.request.workflow_id,
960
+ rootDir: input.rootDir,
961
+ });
962
+ const ledgerReloaded = reloaded.ok &&
963
+ reloaded.blocked.length === 0 &&
964
+ reloaded.entries.some((entry) => entry.evidenceClass === "controlled_conformance_doc_write" &&
965
+ entry.evidenceId === input.ledgerEntryId &&
966
+ entry.record.artifact_sha256_ref === artifactSha256Ref);
967
+ if (!ledgerReloaded)
968
+ try {
969
+ rmSync(ledgerTarget, { force: true });
970
+ rmSync(documentTarget, { force: true });
971
+ }
972
+ catch {
973
+ // Best-effort cleanup only; result remains blocked.
974
+ }
975
+ finally {
976
+ documentRenamed = false;
977
+ ledgerRenamed = false;
978
+ }
979
+ if (!ledgerReloaded)
980
+ return blockControlledConformanceDocWrite({
981
+ request: input.request,
982
+ reason: "controlled conformance doc ledger reload failed",
983
+ });
984
+ return {
985
+ adapterProfile: "controlled_conformance_doc_local_writer",
986
+ status: "write_recorded",
987
+ workflowId: input.request.workflow_id,
988
+ attemptId: input.request.attempt_id,
989
+ targetKind: "release_conformance_doc",
990
+ targetRef: input.request.target_ref,
991
+ writeAttempted: true,
992
+ documentPath,
993
+ ledgerEntryId: input.ledgerEntryId,
994
+ ledgerEvidenceReloaded: true,
995
+ artifactSha256Ref,
996
+ safeNextActions: ["/flowdesk-status", "/flowdesk-export-debug"],
997
+ authority: controlledConformanceDocWriteAuthority(true),
998
+ };
999
+ }
1000
+ catch (error) {
1001
+ try {
1002
+ if (ledgerRenamed) {
1003
+ const ledgerTarget = safeJoinUnderRoot(input.rootDir, preparedLedger.writeIntent.path);
1004
+ rmSync(ledgerTarget, { force: true });
1005
+ }
1006
+ if (documentRenamed) {
1007
+ const documentTarget = safeJoinUnderRoot(input.rootDir, documentPath);
1008
+ rmSync(documentTarget, { force: true });
1009
+ }
1010
+ }
1011
+ catch {
1012
+ // Best-effort cleanup only; result remains blocked.
1013
+ }
1014
+ return blockControlledConformanceDocWrite({
1015
+ request: input.request,
1016
+ reason: error instanceof Error
1017
+ ? error.message
1018
+ : "controlled conformance doc local write failed",
1019
+ });
1020
+ }
1021
+ }
1022
+ export function materializeFlowDeskControlledRedactedAuditExportLocalWriteV1(input) {
1023
+ const errors = [];
1024
+ if (typeof input.rootDir !== "string" || input.rootDir.trim().length === 0)
1025
+ errors.push("rootDir is required");
1026
+ errors.push(...validateRedactedAuditExportJson(input.exportJson));
1027
+ if (input.readiness.status !== "write_ready")
1028
+ errors.push("controlled external write readiness must be write_ready");
1029
+ if (input.readiness.authority.controlledExternalWriteAuthorized !== true)
1030
+ errors.push("controlled external write readiness must authorize the controlled target");
1031
+ if (input.request.target_kind !== "redacted_audit_export")
1032
+ errors.push("local writer only supports redacted_audit_export targets");
1033
+ const recheckedReadiness = prepareFlowDeskControlledExternalWriteAdapterV1({
1034
+ request: input.request,
1035
+ consumedApproval: input.consumedApproval,
1036
+ });
1037
+ if (recheckedReadiness.status !== "write_ready")
1038
+ errors.push(recheckedReadiness.redactedBlockReason ??
1039
+ "controlled external write readiness recheck failed");
1040
+ if (input.readiness.workflowId !== input.request.workflow_id ||
1041
+ input.readiness.attemptId !== input.request.attempt_id ||
1042
+ input.readiness.targetKind !== input.request.target_kind ||
1043
+ input.readiness.targetRef !== input.request.target_ref)
1044
+ errors.push("readiness result does not match request target");
1045
+ if (input.consumedApproval.consumed_at === undefined)
1046
+ errors.push("external_write approval must be consumed before local write");
1047
+ if (input.consumedApproval.consumption_audit_ref === undefined)
1048
+ errors.push("external_write approval must include consumption audit ref");
1049
+ const artifactSha256Ref = sha256Ref(input.exportJson);
1050
+ if (input.request.content_hash_ref !== artifactSha256Ref)
1051
+ errors.push("request content_hash_ref must match redacted audit export sha256");
1052
+ const materializedAt = input.materializedAt ?? new Date().toISOString();
1053
+ if (!Number.isFinite(Date.parse(materializedAt)))
1054
+ errors.push("materializedAt must be a parseable timestamp");
1055
+ if (errors.length > 0)
1056
+ return blockControlledRedactedAuditExportWrite({
1057
+ request: input.request,
1058
+ reason: errors.join(", "),
1059
+ });
1060
+ const exportPath = controlledRedactedAuditExportPathFor(input.request.workflow_id, input.request.target_ref);
1061
+ const ledgerRecord = {
1062
+ schema_version: "flowdesk.controlled_redacted_audit_export_write.v1",
1063
+ ledger_entry_id: input.ledgerEntryId,
1064
+ request_id: input.request.request_id,
1065
+ workflow_id: input.request.workflow_id,
1066
+ attempt_id: input.request.attempt_id,
1067
+ target_kind: "redacted_audit_export",
1068
+ target_ref: input.request.target_ref,
1069
+ approval_id: input.consumedApproval.approval_id,
1070
+ actor_ref: input.consumedApproval.actor_ref,
1071
+ profile_ref: input.consumedApproval.profile_ref,
1072
+ evidence_bundle_hash: input.consumedApproval.evidence_bundle_hash,
1073
+ guard_decision_ref: input.consumedApproval.guard_decision_ref,
1074
+ issuance_audit_ref: input.consumedApproval.issuance_audit_ref,
1075
+ consumption_audit_ref: input.consumedApproval
1076
+ .consumption_audit_ref,
1077
+ redaction_policy_ref: input.request.redaction_policy_ref,
1078
+ content_hash_ref: input.request.content_hash_ref,
1079
+ pre_write_audit_ref: input.request.pre_write_audit_ref,
1080
+ dry_run_ref: input.request.dry_run_ref,
1081
+ artifact_ref: `artifact-${input.request.target_ref}`,
1082
+ artifact_path: exportPath,
1083
+ artifact_sha256_ref: artifactSha256Ref,
1084
+ materialized_at: materializedAt,
1085
+ local_only: true,
1086
+ redacted: true,
1087
+ writeAttempted: true,
1088
+ remoteWriteAttempted: false,
1089
+ githubWriteAttempted: false,
1090
+ connectorWriteAttempted: false,
1091
+ storageWriteAttempted: false,
1092
+ databaseWriteAttempted: false,
1093
+ urlWriteAttempted: false,
1094
+ rawPathWriteAttempted: false,
1095
+ dispatch_authority_enabled: false,
1096
+ realOpenCodeDispatch: false,
1097
+ providerCall: false,
1098
+ actualLaneLaunch: false,
1099
+ runtimeExecution: false,
1100
+ fallbackAuthority: false,
1101
+ toolAuthority: false,
1102
+ hardCancelOrNoReplyAuthority: false,
1103
+ };
1104
+ const preparedLedger = prepareFlowDeskSessionEvidenceWriteIntentV1({
1105
+ workflowId: input.request.workflow_id,
1106
+ evidenceId: input.ledgerEntryId,
1107
+ record: ledgerRecord,
1108
+ });
1109
+ if (!preparedLedger.ok || preparedLedger.writeIntent === undefined)
1110
+ return blockControlledRedactedAuditExportWrite({
1111
+ request: input.request,
1112
+ reason: preparedLedger.errors.join(", ") || "ledger write intent invalid",
1113
+ });
1114
+ const preWriteReload = reloadFlowDeskSessionEvidenceV1({
1115
+ workflowId: input.request.workflow_id,
1116
+ rootDir: input.rootDir,
1117
+ });
1118
+ if (!preWriteReload.ok || preWriteReload.blocked.length > 0)
1119
+ return blockControlledRedactedAuditExportWrite({
1120
+ request: input.request,
1121
+ reason: "controlled redacted audit export pre-write evidence reload failed",
1122
+ });
1123
+ let exportRenamed = false;
1124
+ let ledgerRenamed = false;
1125
+ try {
1126
+ ensureNoSymlinkedDirectory(input.rootDir, exportPath);
1127
+ ensureNoSymlinkedDirectory(input.rootDir, preparedLedger.writeIntent.path);
1128
+ const exportTarget = safeJoinUnderRoot(input.rootDir, exportPath);
1129
+ const exportTemp = safeJoinUnderRoot(input.rootDir, `.flowdesk/sessions/${input.request.workflow_id}/redacted-audit/.${input.request.target_ref}.tmp-controlled-redacted-audit-export-write`);
1130
+ const ledgerTarget = safeJoinUnderRoot(input.rootDir, preparedLedger.writeIntent.path);
1131
+ const ledgerTemp = safeJoinUnderRoot(input.rootDir, preparedLedger.writeIntent.tempPath);
1132
+ if (existsSync(exportTarget) || existsSync(ledgerTarget))
1133
+ return blockControlledRedactedAuditExportWrite({
1134
+ request: input.request,
1135
+ reason: "controlled redacted audit export or ledger target already exists",
1136
+ });
1137
+ if (dirname(exportTarget) !== dirname(exportTemp))
1138
+ return blockControlledRedactedAuditExportWrite({
1139
+ request: input.request,
1140
+ reason: "controlled redacted audit export temp path must stay beside target",
1141
+ });
1142
+ if (dirname(ledgerTarget) !== dirname(ledgerTemp))
1143
+ return blockControlledRedactedAuditExportWrite({
1144
+ request: input.request,
1145
+ reason: "controlled redacted audit export ledger temp path must stay beside target",
1146
+ });
1147
+ mkdirSync(dirname(exportTarget), { recursive: true });
1148
+ mkdirSync(dirname(ledgerTarget), { recursive: true });
1149
+ writeFileSync(exportTemp, input.exportJson, "utf8");
1150
+ writeFileSync(ledgerTemp, JSON.stringify(ledgerRecord), "utf8");
1151
+ renameSync(exportTemp, exportTarget);
1152
+ exportRenamed = true;
1153
+ renameSync(ledgerTemp, ledgerTarget);
1154
+ ledgerRenamed = true;
1155
+ const writtenHash = sha256Ref(readFileSync(exportTarget, "utf8"));
1156
+ if (writtenHash !== artifactSha256Ref) {
1157
+ try {
1158
+ rmSync(ledgerTarget, { force: true });
1159
+ rmSync(exportTarget, { force: true });
1160
+ }
1161
+ catch {
1162
+ // Best-effort cleanup only; result remains blocked.
1163
+ }
1164
+ finally {
1165
+ exportRenamed = false;
1166
+ ledgerRenamed = false;
1167
+ }
1168
+ return blockControlledRedactedAuditExportWrite({
1169
+ request: input.request,
1170
+ reason: "controlled redacted audit export hash verification failed",
1171
+ });
1172
+ }
1173
+ const reloaded = reloadFlowDeskSessionEvidenceV1({
1174
+ workflowId: input.request.workflow_id,
1175
+ rootDir: input.rootDir,
1176
+ });
1177
+ const ledgerReloaded = reloaded.ok &&
1178
+ reloaded.blocked.length === 0 &&
1179
+ reloaded.entries.some((entry) => entry.evidenceClass === "controlled_redacted_audit_export_write" &&
1180
+ entry.evidenceId === input.ledgerEntryId &&
1181
+ entry.record.artifact_sha256_ref === artifactSha256Ref);
1182
+ if (!ledgerReloaded)
1183
+ try {
1184
+ rmSync(ledgerTarget, { force: true });
1185
+ rmSync(exportTarget, { force: true });
1186
+ }
1187
+ catch {
1188
+ // Best-effort cleanup only; result remains blocked.
1189
+ }
1190
+ finally {
1191
+ exportRenamed = false;
1192
+ ledgerRenamed = false;
1193
+ }
1194
+ if (!ledgerReloaded)
1195
+ return blockControlledRedactedAuditExportWrite({
1196
+ request: input.request,
1197
+ reason: "controlled redacted audit export ledger reload failed",
1198
+ });
1199
+ return {
1200
+ adapterProfile: "controlled_redacted_audit_export_local_writer",
1201
+ status: "write_recorded",
1202
+ workflowId: input.request.workflow_id,
1203
+ attemptId: input.request.attempt_id,
1204
+ targetKind: "redacted_audit_export",
1205
+ targetRef: input.request.target_ref,
1206
+ writeAttempted: true,
1207
+ exportPath,
1208
+ ledgerEntryId: input.ledgerEntryId,
1209
+ ledgerEvidenceReloaded: true,
1210
+ artifactSha256Ref,
1211
+ safeNextActions: ["/flowdesk-status", "/flowdesk-export-debug"],
1212
+ authority: controlledRedactedAuditExportWriteAuthority(true),
1213
+ };
1214
+ }
1215
+ catch (error) {
1216
+ try {
1217
+ if (ledgerRenamed) {
1218
+ const ledgerTarget = safeJoinUnderRoot(input.rootDir, preparedLedger.writeIntent.path);
1219
+ rmSync(ledgerTarget, { force: true });
1220
+ }
1221
+ if (exportRenamed) {
1222
+ const exportTarget = safeJoinUnderRoot(input.rootDir, exportPath);
1223
+ rmSync(exportTarget, { force: true });
1224
+ }
1225
+ }
1226
+ catch {
1227
+ // Best-effort cleanup only; result remains blocked.
1228
+ }
1229
+ return blockControlledRedactedAuditExportWrite({
1230
+ request: input.request,
1231
+ reason: error instanceof Error
1232
+ ? error.message
1233
+ : "controlled redacted audit export local write failed",
1234
+ });
1235
+ }
1236
+ }
1237
+ export function prepareFlowDeskFallbackReselectionRegateAdapterV1(input) {
1238
+ const promotion = promoteFlowDeskFallbackReselectionRegateV1(input);
1239
+ if (!promotion.ok ||
1240
+ promotion.fallback_reselection_regate_authority_enabled !== true) {
1241
+ return {
1242
+ adapterProfile: "fallback_reselection_regate_adapter",
1243
+ status: "blocked_before_regate",
1244
+ dispatchAttempted: false,
1245
+ workflowId: input.decision.workflow_id,
1246
+ parentAttemptId: input.decision.parent_attempt_id,
1247
+ newAttemptId: input.decision.new_attempt_id,
1248
+ redactedBlockReason: promotion.errors.join(",") || "fallback reselection regate blocked",
1249
+ safeNextActions: ["/flowdesk-status"],
1250
+ authority: disabledFallbackAuthority(),
1251
+ };
1252
+ }
1253
+ return {
1254
+ adapterProfile: "fallback_reselection_regate_adapter",
1255
+ status: "regate_required",
1256
+ dispatchAttempted: false,
1257
+ workflowId: input.decision.workflow_id,
1258
+ parentAttemptId: input.decision.parent_attempt_id,
1259
+ newAttemptId: input.decision.new_attempt_id,
1260
+ fromProviderQualifiedModelId: input.decision.from_provider_qualified_model_id,
1261
+ toProviderQualifiedModelId: input.decision.to_provider_qualified_model_id,
1262
+ safeNextActions: ["/flowdesk-status", "/flowdesk-run"],
1263
+ authority: disabledFallbackAuthority(),
1264
+ };
1265
+ }
1266
+ export function orchestrateFlowDeskManagedFallbackRegateV1(input) {
1267
+ const plan = planFlowDeskFallbackRegateV1(input);
1268
+ if (!plan.ok || plan.state !== "full_regate_required") {
1269
+ return {
1270
+ adapterProfile: "managed_fallback_regate_orchestrator",
1271
+ status: "blocked_before_regate_plan",
1272
+ dispatchAttempted: false,
1273
+ providerSwitchAttempted: false,
1274
+ sdkCallAttempted: false,
1275
+ workflowId: input.decision.workflow_id,
1276
+ parentAttemptId: input.decision.parent_attempt_id,
1277
+ newAttemptId: input.decision.new_attempt_id,
1278
+ fromProviderQualifiedModelId: input.decision.from_provider_qualified_model_id,
1279
+ toProviderQualifiedModelId: input.decision.to_provider_qualified_model_id,
1280
+ regatePlan: plan,
1281
+ redactedBlockReason: plan.errors.join(",") || "fallback regate plan blocked",
1282
+ safeNextActions: ["/flowdesk-status"],
1283
+ authority: managedFallbackRegateAuthority(false),
1284
+ };
1285
+ }
1286
+ return {
1287
+ adapterProfile: "managed_fallback_regate_orchestrator",
1288
+ status: "regate_plan_ready",
1289
+ dispatchAttempted: false,
1290
+ providerSwitchAttempted: false,
1291
+ sdkCallAttempted: false,
1292
+ workflowId: plan.workflow_id,
1293
+ parentAttemptId: plan.parent_attempt_id,
1294
+ newAttemptId: plan.new_attempt_id,
1295
+ fromProviderQualifiedModelId: plan.from_provider_qualified_model_id,
1296
+ toProviderQualifiedModelId: plan.to_provider_qualified_model_id,
1297
+ regatePlan: plan,
1298
+ safeNextActions: ["/flowdesk-status", "/flowdesk-run"],
1299
+ authority: managedFallbackRegateAuthority(true),
1300
+ };
1301
+ }
1302
+ export function materializeFlowDeskManagedFallbackRegatePlanEvidenceV1(input) {
1303
+ const blockedAuthority = {
1304
+ realOpenCodeDispatch: false,
1305
+ providerCall: false,
1306
+ runtimeExecution: false,
1307
+ actualLaneLaunch: false,
1308
+ fallbackAuthority: false,
1309
+ automaticFallbackAuthorized: false,
1310
+ regatePlanPersisted: false,
1311
+ toolAuthority: false,
1312
+ hardCancelOrNoReplyAuthority: false,
1313
+ };
1314
+ if (typeof input.rootDir !== "string" || input.rootDir.trim().length === 0)
1315
+ return {
1316
+ adapterProfile: "managed_fallback_regate_plan_evidence_materializer",
1317
+ status: "blocked_before_regate_plan_evidence",
1318
+ writeAttempted: false,
1319
+ evidenceReloaded: false,
1320
+ workflowId: input.regatePlan?.workflow_id,
1321
+ evidenceId: input.evidenceId,
1322
+ redactedBlockReason: "rootDir is required",
1323
+ safeNextActions: ["/flowdesk-status"],
1324
+ authority: blockedAuthority,
1325
+ };
1326
+ const planValidation = validateFlowDeskFallbackRegatePlanV1(input.regatePlan);
1327
+ if (!planValidation.ok)
1328
+ return {
1329
+ adapterProfile: "managed_fallback_regate_plan_evidence_materializer",
1330
+ status: "blocked_before_regate_plan_evidence",
1331
+ writeAttempted: false,
1332
+ evidenceReloaded: false,
1333
+ workflowId: input.regatePlan?.workflow_id,
1334
+ evidenceId: input.evidenceId,
1335
+ redactedBlockReason: planValidation.errors.join(", ") ||
1336
+ "fallback regate plan invalid",
1337
+ safeNextActions: ["/flowdesk-status"],
1338
+ authority: blockedAuthority,
1339
+ };
1340
+ if (input.regatePlan.state !== "full_regate_required" ||
1341
+ input.regatePlan.ok !== true)
1342
+ return {
1343
+ adapterProfile: "managed_fallback_regate_plan_evidence_materializer",
1344
+ status: "blocked_before_regate_plan_evidence",
1345
+ writeAttempted: false,
1346
+ evidenceReloaded: false,
1347
+ workflowId: input.regatePlan?.workflow_id,
1348
+ evidenceId: input.evidenceId,
1349
+ redactedBlockReason: "fallback regate plan must be full_regate_required",
1350
+ safeNextActions: ["/flowdesk-status"],
1351
+ authority: blockedAuthority,
1352
+ };
1353
+ const workflowId = input.regatePlan.workflow_id;
1354
+ if (typeof workflowId !== "string" || workflowId.trim().length === 0)
1355
+ return {
1356
+ adapterProfile: "managed_fallback_regate_plan_evidence_materializer",
1357
+ status: "blocked_before_regate_plan_evidence",
1358
+ writeAttempted: false,
1359
+ evidenceReloaded: false,
1360
+ evidenceId: input.evidenceId,
1361
+ redactedBlockReason: "fallback regate plan workflow_id is required",
1362
+ safeNextActions: ["/flowdesk-status"],
1363
+ authority: blockedAuthority,
1364
+ };
1365
+ const preWriteReload = reloadFlowDeskSessionEvidenceV1({
1366
+ workflowId,
1367
+ rootDir: input.rootDir,
1368
+ });
1369
+ if (!preWriteReload.ok || preWriteReload.blocked.length > 0)
1370
+ return {
1371
+ adapterProfile: "managed_fallback_regate_plan_evidence_materializer",
1372
+ status: "blocked_before_regate_plan_evidence",
1373
+ writeAttempted: false,
1374
+ evidenceReloaded: false,
1375
+ workflowId,
1376
+ evidenceId: input.evidenceId,
1377
+ redactedBlockReason: "fallback regate plan pre-write reload failed",
1378
+ safeNextActions: ["/flowdesk-status"],
1379
+ authority: blockedAuthority,
1380
+ };
1381
+ if (preWriteReload.entries.some((entry) => entry.evidenceClass === "fallback_regate_plan" &&
1382
+ entry.evidenceId === input.evidenceId))
1383
+ return {
1384
+ adapterProfile: "managed_fallback_regate_plan_evidence_materializer",
1385
+ status: "blocked_before_regate_plan_evidence",
1386
+ writeAttempted: false,
1387
+ evidenceReloaded: true,
1388
+ workflowId,
1389
+ evidenceId: input.evidenceId,
1390
+ redactedBlockReason: "fallback regate plan evidence already exists",
1391
+ safeNextActions: ["/flowdesk-status"],
1392
+ authority: blockedAuthority,
1393
+ };
1394
+ const prepared = prepareFlowDeskSessionEvidenceWriteIntentV1({
1395
+ workflowId,
1396
+ evidenceId: input.evidenceId,
1397
+ record: input.regatePlan,
1398
+ });
1399
+ if (!prepared.ok || prepared.writeIntent === undefined)
1400
+ return {
1401
+ adapterProfile: "managed_fallback_regate_plan_evidence_materializer",
1402
+ status: "blocked_before_regate_plan_evidence",
1403
+ writeAttempted: false,
1404
+ evidenceReloaded: true,
1405
+ workflowId,
1406
+ evidenceId: input.evidenceId,
1407
+ redactedBlockReason: prepared.errors.join(", ") ||
1408
+ "fallback regate plan evidence intent invalid",
1409
+ safeNextActions: ["/flowdesk-status"],
1410
+ authority: blockedAuthority,
1411
+ };
1412
+ const applied = applyFlowDeskSessionEvidenceWriteIntentsV1(input.rootDir, [
1413
+ prepared.writeIntent,
1414
+ ]);
1415
+ if (!applied.ok)
1416
+ return {
1417
+ adapterProfile: "managed_fallback_regate_plan_evidence_materializer",
1418
+ status: "blocked_before_regate_plan_evidence",
1419
+ writeAttempted: false,
1420
+ evidenceReloaded: true,
1421
+ workflowId,
1422
+ evidenceId: input.evidenceId,
1423
+ redactedBlockReason: applied.errors.join(", ") ||
1424
+ "fallback regate plan evidence write failed",
1425
+ safeNextActions: ["/flowdesk-status"],
1426
+ authority: blockedAuthority,
1427
+ };
1428
+ const postWriteReload = reloadFlowDeskSessionEvidenceV1({
1429
+ workflowId,
1430
+ rootDir: input.rootDir,
1431
+ });
1432
+ const persisted = postWriteReload.ok &&
1433
+ postWriteReload.entries.some((entry) => entry.evidenceClass === "fallback_regate_plan" &&
1434
+ entry.evidenceId === input.evidenceId &&
1435
+ entry.record.state === "full_regate_required");
1436
+ if (!persisted)
1437
+ return {
1438
+ adapterProfile: "managed_fallback_regate_plan_evidence_materializer",
1439
+ status: "blocked_before_regate_plan_evidence",
1440
+ writeAttempted: true,
1441
+ evidenceReloaded: false,
1442
+ workflowId,
1443
+ evidenceId: input.evidenceId,
1444
+ redactedBlockReason: "fallback regate plan evidence reload verification failed",
1445
+ safeNextActions: ["/flowdesk-status"],
1446
+ authority: blockedAuthority,
1447
+ };
1448
+ return {
1449
+ adapterProfile: "managed_fallback_regate_plan_evidence_materializer",
1450
+ status: "regate_plan_evidence_recorded",
1451
+ writeAttempted: true,
1452
+ evidenceReloaded: true,
1453
+ workflowId,
1454
+ evidenceId: input.evidenceId,
1455
+ safeNextActions: ["/flowdesk-status", "/flowdesk-run"],
1456
+ authority: {
1457
+ ...blockedAuthority,
1458
+ regatePlanPersisted: true,
1459
+ },
1460
+ };
1461
+ }
1462
+ export function prepareFlowDeskControlledExternalWriteAdapterV1(input) {
1463
+ const promotion = promoteFlowDeskExternalWriteAuthorityV1(input);
1464
+ if (!promotion.ok || promotion.external_write_authority_enabled !== true) {
1465
+ return {
1466
+ adapterProfile: "controlled_external_write_adapter",
1467
+ status: "blocked_before_write",
1468
+ writeAttempted: false,
1469
+ workflowId: input.request.workflow_id,
1470
+ attemptId: input.request.attempt_id,
1471
+ redactedBlockReason: promotion.errors.join(",") || "controlled external write blocked",
1472
+ safeNextActions: ["/flowdesk-status"],
1473
+ authority: controlledExternalWriteAuthority(false),
1474
+ };
1475
+ }
1476
+ return {
1477
+ adapterProfile: "controlled_external_write_adapter",
1478
+ status: "write_ready",
1479
+ writeAttempted: false,
1480
+ workflowId: input.request.workflow_id,
1481
+ attemptId: input.request.attempt_id,
1482
+ targetKind: input.request.target_kind,
1483
+ targetRef: input.request.target_ref,
1484
+ safeNextActions: ["/flowdesk-status", "/flowdesk-export-debug"],
1485
+ authority: controlledExternalWriteAuthority(true),
1486
+ };
1487
+ }
1488
+ export function prepareFlowDeskReviewerTypedVerdictAcceptanceAdapterV1(input) {
1489
+ const promotion = promoteFlowDeskReviewerTypedVerdictsV1(input);
1490
+ if (!promotion.ok ||
1491
+ promotion.typed_reviewer_verdict_acceptance_enabled !== true) {
1492
+ return {
1493
+ adapterProfile: "reviewer_typed_verdict_acceptance_adapter",
1494
+ status: "blocked_before_acceptance",
1495
+ workflowId: input.workflowId,
1496
+ attemptId: input.attemptId,
1497
+ acceptedVerdictIds: [],
1498
+ acceptedPerspectives: [],
1499
+ redactedBlockReason: promotion.errors.join(",") || "reviewer verdict acceptance blocked",
1500
+ safeNextActions: ["/flowdesk-status"],
1501
+ authority: reviewerTypedVerdictAuthority(false),
1502
+ };
1503
+ }
1504
+ return {
1505
+ adapterProfile: "reviewer_typed_verdict_acceptance_adapter",
1506
+ status: "verdicts_accepted",
1507
+ workflowId: input.workflowId,
1508
+ attemptId: input.attemptId,
1509
+ acceptedVerdictIds: promotion.accepted_verdict_ids ?? [],
1510
+ acceptedPerspectives: promotion.accepted_perspectives ?? [],
1511
+ safeNextActions: ["/flowdesk-status", "/flowdesk-run"],
1512
+ authority: reviewerTypedVerdictAuthority(true),
1513
+ };
1514
+ }
1515
+ function durableReviewerVerdictIds(reloadedEvidence) {
1516
+ return new Set(reloadedEvidence.entries
1517
+ .filter((entry) => entry.evidenceClass === "reviewer_verdict")
1518
+ .map((entry) => entry.record.verdict_id)
1519
+ .filter((value) => typeof value === "string"));
1520
+ }
1521
+ function durableCompleteLifecycleRefs(input) {
1522
+ const verdictIds = new Set(input.verdictIds);
1523
+ const refs = new Map();
1524
+ for (const entry of input.reloadedEvidence.entries) {
1525
+ if (entry.evidenceClass !== "lane_lifecycle")
1526
+ continue;
1527
+ const record = entry.record;
1528
+ if (record.workflow_id === input.workflowId &&
1529
+ record.attempt_id === input.attemptId &&
1530
+ record.state === "complete" &&
1531
+ typeof record.verdict_ref === "string" &&
1532
+ verdictIds.has(record.verdict_ref)) {
1533
+ refs.set(record.verdict_ref, entry.evidenceId);
1534
+ }
1535
+ }
1536
+ return refs;
1537
+ }
1538
+ export function prepareFlowDeskDurableReviewerVerdictLinkageAdapterV1(input) {
1539
+ const acceptance = prepareFlowDeskReviewerTypedVerdictAcceptanceAdapterV1({
1540
+ workflowId: input.workflowId,
1541
+ attemptId: input.attemptId,
1542
+ verdicts: input.verdicts,
1543
+ consumedApproval: input.consumedApproval,
1544
+ });
1545
+ if (acceptance.status !== "verdicts_accepted") {
1546
+ return {
1547
+ adapterProfile: "durable_reviewer_verdict_linkage_adapter",
1548
+ status: "blocked_before_durable_acceptance",
1549
+ workflowId: input.workflowId,
1550
+ attemptId: input.attemptId,
1551
+ linkedVerdictIds: [],
1552
+ linkedLifecycleRefs: [],
1553
+ redactedBlockReason: acceptance.redactedBlockReason ?? "reviewer verdict acceptance blocked",
1554
+ safeNextActions: ["/flowdesk-status"],
1555
+ authority: durableReviewerVerdictAuthority(false),
1556
+ };
1557
+ }
1558
+ if (!input.reloadedEvidence.ok || input.reloadedEvidence.blocked.length > 0) {
1559
+ return {
1560
+ adapterProfile: "durable_reviewer_verdict_linkage_adapter",
1561
+ status: "blocked_before_durable_acceptance",
1562
+ workflowId: input.workflowId,
1563
+ attemptId: input.attemptId,
1564
+ linkedVerdictIds: [],
1565
+ linkedLifecycleRefs: [],
1566
+ redactedBlockReason: "reviewer durable evidence reload is blocked",
1567
+ safeNextActions: ["/flowdesk-status"],
1568
+ authority: durableReviewerVerdictAuthority(false),
1569
+ };
1570
+ }
1571
+ const requiredVerdictIds = acceptance.acceptedVerdictIds;
1572
+ const verdictEvidence = durableReviewerVerdictIds(input.reloadedEvidence);
1573
+ const lifecycleRefs = durableCompleteLifecycleRefs({
1574
+ reloadedEvidence: input.reloadedEvidence,
1575
+ workflowId: input.workflowId,
1576
+ attemptId: input.attemptId,
1577
+ verdictIds: requiredVerdictIds,
1578
+ });
1579
+ const missingVerdicts = requiredVerdictIds.filter((verdictId) => !verdictEvidence.has(verdictId));
1580
+ const missingLifecycle = requiredVerdictIds.filter((verdictId) => !lifecycleRefs.has(verdictId));
1581
+ if (missingVerdicts.length > 0 || missingLifecycle.length > 0) {
1582
+ return {
1583
+ adapterProfile: "durable_reviewer_verdict_linkage_adapter",
1584
+ status: "blocked_before_durable_acceptance",
1585
+ workflowId: input.workflowId,
1586
+ attemptId: input.attemptId,
1587
+ linkedVerdictIds: requiredVerdictIds.filter((verdictId) => verdictEvidence.has(verdictId)),
1588
+ linkedLifecycleRefs: [...lifecycleRefs.values()],
1589
+ redactedBlockReason: [
1590
+ missingVerdicts.length > 0
1591
+ ? "missing durable reviewer verdict evidence"
1592
+ : undefined,
1593
+ missingLifecycle.length > 0
1594
+ ? "missing complete lane lifecycle evidence"
1595
+ : undefined,
1596
+ ]
1597
+ .filter((reason) => reason !== undefined)
1598
+ .join(", "),
1599
+ safeNextActions: ["/flowdesk-status"],
1600
+ authority: durableReviewerVerdictAuthority(false),
1601
+ };
1602
+ }
1603
+ return {
1604
+ adapterProfile: "durable_reviewer_verdict_linkage_adapter",
1605
+ status: "durable_verdicts_accepted",
1606
+ workflowId: input.workflowId,
1607
+ attemptId: input.attemptId,
1608
+ linkedVerdictIds: requiredVerdictIds,
1609
+ linkedLifecycleRefs: [...lifecycleRefs.values()],
1610
+ safeNextActions: ["/flowdesk-status", "/flowdesk-run"],
1611
+ authority: durableReviewerVerdictAuthority(true),
1612
+ };
1613
+ }
1614
+ export function materializeFlowDeskObservedReviewerVerdictEvidenceV1(input) {
1615
+ const evidenceId = input.evidenceId ?? input.observation.verdictId;
1616
+ if (typeof input.rootDir !== "string" || input.rootDir.trim().length === 0)
1617
+ return blockObservedReviewerVerdictEvidence({
1618
+ observation: input.observation,
1619
+ evidenceId,
1620
+ reason: "rootDir is required",
1621
+ });
1622
+ if (input.observation.status !== "verdict_observed" ||
1623
+ input.observation.verdict === undefined)
1624
+ return blockObservedReviewerVerdictEvidence({
1625
+ observation: input.observation,
1626
+ evidenceId,
1627
+ reason: "reviewer verdict observation must be verdict_observed",
1628
+ });
1629
+ if (evidenceId === undefined)
1630
+ return blockObservedReviewerVerdictEvidence({
1631
+ observation: input.observation,
1632
+ reason: "reviewer verdict evidence id is required",
1633
+ });
1634
+ const validation = validateTopTierReviewVerdictV1(input.observation.verdict);
1635
+ if (!validation.ok)
1636
+ return blockObservedReviewerVerdictEvidence({
1637
+ observation: input.observation,
1638
+ evidenceId,
1639
+ reason: validation.errors.join(", ") || "reviewer verdict is invalid",
1640
+ });
1641
+ const preWriteReload = reloadFlowDeskSessionEvidenceV1({
1642
+ workflowId: input.observation.workflowId,
1643
+ rootDir: input.rootDir,
1644
+ });
1645
+ if (!preWriteReload.ok || preWriteReload.blocked.length > 0)
1646
+ return blockObservedReviewerVerdictEvidence({
1647
+ observation: input.observation,
1648
+ evidenceId,
1649
+ reason: "reviewer verdict pre-write evidence reload failed",
1650
+ evidenceReloaded: false,
1651
+ });
1652
+ if (preWriteReload.entries.some((entry) => entry.evidenceClass === "reviewer_verdict" &&
1653
+ entry.evidenceId === evidenceId))
1654
+ return blockObservedReviewerVerdictEvidence({
1655
+ observation: input.observation,
1656
+ evidenceId,
1657
+ reason: "reviewer verdict evidence already exists",
1658
+ evidenceReloaded: true,
1659
+ });
1660
+ const prepared = prepareFlowDeskSessionEvidenceWriteIntentV1({
1661
+ workflowId: input.observation.workflowId,
1662
+ evidenceId,
1663
+ record: input.observation.verdict,
1664
+ });
1665
+ if (!prepared.ok || prepared.writeIntent === undefined)
1666
+ return blockObservedReviewerVerdictEvidence({
1667
+ observation: input.observation,
1668
+ evidenceId,
1669
+ reason: prepared.errors.join(", ") ||
1670
+ "reviewer verdict evidence intent invalid",
1671
+ evidenceReloaded: true,
1672
+ });
1673
+ const applied = applyFlowDeskSessionEvidenceWriteIntentsV1(input.rootDir, [
1674
+ prepared.writeIntent,
1675
+ ]);
1676
+ if (!applied.ok)
1677
+ return blockObservedReviewerVerdictEvidence({
1678
+ observation: input.observation,
1679
+ evidenceId,
1680
+ reason: applied.errors.join(", ") || "reviewer verdict evidence write failed",
1681
+ evidenceReloaded: true,
1682
+ });
1683
+ const postWriteReload = reloadFlowDeskSessionEvidenceV1({
1684
+ workflowId: input.observation.workflowId,
1685
+ rootDir: input.rootDir,
1686
+ });
1687
+ const persisted = postWriteReload.ok &&
1688
+ postWriteReload.entries.some((entry) => entry.evidenceClass === "reviewer_verdict" &&
1689
+ entry.evidenceId === evidenceId &&
1690
+ entry.record.verdict_id === input.observation.verdictId);
1691
+ if (!persisted)
1692
+ return blockObservedReviewerVerdictEvidence({
1693
+ observation: input.observation,
1694
+ evidenceId,
1695
+ reason: "reviewer verdict evidence reload verification failed",
1696
+ evidenceReloaded: false,
1697
+ });
1698
+ return {
1699
+ adapterProfile: "observed_reviewer_verdict_evidence_materializer",
1700
+ status: "verdict_evidence_recorded",
1701
+ writeAttempted: true,
1702
+ workflowId: input.observation.workflowId,
1703
+ sessionRef: input.observation.sessionRef,
1704
+ lanePlanRef: input.observation.lanePlanRef,
1705
+ bindingRef: input.observation.bindingRef,
1706
+ perspective: input.observation.perspective,
1707
+ verdictId: input.observation.verdictId,
1708
+ evidenceId,
1709
+ evidenceReloaded: true,
1710
+ safeNextActions: ["/flowdesk-status"],
1711
+ authority: observedReviewerVerdictEvidenceAuthority(true),
1712
+ };
1713
+ }
1714
+ function idempotencySnapshotFrom(entry) {
1715
+ if (entry?.evidenceClass !== "dispatch_idempotency")
1716
+ return undefined;
1717
+ return entry.record;
1718
+ }
1719
+ function existingIdempotencySnapshot(reloadedEvidence) {
1720
+ const snapshots = reloadedEvidence.entries
1721
+ .map(idempotencySnapshotFrom)
1722
+ .filter((snapshot) => snapshot !== undefined);
1723
+ const latest = snapshots[snapshots.length - 1];
1724
+ if (latest === undefined)
1725
+ return undefined;
1726
+ const entriesByKey = new Map();
1727
+ for (const snapshot of snapshots) {
1728
+ for (const entry of snapshot.entries)
1729
+ entriesByKey.set(entry.idempotency_key, entry);
1730
+ }
1731
+ return { ...latest, entries: [...entriesByKey.values()] };
1732
+ }
1733
+ function currentIdempotencySnapshot(rootDir, workflowId) {
1734
+ const reloaded = reloadFlowDeskSessionEvidenceV1({ workflowId, rootDir });
1735
+ if (!reloaded.ok || reloaded.blocked.length > 0)
1736
+ return { ok: false, redactedFailureReason: "reservation reload failed" };
1737
+ return { ok: true, snapshot: existingIdempotencySnapshot(reloaded) };
1738
+ }
1739
+ function reservationKey(manifest) {
1740
+ return `${manifest.workflow_id}:${manifest.attempt_id}:${manifest.idempotency_key}`;
1741
+ }
1742
+ function snapshotRefFor(manifest, suffix) {
1743
+ return `idempotency-${manifest.attempt_id}-${suffix}`
1744
+ .replaceAll(/[^A-Za-z0-9_.:-]/g, "-")
1745
+ .slice(0, 96);
1746
+ }
1747
+ function hasMatchingEntry(snapshot, manifest, state) {
1748
+ return snapshot.entries.some((entry) => entry.attempt_id === manifest.attempt_id &&
1749
+ entry.idempotency_key === manifest.idempotency_key &&
1750
+ (state === undefined || entry.state === state));
1751
+ }
1752
+ function materializeSnapshot(input) {
1753
+ const prepared = prepareFlowDeskSessionEvidenceWriteIntentV1({
1754
+ workflowId: input.manifest.workflow_id,
1755
+ evidenceId: input.evidenceId,
1756
+ record: input.snapshot,
1757
+ });
1758
+ if (!prepared.ok || prepared.writeIntent === undefined) {
1759
+ return {
1760
+ ok: false,
1761
+ reservationEvidenceReloaded: false,
1762
+ redactedFailureReason: "reservation write intent invalid",
1763
+ };
1764
+ }
1765
+ const applied = applyFlowDeskSessionEvidenceWriteIntentsV1(input.rootDir, [
1766
+ prepared.writeIntent,
1767
+ ]);
1768
+ if (!applied.ok) {
1769
+ return {
1770
+ ok: false,
1771
+ reservationEvidenceReloaded: false,
1772
+ redactedFailureReason: "reservation materialization failed",
1773
+ };
1774
+ }
1775
+ const reloaded = reloadFlowDeskSessionEvidenceV1({
1776
+ workflowId: input.manifest.workflow_id,
1777
+ rootDir: input.rootDir,
1778
+ });
1779
+ if (!reloaded.ok || reloaded.blocked.length > 0) {
1780
+ return {
1781
+ ok: false,
1782
+ reservationEvidenceReloaded: false,
1783
+ redactedFailureReason: "reservation reload failed",
1784
+ };
1785
+ }
1786
+ const reloadedSnapshot = idempotencySnapshotFrom(reloaded.entries.find((entry) => entry.evidenceClass === "dispatch_idempotency" &&
1787
+ entry.evidenceId === input.evidenceId));
1788
+ if (reloadedSnapshot === undefined ||
1789
+ !hasMatchingEntry(reloadedSnapshot, input.manifest, input.expectedState)) {
1790
+ return {
1791
+ ok: false,
1792
+ reservationEvidenceReloaded: false,
1793
+ redactedFailureReason: "reservation evidence missing after reload",
1794
+ };
1795
+ }
1796
+ return {
1797
+ ok: true,
1798
+ reservationEvidenceReloaded: true,
1799
+ snapshot: reloadedSnapshot,
1800
+ };
1801
+ }
1802
+ export function createFlowDeskManagedDispatchBetaDurableReservationStoreV1(options) {
1803
+ const now = options.now ?? (() => new Date());
1804
+ const reservedSnapshots = new Map();
1805
+ return {
1806
+ reserve(input) {
1807
+ const recordedAt = now().toISOString();
1808
+ const evidenceId = snapshotRefFor(input.manifest, "reserved");
1809
+ const current = currentIdempotencySnapshot(options.rootDir, input.manifest.workflow_id);
1810
+ if (!current.ok) {
1811
+ return {
1812
+ ok: false,
1813
+ reservationEvidenceReloaded: false,
1814
+ redactedFailureReason: current.redactedFailureReason,
1815
+ };
1816
+ }
1817
+ const reservation = prepareFlowDeskDispatchIdempotencyReservationV1({
1818
+ workflowId: input.manifest.workflow_id,
1819
+ attemptId: input.manifest.attempt_id,
1820
+ idempotencyKey: input.manifest.idempotency_key,
1821
+ snapshotRef: evidenceId,
1822
+ reservedAt: recordedAt,
1823
+ existingSnapshot: current.snapshot ??
1824
+ existingIdempotencySnapshot(input.reloadedEvidence),
1825
+ });
1826
+ if (!reservation.reservation_prepared ||
1827
+ reservation.snapshot === undefined) {
1828
+ return {
1829
+ ok: false,
1830
+ reservationEvidenceReloaded: false,
1831
+ redactedFailureReason: "reservation preparation blocked",
1832
+ };
1833
+ }
1834
+ const materialized = materializeSnapshot({
1835
+ rootDir: options.rootDir,
1836
+ manifest: input.manifest,
1837
+ evidenceId,
1838
+ snapshot: reservation.snapshot,
1839
+ expectedState: "reserved",
1840
+ });
1841
+ if (materialized.ok && materialized.snapshot !== undefined)
1842
+ reservedSnapshots.set(reservationKey(input.manifest), materialized.snapshot);
1843
+ return {
1844
+ ok: materialized.ok,
1845
+ reservationEvidenceReloaded: materialized.reservationEvidenceReloaded,
1846
+ ...(materialized.redactedFailureReason === undefined
1847
+ ? {}
1848
+ : { redactedFailureReason: materialized.redactedFailureReason }),
1849
+ };
1850
+ },
1851
+ recordDispatchFailure(input) {
1852
+ const recordedAt = now().toISOString();
1853
+ const evidenceId = snapshotRefFor(input.manifest, "dispatch-failed");
1854
+ const current = currentIdempotencySnapshot(options.rootDir, input.manifest.workflow_id);
1855
+ if (!current.ok) {
1856
+ return {
1857
+ ok: false,
1858
+ reservationEvidenceReloaded: false,
1859
+ redactedFailureReason: current.redactedFailureReason,
1860
+ };
1861
+ }
1862
+ const stateUpdate = prepareFlowDeskDispatchIdempotencyStateUpdateV1({
1863
+ workflowId: input.manifest.workflow_id,
1864
+ attemptId: input.manifest.attempt_id,
1865
+ idempotencyKey: input.manifest.idempotency_key,
1866
+ snapshotRef: evidenceId,
1867
+ recordedAt,
1868
+ nextState: "dispatch_failed",
1869
+ existingSnapshot: reservedSnapshots.get(reservationKey(input.manifest)) ??
1870
+ current.snapshot ??
1871
+ existingIdempotencySnapshot(input.reloadedEvidence),
1872
+ });
1873
+ if (!stateUpdate.state_update_prepared ||
1874
+ stateUpdate.snapshot === undefined) {
1875
+ return {
1876
+ ok: false,
1877
+ reservationEvidenceReloaded: false,
1878
+ redactedFailureReason: "failure state preparation blocked",
1879
+ };
1880
+ }
1881
+ const materialized = materializeSnapshot({
1882
+ rootDir: options.rootDir,
1883
+ manifest: input.manifest,
1884
+ evidenceId,
1885
+ snapshot: stateUpdate.snapshot,
1886
+ expectedState: "dispatch_failed",
1887
+ });
1888
+ if (materialized.ok && materialized.snapshot !== undefined)
1889
+ reservedSnapshots.set(reservationKey(input.manifest), materialized.snapshot);
1890
+ return {
1891
+ ok: materialized.ok,
1892
+ reservationEvidenceReloaded: materialized.reservationEvidenceReloaded,
1893
+ ...(materialized.redactedFailureReason === undefined
1894
+ ? {}
1895
+ : { redactedFailureReason: materialized.redactedFailureReason }),
1896
+ };
1897
+ },
12
1898
  };
13
1899
  }
14
1900
  function enabledDispatchAuthority() {
@@ -16,15 +1902,19 @@ function enabledDispatchAuthority() {
16
1902
  ...disabledAuthority(),
17
1903
  realOpenCodeDispatch: true,
18
1904
  providerCall: true,
19
- runtimeExecution: true
1905
+ runtimeExecution: true,
20
1906
  };
21
1907
  }
22
1908
  function verificationFor(input) {
23
1909
  return {
24
1910
  ambiguityQuarantined: input.ambiguityQuarantined === true,
25
- ...(input.configuredVerificationRef === undefined ? {} : { configuredVerificationRef: input.configuredVerificationRef }),
26
- ...(input.preDispatchAuditRef === undefined ? {} : { preDispatchAuditRef: input.preDispatchAuditRef }),
27
- defaultRelease1ServerBehaviorUnchanged: true
1911
+ ...(input.configuredVerificationRef === undefined
1912
+ ? {}
1913
+ : { configuredVerificationRef: input.configuredVerificationRef }),
1914
+ ...(input.preDispatchAuditRef === undefined
1915
+ ? {}
1916
+ : { preDispatchAuditRef: input.preDispatchAuditRef }),
1917
+ defaultRelease1ServerBehaviorUnchanged: true,
28
1918
  };
29
1919
  }
30
1920
  function blocked(input, guardDecision, redactedBlockReason = guardDecision.redacted_reason) {
@@ -35,7 +1925,7 @@ function blocked(input, guardDecision, redactedBlockReason = guardDecision.redac
35
1925
  guardDecision,
36
1926
  redactedBlockReason,
37
1927
  authority: disabledAuthority(),
38
- verification: verificationFor(input)
1928
+ verification: verificationFor(input),
39
1929
  };
40
1930
  }
41
1931
  function parseProviderQualifiedModelId(value) {
@@ -48,17 +1938,51 @@ function parseProviderQualifiedModelId(value) {
48
1938
  return undefined;
49
1939
  return { providerID, modelID };
50
1940
  }
1941
+ function opencodeRuntimeProviderIDForFlowDeskProviderFamily(providerFamily) {
1942
+ switch (providerFamily) {
1943
+ case "claude":
1944
+ return "anthropic";
1945
+ case "gemini":
1946
+ return "google";
1947
+ case "openai":
1948
+ return "openai";
1949
+ default:
1950
+ return undefined;
1951
+ }
1952
+ }
1953
+ function opencodeRuntimeModelForFlowDeskModel(model) {
1954
+ const providerID = opencodeRuntimeProviderIDForFlowDeskProviderFamily(model.providerID);
1955
+ return providerID === undefined
1956
+ ? undefined
1957
+ : { providerID, modelID: model.modelID };
1958
+ }
51
1959
  function refFrom(label, value) {
52
- const safe = value.replaceAll(/[^A-Za-z0-9_.:-]/g, "-").replaceAll(/-+/g, "-").slice(0, 96);
1960
+ const safe = value
1961
+ .replaceAll(/[^A-Za-z0-9_.:-]/g, "-")
1962
+ .replaceAll(/-+/g, "-")
1963
+ .slice(0, 96);
53
1964
  return `${label}-${safe.length > 0 ? safe : "unknown"}`;
54
1965
  }
55
1966
  function asRecord(value) {
56
- return typeof value === "object" && value !== null && !Array.isArray(value) ? value : undefined;
1967
+ return typeof value === "object" && value !== null && !Array.isArray(value)
1968
+ ? value
1969
+ : undefined;
57
1970
  }
58
1971
  function responseData(value) {
59
1972
  const record = asRecord(value);
60
1973
  return record !== undefined && "data" in record ? record.data : value;
61
1974
  }
1975
+ function isSdkErrorResponse(value) {
1976
+ const record = asRecord(value);
1977
+ const data = asRecord(responseData(value));
1978
+ return record?.error !== undefined || data?.error !== undefined;
1979
+ }
1980
+ async function callSdkWithLegacyFallback(method, thisArg, currentOptions, legacyOptions) {
1981
+ const current = await method.call(thisArg, currentOptions);
1982
+ if (!isSdkErrorResponse(current))
1983
+ return current;
1984
+ return method.call(thisArg, legacyOptions);
1985
+ }
62
1986
  function arrayData(value) {
63
1987
  const data = responseData(value);
64
1988
  if (Array.isArray(data))
@@ -71,10 +1995,33 @@ function modelRef(value) {
71
1995
  if (record === undefined)
72
1996
  return undefined;
73
1997
  const providerID = typeof record.providerID === "string" ? record.providerID : undefined;
74
- const modelID = typeof record.modelID === "string" ? record.modelID : typeof record.id === "string" ? record.id : undefined;
75
- return providerID !== undefined && modelID !== undefined ? refFrom("model", `${providerID}-${modelID}`) : undefined;
1998
+ const modelID = typeof record.modelID === "string"
1999
+ ? record.modelID
2000
+ : typeof record.id === "string"
2001
+ ? record.id
2002
+ : undefined;
2003
+ return providerID !== undefined && modelID !== undefined
2004
+ ? refFrom("model", `${providerID}-${modelID}`)
2005
+ : undefined;
2006
+ }
2007
+ function sessionIdFromResponse(value) {
2008
+ const data = responseData(value);
2009
+ const record = asRecord(data);
2010
+ return typeof record?.id === "string" && record.id.trim().length > 0
2011
+ ? record.id
2012
+ : undefined;
2013
+ }
2014
+ function agentIdFromRef(value) {
2015
+ if (value === undefined || !value.startsWith("agent-"))
2016
+ return undefined;
2017
+ const agent = value.slice("agent-".length).trim();
2018
+ return agent.length > 0 ? agent : undefined;
76
2019
  }
77
2020
  function firstMessageRef(value) {
2021
+ const direct = asRecord(responseData(value));
2022
+ const directInfo = asRecord(direct?.info) ?? direct;
2023
+ if (typeof directInfo?.id === "string")
2024
+ return refFrom("message", directInfo.id);
78
2025
  const messages = arrayData(value);
79
2026
  for (const message of messages) {
80
2027
  const record = asRecord(message);
@@ -84,6 +2031,133 @@ function firstMessageRef(value) {
84
2031
  }
85
2032
  return undefined;
86
2033
  }
2034
+ function lifecycleMessageRefFrom(value) {
2035
+ if (value === undefined)
2036
+ return undefined;
2037
+ if (value.startsWith("msg-"))
2038
+ return value;
2039
+ if (value.startsWith("message-"))
2040
+ return `msg-${value.slice("message-".length)}`;
2041
+ return value;
2042
+ }
2043
+ function messageTextCandidates(value) {
2044
+ return arrayData(value).flatMap((message) => {
2045
+ const record = asRecord(message);
2046
+ const parts = Array.isArray(record?.parts) ? record.parts : [];
2047
+ return parts
2048
+ .flatMap((part) => {
2049
+ const partRecord = asRecord(part);
2050
+ const text = typeof partRecord?.text === "string"
2051
+ ? partRecord.text
2052
+ : typeof partRecord?.content === "string"
2053
+ ? partRecord.content
2054
+ : undefined;
2055
+ return text === undefined || text.length > 20_000 ? [] : [text.trim()];
2056
+ })
2057
+ .filter((text) => text.startsWith("{") && text.endsWith("}"));
2058
+ });
2059
+ }
2060
+ function parseJsonCandidate(text) {
2061
+ try {
2062
+ return JSON.parse(text);
2063
+ }
2064
+ catch {
2065
+ return undefined;
2066
+ }
2067
+ }
2068
+ function topTierVerdictCandidates(messagesResponse) {
2069
+ const direct = arrayData(messagesResponse).filter((message) => asRecord(message)?.schema_version ===
2070
+ "flowdesk.top_tier_review_verdict.v1");
2071
+ const parsed = messageTextCandidates(messagesResponse)
2072
+ .map(parseJsonCandidate)
2073
+ .filter((candidate) => candidate !== undefined);
2074
+ return [...direct, ...parsed];
2075
+ }
2076
+ function verdictMatchesRequest(verdict, request) {
2077
+ return [
2078
+ verdict.workflow_id === request.workflowId
2079
+ ? undefined
2080
+ : "verdict workflow_id mismatch",
2081
+ verdict.lane_plan_ref === request.lanePlanRef
2082
+ ? undefined
2083
+ : "verdict lane_plan_ref mismatch",
2084
+ verdict.binding_ref === request.bindingRef
2085
+ ? undefined
2086
+ : "verdict binding_ref mismatch",
2087
+ verdict.perspective === request.perspective
2088
+ ? undefined
2089
+ : "verdict perspective mismatch",
2090
+ ].filter((error) => error !== undefined);
2091
+ }
2092
+ export async function observeInjectedSdkReviewerVerdictV1(input) {
2093
+ const base = {
2094
+ adapterProfile: "injected_sdk_reviewer_verdict_observation",
2095
+ sessionRef: refFrom("session", input.request.sessionId),
2096
+ workflowId: input.request.workflowId,
2097
+ lanePlanRef: input.request.lanePlanRef,
2098
+ bindingRef: input.request.bindingRef,
2099
+ perspective: input.request.perspective,
2100
+ authority: disabledAuthority(),
2101
+ };
2102
+ const messages = input.client.session.messages;
2103
+ if (messages === undefined) {
2104
+ return {
2105
+ ...base,
2106
+ status: "observation_unavailable",
2107
+ observationAttempted: false,
2108
+ redactedErrors: ["session_messages_api_missing"],
2109
+ };
2110
+ }
2111
+ try {
2112
+ const messagesResponse = await callSdkWithLegacyFallback(messages, input.client.session, {
2113
+ sessionID: input.request.sessionId,
2114
+ ...(input.request.directory === undefined
2115
+ ? {}
2116
+ : { directory: input.request.directory }),
2117
+ }, {
2118
+ path: { id: input.request.sessionId },
2119
+ ...(input.request.directory === undefined
2120
+ ? {}
2121
+ : { query: { directory: input.request.directory } }),
2122
+ });
2123
+ const errors = [];
2124
+ for (const candidate of topTierVerdictCandidates(messagesResponse)) {
2125
+ const validation = validateTopTierReviewVerdictV1(candidate);
2126
+ if (!validation.ok) {
2127
+ errors.push(...validation.errors.slice(0, 5));
2128
+ continue;
2129
+ }
2130
+ const verdict = candidate;
2131
+ const matchErrors = verdictMatchesRequest(verdict, input.request);
2132
+ if (matchErrors.length > 0) {
2133
+ errors.push(...matchErrors);
2134
+ continue;
2135
+ }
2136
+ return {
2137
+ ...base,
2138
+ status: "verdict_observed",
2139
+ observationAttempted: true,
2140
+ verdictId: verdict.verdict_id,
2141
+ verdict,
2142
+ redactedErrors: [],
2143
+ };
2144
+ }
2145
+ return {
2146
+ ...base,
2147
+ status: errors.length > 0 ? "invalid_verdict" : "missing_verdict",
2148
+ observationAttempted: true,
2149
+ redactedErrors: [...new Set(errors)],
2150
+ };
2151
+ }
2152
+ catch {
2153
+ return {
2154
+ ...base,
2155
+ status: "observation_failed",
2156
+ observationAttempted: true,
2157
+ redactedErrors: ["session_messages_read_failed"],
2158
+ };
2159
+ }
2160
+ }
87
2161
  export async function observeInjectedSdkLaneV1(input) {
88
2162
  const base = {
89
2163
  adapterProfile: "injected_sdk_lane_observation_probe",
@@ -91,7 +2165,7 @@ export async function observeInjectedSdkLaneV1(input) {
91
2165
  laneId: input.request.laneId,
92
2166
  requestedAgentRef: refFrom("agent", input.request.requestedAgent),
93
2167
  requestedModelRef: refFrom("model", input.request.requestedProviderQualifiedModelId),
94
- authority: disabledAuthority()
2168
+ authority: disabledAuthority(),
95
2169
  };
96
2170
  const children = input.client.session.children;
97
2171
  if (children === undefined) {
@@ -99,24 +2173,45 @@ export async function observeInjectedSdkLaneV1(input) {
99
2173
  ...base,
100
2174
  status: "observation_unavailable",
101
2175
  observationAttempted: false,
102
- missingLabels: ["session_children_api_missing"]
2176
+ missingLabels: ["session_children_api_missing"],
103
2177
  };
104
2178
  }
105
2179
  try {
106
- const childrenResponse = await children.call(input.client.session, {
2180
+ const childrenResponse = await callSdkWithLegacyFallback(children, input.client.session, {
2181
+ sessionID: input.request.parentSessionId,
2182
+ ...(input.request.directory === undefined
2183
+ ? {}
2184
+ : { directory: input.request.directory }),
2185
+ }, {
107
2186
  path: { id: input.request.parentSessionId },
108
- ...(input.request.directory === undefined ? {} : { query: { directory: input.request.directory } })
2187
+ ...(input.request.directory === undefined
2188
+ ? {}
2189
+ : { query: { directory: input.request.directory } }),
109
2190
  });
110
- const childRecord = arrayData(childrenResponse).map(asRecord).find((record) => record !== undefined);
2191
+ const childRecord = arrayData(childrenResponse)
2192
+ .map(asRecord)
2193
+ .find((record) => record !== undefined);
111
2194
  const childSessionId = typeof childRecord?.id === "string" ? childRecord.id : undefined;
112
- const childSessionRef = childSessionId === undefined ? undefined : refFrom("child-session", childSessionId);
113
- const observedAgentRef = typeof childRecord?.agent === "string" ? refFrom("agent", childRecord.agent) : undefined;
2195
+ const childSessionRef = childSessionId === undefined
2196
+ ? undefined
2197
+ : refFrom("child-session", childSessionId);
2198
+ const observedAgentRef = typeof childRecord?.agent === "string"
2199
+ ? refFrom("agent", childRecord.agent)
2200
+ : undefined;
114
2201
  const observedModelRef = modelRef(childRecord?.model);
115
2202
  let messageRef;
116
- if (childSessionId !== undefined && input.client.session.messages !== undefined) {
117
- const messagesResponse = await input.client.session.messages.call(input.client.session, {
2203
+ if (childSessionId !== undefined &&
2204
+ input.client.session.messages !== undefined) {
2205
+ const messagesResponse = await callSdkWithLegacyFallback(input.client.session.messages, input.client.session, {
2206
+ sessionID: childSessionId,
2207
+ ...(input.request.directory === undefined
2208
+ ? {}
2209
+ : { directory: input.request.directory }),
2210
+ }, {
118
2211
  path: { id: childSessionId },
119
- ...(input.request.directory === undefined ? {} : { query: { directory: input.request.directory } })
2212
+ ...(input.request.directory === undefined
2213
+ ? {}
2214
+ : { query: { directory: input.request.directory } }),
120
2215
  });
121
2216
  messageRef = firstMessageRef(messagesResponse);
122
2217
  }
@@ -134,7 +2229,7 @@ export async function observeInjectedSdkLaneV1(input) {
134
2229
  ...(messageRef === undefined ? {} : { messageRef }),
135
2230
  ...(observedAgentRef === undefined ? {} : { observedAgentRef }),
136
2231
  ...(observedModelRef === undefined ? {} : { observedModelRef }),
137
- missingLabels
2232
+ missingLabels,
138
2233
  };
139
2234
  }
140
2235
  catch {
@@ -142,10 +2237,436 @@ export async function observeInjectedSdkLaneV1(input) {
142
2237
  ...base,
143
2238
  status: "observation_failed",
144
2239
  observationAttempted: true,
145
- missingLabels: ["session_observation_failed"]
2240
+ missingLabels: ["session_observation_failed"],
146
2241
  };
147
2242
  }
148
2243
  }
2244
+ export async function launchFlowDeskInjectedSdkRuntimeLaneFromPlanV1(input) {
2245
+ const plan = input.launchPlan;
2246
+ const planValidation = validateFlowDeskRuntimeLaneLaunchPlanV1(plan);
2247
+ if (!planValidation.ok)
2248
+ return blockedRuntimeLaneLaunch(`Runtime lane launch plan invalid: ${planValidation.errors.join(",") || "unknown"}.`, plan);
2249
+ if (plan.state !== "launch_ready")
2250
+ return blockedRuntimeLaneLaunch(`Runtime lane launch plan is not ready: ${plan.blocked_labels.join(",") || "blocked"}.`, plan);
2251
+ if (input.request.allowActualLaneLaunch !== true)
2252
+ return blockedRuntimeLaneLaunch("Explicit actual runtime lane launch opt-in is required.", plan);
2253
+ const parentSessionRef = refFrom("ses", input.request.parentSessionId);
2254
+ if (plan.parent_session_ref !== parentSessionRef)
2255
+ return blockedRuntimeLaneLaunch("Runtime lane launch parent session does not match the launch plan binding.", plan);
2256
+ const agent = agentIdFromRef(plan.agent_ref);
2257
+ const model = plan.provider_qualified_model_id === undefined
2258
+ ? undefined
2259
+ : parseProviderQualifiedModelId(plan.provider_qualified_model_id);
2260
+ const runtimeModel = model === undefined
2261
+ ? undefined
2262
+ : opencodeRuntimeModelForFlowDeskModel(model);
2263
+ const text = input.request.promptText.trim();
2264
+ if (agent === undefined || runtimeModel === undefined || text.length === 0)
2265
+ return blockedRuntimeLaneLaunch("Runtime lane launch is missing an agent, runtime model, or bounded prompt text.", plan);
2266
+ if (text.length > 20_000)
2267
+ return blockedRuntimeLaneLaunch("Runtime lane launch prompt text exceeds the bounded prompt limit.", plan);
2268
+ const create = input.client.session.create;
2269
+ if (create === undefined)
2270
+ return blockedRuntimeLaneLaunch("Injected OpenCode client is missing session.create for child lane launch.", plan);
2271
+ const dispatchMethod = input.request.dispatchMethod ?? "promptAsync";
2272
+ const dispatch = input.client.session[dispatchMethod];
2273
+ if (dispatch === undefined)
2274
+ return blockedRuntimeLaneLaunch("Injected OpenCode client is missing the requested session prompt method.", plan);
2275
+ let childSessionId;
2276
+ try {
2277
+ childSessionId = sessionIdFromResponse(await callSdkWithLegacyFallback(create, input.client.session, {
2278
+ parentID: input.request.parentSessionId,
2279
+ ...(input.request.title === undefined
2280
+ ? {}
2281
+ : { title: input.request.title.slice(0, 120) }),
2282
+ }, {
2283
+ body: {
2284
+ parentID: input.request.parentSessionId,
2285
+ ...(input.request.title === undefined
2286
+ ? {}
2287
+ : { title: input.request.title.slice(0, 120) }),
2288
+ },
2289
+ }));
2290
+ }
2291
+ catch {
2292
+ return {
2293
+ adapterProfile: "injected_sdk_runtime_lane_launch_adapter",
2294
+ status: "lane_launch_failed",
2295
+ createAttempted: true,
2296
+ promptAttempted: false,
2297
+ workflowId: plan.workflow_id,
2298
+ attemptId: plan.attempt_id,
2299
+ laneId: plan.lane_id,
2300
+ parentSessionRef: plan.parent_session_ref,
2301
+ redactedErrorCategory: "runtime",
2302
+ safeNextActions: ["/flowdesk-status", "/flowdesk-export-debug"],
2303
+ authority: runtimeLaneLaunchAuthority(false),
2304
+ };
2305
+ }
2306
+ if (childSessionId === undefined)
2307
+ return {
2308
+ adapterProfile: "injected_sdk_runtime_lane_launch_adapter",
2309
+ status: "lane_launch_failed",
2310
+ createAttempted: true,
2311
+ promptAttempted: false,
2312
+ workflowId: plan.workflow_id,
2313
+ attemptId: plan.attempt_id,
2314
+ laneId: plan.lane_id,
2315
+ parentSessionRef: plan.parent_session_ref,
2316
+ redactedErrorCategory: "runtime",
2317
+ safeNextActions: ["/flowdesk-status", "/flowdesk-export-debug"],
2318
+ authority: runtimeLaneLaunchAuthority(false),
2319
+ };
2320
+ let response;
2321
+ try {
2322
+ response = await callSdkWithLegacyFallback(dispatch, input.client.session, {
2323
+ sessionID: childSessionId,
2324
+ ...(input.request.directory === undefined
2325
+ ? {}
2326
+ : { directory: input.request.directory }),
2327
+ model: runtimeModel,
2328
+ agent,
2329
+ parts: [{ type: "text", text }],
2330
+ }, {
2331
+ path: { id: childSessionId },
2332
+ ...(input.request.directory === undefined
2333
+ ? {}
2334
+ : { query: { directory: input.request.directory } }),
2335
+ body: {
2336
+ model: runtimeModel,
2337
+ agent,
2338
+ parts: [{ type: "text", text }],
2339
+ },
2340
+ });
2341
+ if (isSdkErrorResponse(response))
2342
+ throw new Error("sdk prompt failed");
2343
+ }
2344
+ catch {
2345
+ return {
2346
+ adapterProfile: "injected_sdk_runtime_lane_launch_adapter",
2347
+ status: "lane_launch_failed",
2348
+ createAttempted: true,
2349
+ promptAttempted: true,
2350
+ workflowId: plan.workflow_id,
2351
+ attemptId: plan.attempt_id,
2352
+ laneId: plan.lane_id,
2353
+ parentSessionRef: plan.parent_session_ref,
2354
+ childSessionRef: refFrom("ses", childSessionId),
2355
+ agent,
2356
+ model: runtimeModel,
2357
+ dispatchMethod,
2358
+ redactedErrorCategory: "provider_api",
2359
+ safeNextActions: ["/flowdesk-status", "/flowdesk-export-debug"],
2360
+ authority: runtimeLaneLaunchAuthority(false),
2361
+ };
2362
+ }
2363
+ return {
2364
+ adapterProfile: "injected_sdk_runtime_lane_launch_adapter",
2365
+ status: "lane_launch_started",
2366
+ createAttempted: true,
2367
+ promptAttempted: true,
2368
+ workflowId: plan.workflow_id,
2369
+ attemptId: plan.attempt_id,
2370
+ laneId: plan.lane_id,
2371
+ parentSessionRef: plan.parent_session_ref,
2372
+ childSessionRef: refFrom("ses", childSessionId),
2373
+ ...(firstMessageRef(response) === undefined
2374
+ ? {}
2375
+ : { messageRef: firstMessageRef(response) }),
2376
+ agent,
2377
+ model: runtimeModel,
2378
+ dispatchMethod,
2379
+ safeNextActions: ["/flowdesk-status"],
2380
+ authority: runtimeLaneLaunchAuthority(true),
2381
+ };
2382
+ }
2383
+ export function materializeFlowDeskRuntimeLaneLaunchLifecycleEvidenceV1(input) {
2384
+ if (typeof input.rootDir !== "string" || input.rootDir.trim().length === 0)
2385
+ return blockRuntimeLaneLaunchLifecycle({
2386
+ plan: input.launchPlan,
2387
+ evidenceId: input.evidenceId,
2388
+ reason: "rootDir is required",
2389
+ });
2390
+ const planValidation = validateFlowDeskRuntimeLaneLaunchPlanV1(input.launchPlan);
2391
+ if (!planValidation.ok || input.launchPlan.state !== "launch_ready")
2392
+ return blockRuntimeLaneLaunchLifecycle({
2393
+ plan: input.launchPlan,
2394
+ evidenceId: input.evidenceId,
2395
+ reason: planValidation.errors.join(", ") ||
2396
+ "runtime lane launch plan must be launch_ready",
2397
+ });
2398
+ if (input.launchResult.status !== "lane_launch_started" &&
2399
+ input.launchResult.status !== "lane_launch_failed")
2400
+ return blockRuntimeLaneLaunchLifecycle({
2401
+ plan: input.launchPlan,
2402
+ evidenceId: input.evidenceId,
2403
+ reason: "runtime lane launch result must be started or failed",
2404
+ });
2405
+ if (input.launchResult.workflowId !== input.launchPlan.workflow_id ||
2406
+ input.launchResult.attemptId !== input.launchPlan.attempt_id ||
2407
+ input.launchResult.laneId !== input.launchPlan.lane_id ||
2408
+ input.launchResult.parentSessionRef !== input.launchPlan.parent_session_ref)
2409
+ return blockRuntimeLaneLaunchLifecycle({
2410
+ plan: input.launchPlan,
2411
+ evidenceId: input.evidenceId,
2412
+ reason: "runtime lane launch result does not match launch plan binding",
2413
+ });
2414
+ const state = input.launchResult.status === "lane_launch_started"
2415
+ ? "running"
2416
+ : "invocation_failed";
2417
+ const record = {
2418
+ schema_version: "flowdesk.lane_lifecycle_record.v1",
2419
+ lane_id: input.launchPlan.lane_id ?? "lane-missing",
2420
+ workflow_id: input.launchPlan.workflow_id ?? "workflow-missing",
2421
+ attempt_id: input.launchPlan.attempt_id ?? "attempt-missing",
2422
+ parent_session_ref: input.launchPlan.parent_session_ref ?? "ses-missing",
2423
+ ...(input.launchResult.childSessionRef === undefined
2424
+ ? {}
2425
+ : { child_session_ref: input.launchResult.childSessionRef }),
2426
+ ...(lifecycleMessageRefFrom(input.launchResult.messageRef) === undefined
2427
+ ? {}
2428
+ : { message_ref: lifecycleMessageRefFrom(input.launchResult.messageRef) }),
2429
+ agent_ref: input.launchPlan.agent_ref ?? "agent-missing",
2430
+ provider_qualified_model_id: input.launchPlan.provider_qualified_model_id ?? "claude/missing",
2431
+ state,
2432
+ timeout_ms: input.timeoutMs ?? 0,
2433
+ orphan_max_age_ms: input.orphanMaxAgeMs ?? 0,
2434
+ retry_count: input.retryCount ?? 0,
2435
+ created_at: input.observedAt,
2436
+ updated_at: input.observedAt,
2437
+ dispatch_authority_enabled: false,
2438
+ providerCall: false,
2439
+ actualLaneLaunch: false,
2440
+ runtimeExecution: false,
2441
+ };
2442
+ const preWriteReload = reloadFlowDeskSessionEvidenceV1({
2443
+ workflowId: record.workflow_id,
2444
+ rootDir: input.rootDir,
2445
+ });
2446
+ if (!preWriteReload.ok || preWriteReload.blocked.length > 0)
2447
+ return blockRuntimeLaneLaunchLifecycle({
2448
+ plan: input.launchPlan,
2449
+ evidenceId: input.evidenceId,
2450
+ reason: "lane lifecycle pre-write evidence reload failed",
2451
+ evidenceReloaded: false,
2452
+ });
2453
+ if (preWriteReload.entries.some((entry) => entry.evidenceClass === "lane_lifecycle" &&
2454
+ entry.evidenceId === input.evidenceId))
2455
+ return blockRuntimeLaneLaunchLifecycle({
2456
+ plan: input.launchPlan,
2457
+ evidenceId: input.evidenceId,
2458
+ reason: "lane lifecycle evidence already exists",
2459
+ evidenceReloaded: true,
2460
+ });
2461
+ const prepared = prepareFlowDeskSessionEvidenceWriteIntentV1({
2462
+ workflowId: record.workflow_id,
2463
+ evidenceId: input.evidenceId,
2464
+ record: record,
2465
+ });
2466
+ if (!prepared.ok || prepared.writeIntent === undefined)
2467
+ return blockRuntimeLaneLaunchLifecycle({
2468
+ plan: input.launchPlan,
2469
+ evidenceId: input.evidenceId,
2470
+ reason: prepared.errors.join(", ") || "lane lifecycle evidence intent invalid",
2471
+ evidenceReloaded: true,
2472
+ });
2473
+ const applied = applyFlowDeskSessionEvidenceWriteIntentsV1(input.rootDir, [
2474
+ prepared.writeIntent,
2475
+ ]);
2476
+ if (!applied.ok)
2477
+ return blockRuntimeLaneLaunchLifecycle({
2478
+ plan: input.launchPlan,
2479
+ evidenceId: input.evidenceId,
2480
+ reason: applied.errors.join(", ") || "lane lifecycle evidence write failed",
2481
+ evidenceReloaded: true,
2482
+ });
2483
+ const postWriteReload = reloadFlowDeskSessionEvidenceV1({
2484
+ workflowId: record.workflow_id,
2485
+ rootDir: input.rootDir,
2486
+ });
2487
+ const persisted = postWriteReload.ok &&
2488
+ postWriteReload.entries.some((entry) => entry.evidenceClass === "lane_lifecycle" &&
2489
+ entry.evidenceId === input.evidenceId &&
2490
+ entry.record.lane_id === record.lane_id &&
2491
+ entry.record.state === record.state);
2492
+ if (!persisted)
2493
+ return blockRuntimeLaneLaunchLifecycle({
2494
+ plan: input.launchPlan,
2495
+ evidenceId: input.evidenceId,
2496
+ reason: "lane lifecycle evidence reload verification failed",
2497
+ evidenceReloaded: false,
2498
+ });
2499
+ return {
2500
+ adapterProfile: "runtime_lane_launch_lifecycle_materializer",
2501
+ status: "lane_lifecycle_recorded",
2502
+ writeAttempted: true,
2503
+ evidenceReloaded: true,
2504
+ workflowId: record.workflow_id,
2505
+ attemptId: record.attempt_id,
2506
+ laneId: record.lane_id,
2507
+ evidenceId: input.evidenceId,
2508
+ lifecycleState: record.state,
2509
+ safeNextActions: ["/flowdesk-status"],
2510
+ authority: runtimeLaneLaunchLifecycleAuthority(true),
2511
+ };
2512
+ }
2513
+ export function materializeFlowDeskRuntimeLaneCompleteLifecycleEvidenceV1(input) {
2514
+ if (typeof input.rootDir !== "string" || input.rootDir.trim().length === 0)
2515
+ return blockRuntimeLaneLaunchLifecycle({
2516
+ plan: input.launchPlan,
2517
+ evidenceId: input.evidenceId,
2518
+ reason: "rootDir is required",
2519
+ });
2520
+ const planValidation = validateFlowDeskRuntimeLaneLaunchPlanV1(input.launchPlan);
2521
+ if (!planValidation.ok || input.launchPlan.state !== "launch_ready")
2522
+ return blockRuntimeLaneLaunchLifecycle({
2523
+ plan: input.launchPlan,
2524
+ evidenceId: input.evidenceId,
2525
+ reason: planValidation.errors.join(", ") ||
2526
+ "runtime lane launch plan must be launch_ready",
2527
+ });
2528
+ if (input.launchResult.status !== "lane_launch_started")
2529
+ return blockRuntimeLaneLaunchLifecycle({
2530
+ plan: input.launchPlan,
2531
+ evidenceId: input.evidenceId,
2532
+ reason: "runtime lane launch result must be lane_launch_started",
2533
+ });
2534
+ if (input.launchResult.workflowId !== input.launchPlan.workflow_id ||
2535
+ input.launchResult.attemptId !== input.launchPlan.attempt_id ||
2536
+ input.launchResult.laneId !== input.launchPlan.lane_id ||
2537
+ input.launchResult.parentSessionRef !== input.launchPlan.parent_session_ref)
2538
+ return blockRuntimeLaneLaunchLifecycle({
2539
+ plan: input.launchPlan,
2540
+ evidenceId: input.evidenceId,
2541
+ reason: "runtime lane launch result does not match launch plan binding",
2542
+ });
2543
+ if (input.verdictObservation.status !== "verdict_observed" ||
2544
+ input.verdictObservation.verdict === undefined ||
2545
+ input.verdictObservation.verdictId === undefined)
2546
+ return blockRuntimeLaneLaunchLifecycle({
2547
+ plan: input.launchPlan,
2548
+ evidenceId: input.evidenceId,
2549
+ reason: "reviewer verdict observation must be verdict_observed",
2550
+ });
2551
+ if (input.verdictObservation.workflowId !== input.launchPlan.workflow_id)
2552
+ return blockRuntimeLaneLaunchLifecycle({
2553
+ plan: input.launchPlan,
2554
+ evidenceId: input.evidenceId,
2555
+ reason: "reviewer verdict observation workflow does not match launch plan",
2556
+ });
2557
+ const verdictValidation = validateTopTierReviewVerdictV1(input.verdictObservation.verdict);
2558
+ if (!verdictValidation.ok)
2559
+ return blockRuntimeLaneLaunchLifecycle({
2560
+ plan: input.launchPlan,
2561
+ evidenceId: input.evidenceId,
2562
+ reason: verdictValidation.errors.join(", ") || "reviewer verdict is invalid",
2563
+ });
2564
+ const childSessionRef = input.launchResult.childSessionRef;
2565
+ const messageRef = lifecycleMessageRefFrom(input.launchResult.messageRef);
2566
+ if (childSessionRef === undefined || messageRef === undefined)
2567
+ return blockRuntimeLaneLaunchLifecycle({
2568
+ plan: input.launchPlan,
2569
+ evidenceId: input.evidenceId,
2570
+ reason: "complete lane lifecycle requires child and message refs",
2571
+ });
2572
+ const record = {
2573
+ schema_version: "flowdesk.lane_lifecycle_record.v1",
2574
+ lane_id: input.launchPlan.lane_id ?? "lane-missing",
2575
+ workflow_id: input.launchPlan.workflow_id ?? "workflow-missing",
2576
+ attempt_id: input.launchPlan.attempt_id ?? "attempt-missing",
2577
+ parent_session_ref: input.launchPlan.parent_session_ref ?? "ses-missing",
2578
+ child_session_ref: childSessionRef,
2579
+ message_ref: messageRef,
2580
+ agent_ref: input.launchPlan.agent_ref ?? "agent-missing",
2581
+ provider_qualified_model_id: input.launchPlan.provider_qualified_model_id ?? "claude/missing",
2582
+ state: "complete",
2583
+ verdict_ref: input.verdictObservation.verdictId,
2584
+ output_ref: input.outputRef,
2585
+ runtime_echo_ref: input.runtimeEchoRef,
2586
+ telemetry_ref: input.telemetryRef,
2587
+ timeout_ms: input.timeoutMs ?? 0,
2588
+ orphan_max_age_ms: input.orphanMaxAgeMs ?? 0,
2589
+ retry_count: input.retryCount ?? 0,
2590
+ created_at: input.observedAt,
2591
+ updated_at: input.observedAt,
2592
+ dispatch_authority_enabled: false,
2593
+ providerCall: false,
2594
+ actualLaneLaunch: false,
2595
+ runtimeExecution: false,
2596
+ };
2597
+ const preWriteReload = reloadFlowDeskSessionEvidenceV1({
2598
+ workflowId: record.workflow_id,
2599
+ rootDir: input.rootDir,
2600
+ });
2601
+ if (!preWriteReload.ok || preWriteReload.blocked.length > 0)
2602
+ return blockRuntimeLaneLaunchLifecycle({
2603
+ plan: input.launchPlan,
2604
+ evidenceId: input.evidenceId,
2605
+ reason: "complete lane lifecycle pre-write evidence reload failed",
2606
+ evidenceReloaded: false,
2607
+ });
2608
+ if (preWriteReload.entries.some((entry) => entry.evidenceClass === "lane_lifecycle" &&
2609
+ entry.evidenceId === input.evidenceId))
2610
+ return blockRuntimeLaneLaunchLifecycle({
2611
+ plan: input.launchPlan,
2612
+ evidenceId: input.evidenceId,
2613
+ reason: "lane lifecycle evidence already exists",
2614
+ evidenceReloaded: true,
2615
+ });
2616
+ const prepared = prepareFlowDeskSessionEvidenceWriteIntentV1({
2617
+ workflowId: record.workflow_id,
2618
+ evidenceId: input.evidenceId,
2619
+ record: record,
2620
+ });
2621
+ if (!prepared.ok || prepared.writeIntent === undefined)
2622
+ return blockRuntimeLaneLaunchLifecycle({
2623
+ plan: input.launchPlan,
2624
+ evidenceId: input.evidenceId,
2625
+ reason: prepared.errors.join(", ") ||
2626
+ "complete lane lifecycle evidence intent invalid",
2627
+ evidenceReloaded: true,
2628
+ });
2629
+ const applied = applyFlowDeskSessionEvidenceWriteIntentsV1(input.rootDir, [
2630
+ prepared.writeIntent,
2631
+ ]);
2632
+ if (!applied.ok)
2633
+ return blockRuntimeLaneLaunchLifecycle({
2634
+ plan: input.launchPlan,
2635
+ evidenceId: input.evidenceId,
2636
+ reason: applied.errors.join(", ") ||
2637
+ "complete lane lifecycle evidence write failed",
2638
+ evidenceReloaded: true,
2639
+ });
2640
+ const postWriteReload = reloadFlowDeskSessionEvidenceV1({
2641
+ workflowId: record.workflow_id,
2642
+ rootDir: input.rootDir,
2643
+ });
2644
+ const persisted = postWriteReload.ok &&
2645
+ postWriteReload.entries.some((entry) => entry.evidenceClass === "lane_lifecycle" &&
2646
+ entry.evidenceId === input.evidenceId &&
2647
+ entry.record.state === "complete" &&
2648
+ entry.record.verdict_ref === input.verdictObservation.verdictId);
2649
+ if (!persisted)
2650
+ return blockRuntimeLaneLaunchLifecycle({
2651
+ plan: input.launchPlan,
2652
+ evidenceId: input.evidenceId,
2653
+ reason: "complete lane lifecycle evidence reload verification failed",
2654
+ evidenceReloaded: false,
2655
+ });
2656
+ return {
2657
+ adapterProfile: "runtime_lane_launch_lifecycle_materializer",
2658
+ status: "lane_lifecycle_recorded",
2659
+ writeAttempted: true,
2660
+ evidenceReloaded: true,
2661
+ workflowId: record.workflow_id,
2662
+ attemptId: record.attempt_id,
2663
+ laneId: record.lane_id,
2664
+ evidenceId: input.evidenceId,
2665
+ lifecycleState: "complete",
2666
+ safeNextActions: ["/flowdesk-status"],
2667
+ authority: runtimeLaneLaunchLifecycleAuthority(true),
2668
+ };
2669
+ }
149
2670
  function promptTextFrom(request) {
150
2671
  const text = request.promptText?.trim() ?? request.promptSummary?.trim() ?? "";
151
2672
  return text.length > 0 ? text.slice(0, 20_000) : undefined;
@@ -153,12 +2674,14 @@ function promptTextFrom(request) {
153
2674
  function dispatchOptions(request, model, text) {
154
2675
  return {
155
2676
  path: { id: request.sessionId },
156
- ...(request.directory === undefined ? {} : { query: { directory: request.directory } }),
2677
+ ...(request.directory === undefined
2678
+ ? {}
2679
+ : { query: { directory: request.directory } }),
157
2680
  body: {
158
2681
  model,
159
2682
  agent: request.agent,
160
- parts: [{ type: "text", text }]
161
- }
2683
+ parts: [{ type: "text", text }],
2684
+ },
162
2685
  };
163
2686
  }
164
2687
  export async function dispatchManagedDispatchBetaPromptV1(input) {
@@ -166,27 +2689,82 @@ export async function dispatchManagedDispatchBetaPromptV1(input) {
166
2689
  if (guardDecision.status !== "eligible")
167
2690
  return blocked(input.boundaryInput, guardDecision);
168
2691
  const approvedProviderQualifiedModelId = input.boundaryInput.guardApproval?.provider_qualified_model_id;
169
- if (approvedProviderQualifiedModelId === undefined || input.request.provider_qualified_model_id !== approvedProviderQualifiedModelId) {
2692
+ if (approvedProviderQualifiedModelId === undefined ||
2693
+ input.request.provider_qualified_model_id !==
2694
+ approvedProviderQualifiedModelId) {
170
2695
  return blocked(input.boundaryInput, guardDecision, "Dispatch request model must exactly match Guard-approved provider-qualified model.");
171
2696
  }
172
2697
  const model = parseProviderQualifiedModelId(approvedProviderQualifiedModelId);
173
- if (model === undefined || model.providerID !== input.boundaryInput.guardApproval?.provider_family) {
2698
+ if (model === undefined ||
2699
+ model.providerID !== input.boundaryInput.guardApproval?.provider_family) {
174
2700
  return blocked(input.boundaryInput, guardDecision, "Guard-approved provider-qualified model is invalid or provider-mismatched.");
175
2701
  }
2702
+ const runtimeModel = opencodeRuntimeModelForFlowDeskModel(model);
2703
+ if (runtimeModel === undefined) {
2704
+ return blocked(input.boundaryInput, guardDecision, "Guard-approved provider family is not mapped to an OpenCode runtime provider.");
2705
+ }
176
2706
  const text = promptTextFrom(input.request);
177
- if (input.request.sessionId.trim().length === 0 || input.request.agent.trim().length === 0 || text === undefined) {
2707
+ if (input.request.sessionId.trim().length === 0 ||
2708
+ input.request.agent.trim().length === 0 ||
2709
+ text === undefined) {
178
2710
  return blocked(input.boundaryInput, guardDecision, "Dispatch request is missing session, agent, or bounded prompt text.");
179
2711
  }
2712
+ if (input.dispatchManifest === undefined ||
2713
+ input.reloadedEvidence === undefined) {
2714
+ return blocked(input.boundaryInput, guardDecision, "Dispatch attempt manifest and durable evidence reload are required before SDK call.");
2715
+ }
2716
+ const precall = evaluateFlowDeskDispatchAttemptDurablePrecallV1({
2717
+ manifest: input.dispatchManifest,
2718
+ reloadedEvidence: input.reloadedEvidence,
2719
+ });
2720
+ if (!precall.sdk_call_permitted) {
2721
+ return blocked(input.boundaryInput, guardDecision, `Dispatch pre-call gate blocked: ${precall.blocked_labels.join(",") || precall.errors.join(",") || "unknown"}.`);
2722
+ }
2723
+ const consumedApproval = input.reloadedEvidence.entries.find((entry) => entry.evidenceClass === "production_approval_source" &&
2724
+ entry.record.approval_id === input.dispatchManifest?.approval_ref)?.record;
2725
+ if (consumedApproval === undefined) {
2726
+ return blocked(input.boundaryInput, guardDecision, "Durable dispatch pre-call gate did not expose a reloaded consumed approval source.");
2727
+ }
2728
+ const { durable_provenance_required: _durableProvenanceRequired, reloaded_approval_source_ref: _reloadedApprovalSourceRef, reloaded_pre_dispatch_audit_ref: _reloadedPreDispatchAuditRef, reloaded_idempotency_snapshot_ref: _reloadedIdempotencySnapshotRef, ...promotionPrecall } = precall;
2729
+ if (input.boundaryInput.preDispatchAuditRef === undefined ||
2730
+ input.boundaryInput.runtimeEchoEvidence?.conformance_ref === undefined) {
2731
+ return blocked(input.boundaryInput, guardDecision, "Managed-dispatch promotion requires matching audit and conformance refs before SDK call.");
2732
+ }
2733
+ const promotion = promoteFlowDeskManagedDispatchBetaAuthorityV1({
2734
+ guardDecision,
2735
+ precallEvaluation: promotionPrecall,
2736
+ consumedApproval,
2737
+ auditRef: input.boundaryInput.preDispatchAuditRef,
2738
+ conformanceRef: input.boundaryInput.runtimeEchoEvidence.conformance_ref,
2739
+ });
2740
+ if (!promotion.ok ||
2741
+ promotion.managed_dispatch_beta_authority_enabled !== true) {
2742
+ return blocked(input.boundaryInput, guardDecision, `Managed-dispatch promotion blocked: ${promotion.errors.join(",") || "unknown"}.`);
2743
+ }
180
2744
  const dispatchMethod = input.request.dispatchMethod ?? "promptAsync";
181
2745
  const dispatch = input.client.session[dispatchMethod];
182
2746
  if (dispatch === undefined)
183
2747
  return blocked(input.boundaryInput, guardDecision, "Injected OpenCode client is missing the requested session prompt method.");
184
- const options = dispatchOptions(input.request, model, text);
2748
+ if (input.reservationStore === undefined) {
2749
+ return blocked(input.boundaryInput, guardDecision, "Dispatch idempotency reservation materialization is required before SDK call.");
2750
+ }
2751
+ const reservation = await input.reservationStore.reserve({
2752
+ manifest: input.dispatchManifest,
2753
+ reloadedEvidence: input.reloadedEvidence,
2754
+ });
2755
+ if (!reservation.ok || reservation.reservationEvidenceReloaded !== true) {
2756
+ return blocked(input.boundaryInput, guardDecision, `Dispatch idempotency reservation materialization blocked: ${reservation.redactedFailureReason ?? "reload not proven"}.`);
2757
+ }
2758
+ const options = dispatchOptions(input.request, runtimeModel, text);
185
2759
  let response;
186
2760
  try {
187
2761
  response = await dispatch.call(input.client.session, options);
188
2762
  }
189
2763
  catch {
2764
+ const failureRecord = await input.reservationStore.recordDispatchFailure({
2765
+ manifest: input.dispatchManifest,
2766
+ reloadedEvidence: input.reloadedEvidence,
2767
+ });
190
2768
  return {
191
2769
  adapterProfile: flowdeskManagedDispatchBetaAdapterProfile,
192
2770
  status: "dispatch_failed",
@@ -195,26 +2773,34 @@ export async function dispatchManagedDispatchBetaPromptV1(input) {
195
2773
  guardDecision,
196
2774
  sessionId: input.request.sessionId,
197
2775
  agent: input.request.agent,
198
- model,
199
- ...(input.request.directory === undefined ? {} : { directory: input.request.directory }),
200
- redactedErrorCategory: "provider_api",
2776
+ model: runtimeModel,
2777
+ ...(input.request.directory === undefined
2778
+ ? {}
2779
+ : { directory: input.request.directory }),
2780
+ redactedErrorCategory: failureRecord.ok && failureRecord.reservationEvidenceReloaded
2781
+ ? "provider_api"
2782
+ : "runtime",
201
2783
  authority: { ...enabledDispatchAuthority(), runtimeExecution: false },
202
- verification: verificationFor(input.boundaryInput)
2784
+ verification: verificationFor(input.boundaryInput),
203
2785
  };
204
2786
  }
205
2787
  return {
206
2788
  adapterProfile: flowdeskManagedDispatchBetaAdapterProfile,
207
- status: dispatchMethod === "promptAsync" ? "dispatch_accepted" : "dispatch_completed",
2789
+ status: dispatchMethod === "promptAsync"
2790
+ ? "dispatch_accepted"
2791
+ : "dispatch_completed",
208
2792
  dispatchAttempted: true,
209
2793
  dispatchMethod,
210
2794
  guardDecision,
211
2795
  sessionId: input.request.sessionId,
212
2796
  agent: input.request.agent,
213
- model,
214
- ...(input.request.directory === undefined ? {} : { directory: input.request.directory }),
2797
+ model: runtimeModel,
2798
+ ...(input.request.directory === undefined
2799
+ ? {}
2800
+ : { directory: input.request.directory }),
215
2801
  ...(response === undefined ? {} : { response }),
216
2802
  authority: enabledDispatchAuthority(),
217
- verification: verificationFor(input.boundaryInput)
2803
+ verification: verificationFor(input.boundaryInput),
218
2804
  };
219
2805
  }
220
2806
  //# sourceMappingURL=managed-dispatch-adapter.js.map