@osolmaz/pi-workflows 0.11.2 → 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -10
- package/dist/builtins/autoimplement-command-batches.d.ts +52 -0
- package/dist/builtins/autoimplement-command-batches.js +258 -0
- package/dist/builtins/autoimplement-command-batches.js.map +1 -0
- package/dist/builtins/autoimplement.workflow.d.ts +509 -124
- package/dist/builtins/autoimplement.workflow.js +884 -327
- package/dist/builtins/autoimplement.workflow.js.map +1 -1
- package/dist/builtins/catalog.js +5 -3
- package/dist/builtins/catalog.js.map +1 -1
- package/dist/builtins/index.d.ts +4 -1
- package/dist/builtins/index.js +3 -1
- package/dist/builtins/index.js.map +1 -1
- package/dist/builtins/monitor.workflow.d.ts +2 -4
- package/dist/builtins/monitor.workflow.js +26 -128
- package/dist/builtins/monitor.workflow.js.map +1 -1
- package/dist/builtins/pi-agent-group.d.ts +72 -0
- package/dist/builtins/pi-agent-group.js +1087 -0
- package/dist/builtins/pi-agent-group.js.map +1 -0
- package/dist/builtins/plan-approval.workflow.d.ts +39 -5
- package/dist/builtins/plan-approval.workflow.js +92 -14
- package/dist/builtins/plan-approval.workflow.js.map +1 -1
- package/dist/builtins/plan-change.workflow.d.ts +301 -0
- package/dist/builtins/plan-change.workflow.js +256 -0
- package/dist/builtins/plan-change.workflow.js.map +1 -0
- package/dist/builtins/plan-presentation.js +2 -2
- package/dist/builtins/plan-presentation.js.map +1 -1
- package/dist/builtins/sanity-check.workflow.d.ts +85 -0
- package/dist/builtins/sanity-check.workflow.js +482 -0
- package/dist/builtins/sanity-check.workflow.js.map +1 -0
- package/dist/controllers/sqlite.d.ts +90 -2
- package/dist/controllers/sqlite.js +380 -5
- package/dist/controllers/sqlite.js.map +1 -1
- package/dist/extension/decision-channels.d.ts +2 -2
- package/dist/extension/decision-channels.js +29 -35
- package/dist/extension/decision-channels.js.map +1 -1
- package/dist/extension/deferred-turn-coordinator.d.ts +32 -0
- package/dist/extension/deferred-turn-coordinator.js +143 -0
- package/dist/extension/deferred-turn-coordinator.js.map +1 -0
- package/dist/extension/deferred-turn.d.ts +44 -0
- package/dist/extension/deferred-turn.js +110 -0
- package/dist/extension/deferred-turn.js.map +1 -0
- package/dist/extension/index.js +348 -80
- package/dist/extension/index.js.map +1 -1
- package/dist/extension/session-events.d.ts +2 -2
- package/dist/extension/step-message.d.ts +1 -0
- package/dist/extension/step-message.js.map +1 -1
- package/dist/extension/widget.js +23 -3
- package/dist/extension/widget.js.map +1 -1
- package/dist/herdr/setup.js +4 -4
- package/dist/render/graph-render.js +1 -2
- package/dist/render/graph-render.js.map +1 -1
- package/dist/viewer/render.js +7 -6
- package/dist/viewer/render.js.map +1 -1
- package/dist/workflows/catalog.js +7 -2
- package/dist/workflows/catalog.js.map +1 -1
- package/dist/workflows/command-batch.d.ts +38 -0
- package/dist/workflows/command-batch.js +176 -0
- package/dist/workflows/command-batch.js.map +1 -0
- package/dist/workflows/composition.js +8 -0
- package/dist/workflows/composition.js.map +1 -1
- package/dist/workflows/decision-presentation.d.ts +1 -1
- package/dist/workflows/decision-presentation.js +51 -38
- package/dist/workflows/decision-presentation.js.map +1 -1
- package/dist/workflows/engine.d.ts +2 -2
- package/dist/workflows/engine.js +22 -17
- package/dist/workflows/engine.js.map +1 -1
- package/dist/workflows/errors.d.ts +13 -0
- package/dist/workflows/errors.js +15 -0
- package/dist/workflows/errors.js.map +1 -1
- package/dist/workflows/human-decision.d.ts +16 -4
- package/dist/workflows/human-decision.js +175 -72
- package/dist/workflows/human-decision.js.map +1 -1
- package/dist/workflows/index.d.ts +3 -2
- package/dist/workflows/index.js +2 -1
- package/dist/workflows/index.js.map +1 -1
- package/dist/workflows/progress.d.ts +1 -0
- package/dist/workflows/progress.js +15 -3
- package/dist/workflows/progress.js.map +1 -1
- package/dist/workflows/schema.js +12 -1
- package/dist/workflows/schema.js.map +1 -1
- package/dist/workflows/shell.d.ts +4 -0
- package/dist/workflows/shell.js +6 -0
- package/dist/workflows/shell.js.map +1 -1
- package/dist/workflows/store.js +8 -1
- package/dist/workflows/store.js.map +1 -1
- package/dist/workflows/types.d.ts +37 -48
- package/docs/2026-08-18-herdr-piw-plan.md +1 -1
- package/docs/2026-08-20-durable-workflow-launch-plan.md +19 -23
- package/docs/CONTROLLERS.md +2 -2
- package/docs/DEFERRED_TURNS.md +298 -0
- package/docs/DESIGN_PHILOSOPHY.md +1 -1
- package/docs/HUMAN_DECISIONS.md +30 -40
- package/docs/HUMAN_DECISION_PRESENTATIONS.md +17 -27
- package/docs/MONITOR.md +7 -13
- package/docs/WORKFLOW_COMPOSITION.md +10 -9
- package/docs/WORKFLOW_STEP_MESSAGES.md +5 -5
- package/docs/WORKFLOW_UPDATES.md +28 -5
- package/docs/plans/2026-08-04-controller-runtime-plan.md +1 -1
- package/docs/plans/2026-08-05-always-on-workflows-plan.md +1 -1
- package/docs/plans/2026-08-10-agent-managed-monitor-workflows-plan.md +9 -9
- package/docs/plans/2026-08-13-built-in-workflow-catalog-plan.md +1 -1
- package/docs/plans/2026-08-13-session-addressed-workflow-notifications-plan.md +1 -1
- package/docs/plans/2026-08-16-workflow-updates-plan.md +3 -3
- package/docs/plans/2026-08-17-bundled-skills-plan.md +6 -6
- package/docs/plans/2026-08-19-human-decision-gates-plan.md +5 -5
- package/docs/plans/2026-08-19-human-decision-presentations-plan.md +1 -1
- package/docs/plans/2026-08-19-provider-compatible-workflow-tool-schema-plan.md +1 -1
- package/docs/plans/2026-08-19-workflow-composition-plan.md +6 -6
- package/docs/plans/2026-08-20-autoimplement-blocker-challenge-plan.md +3 -3
- package/docs/plans/2026-08-20-bounded-command-batches-plan.md +206 -0
- package/docs/plans/2026-08-20-herdr-plugin-sync-plan.md +9 -9
- package/docs/plans/2026-08-21-autoimplement-timeout-fallback-plan.md +242 -0
- package/docs/plans/2026-08-21-deferred-turn-intents-plan.md +324 -0
- package/docs/plans/2026-08-21-plan-change-approval-policy-plan.md +322 -0
- package/docs/plans/2026-08-21-sanity-check-plan.md +283 -0
- package/docs/run-bundles.md +8 -6
- package/docs/workflows.md +85 -22
- package/examples/workflows/approved-plan.workflow.ts +19 -46
- package/examples/workflows/command-batch.workflow.ts +50 -0
- package/examples/workflows/sanity-check.workflow.ts +1 -0
- package/herdr-plugin.toml +3 -3
- package/package.json +7 -7
- package/schemas/decision-presentation-v1.schema.json +1 -1
- package/schemas/human-decision-accepted-v1.schema.json +16 -4
- package/schemas/human-decision-answer-attempt-v1.schema.json +1 -1
- package/schemas/human-decision-cancellation-v1.schema.json +1 -1
- package/schemas/human-decision-continuation-v1.schema.json +11 -2
- package/schemas/human-decision-delivery-v1.schema.json +9 -1
- package/schemas/human-decision-receipt-v1.schema.json +9 -1
- package/schemas/human-decision-request-v1.schema.json +25 -5
- package/schemas/human-decision-resolution-v1.schema.json +1 -1
- package/schemas/human-decision-settlement-v1.schema.json +1 -1
- package/skills/autodoc/SKILL.md +33 -2
- package/skills/autoimplement/SKILL.md +93 -12
- package/skills/autoplan/SKILL.md +26 -2
- package/skills/monitor/SKILL.md +66 -24
- package/skills/pi-workflows/SKILL.md +21 -6
- package/skills/sanity-check/SKILL.md +44 -0
- package/src/builtins/autoimplement-command-batches.ts +364 -0
- package/src/builtins/autoimplement.workflow.ts +1075 -370
- package/src/builtins/catalog.ts +5 -3
- package/src/builtins/index.ts +24 -0
- package/src/builtins/monitor.workflow.ts +27 -150
- package/src/builtins/pi-agent-group.ts +1407 -0
- package/src/builtins/plan-approval.workflow.ts +157 -24
- package/src/builtins/plan-change.workflow.ts +321 -0
- package/src/builtins/plan-presentation.ts +2 -2
- package/src/builtins/sanity-check.workflow.ts +769 -0
- package/src/controllers/sqlite.ts +580 -7
- package/src/extension/decision-channels.ts +36 -66
- package/src/extension/deferred-turn-coordinator.ts +171 -0
- package/src/extension/deferred-turn.ts +166 -0
- package/src/extension/index.ts +438 -90
- package/src/extension/session-events.ts +2 -2
- package/src/extension/step-message.ts +1 -0
- package/src/extension/widget.ts +24 -5
- package/src/herdr/setup.ts +4 -4
- package/src/render/graph-render.ts +1 -2
- package/src/viewer/render.ts +7 -6
- package/src/workflows/catalog.ts +7 -2
- package/src/workflows/command-batch.ts +254 -0
- package/src/workflows/composition.ts +9 -0
- package/src/workflows/decision-presentation.ts +56 -43
- package/src/workflows/engine.ts +25 -20
- package/src/workflows/errors.ts +24 -0
- package/src/workflows/human-decision.ts +218 -101
- package/src/workflows/index.ts +20 -11
- package/src/workflows/progress.ts +18 -3
- package/src/workflows/schema.ts +19 -1
- package/src/workflows/shell.ts +10 -0
- package/src/workflows/store.ts +8 -1
- package/src/workflows/types.ts +43 -59
- package/schemas/human-decision-accepted-v2.schema.json +0 -50
- package/schemas/human-decision-delivery-v2.schema.json +0 -36
- package/schemas/human-decision-receipt-v2.schema.json +0 -39
- package/schemas/human-decision-request-v2.schema.json +0 -69
- package/schemas/human-decision-resolution-v2.schema.json +0 -27
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type ControllerStore, type EffectReservation, type QueueItem, type QueueRequeueOptions, type WorkflowRecordUpdate, type WorkflowReservation } from "./store.js";
|
|
2
2
|
import type { ChildWorkflowRecord, ControllerEvent, ControllerQueueClaim, ControllerResource, ControllerResourceRef, ControllerResourceStatus, EffectRecord, JsonObject } from "./types.js";
|
|
3
|
+
declare const TURN_INTENT_FACTS_SCHEMA = "pi-workflows.deferred-turn-facts.v1";
|
|
3
4
|
export type WorkflowRunLaunchStatus = "queued" | "starting" | "running" | "parked" | "done" | "failed" | "cancelled";
|
|
4
5
|
/** A user-started workflow run tracked by the durable run queue. */
|
|
5
6
|
export type WorkflowRunQueueRecord = {
|
|
@@ -34,12 +35,42 @@ export type WorkflowNotificationRecord = {
|
|
|
34
35
|
attemptId: string;
|
|
35
36
|
notificationIndex: number;
|
|
36
37
|
targetSessionId: string;
|
|
37
|
-
kind: "progress" | "final"
|
|
38
|
+
kind: "progress" | "final";
|
|
38
39
|
content: string;
|
|
39
40
|
createdAt: string;
|
|
40
41
|
deliveryClaimExpiresAt: string | null;
|
|
41
42
|
deliveredAt: string | null;
|
|
42
43
|
};
|
|
44
|
+
export type WorkflowTurnIntentCause = "agentCancelled" | "timedOut" | "failed" | "launchFailed" | "controllerInterrupted" | "claimLost";
|
|
45
|
+
export type WorkflowTurnIntentResolution = "workflowPrompt" | "presentation" | "fallback";
|
|
46
|
+
export type WorkflowTurnIntentFacts = JsonObject & {
|
|
47
|
+
schema: typeof TURN_INTENT_FACTS_SCHEMA;
|
|
48
|
+
workflowName: string;
|
|
49
|
+
runId: string;
|
|
50
|
+
observedState: string;
|
|
51
|
+
cause: WorkflowTurnIntentCause;
|
|
52
|
+
nodeId: string | null;
|
|
53
|
+
attemptId: string | null;
|
|
54
|
+
reason: string | null;
|
|
55
|
+
handoff: boolean;
|
|
56
|
+
};
|
|
57
|
+
export type WorkflowTurnIntentRecord = {
|
|
58
|
+
intentId: string;
|
|
59
|
+
sourceEventId: string;
|
|
60
|
+
runId: string;
|
|
61
|
+
workflowRef: string;
|
|
62
|
+
targetSessionId: string;
|
|
63
|
+
cause: WorkflowTurnIntentCause;
|
|
64
|
+
nodeId: string | null;
|
|
65
|
+
attemptId: string | null;
|
|
66
|
+
fallbackFacts: WorkflowTurnIntentFacts;
|
|
67
|
+
requestedAt: string;
|
|
68
|
+
eligibleAt: string | null;
|
|
69
|
+
resolvedAt: string | null;
|
|
70
|
+
resolution: WorkflowTurnIntentResolution | null;
|
|
71
|
+
resolutionMessageId: string | null;
|
|
72
|
+
deliveryClaimExpiresAt: string | null;
|
|
73
|
+
};
|
|
43
74
|
/** One run lifecycle transition in the cross-session event feed. */
|
|
44
75
|
export type RunEventRecord = {
|
|
45
76
|
seq: number;
|
|
@@ -138,6 +169,7 @@ export declare class SqliteControllerStore implements ControllerStore {
|
|
|
138
169
|
private configure;
|
|
139
170
|
private initializeSchema;
|
|
140
171
|
private assertAlphaSchemaLayout;
|
|
172
|
+
private assertNoLegacyPendingLaunchNotifications;
|
|
141
173
|
private transaction;
|
|
142
174
|
private resourceRow;
|
|
143
175
|
private requireResource;
|
|
@@ -291,13 +323,68 @@ export declare class SqliteControllerStore implements ControllerStore {
|
|
|
291
323
|
listRunEventsAfter(seq: number, options?: {
|
|
292
324
|
limit?: number;
|
|
293
325
|
}): RunEventRecord[];
|
|
326
|
+
ensureWorkflowTurnIntent(options: {
|
|
327
|
+
intentId: string;
|
|
328
|
+
sourceEventId: string;
|
|
329
|
+
runId: string;
|
|
330
|
+
workflowRef: string;
|
|
331
|
+
targetSessionId: string;
|
|
332
|
+
cause: WorkflowTurnIntentCause;
|
|
333
|
+
nodeId?: string | null;
|
|
334
|
+
attemptId?: string | null;
|
|
335
|
+
fallbackFacts: WorkflowTurnIntentFacts;
|
|
336
|
+
eligible?: boolean;
|
|
337
|
+
now?: string;
|
|
338
|
+
}): WorkflowTurnIntentRecord;
|
|
339
|
+
getWorkflowTurnIntent(intentId: string): WorkflowTurnIntentRecord | undefined;
|
|
340
|
+
findPendingWorkflowTurnIntent(options: {
|
|
341
|
+
runId: string;
|
|
342
|
+
targetSessionId: string;
|
|
343
|
+
}): WorkflowTurnIntentRecord | undefined;
|
|
344
|
+
claimWorkflowTurnIntent(options: {
|
|
345
|
+
intentId: string;
|
|
346
|
+
targetSessionId: string;
|
|
347
|
+
claimToken: string;
|
|
348
|
+
leaseMs: number;
|
|
349
|
+
now?: string;
|
|
350
|
+
}): WorkflowTurnIntentRecord | undefined;
|
|
351
|
+
claimEligibleWorkflowTurnIntents(options: {
|
|
352
|
+
targetSessionId: string;
|
|
353
|
+
claimToken: string;
|
|
354
|
+
leaseMs: number;
|
|
355
|
+
limit?: number;
|
|
356
|
+
now?: string;
|
|
357
|
+
}): WorkflowTurnIntentRecord[];
|
|
358
|
+
makeWorkflowTurnIntentEligible(options: {
|
|
359
|
+
intentId: string;
|
|
360
|
+
fallbackFacts: WorkflowTurnIntentFacts;
|
|
361
|
+
now?: string;
|
|
362
|
+
}): boolean;
|
|
363
|
+
resolveWorkflowTurnIntent(options: {
|
|
364
|
+
intentId: string;
|
|
365
|
+
targetSessionId: string;
|
|
366
|
+
claimToken: string;
|
|
367
|
+
resolution: WorkflowTurnIntentResolution;
|
|
368
|
+
messageId: string;
|
|
369
|
+
now?: string;
|
|
370
|
+
}): boolean;
|
|
371
|
+
releaseWorkflowTurnIntentClaim(options: {
|
|
372
|
+
intentId: string;
|
|
373
|
+
targetSessionId: string;
|
|
374
|
+
claimToken: string;
|
|
375
|
+
}): boolean;
|
|
376
|
+
listWorkflowTurnIntents(options?: {
|
|
377
|
+
targetSessionId?: string;
|
|
378
|
+
runId?: string;
|
|
379
|
+
limit?: number;
|
|
380
|
+
}): WorkflowTurnIntentRecord[];
|
|
294
381
|
enqueueWorkflowNotification(options: {
|
|
295
382
|
runId: string;
|
|
296
383
|
nodeId: string;
|
|
297
384
|
attemptId: string;
|
|
298
385
|
notificationIndex: number;
|
|
299
386
|
targetSessionId: string;
|
|
300
|
-
kind: "progress" | "final"
|
|
387
|
+
kind: "progress" | "final";
|
|
301
388
|
content: string;
|
|
302
389
|
notificationId?: string;
|
|
303
390
|
now?: string;
|
|
@@ -319,3 +406,4 @@ export declare class SqliteControllerStore implements ControllerStore {
|
|
|
319
406
|
private requireWorkflowRun;
|
|
320
407
|
private requireEvent;
|
|
321
408
|
}
|
|
409
|
+
export {};
|
|
@@ -9,6 +9,7 @@ const MAX_KEY_LENGTH = 512;
|
|
|
9
9
|
const MAX_ERROR_LENGTH = 8_192;
|
|
10
10
|
const MAX_EVENT_BYTES = 64 * 1024;
|
|
11
11
|
const MAX_RESOURCE_VALUE_BYTES = 1024 * 1024;
|
|
12
|
+
const TURN_INTENT_FACTS_SCHEMA = "pi-workflows.deferred-turn-facts.v1";
|
|
12
13
|
export class SqliteControllerStore {
|
|
13
14
|
filePath;
|
|
14
15
|
database;
|
|
@@ -472,8 +473,10 @@ export class SqliteControllerStore {
|
|
|
472
473
|
const existingQueue = this.database
|
|
473
474
|
.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'workflow_run_queue'")
|
|
474
475
|
.get();
|
|
475
|
-
if (existingQueue !== undefined)
|
|
476
|
-
this.assertAlphaSchemaLayout();
|
|
476
|
+
if (existingQueue !== undefined) {
|
|
477
|
+
this.assertAlphaSchemaLayout(false);
|
|
478
|
+
this.assertNoLegacyPendingLaunchNotifications();
|
|
479
|
+
}
|
|
477
480
|
this.transaction(() => {
|
|
478
481
|
this.database
|
|
479
482
|
.prepare("INSERT OR IGNORE INTO schema_info (singleton, schema_id) VALUES (1, ?)")
|
|
@@ -481,9 +484,10 @@ export class SqliteControllerStore {
|
|
|
481
484
|
this.database.exec(SCHEMA_SQL);
|
|
482
485
|
});
|
|
483
486
|
}
|
|
484
|
-
this.assertAlphaSchemaLayout();
|
|
487
|
+
this.assertAlphaSchemaLayout(true);
|
|
488
|
+
this.assertNoLegacyPendingLaunchNotifications();
|
|
485
489
|
}
|
|
486
|
-
assertAlphaSchemaLayout() {
|
|
490
|
+
assertAlphaSchemaLayout(requireTurnIntents) {
|
|
487
491
|
const requiredQueueColumns = new Set([
|
|
488
492
|
"run_id",
|
|
489
493
|
"workflow_ref",
|
|
@@ -511,6 +515,46 @@ export class SqliteControllerStore {
|
|
|
511
515
|
if (missing.length > 0) {
|
|
512
516
|
throw new Error("Controller store uses an incompatible alpha layout. Preserve needed run bundles, then reset the project controller store.");
|
|
513
517
|
}
|
|
518
|
+
if (!requireTurnIntents)
|
|
519
|
+
return;
|
|
520
|
+
const requiredIntentColumns = new Set([
|
|
521
|
+
"intent_id",
|
|
522
|
+
"source_event_id",
|
|
523
|
+
"run_id",
|
|
524
|
+
"workflow_ref",
|
|
525
|
+
"target_session_id",
|
|
526
|
+
"cause",
|
|
527
|
+
"node_id",
|
|
528
|
+
"attempt_id",
|
|
529
|
+
"fallback_facts_json",
|
|
530
|
+
"requested_at",
|
|
531
|
+
"eligible_at",
|
|
532
|
+
"resolved_at",
|
|
533
|
+
"resolution",
|
|
534
|
+
"resolution_message_id",
|
|
535
|
+
"delivery_claim_token",
|
|
536
|
+
"delivery_claim_expires_at",
|
|
537
|
+
]);
|
|
538
|
+
const intentColumns = new Set(this.database.pragma("table_info(workflow_turn_intents)").map((column) => column.name));
|
|
539
|
+
const missingIntentColumns = [...requiredIntentColumns].filter((column) => !intentColumns.has(column));
|
|
540
|
+
if (missingIntentColumns.length > 0) {
|
|
541
|
+
throw new Error("Controller store uses an incompatible alpha turn-intent layout. Preserve needed run bundles, then reset the project controller store.");
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
assertNoLegacyPendingLaunchNotifications() {
|
|
545
|
+
const notificationTable = this.database
|
|
546
|
+
.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'workflow_notifications'")
|
|
547
|
+
.get();
|
|
548
|
+
if (notificationTable === undefined) {
|
|
549
|
+
throw new Error("Controller store uses an incompatible alpha notification layout. Preserve needed run bundles, then reset the project controller store.");
|
|
550
|
+
}
|
|
551
|
+
const legacy = this.database
|
|
552
|
+
.prepare(`SELECT 1 FROM workflow_notifications
|
|
553
|
+
WHERE kind = 'launch_failure' AND delivered_at IS NULL LIMIT 1`)
|
|
554
|
+
.get();
|
|
555
|
+
if (legacy !== undefined) {
|
|
556
|
+
throw new Error("Controller store contains pending alpha launch-failure notifications. Preserve needed run bundles, then reset the project controller store.");
|
|
557
|
+
}
|
|
514
558
|
}
|
|
515
559
|
transaction(task) {
|
|
516
560
|
this.database.exec("BEGIN IMMEDIATE");
|
|
@@ -943,6 +987,179 @@ export class SqliteControllerStore {
|
|
|
943
987
|
payload: parseStoredJson(row.payload_json, "run event payload"),
|
|
944
988
|
}));
|
|
945
989
|
}
|
|
990
|
+
ensureWorkflowTurnIntent(options) {
|
|
991
|
+
validateKey(options.intentId, "workflow turn intent id");
|
|
992
|
+
validateKey(options.sourceEventId, "workflow turn source event id");
|
|
993
|
+
validateRunId(options.runId);
|
|
994
|
+
validateKey(options.workflowRef, "workflow turn workflow ref");
|
|
995
|
+
validateKey(options.targetSessionId, "workflow turn target session id");
|
|
996
|
+
validateWorkflowTurnIntentCause(options.cause);
|
|
997
|
+
const nodeId = options.nodeId ?? null;
|
|
998
|
+
const attemptId = options.attemptId ?? null;
|
|
999
|
+
if (nodeId !== null)
|
|
1000
|
+
validateKey(nodeId, "workflow turn node id");
|
|
1001
|
+
if (attemptId !== null)
|
|
1002
|
+
validateKey(attemptId, "workflow turn attempt id");
|
|
1003
|
+
const factsJson = workflowTurnIntentFactsJson(options.fallbackFacts);
|
|
1004
|
+
const now = validTimestamp(options.now);
|
|
1005
|
+
const eligibleAt = options.eligible === true ? now : null;
|
|
1006
|
+
this.database
|
|
1007
|
+
.prepare(`INSERT INTO workflow_turn_intents (
|
|
1008
|
+
intent_id, source_event_id, run_id, workflow_ref, target_session_id,
|
|
1009
|
+
cause, node_id, attempt_id, fallback_facts_json, requested_at, eligible_at,
|
|
1010
|
+
resolved_at, resolution, resolution_message_id,
|
|
1011
|
+
delivery_claim_token, delivery_claim_expires_at
|
|
1012
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, NULL, NULL, NULL, NULL)
|
|
1013
|
+
ON CONFLICT(intent_id) DO NOTHING`)
|
|
1014
|
+
.run(options.intentId, options.sourceEventId, options.runId, options.workflowRef, options.targetSessionId, options.cause, nodeId, attemptId, factsJson, now, eligibleAt);
|
|
1015
|
+
const existing = this.getWorkflowTurnIntent(options.intentId);
|
|
1016
|
+
if (existing === undefined) {
|
|
1017
|
+
throw new Error(`Workflow turn intent was not stored: ${options.intentId}`);
|
|
1018
|
+
}
|
|
1019
|
+
if (existing.sourceEventId !== options.sourceEventId ||
|
|
1020
|
+
existing.runId !== options.runId ||
|
|
1021
|
+
existing.workflowRef !== options.workflowRef ||
|
|
1022
|
+
existing.targetSessionId !== options.targetSessionId ||
|
|
1023
|
+
existing.cause !== options.cause ||
|
|
1024
|
+
existing.nodeId !== nodeId ||
|
|
1025
|
+
existing.attemptId !== attemptId) {
|
|
1026
|
+
throw new Error(`Workflow turn intent identity conflict: ${options.intentId}`);
|
|
1027
|
+
}
|
|
1028
|
+
return existing;
|
|
1029
|
+
}
|
|
1030
|
+
getWorkflowTurnIntent(intentId) {
|
|
1031
|
+
validateKey(intentId, "workflow turn intent id");
|
|
1032
|
+
const row = this.database
|
|
1033
|
+
.prepare("SELECT * FROM workflow_turn_intents WHERE intent_id = ?")
|
|
1034
|
+
.get(intentId);
|
|
1035
|
+
return row === undefined ? undefined : workflowTurnIntentFromRow(row);
|
|
1036
|
+
}
|
|
1037
|
+
findPendingWorkflowTurnIntent(options) {
|
|
1038
|
+
validateRunId(options.runId);
|
|
1039
|
+
validateKey(options.targetSessionId, "workflow turn target session id");
|
|
1040
|
+
const row = this.database
|
|
1041
|
+
.prepare(`SELECT * FROM workflow_turn_intents
|
|
1042
|
+
WHERE run_id = ? AND target_session_id = ? AND resolved_at IS NULL
|
|
1043
|
+
ORDER BY requested_at DESC, intent_id DESC LIMIT 1`)
|
|
1044
|
+
.get(options.runId, options.targetSessionId);
|
|
1045
|
+
return row === undefined ? undefined : workflowTurnIntentFromRow(row);
|
|
1046
|
+
}
|
|
1047
|
+
claimWorkflowTurnIntent(options) {
|
|
1048
|
+
validateKey(options.intentId, "workflow turn intent id");
|
|
1049
|
+
validateKey(options.targetSessionId, "workflow turn target session id");
|
|
1050
|
+
validateKey(options.claimToken, "workflow turn claim token");
|
|
1051
|
+
validateDuration(options.leaseMs, "leaseMs");
|
|
1052
|
+
const nowMs = epoch(validTimestamp(options.now));
|
|
1053
|
+
const result = this.database
|
|
1054
|
+
.prepare(`UPDATE workflow_turn_intents
|
|
1055
|
+
SET delivery_claim_token = ?, delivery_claim_expires_at = ?
|
|
1056
|
+
WHERE intent_id = ? AND target_session_id = ? AND resolved_at IS NULL
|
|
1057
|
+
AND (delivery_claim_expires_at IS NULL OR delivery_claim_expires_at <= ?)`)
|
|
1058
|
+
.run(options.claimToken, nowMs + options.leaseMs, options.intentId, options.targetSessionId, nowMs);
|
|
1059
|
+
if (result.changes !== 1)
|
|
1060
|
+
return undefined;
|
|
1061
|
+
const row = this.database
|
|
1062
|
+
.prepare("SELECT * FROM workflow_turn_intents WHERE intent_id = ?")
|
|
1063
|
+
.get(options.intentId);
|
|
1064
|
+
return workflowTurnIntentFromRow(row);
|
|
1065
|
+
}
|
|
1066
|
+
claimEligibleWorkflowTurnIntents(options) {
|
|
1067
|
+
validateKey(options.targetSessionId, "workflow turn target session id");
|
|
1068
|
+
validateKey(options.claimToken, "workflow turn claim token");
|
|
1069
|
+
validateDuration(options.leaseMs, "leaseMs");
|
|
1070
|
+
const limit = options.limit ?? 20;
|
|
1071
|
+
if (!Number.isSafeInteger(limit) || limit <= 0 || limit > 100) {
|
|
1072
|
+
throw new Error("Workflow turn intent limit must be an integer from 1 through 100");
|
|
1073
|
+
}
|
|
1074
|
+
const now = validTimestamp(options.now);
|
|
1075
|
+
const nowMs = epoch(now);
|
|
1076
|
+
return this.transaction(() => {
|
|
1077
|
+
const ids = this.database
|
|
1078
|
+
.prepare(`SELECT intent_id FROM workflow_turn_intents
|
|
1079
|
+
WHERE target_session_id = ? AND resolved_at IS NULL AND eligible_at IS NOT NULL
|
|
1080
|
+
AND eligible_at <= ?
|
|
1081
|
+
AND (delivery_claim_expires_at IS NULL OR delivery_claim_expires_at <= ?)
|
|
1082
|
+
ORDER BY eligible_at, intent_id LIMIT ?`)
|
|
1083
|
+
.all(options.targetSessionId, now, nowMs, limit);
|
|
1084
|
+
if (ids.length === 0)
|
|
1085
|
+
return [];
|
|
1086
|
+
const placeholders = ids.map(() => "?").join(", ");
|
|
1087
|
+
this.database
|
|
1088
|
+
.prepare(`UPDATE workflow_turn_intents
|
|
1089
|
+
SET delivery_claim_token = ?, delivery_claim_expires_at = ?
|
|
1090
|
+
WHERE intent_id IN (${placeholders}) AND resolved_at IS NULL
|
|
1091
|
+
AND (delivery_claim_expires_at IS NULL OR delivery_claim_expires_at <= ?)`)
|
|
1092
|
+
.run(options.claimToken, nowMs + options.leaseMs, ...ids.map((row) => row.intent_id), nowMs);
|
|
1093
|
+
const rows = this.database
|
|
1094
|
+
.prepare(`SELECT * FROM workflow_turn_intents WHERE delivery_claim_token = ?
|
|
1095
|
+
ORDER BY eligible_at, intent_id`)
|
|
1096
|
+
.all(options.claimToken);
|
|
1097
|
+
return rows.map(workflowTurnIntentFromRow);
|
|
1098
|
+
});
|
|
1099
|
+
}
|
|
1100
|
+
makeWorkflowTurnIntentEligible(options) {
|
|
1101
|
+
validateKey(options.intentId, "workflow turn intent id");
|
|
1102
|
+
const result = this.database
|
|
1103
|
+
.prepare(`UPDATE workflow_turn_intents
|
|
1104
|
+
SET fallback_facts_json = ?, eligible_at = COALESCE(eligible_at, ?)
|
|
1105
|
+
WHERE intent_id = ? AND resolved_at IS NULL`)
|
|
1106
|
+
.run(workflowTurnIntentFactsJson(options.fallbackFacts), validTimestamp(options.now), options.intentId);
|
|
1107
|
+
return result.changes === 1;
|
|
1108
|
+
}
|
|
1109
|
+
resolveWorkflowTurnIntent(options) {
|
|
1110
|
+
validateKey(options.intentId, "workflow turn intent id");
|
|
1111
|
+
validateKey(options.targetSessionId, "workflow turn target session id");
|
|
1112
|
+
validateKey(options.claimToken, "workflow turn claim token");
|
|
1113
|
+
validateWorkflowTurnIntentResolution(options.resolution);
|
|
1114
|
+
validateKey(options.messageId, "workflow turn resolution message id");
|
|
1115
|
+
const result = this.database
|
|
1116
|
+
.prepare(`UPDATE workflow_turn_intents
|
|
1117
|
+
SET resolved_at = ?, resolution = ?, resolution_message_id = ?,
|
|
1118
|
+
delivery_claim_token = NULL, delivery_claim_expires_at = NULL
|
|
1119
|
+
WHERE intent_id = ? AND target_session_id = ? AND resolved_at IS NULL
|
|
1120
|
+
AND delivery_claim_token = ?`)
|
|
1121
|
+
.run(validTimestamp(options.now), options.resolution, options.messageId, options.intentId, options.targetSessionId, options.claimToken);
|
|
1122
|
+
return result.changes === 1;
|
|
1123
|
+
}
|
|
1124
|
+
releaseWorkflowTurnIntentClaim(options) {
|
|
1125
|
+
validateKey(options.intentId, "workflow turn intent id");
|
|
1126
|
+
validateKey(options.targetSessionId, "workflow turn target session id");
|
|
1127
|
+
validateKey(options.claimToken, "workflow turn claim token");
|
|
1128
|
+
const result = this.database
|
|
1129
|
+
.prepare(`UPDATE workflow_turn_intents
|
|
1130
|
+
SET delivery_claim_token = NULL, delivery_claim_expires_at = NULL
|
|
1131
|
+
WHERE intent_id = ? AND target_session_id = ? AND resolved_at IS NULL
|
|
1132
|
+
AND delivery_claim_token = ?`)
|
|
1133
|
+
.run(options.intentId, options.targetSessionId, options.claimToken);
|
|
1134
|
+
return result.changes === 1;
|
|
1135
|
+
}
|
|
1136
|
+
listWorkflowTurnIntents(options = {}) {
|
|
1137
|
+
if (options.targetSessionId !== undefined) {
|
|
1138
|
+
validateKey(options.targetSessionId, "workflow turn target session id");
|
|
1139
|
+
}
|
|
1140
|
+
if (options.runId !== undefined)
|
|
1141
|
+
validateRunId(options.runId);
|
|
1142
|
+
const limit = options.limit ?? 20;
|
|
1143
|
+
if (!Number.isSafeInteger(limit) || limit <= 0 || limit > 100) {
|
|
1144
|
+
throw new Error("Workflow turn intent limit must be an integer from 1 through 100");
|
|
1145
|
+
}
|
|
1146
|
+
const clauses = [];
|
|
1147
|
+
const values = [];
|
|
1148
|
+
if (options.targetSessionId !== undefined) {
|
|
1149
|
+
clauses.push("target_session_id = ?");
|
|
1150
|
+
values.push(options.targetSessionId);
|
|
1151
|
+
}
|
|
1152
|
+
if (options.runId !== undefined) {
|
|
1153
|
+
clauses.push("run_id = ?");
|
|
1154
|
+
values.push(options.runId);
|
|
1155
|
+
}
|
|
1156
|
+
const where = clauses.length === 0 ? "" : `WHERE ${clauses.join(" AND ")}`;
|
|
1157
|
+
const rows = this.database
|
|
1158
|
+
.prepare(`SELECT * FROM workflow_turn_intents ${where}
|
|
1159
|
+
ORDER BY requested_at DESC, intent_id DESC LIMIT ?`)
|
|
1160
|
+
.all(...values, limit);
|
|
1161
|
+
return rows.map(workflowTurnIntentFromRow);
|
|
1162
|
+
}
|
|
946
1163
|
enqueueWorkflowNotification(options) {
|
|
947
1164
|
validateRunId(options.runId);
|
|
948
1165
|
validateKey(options.nodeId, "workflow node id");
|
|
@@ -1121,7 +1338,7 @@ const SCHEMA_SQL = `
|
|
|
1121
1338
|
attempt_id TEXT NOT NULL,
|
|
1122
1339
|
notification_index INTEGER NOT NULL CHECK (notification_index > 0),
|
|
1123
1340
|
target_session_id TEXT NOT NULL,
|
|
1124
|
-
kind TEXT NOT NULL CHECK (kind IN ('progress', 'final'
|
|
1341
|
+
kind TEXT NOT NULL CHECK (kind IN ('progress', 'final')),
|
|
1125
1342
|
content TEXT NOT NULL,
|
|
1126
1343
|
created_at TEXT NOT NULL,
|
|
1127
1344
|
delivery_claim_token TEXT,
|
|
@@ -1133,6 +1350,40 @@ const SCHEMA_SQL = `
|
|
|
1133
1350
|
ON workflow_notifications(target_session_id, delivery_claim_expires_at, created_at)
|
|
1134
1351
|
WHERE delivered_at IS NULL;
|
|
1135
1352
|
|
|
1353
|
+
CREATE TABLE IF NOT EXISTS workflow_turn_intents (
|
|
1354
|
+
intent_id TEXT PRIMARY KEY,
|
|
1355
|
+
source_event_id TEXT NOT NULL,
|
|
1356
|
+
run_id TEXT NOT NULL,
|
|
1357
|
+
workflow_ref TEXT NOT NULL,
|
|
1358
|
+
target_session_id TEXT NOT NULL,
|
|
1359
|
+
cause TEXT NOT NULL CHECK (
|
|
1360
|
+
cause IN ('agentCancelled', 'timedOut', 'failed', 'launchFailed',
|
|
1361
|
+
'controllerInterrupted', 'claimLost')
|
|
1362
|
+
),
|
|
1363
|
+
node_id TEXT,
|
|
1364
|
+
attempt_id TEXT,
|
|
1365
|
+
fallback_facts_json TEXT NOT NULL,
|
|
1366
|
+
requested_at TEXT NOT NULL,
|
|
1367
|
+
eligible_at TEXT,
|
|
1368
|
+
resolved_at TEXT,
|
|
1369
|
+
resolution TEXT CHECK (resolution IN ('workflowPrompt', 'presentation', 'fallback')),
|
|
1370
|
+
resolution_message_id TEXT,
|
|
1371
|
+
delivery_claim_token TEXT,
|
|
1372
|
+
delivery_claim_expires_at INTEGER,
|
|
1373
|
+
CHECK (
|
|
1374
|
+
(resolved_at IS NULL AND resolution IS NULL AND resolution_message_id IS NULL) OR
|
|
1375
|
+
(resolved_at IS NOT NULL AND resolution IS NOT NULL AND resolution_message_id IS NOT NULL)
|
|
1376
|
+
)
|
|
1377
|
+
);
|
|
1378
|
+
CREATE UNIQUE INDEX IF NOT EXISTS workflow_turn_intents_one_pending_run
|
|
1379
|
+
ON workflow_turn_intents(run_id, target_session_id) WHERE resolved_at IS NULL;
|
|
1380
|
+
CREATE INDEX IF NOT EXISTS workflow_turn_intents_pending_run
|
|
1381
|
+
ON workflow_turn_intents(run_id, target_session_id, requested_at)
|
|
1382
|
+
WHERE resolved_at IS NULL;
|
|
1383
|
+
CREATE INDEX IF NOT EXISTS workflow_turn_intents_eligible_session
|
|
1384
|
+
ON workflow_turn_intents(target_session_id, eligible_at, intent_id)
|
|
1385
|
+
WHERE resolved_at IS NULL AND eligible_at IS NOT NULL;
|
|
1386
|
+
|
|
1136
1387
|
CREATE TABLE IF NOT EXISTS effects (
|
|
1137
1388
|
effect_key TEXT NOT NULL,
|
|
1138
1389
|
resource_uid TEXT NOT NULL,
|
|
@@ -1219,7 +1470,48 @@ function workflowFromRow(row) {
|
|
|
1219
1470
|
...(row.error === null ? {} : { error: row.error }),
|
|
1220
1471
|
};
|
|
1221
1472
|
}
|
|
1473
|
+
function workflowTurnIntentFromRow(row) {
|
|
1474
|
+
const cause = validateWorkflowTurnIntentCause(row.cause);
|
|
1475
|
+
const resolution = row.resolution === null ? null : validateWorkflowTurnIntentResolution(row.resolution);
|
|
1476
|
+
const fallbackFacts = parseStoredJson(row.fallback_facts_json, "workflow turn intent facts");
|
|
1477
|
+
validateWorkflowTurnIntentFacts(fallbackFacts);
|
|
1478
|
+
validateKey(row.intent_id, "workflow turn intent id");
|
|
1479
|
+
validateKey(row.source_event_id, "workflow turn source event id");
|
|
1480
|
+
validateRunId(row.run_id);
|
|
1481
|
+
validateKey(row.workflow_ref, "workflow turn workflow ref");
|
|
1482
|
+
validateKey(row.target_session_id, "workflow turn target session id");
|
|
1483
|
+
validTimestamp(row.requested_at);
|
|
1484
|
+
if (row.eligible_at !== null)
|
|
1485
|
+
validTimestamp(row.eligible_at);
|
|
1486
|
+
if (row.resolved_at !== null)
|
|
1487
|
+
validTimestamp(row.resolved_at);
|
|
1488
|
+
const allResolutionFieldsNull = row.resolved_at === null && row.resolution === null && row.resolution_message_id === null;
|
|
1489
|
+
const allResolutionFieldsSet = row.resolved_at !== null && row.resolution !== null && row.resolution_message_id !== null;
|
|
1490
|
+
if (!allResolutionFieldsNull && !allResolutionFieldsSet) {
|
|
1491
|
+
throw new Error(`Workflow turn intent ${row.intent_id} has inconsistent resolution fields`);
|
|
1492
|
+
}
|
|
1493
|
+
return {
|
|
1494
|
+
intentId: row.intent_id,
|
|
1495
|
+
sourceEventId: row.source_event_id,
|
|
1496
|
+
runId: row.run_id,
|
|
1497
|
+
workflowRef: row.workflow_ref,
|
|
1498
|
+
targetSessionId: row.target_session_id,
|
|
1499
|
+
cause,
|
|
1500
|
+
nodeId: row.node_id,
|
|
1501
|
+
attemptId: row.attempt_id,
|
|
1502
|
+
fallbackFacts,
|
|
1503
|
+
requestedAt: row.requested_at,
|
|
1504
|
+
eligibleAt: row.eligible_at,
|
|
1505
|
+
resolvedAt: row.resolved_at,
|
|
1506
|
+
resolution,
|
|
1507
|
+
resolutionMessageId: row.resolution_message_id,
|
|
1508
|
+
deliveryClaimExpiresAt: row.delivery_claim_expires_at === null ? null : iso(row.delivery_claim_expires_at),
|
|
1509
|
+
};
|
|
1510
|
+
}
|
|
1222
1511
|
function workflowNotificationFromRow(row) {
|
|
1512
|
+
if (row.kind !== "progress" && row.kind !== "final") {
|
|
1513
|
+
throw new Error("Controller store contains an incompatible pending alpha workflow notification. Preserve needed run bundles, then reset the project controller store.");
|
|
1514
|
+
}
|
|
1223
1515
|
return {
|
|
1224
1516
|
notificationId: row.notification_id,
|
|
1225
1517
|
runId: row.run_id,
|
|
@@ -1234,6 +1526,89 @@ function workflowNotificationFromRow(row) {
|
|
|
1234
1526
|
deliveredAt: row.delivered_at,
|
|
1235
1527
|
};
|
|
1236
1528
|
}
|
|
1529
|
+
function workflowTurnIntentFactsJson(facts) {
|
|
1530
|
+
validateWorkflowTurnIntentFacts(facts);
|
|
1531
|
+
const json = canonicalJson(facts, "workflow turn intent facts");
|
|
1532
|
+
validateJsonSize(json, "Workflow turn intent facts", MAX_EVENT_BYTES);
|
|
1533
|
+
return json;
|
|
1534
|
+
}
|
|
1535
|
+
function validateWorkflowTurnIntentFacts(facts) {
|
|
1536
|
+
if (facts === null || typeof facts !== "object" || Array.isArray(facts)) {
|
|
1537
|
+
throw new Error("Workflow turn intent facts must be an object");
|
|
1538
|
+
}
|
|
1539
|
+
const candidate = facts;
|
|
1540
|
+
if (candidate.schema !== TURN_INTENT_FACTS_SCHEMA) {
|
|
1541
|
+
throw new Error(`Workflow turn intent facts schema must be ${TURN_INTENT_FACTS_SCHEMA}`);
|
|
1542
|
+
}
|
|
1543
|
+
const allowedKeys = new Set([
|
|
1544
|
+
"schema",
|
|
1545
|
+
"workflowName",
|
|
1546
|
+
"runId",
|
|
1547
|
+
"observedState",
|
|
1548
|
+
"cause",
|
|
1549
|
+
"nodeId",
|
|
1550
|
+
"attemptId",
|
|
1551
|
+
"reason",
|
|
1552
|
+
"handoff",
|
|
1553
|
+
]);
|
|
1554
|
+
for (const key of Object.keys(candidate)) {
|
|
1555
|
+
if (!allowedKeys.has(key))
|
|
1556
|
+
throw new Error(`Unknown workflow turn intent facts field: ${key}`);
|
|
1557
|
+
}
|
|
1558
|
+
if (typeof candidate.workflowName !== "string") {
|
|
1559
|
+
throw new Error("Workflow turn intent workflowName must be a string");
|
|
1560
|
+
}
|
|
1561
|
+
if (typeof candidate.runId !== "string") {
|
|
1562
|
+
throw new Error("Workflow turn intent runId must be a string");
|
|
1563
|
+
}
|
|
1564
|
+
if (typeof candidate.observedState !== "string") {
|
|
1565
|
+
throw new Error("Workflow turn intent observedState must be a string");
|
|
1566
|
+
}
|
|
1567
|
+
if (typeof candidate.cause !== "string") {
|
|
1568
|
+
throw new Error("Workflow turn intent cause must be a string");
|
|
1569
|
+
}
|
|
1570
|
+
validateKey(candidate.workflowName, "workflow turn facts workflow name");
|
|
1571
|
+
validateRunId(candidate.runId);
|
|
1572
|
+
validateKey(candidate.observedState, "workflow turn facts observed state");
|
|
1573
|
+
validateWorkflowTurnIntentCause(candidate.cause);
|
|
1574
|
+
if (candidate.nodeId !== null && typeof candidate.nodeId !== "string") {
|
|
1575
|
+
throw new Error("Workflow turn intent nodeId must be a string or null");
|
|
1576
|
+
}
|
|
1577
|
+
if (candidate.attemptId !== null && typeof candidate.attemptId !== "string") {
|
|
1578
|
+
throw new Error("Workflow turn intent attemptId must be a string or null");
|
|
1579
|
+
}
|
|
1580
|
+
if (candidate.reason !== null && typeof candidate.reason !== "string") {
|
|
1581
|
+
throw new Error("Workflow turn intent reason must be a string or null");
|
|
1582
|
+
}
|
|
1583
|
+
if (candidate.nodeId !== null)
|
|
1584
|
+
validateKey(candidate.nodeId, "workflow turn facts node id");
|
|
1585
|
+
if (candidate.attemptId !== null) {
|
|
1586
|
+
validateKey(candidate.attemptId, "workflow turn facts attempt id");
|
|
1587
|
+
}
|
|
1588
|
+
if (candidate.reason !== null && candidate.reason.length > MAX_ERROR_LENGTH) {
|
|
1589
|
+
throw new Error(`Workflow turn intent reason must be at most ${MAX_ERROR_LENGTH} characters`);
|
|
1590
|
+
}
|
|
1591
|
+
if (typeof candidate.handoff !== "boolean") {
|
|
1592
|
+
throw new Error("Workflow turn intent handoff must be a boolean");
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
function validateWorkflowTurnIntentCause(value) {
|
|
1596
|
+
if (value !== "agentCancelled" &&
|
|
1597
|
+
value !== "timedOut" &&
|
|
1598
|
+
value !== "failed" &&
|
|
1599
|
+
value !== "launchFailed" &&
|
|
1600
|
+
value !== "controllerInterrupted" &&
|
|
1601
|
+
value !== "claimLost") {
|
|
1602
|
+
throw new Error(`Invalid workflow turn intent cause: ${value}`);
|
|
1603
|
+
}
|
|
1604
|
+
return value;
|
|
1605
|
+
}
|
|
1606
|
+
function validateWorkflowTurnIntentResolution(value) {
|
|
1607
|
+
if (value !== "workflowPrompt" && value !== "presentation" && value !== "fallback") {
|
|
1608
|
+
throw new Error(`Invalid workflow turn intent resolution: ${value}`);
|
|
1609
|
+
}
|
|
1610
|
+
return value;
|
|
1611
|
+
}
|
|
1237
1612
|
function workflowRunFromRow(row) {
|
|
1238
1613
|
return {
|
|
1239
1614
|
runId: row.run_id,
|