ai-ctrl-workflow-logic 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "ai-ctrl-workflow-logic",
3
+ "version": "0.1.0",
4
+ "private": false,
5
+ "description": "WorkCtrlFlow-backed AI Ctrl Workflow adapter logic",
6
+ "type": "module",
7
+ "main": "./src/index.ts",
8
+ "exports": {
9
+ ".": "./src/index.ts",
10
+ "./browser": "./src/browser.ts"
11
+ },
12
+ "dependencies": {
13
+ "ai-ctrl-workflow-contract": "0.1.0",
14
+ "ai-workflow-logic": "0.1.0",
15
+ "ai-workflow-contract": "0.1.0",
16
+ "instant-ctrl-flow-contract": "0.1.0",
17
+ "work-ctrl-flow-contract": "0.1.0",
18
+ "work-ctrl-flow-logic": "0.1.0"
19
+ },
20
+ "files": [
21
+ "src"
22
+ ],
23
+ "publishConfig": {
24
+ "access": "public",
25
+ "registry": "https://registry.npmjs.com"
26
+ }
27
+ }
package/src/browser.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './index.js';
package/src/index.ts ADDED
@@ -0,0 +1,152 @@
1
+ import type {
2
+ AICtrlWorkflowController,
3
+ AICtrlWorkflowNodeRecord,
4
+ AICtrlWorkflowRunRecord,
5
+ } from 'ai-ctrl-workflow-contract';
6
+ import type {
7
+ AICtrlWorkflowDefinitionBinding,
8
+ AICtrlWorkflowRuntime,
9
+ AIWorkflowEffectProvider,
10
+ AIWorkflowRuntimeContext,
11
+ AIWorkflowStateStore,
12
+ } from 'ai-workflow-contract';
13
+ import { createAIWorkflowRuntimeContext, defineAICtrlWorkflow } from 'ai-workflow-logic';
14
+ import type { FlowCodeResolver } from 'instant-ctrl-flow-contract';
15
+ import type {
16
+ ResumeSignal,
17
+ TickOutcome,
18
+ WorkCtrlFlowSnapshot,
19
+ WorkCtrlFlowRuntime,
20
+ WorkCtrlFlowStartOptions,
21
+ WorkCtrlFlowStore,
22
+ } from 'work-ctrl-flow-contract';
23
+ import { WorkCtrlFlowEngine, type WorkCtrlFlowDefinition, type WorkCtrlFlowEngineOptions } from 'work-ctrl-flow-logic';
24
+
25
+ export interface CreateAICtrlWorkflowRuntimeOptions<
26
+ Effects extends AIWorkflowEffectProvider,
27
+ Store extends AIWorkflowStateStore,
28
+ > {
29
+ readonly runtime?: WorkCtrlFlowRuntime;
30
+ readonly ai: AIWorkflowRuntimeContext<Effects, Store>;
31
+ }
32
+
33
+ export function createAICtrlWorkflowRuntime<
34
+ Effects extends AIWorkflowEffectProvider,
35
+ Store extends AIWorkflowStateStore,
36
+ >(
37
+ options: CreateAICtrlWorkflowRuntimeOptions<Effects, Store>,
38
+ ): AICtrlWorkflowRuntime<Effects, Store> {
39
+ return {
40
+ ...(options.runtime ?? {}),
41
+ ai: createAIWorkflowRuntimeContext(options.ai),
42
+ };
43
+ }
44
+
45
+ export interface CreateAICtrlWorkflowControllerOptions
46
+ extends Omit<WorkCtrlFlowEngineOptions, 'definition' | 'runtime' | 'expectedForm'> {
47
+ readonly binding: AICtrlWorkflowDefinitionBinding;
48
+ readonly resolveCode: FlowCodeResolver<AICtrlWorkflowRuntime>;
49
+ readonly resolveFlow?: WorkCtrlFlowDefinition['resolveFlow'];
50
+ readonly ai: AIWorkflowRuntimeContext;
51
+ readonly runtime?: WorkCtrlFlowRuntime;
52
+ }
53
+
54
+ export function createAICtrlWorkflowController(
55
+ options: CreateAICtrlWorkflowControllerOptions,
56
+ ): AICtrlWorkflowController {
57
+ const definition: WorkCtrlFlowDefinition = {
58
+ spec: options.binding.definition,
59
+ resolveCode: options.resolveCode as FlowCodeResolver<WorkCtrlFlowRuntime>,
60
+ resolveFlow: options.resolveFlow,
61
+ };
62
+ const engine = new WorkCtrlFlowEngine({
63
+ ...options,
64
+ expectedForm: 'WorkCtrlFlow',
65
+ definition,
66
+ runtime: createAICtrlWorkflowRuntime({ runtime: options.runtime, ai: options.ai }),
67
+ });
68
+ return {
69
+ store: options.store,
70
+ start: (treeId, startOptions) => engine.start(treeId, startOptions),
71
+ resume: (treeId, signal) => engine.resume(treeId, signal),
72
+ refresh: (treeId) => engine.refresh(treeId),
73
+ getOutcome: (treeId) => engine.getOutcome(treeId),
74
+ };
75
+ }
76
+
77
+ export function defineWorkCtrlFlowBackedAICtrlWorkflow(
78
+ definition: AICtrlWorkflowDefinitionBinding['definition'],
79
+ ): AICtrlWorkflowDefinitionBinding {
80
+ return defineAICtrlWorkflow(definition);
81
+ }
82
+
83
+ function nodeIdFromInvocationKey(invocationKey: string): string {
84
+ const withoutVisit = invocationKey.slice(0, invocationKey.lastIndexOf('#'));
85
+ return withoutVisit.slice(withoutVisit.lastIndexOf('/') + 1);
86
+ }
87
+
88
+ export function projectAICtrlWorkflowRunRecord(
89
+ snapshot: WorkCtrlFlowSnapshot,
90
+ outcome?: TickOutcome,
91
+ ): AICtrlWorkflowRunRecord {
92
+ const nodes = new Map<string, AICtrlWorkflowNodeRecord>();
93
+ for (const [invocationKey, result] of Object.entries(snapshot.nodeResults)) {
94
+ nodes.set(invocationKey, {
95
+ invocationKey,
96
+ nodeId: nodeIdFromInvocationKey(invocationKey),
97
+ status: result === 'Success' ? 'Succeeded' : 'Failed',
98
+ });
99
+ }
100
+ for (const [invocationKey, wait] of Object.entries(snapshot.waitResults)) {
101
+ nodes.set(invocationKey, {
102
+ invocationKey,
103
+ nodeId: nodeIdFromInvocationKey(invocationKey),
104
+ status: wait.result === 'Success' ? 'Succeeded' : 'Failed',
105
+ });
106
+ }
107
+ for (const handle of snapshot.openWaitHandles) {
108
+ nodes.set(handle.invocationKey, {
109
+ invocationKey: handle.invocationKey,
110
+ nodeId: handle.nodeKey,
111
+ status: 'Waiting',
112
+ });
113
+ }
114
+ return {
115
+ treeId: snapshot.treeId,
116
+ fqn: snapshot.fqn,
117
+ status: outcome?.status ?? snapshot.status,
118
+ tickNo: outcome?.tickNo ?? snapshot.lastTickNo,
119
+ openWaitHandles: snapshot.openWaitHandles,
120
+ nodes: [...nodes.values()],
121
+ };
122
+ }
123
+
124
+ export async function startAICtrlWorkflowRun(
125
+ controller: AICtrlWorkflowController,
126
+ treeId: string,
127
+ options: WorkCtrlFlowStartOptions = {},
128
+ ): Promise<AICtrlWorkflowRunRecord> {
129
+ const outcome = await controller.start(treeId, options);
130
+ const snapshot = await controller.store.load(treeId);
131
+ if (!snapshot) throw new Error(`AI Ctrl Workflow snapshot missing for ${treeId}`);
132
+ return projectAICtrlWorkflowRunRecord(snapshot, outcome);
133
+ }
134
+
135
+ export async function resumeAICtrlWorkflowRun(
136
+ controller: AICtrlWorkflowController,
137
+ treeId: string,
138
+ signal: ResumeSignal,
139
+ ): Promise<AICtrlWorkflowRunRecord> {
140
+ const outcome = await controller.resume(treeId, signal);
141
+ const snapshot = await controller.store.load(treeId);
142
+ if (!snapshot) throw new Error(`AI Ctrl Workflow snapshot missing for ${treeId}`);
143
+ return projectAICtrlWorkflowRunRecord(snapshot, outcome);
144
+ }
145
+
146
+ export async function getAICtrlWorkflowRunRecord(
147
+ controller: AICtrlWorkflowController,
148
+ treeId: string,
149
+ ): Promise<AICtrlWorkflowRunRecord | undefined> {
150
+ const snapshot = await controller.store.load(treeId);
151
+ return snapshot ? projectAICtrlWorkflowRunRecord(snapshot, await controller.getOutcome(treeId)) : undefined;
152
+ }