@framers/agentos 0.1.69 → 0.1.71
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/dist/api/agent.d.ts +98 -2
- package/dist/api/agent.d.ts.map +1 -1
- package/dist/api/agent.js +64 -9
- package/dist/api/agent.js.map +1 -1
- package/dist/api/generateImage.d.ts +109 -0
- package/dist/api/generateImage.d.ts.map +1 -0
- package/dist/api/generateImage.js +41 -0
- package/dist/api/generateImage.js.map +1 -0
- package/dist/api/generateText.d.ts +73 -3
- package/dist/api/generateText.d.ts.map +1 -1
- package/dist/api/generateText.js +29 -4
- package/dist/api/generateText.js.map +1 -1
- package/dist/api/model.d.ts +74 -4
- package/dist/api/model.d.ts.map +1 -1
- package/dist/api/model.js +79 -5
- package/dist/api/model.js.map +1 -1
- package/dist/api/streamText.d.ts +37 -1
- package/dist/api/streamText.d.ts.map +1 -1
- package/dist/api/streamText.js +188 -49
- package/dist/api/streamText.js.map +1 -1
- package/dist/api/toolAdapter.d.ts +58 -0
- package/dist/api/toolAdapter.d.ts.map +1 -0
- package/dist/api/{tool-adapter.js → toolAdapter.js} +24 -2
- package/dist/api/toolAdapter.js.map +1 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/orchestration/builders/AgentGraph.d.ts +250 -0
- package/dist/orchestration/builders/AgentGraph.d.ts.map +1 -0
- package/dist/orchestration/builders/AgentGraph.js +338 -0
- package/dist/orchestration/builders/AgentGraph.js.map +1 -0
- package/dist/orchestration/builders/MissionBuilder.d.ts +231 -0
- package/dist/orchestration/builders/MissionBuilder.d.ts.map +1 -0
- package/dist/orchestration/builders/MissionBuilder.js +321 -0
- package/dist/orchestration/builders/MissionBuilder.js.map +1 -0
- package/dist/orchestration/builders/WorkflowBuilder.d.ts +265 -0
- package/dist/orchestration/builders/WorkflowBuilder.d.ts.map +1 -0
- package/dist/orchestration/builders/WorkflowBuilder.js +472 -0
- package/dist/orchestration/builders/WorkflowBuilder.js.map +1 -0
- package/dist/orchestration/builders/index.d.ts +7 -0
- package/dist/orchestration/builders/index.d.ts.map +1 -0
- package/dist/orchestration/builders/index.js +5 -0
- package/dist/orchestration/builders/index.js.map +1 -0
- package/dist/orchestration/builders/nodes.d.ts +36 -0
- package/dist/orchestration/builders/nodes.d.ts.map +1 -0
- package/dist/orchestration/builders/nodes.js +98 -0
- package/dist/orchestration/builders/nodes.js.map +1 -0
- package/dist/orchestration/checkpoint/ICheckpointStore.d.ts +152 -0
- package/dist/orchestration/checkpoint/ICheckpointStore.d.ts.map +1 -0
- package/dist/orchestration/checkpoint/ICheckpointStore.js +8 -0
- package/dist/orchestration/checkpoint/ICheckpointStore.js.map +1 -0
- package/dist/orchestration/checkpoint/InMemoryCheckpointStore.d.ts +89 -0
- package/dist/orchestration/checkpoint/InMemoryCheckpointStore.d.ts.map +1 -0
- package/dist/orchestration/checkpoint/InMemoryCheckpointStore.js +156 -0
- package/dist/orchestration/checkpoint/InMemoryCheckpointStore.js.map +1 -0
- package/dist/orchestration/checkpoint/index.d.ts +10 -0
- package/dist/orchestration/checkpoint/index.d.ts.map +1 -0
- package/dist/orchestration/checkpoint/index.js +10 -0
- package/dist/orchestration/checkpoint/index.js.map +1 -0
- package/dist/orchestration/compiler/GraphCompiler.d.ts +85 -0
- package/dist/orchestration/compiler/GraphCompiler.d.ts.map +1 -0
- package/dist/orchestration/compiler/GraphCompiler.js +71 -0
- package/dist/orchestration/compiler/GraphCompiler.js.map +1 -0
- package/dist/orchestration/compiler/MissionCompiler.d.ts +181 -0
- package/dist/orchestration/compiler/MissionCompiler.d.ts.map +1 -0
- package/dist/orchestration/compiler/MissionCompiler.js +219 -0
- package/dist/orchestration/compiler/MissionCompiler.js.map +1 -0
- package/dist/orchestration/compiler/SchemaLowering.d.ts +37 -0
- package/dist/orchestration/compiler/SchemaLowering.d.ts.map +1 -0
- package/dist/orchestration/compiler/SchemaLowering.js +93 -0
- package/dist/orchestration/compiler/SchemaLowering.js.map +1 -0
- package/dist/orchestration/compiler/Validator.d.ts +61 -0
- package/dist/orchestration/compiler/Validator.d.ts.map +1 -0
- package/dist/orchestration/compiler/Validator.js +96 -0
- package/dist/orchestration/compiler/Validator.js.map +1 -0
- package/dist/orchestration/compiler/index.d.ts +16 -0
- package/dist/orchestration/compiler/index.d.ts.map +1 -0
- package/dist/orchestration/compiler/index.js +13 -0
- package/dist/orchestration/compiler/index.js.map +1 -0
- package/dist/orchestration/events/GraphEvent.d.ts +233 -0
- package/dist/orchestration/events/GraphEvent.d.ts.map +1 -0
- package/dist/orchestration/events/GraphEvent.js +203 -0
- package/dist/orchestration/events/GraphEvent.js.map +1 -0
- package/dist/orchestration/events/index.d.ts +2 -0
- package/dist/orchestration/events/index.d.ts.map +1 -0
- package/dist/orchestration/events/index.js +2 -0
- package/dist/orchestration/events/index.js.map +1 -0
- package/dist/orchestration/index.d.ts +20 -0
- package/dist/orchestration/index.d.ts.map +1 -0
- package/dist/orchestration/index.js +26 -0
- package/dist/orchestration/index.js.map +1 -0
- package/dist/orchestration/ir/index.d.ts +2 -0
- package/dist/orchestration/ir/index.d.ts.map +1 -0
- package/dist/orchestration/ir/index.js +2 -0
- package/dist/orchestration/ir/index.js.map +1 -0
- package/dist/orchestration/ir/types.d.ts +540 -0
- package/dist/orchestration/ir/types.d.ts.map +1 -0
- package/dist/orchestration/ir/types.js +20 -0
- package/dist/orchestration/ir/types.js.map +1 -0
- package/dist/orchestration/runtime/GraphRuntime.d.ts +135 -0
- package/dist/orchestration/runtime/GraphRuntime.d.ts.map +1 -0
- package/dist/orchestration/runtime/GraphRuntime.js +381 -0
- package/dist/orchestration/runtime/GraphRuntime.js.map +1 -0
- package/dist/orchestration/runtime/LoopController.d.ts +173 -0
- package/dist/orchestration/runtime/LoopController.d.ts.map +1 -0
- package/dist/orchestration/runtime/LoopController.js +130 -0
- package/dist/orchestration/runtime/LoopController.js.map +1 -0
- package/dist/orchestration/runtime/NodeExecutor.d.ts +206 -0
- package/dist/orchestration/runtime/NodeExecutor.d.ts.map +1 -0
- package/dist/orchestration/runtime/NodeExecutor.js +227 -0
- package/dist/orchestration/runtime/NodeExecutor.js.map +1 -0
- package/dist/orchestration/runtime/NodeScheduler.d.ts +85 -0
- package/dist/orchestration/runtime/NodeScheduler.d.ts.map +1 -0
- package/dist/orchestration/runtime/NodeScheduler.js +180 -0
- package/dist/orchestration/runtime/NodeScheduler.js.map +1 -0
- package/dist/orchestration/runtime/StateManager.d.ts +122 -0
- package/dist/orchestration/runtime/StateManager.d.ts.map +1 -0
- package/dist/orchestration/runtime/StateManager.js +243 -0
- package/dist/orchestration/runtime/StateManager.js.map +1 -0
- package/dist/orchestration/runtime/index.d.ts +16 -0
- package/dist/orchestration/runtime/index.d.ts.map +1 -0
- package/dist/orchestration/runtime/index.js +13 -0
- package/dist/orchestration/runtime/index.js.map +1 -0
- package/package.json +10 -4
- package/dist/api/tool-adapter.d.ts +0 -12
- package/dist/api/tool-adapter.d.ts.map +0 -1
- package/dist/api/tool-adapter.js.map +0 -1
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file NodeExecutor.ts
|
|
3
|
+
* @description Dispatches execution to the appropriate handler based on `GraphNode.executorConfig.type`.
|
|
4
|
+
*
|
|
5
|
+
* The executor is intentionally thin — it contains no retry logic (handled by `GraphRuntime`),
|
|
6
|
+
* no state mutation (handled by `StateManager`), and no event emission (handled by the caller).
|
|
7
|
+
* Each private method maps one-to-one with a `NodeExecutorConfig` variant.
|
|
8
|
+
*
|
|
9
|
+
* Execution flow:
|
|
10
|
+
* `execute()` → optional timeout race → `executeNode()` → variant handler
|
|
11
|
+
*
|
|
12
|
+
* Placeholders for `gmi`, `extension`, and `subgraph` nodes are wired in `GraphRuntime`
|
|
13
|
+
* after the `LoopController` and extension managers are available.
|
|
14
|
+
*/
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// NodeExecutor
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
/**
|
|
19
|
+
* Stateless executor that dispatches a `GraphNode` to the appropriate handler.
|
|
20
|
+
*
|
|
21
|
+
* One `NodeExecutor` instance is typically shared across the lifetime of a `GraphRuntime`
|
|
22
|
+
* and reused for every node invocation within every run. All state is passed through
|
|
23
|
+
* `GraphState` and returned via `NodeExecutionResult`.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* const executor = new NodeExecutor({ toolOrchestrator, guardrailEngine });
|
|
28
|
+
* const result = await executor.execute(node, graphState);
|
|
29
|
+
* if (!result.success) console.error(result.error);
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export class NodeExecutor {
|
|
33
|
+
/**
|
|
34
|
+
* @param deps - External service adapters. All fields are optional; missing services
|
|
35
|
+
* cause graceful degradation rather than hard failures.
|
|
36
|
+
*/
|
|
37
|
+
constructor(deps) {
|
|
38
|
+
this.deps = deps;
|
|
39
|
+
}
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
// Public API
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
/**
|
|
44
|
+
* Execute `node` against the provided `state`, optionally racing against a timeout.
|
|
45
|
+
*
|
|
46
|
+
* If `node.timeout` is set, execution races against a timer that resolves with a
|
|
47
|
+
* `success: false` result after the specified number of milliseconds.
|
|
48
|
+
*
|
|
49
|
+
* @param node - Immutable node descriptor from the compiled graph IR.
|
|
50
|
+
* @param state - Current (partial) graph state threaded from the runtime.
|
|
51
|
+
* @returns A `NodeExecutionResult` describing the outcome.
|
|
52
|
+
*/
|
|
53
|
+
async execute(node, state) {
|
|
54
|
+
if (node.timeout) {
|
|
55
|
+
return Promise.race([
|
|
56
|
+
this.executeNode(node, state),
|
|
57
|
+
this.buildTimeoutPromise(node.timeout, node.id),
|
|
58
|
+
]);
|
|
59
|
+
}
|
|
60
|
+
return this.executeNode(node, state);
|
|
61
|
+
}
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
// Internal dispatch
|
|
64
|
+
// ---------------------------------------------------------------------------
|
|
65
|
+
/**
|
|
66
|
+
* Dispatches to the correct private handler based on `executorConfig.type`.
|
|
67
|
+
*
|
|
68
|
+
* Each branch receives only the narrowed config type it needs, keeping handler
|
|
69
|
+
* signatures precise and avoiding accidental access to unrelated fields.
|
|
70
|
+
*/
|
|
71
|
+
async executeNode(node, state) {
|
|
72
|
+
const config = node.executorConfig;
|
|
73
|
+
switch (config.type) {
|
|
74
|
+
case 'tool':
|
|
75
|
+
return this.executeTool(config, state);
|
|
76
|
+
case 'router':
|
|
77
|
+
return this.executeRouter(config, state);
|
|
78
|
+
case 'guardrail':
|
|
79
|
+
return this.executeGuardrail(config, state);
|
|
80
|
+
case 'human':
|
|
81
|
+
return this.executeHuman(config);
|
|
82
|
+
case 'gmi':
|
|
83
|
+
// GMI execution is delegated to `LoopController` and wired in `GraphRuntime`.
|
|
84
|
+
// This placeholder allows the executor to be used before the LLM subsystem is ready.
|
|
85
|
+
return { success: true, output: 'gmi-placeholder' };
|
|
86
|
+
case 'extension':
|
|
87
|
+
// Extension execution is wired by `GraphRuntime` once the extension manager is available.
|
|
88
|
+
return { success: true, output: 'extension-placeholder' };
|
|
89
|
+
case 'subgraph':
|
|
90
|
+
// Subgraph delegation is wired by `GraphRuntime` once nested graph lookup is available.
|
|
91
|
+
return { success: true, output: 'subgraph-placeholder' };
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// ---------------------------------------------------------------------------
|
|
95
|
+
// Variant handlers
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
/**
|
|
98
|
+
* Invokes a registered `ITool` via `ToolOrchestrator.processToolCall()`.
|
|
99
|
+
*
|
|
100
|
+
* Static args from `config.args` are merged into the call. The orchestrator
|
|
101
|
+
* is responsible for argument validation and schema enforcement.
|
|
102
|
+
*
|
|
103
|
+
* @param config - `{ type: 'tool'; toolName: string; args?: Record<string, unknown> }`
|
|
104
|
+
* @param state - Current graph state (not used directly but available for future extension).
|
|
105
|
+
*/
|
|
106
|
+
async executeTool(config, _state) {
|
|
107
|
+
if (!this.deps.toolOrchestrator) {
|
|
108
|
+
return {
|
|
109
|
+
success: false,
|
|
110
|
+
error: 'No ToolOrchestrator configured',
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
const result = await this.deps.toolOrchestrator.processToolCall({
|
|
114
|
+
toolCallRequest: {
|
|
115
|
+
toolName: config.toolName,
|
|
116
|
+
arguments: config.args ?? {},
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
return {
|
|
120
|
+
success: result.success ?? !result.isError,
|
|
121
|
+
output: result.output,
|
|
122
|
+
error: result.error,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Evaluates a `GraphCondition` and returns the resolved target node id as `routeTarget`.
|
|
127
|
+
*
|
|
128
|
+
* Two condition strategies are supported:
|
|
129
|
+
* - `function` — calls the runtime-registered TypeScript `fn` directly.
|
|
130
|
+
* - `expression` — delegates to `evaluateExpression()` for DSL string evaluation.
|
|
131
|
+
*
|
|
132
|
+
* @param config - `{ type: 'router'; condition: GraphCondition }`
|
|
133
|
+
* @param state - Current graph state passed to the condition function/evaluator.
|
|
134
|
+
*/
|
|
135
|
+
async executeRouter(config, state) {
|
|
136
|
+
let target;
|
|
137
|
+
if (config.condition.type === 'function') {
|
|
138
|
+
// The function condition receives the full state and returns a node id.
|
|
139
|
+
target = config.condition.fn(state);
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
// Expression-based conditions are evaluated by the minimal DSL interpreter.
|
|
143
|
+
target = this.evaluateExpression(config.condition.expr, state);
|
|
144
|
+
}
|
|
145
|
+
return { success: true, routeTarget: target };
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Evaluates a set of guardrails against `state.scratch` and either passes through
|
|
149
|
+
* or triggers the configured violation action.
|
|
150
|
+
*
|
|
151
|
+
* When no `guardrailEngine` is configured, the node always passes (permissive default).
|
|
152
|
+
* Violation handling currently supports `'reroute'`; `'block'`, `'warn'`, and `'sanitize'`
|
|
153
|
+
* are propagated via `success: false` for the runtime to handle.
|
|
154
|
+
*
|
|
155
|
+
* @param config - Guardrail node config with `guardrailIds`, `onViolation`, and optional `rerouteTarget`.
|
|
156
|
+
* @param state - Current graph state; `state.scratch` is passed to the engine as the content payload.
|
|
157
|
+
*/
|
|
158
|
+
async executeGuardrail(config, state) {
|
|
159
|
+
if (!this.deps.guardrailEngine) {
|
|
160
|
+
// Permissive fallback: no engine means no enforcement.
|
|
161
|
+
return {
|
|
162
|
+
success: true,
|
|
163
|
+
output: { passed: true, message: 'No guardrail engine configured' },
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
const result = await this.deps.guardrailEngine.evaluate(state.scratch, config.guardrailIds);
|
|
167
|
+
if (!result.passed && config.onViolation === 'reroute' && config.rerouteTarget) {
|
|
168
|
+
// Soft violation: redirect the graph to the recovery branch.
|
|
169
|
+
return { success: true, routeTarget: config.rerouteTarget };
|
|
170
|
+
}
|
|
171
|
+
// For all other violation actions (block, warn, sanitize) the runtime inspects
|
|
172
|
+
// `success: false` and acts according to its own policy.
|
|
173
|
+
return {
|
|
174
|
+
success: result.passed,
|
|
175
|
+
output: result,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Suspends execution and surfaces a prompt to a human operator.
|
|
180
|
+
*
|
|
181
|
+
* The runtime must treat `interrupt: true` as a signal to persist state, emit an
|
|
182
|
+
* `interrupt` event, and halt the current run until the operator provides a response.
|
|
183
|
+
*
|
|
184
|
+
* @param config - `{ type: 'human'; prompt: string }`
|
|
185
|
+
*/
|
|
186
|
+
executeHuman(config) {
|
|
187
|
+
return Promise.resolve({
|
|
188
|
+
success: false,
|
|
189
|
+
interrupt: true,
|
|
190
|
+
error: 'Awaiting human input',
|
|
191
|
+
output: { prompt: config.prompt },
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
// ---------------------------------------------------------------------------
|
|
195
|
+
// Utilities
|
|
196
|
+
// ---------------------------------------------------------------------------
|
|
197
|
+
/**
|
|
198
|
+
* Minimal DSL expression evaluator for `{ type: 'expression' }` routing conditions.
|
|
199
|
+
*
|
|
200
|
+
* Current implementation is a stub that returns the expression string unchanged.
|
|
201
|
+
* A full implementation would parse `"scratch.confidence > 0.8 ? 'approve' : 'review'"`
|
|
202
|
+
* using a sandboxed interpreter with dot-path access to `state` fields.
|
|
203
|
+
*
|
|
204
|
+
* @param expr - The DSL expression string from `GraphConditionExpr`.
|
|
205
|
+
* @param state - Current graph state (available for a real implementation to traverse).
|
|
206
|
+
* @returns The resolved target node id (or the raw expression until the evaluator is complete).
|
|
207
|
+
*
|
|
208
|
+
* @todo Implement a sandboxed expression interpreter (tracked separately).
|
|
209
|
+
*/
|
|
210
|
+
evaluateExpression(expr, _state) {
|
|
211
|
+
return expr;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Builds a `Promise` that resolves with a timeout-failure result after `ms` milliseconds.
|
|
215
|
+
*
|
|
216
|
+
* Races against `executeNode()` inside `execute()` to enforce `GraphNode.timeout`.
|
|
217
|
+
*
|
|
218
|
+
* @param ms - Timeout duration in milliseconds.
|
|
219
|
+
* @param nodeId - Node id included in the error message for debugging.
|
|
220
|
+
*/
|
|
221
|
+
buildTimeoutPromise(ms, nodeId) {
|
|
222
|
+
return new Promise((resolve) => {
|
|
223
|
+
setTimeout(() => resolve({ success: false, error: `Node ${nodeId} timeout after ${ms}ms` }), ms);
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
//# sourceMappingURL=NodeExecutor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NodeExecutor.js","sourceRoot":"","sources":["../../../src/orchestration/runtime/NodeExecutor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file NodeScheduler.ts
|
|
3
|
+
* @description Topological ordering, cycle detection, and ready-node detection
|
|
4
|
+
* for compiled execution graph scheduling in the AgentOS Unified Orchestration Layer.
|
|
5
|
+
*
|
|
6
|
+
* Uses Kahn's algorithm (BFS-based) for topological sorting, which also serves
|
|
7
|
+
* as the foundation for cycle detection. The scheduler treats START and END as
|
|
8
|
+
* virtual nodes that participate in edge traversal but are excluded from the
|
|
9
|
+
* returned topological order.
|
|
10
|
+
*/
|
|
11
|
+
import type { GraphNode, GraphEdge } from '../ir/types.js';
|
|
12
|
+
/**
|
|
13
|
+
* Schedules graph node execution by computing topological ordering, detecting
|
|
14
|
+
* structural issues (cycles, unreachable nodes), and determining which nodes
|
|
15
|
+
* are ready to run given a set of already-completed nodes.
|
|
16
|
+
*
|
|
17
|
+
* All methods are pure and stateless with respect to execution — the scheduler
|
|
18
|
+
* only reads the static graph structure provided at construction time.
|
|
19
|
+
*/
|
|
20
|
+
export declare class NodeScheduler {
|
|
21
|
+
/** Maps each node id to its outgoing neighbour ids. Includes START and END. */
|
|
22
|
+
private adjacency;
|
|
23
|
+
/** Maps each node id to the ids of nodes that have edges pointing into it. Includes START and END. */
|
|
24
|
+
private predecessors;
|
|
25
|
+
/** Set of real (non-sentinel) node ids derived from the provided GraphNode array. */
|
|
26
|
+
private nodeIds;
|
|
27
|
+
/**
|
|
28
|
+
* Constructs a NodeScheduler from a compiled graph's node and edge lists.
|
|
29
|
+
*
|
|
30
|
+
* @param nodes - All real (non-sentinel) graph nodes.
|
|
31
|
+
* @param edges - All directed edges, including those from/to START or END sentinels.
|
|
32
|
+
*/
|
|
33
|
+
constructor(nodes: GraphNode[], edges: GraphEdge[]);
|
|
34
|
+
/**
|
|
35
|
+
* Returns real node ids in a valid topological execution order using Kahn's
|
|
36
|
+
* algorithm (BFS over in-degree).
|
|
37
|
+
*
|
|
38
|
+
* START and END sentinels are intentionally excluded from the returned array
|
|
39
|
+
* because they are virtual control-flow markers, not executable nodes.
|
|
40
|
+
*
|
|
41
|
+
* If the graph contains a cycle, the returned array will be shorter than
|
|
42
|
+
* `nodeIds.size` — use {@link hasCycles} to distinguish this case explicitly.
|
|
43
|
+
*
|
|
44
|
+
* @returns Ordered array of real node ids; empty if there are no real nodes.
|
|
45
|
+
*/
|
|
46
|
+
topologicalSort(): string[];
|
|
47
|
+
/**
|
|
48
|
+
* Returns `true` if the graph contains at least one directed cycle among the
|
|
49
|
+
* real nodes (sentinels are excluded from cycle detection).
|
|
50
|
+
*
|
|
51
|
+
* Implemented by comparing the length of the topological sort result against
|
|
52
|
+
* the total number of real nodes: Kahn's algorithm processes every node in a
|
|
53
|
+
* DAG, so any shortfall indicates nodes trapped inside a cycle.
|
|
54
|
+
*
|
|
55
|
+
* @returns `true` when a cycle exists; `false` for a valid DAG.
|
|
56
|
+
*/
|
|
57
|
+
hasCycles(): boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Returns the ids of all real nodes that are eligible to execute next, given
|
|
60
|
+
* the set of nodes that have already finished (completed or skipped).
|
|
61
|
+
*
|
|
62
|
+
* A node is "ready" when:
|
|
63
|
+
* 1. It has not already completed or been skipped.
|
|
64
|
+
* 2. Every one of its predecessors is either the START sentinel or a member of
|
|
65
|
+
* the completed/skipped set.
|
|
66
|
+
*
|
|
67
|
+
* @param completedNodeIds - Node ids that have successfully finished execution.
|
|
68
|
+
* @param skippedNodeIds - Node ids that were bypassed (e.g. via conditional routing).
|
|
69
|
+
* @returns Array of node ids that can be dispatched for execution immediately.
|
|
70
|
+
*/
|
|
71
|
+
getReadyNodes(completedNodeIds: string[], skippedNodeIds?: string[]): string[];
|
|
72
|
+
/**
|
|
73
|
+
* Returns the ids of all real nodes that are not reachable from the START
|
|
74
|
+
* sentinel via a BFS traversal of the adjacency list.
|
|
75
|
+
*
|
|
76
|
+
* Unreachable (orphan) nodes indicate a structural authoring error: they can
|
|
77
|
+
* never execute because no execution path leads to them. The runtime may
|
|
78
|
+
* choose to warn, error, or prune these nodes before starting a run.
|
|
79
|
+
*
|
|
80
|
+
* @returns Array of node ids that cannot be reached from START; empty for a
|
|
81
|
+
* well-formed graph.
|
|
82
|
+
*/
|
|
83
|
+
getUnreachableNodes(): string[];
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=NodeScheduler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NodeScheduler.d.ts","sourceRoot":"","sources":["../../../src/orchestration/runtime/NodeScheduler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAG3D;;;;;;;GAOG;AACH,qBAAa,aAAa;IACxB,+EAA+E;IAC/E,OAAO,CAAC,SAAS,CAAoC;IAErD,sGAAsG;IACtG,OAAO,CAAC,YAAY,CAAoC;IAExD,qFAAqF;IACrF,OAAO,CAAC,OAAO,CAAc;IAE7B;;;;;OAKG;gBACS,KAAK,EAAE,SAAS,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE;IAoBlD;;;;;;;;;;;OAWG;IACH,eAAe,IAAI,MAAM,EAAE;IAwD3B;;;;;;;;;OASG;IACH,SAAS,IAAI,OAAO;IAQpB;;;;;;;;;;;;OAYG;IACH,aAAa,CAAC,gBAAgB,EAAE,MAAM,EAAE,EAAE,cAAc,GAAE,MAAM,EAAO,GAAG,MAAM,EAAE;IAuBlF;;;;;;;;;;OAUG;IACH,mBAAmB,IAAI,MAAM,EAAE;CAmBhC"}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file NodeScheduler.ts
|
|
3
|
+
* @description Topological ordering, cycle detection, and ready-node detection
|
|
4
|
+
* for compiled execution graph scheduling in the AgentOS Unified Orchestration Layer.
|
|
5
|
+
*
|
|
6
|
+
* Uses Kahn's algorithm (BFS-based) for topological sorting, which also serves
|
|
7
|
+
* as the foundation for cycle detection. The scheduler treats START and END as
|
|
8
|
+
* virtual nodes that participate in edge traversal but are excluded from the
|
|
9
|
+
* returned topological order.
|
|
10
|
+
*/
|
|
11
|
+
import { START, END } from '../ir/types.js';
|
|
12
|
+
/**
|
|
13
|
+
* Schedules graph node execution by computing topological ordering, detecting
|
|
14
|
+
* structural issues (cycles, unreachable nodes), and determining which nodes
|
|
15
|
+
* are ready to run given a set of already-completed nodes.
|
|
16
|
+
*
|
|
17
|
+
* All methods are pure and stateless with respect to execution — the scheduler
|
|
18
|
+
* only reads the static graph structure provided at construction time.
|
|
19
|
+
*/
|
|
20
|
+
export class NodeScheduler {
|
|
21
|
+
/**
|
|
22
|
+
* Constructs a NodeScheduler from a compiled graph's node and edge lists.
|
|
23
|
+
*
|
|
24
|
+
* @param nodes - All real (non-sentinel) graph nodes.
|
|
25
|
+
* @param edges - All directed edges, including those from/to START or END sentinels.
|
|
26
|
+
*/
|
|
27
|
+
constructor(nodes, edges) {
|
|
28
|
+
/** Maps each node id to its outgoing neighbour ids. Includes START and END. */
|
|
29
|
+
this.adjacency = new Map();
|
|
30
|
+
/** Maps each node id to the ids of nodes that have edges pointing into it. Includes START and END. */
|
|
31
|
+
this.predecessors = new Map();
|
|
32
|
+
this.nodeIds = new Set(nodes.map(n => n.id));
|
|
33
|
+
// Initialise adjacency and predecessor lists for every real node plus the
|
|
34
|
+
// two virtual sentinels so edge lookups never need a null-check.
|
|
35
|
+
for (const id of [START, END, ...this.nodeIds]) {
|
|
36
|
+
this.adjacency.set(id, []);
|
|
37
|
+
this.predecessors.set(id, []);
|
|
38
|
+
}
|
|
39
|
+
for (const edge of edges) {
|
|
40
|
+
this.adjacency.get(edge.source)?.push(edge.target);
|
|
41
|
+
this.predecessors.get(edge.target)?.push(edge.source);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
// Topological ordering
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
/**
|
|
48
|
+
* Returns real node ids in a valid topological execution order using Kahn's
|
|
49
|
+
* algorithm (BFS over in-degree).
|
|
50
|
+
*
|
|
51
|
+
* START and END sentinels are intentionally excluded from the returned array
|
|
52
|
+
* because they are virtual control-flow markers, not executable nodes.
|
|
53
|
+
*
|
|
54
|
+
* If the graph contains a cycle, the returned array will be shorter than
|
|
55
|
+
* `nodeIds.size` — use {@link hasCycles} to distinguish this case explicitly.
|
|
56
|
+
*
|
|
57
|
+
* @returns Ordered array of real node ids; empty if there are no real nodes.
|
|
58
|
+
*/
|
|
59
|
+
topologicalSort() {
|
|
60
|
+
// Compute in-degree for every real node (edges from sentinels count too).
|
|
61
|
+
const inDegree = new Map();
|
|
62
|
+
for (const id of this.nodeIds) {
|
|
63
|
+
const preds = this.predecessors.get(id) ?? [];
|
|
64
|
+
inDegree.set(id, preds.length);
|
|
65
|
+
}
|
|
66
|
+
// Seed the queue with nodes that have no predecessors at all, or whose
|
|
67
|
+
// only predecessors are START (treated as "already satisfied" at t=0).
|
|
68
|
+
const queue = [];
|
|
69
|
+
for (const [id, _degree] of inDegree) {
|
|
70
|
+
// A node is initially ready when all its predecessors are sentinels
|
|
71
|
+
// (START/END) or it has no predecessors.
|
|
72
|
+
const realPredCount = (this.predecessors.get(id) ?? []).filter(p => p !== START && p !== END).length;
|
|
73
|
+
if (realPredCount === 0) {
|
|
74
|
+
queue.push(id);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const sorted = [];
|
|
78
|
+
// Track a "virtual" in-degree that only counts real predecessors so that
|
|
79
|
+
// START/END sentinel edges don't artificially inflate the degree.
|
|
80
|
+
const realInDegree = new Map();
|
|
81
|
+
for (const id of this.nodeIds) {
|
|
82
|
+
realInDegree.set(id, (this.predecessors.get(id) ?? []).filter(p => p !== START && p !== END).length);
|
|
83
|
+
}
|
|
84
|
+
while (queue.length > 0) {
|
|
85
|
+
const current = queue.shift();
|
|
86
|
+
sorted.push(current);
|
|
87
|
+
for (const neighbour of this.adjacency.get(current) ?? []) {
|
|
88
|
+
// Skip sentinel targets — they have no real in-degree entry.
|
|
89
|
+
if (!this.nodeIds.has(neighbour))
|
|
90
|
+
continue;
|
|
91
|
+
const remaining = (realInDegree.get(neighbour) ?? 0) - 1;
|
|
92
|
+
realInDegree.set(neighbour, remaining);
|
|
93
|
+
if (remaining === 0) {
|
|
94
|
+
queue.push(neighbour);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return sorted;
|
|
99
|
+
}
|
|
100
|
+
// ---------------------------------------------------------------------------
|
|
101
|
+
// Cycle detection
|
|
102
|
+
// ---------------------------------------------------------------------------
|
|
103
|
+
/**
|
|
104
|
+
* Returns `true` if the graph contains at least one directed cycle among the
|
|
105
|
+
* real nodes (sentinels are excluded from cycle detection).
|
|
106
|
+
*
|
|
107
|
+
* Implemented by comparing the length of the topological sort result against
|
|
108
|
+
* the total number of real nodes: Kahn's algorithm processes every node in a
|
|
109
|
+
* DAG, so any shortfall indicates nodes trapped inside a cycle.
|
|
110
|
+
*
|
|
111
|
+
* @returns `true` when a cycle exists; `false` for a valid DAG.
|
|
112
|
+
*/
|
|
113
|
+
hasCycles() {
|
|
114
|
+
return this.topologicalSort().length < this.nodeIds.size;
|
|
115
|
+
}
|
|
116
|
+
// ---------------------------------------------------------------------------
|
|
117
|
+
// Ready-node detection
|
|
118
|
+
// ---------------------------------------------------------------------------
|
|
119
|
+
/**
|
|
120
|
+
* Returns the ids of all real nodes that are eligible to execute next, given
|
|
121
|
+
* the set of nodes that have already finished (completed or skipped).
|
|
122
|
+
*
|
|
123
|
+
* A node is "ready" when:
|
|
124
|
+
* 1. It has not already completed or been skipped.
|
|
125
|
+
* 2. Every one of its predecessors is either the START sentinel or a member of
|
|
126
|
+
* the completed/skipped set.
|
|
127
|
+
*
|
|
128
|
+
* @param completedNodeIds - Node ids that have successfully finished execution.
|
|
129
|
+
* @param skippedNodeIds - Node ids that were bypassed (e.g. via conditional routing).
|
|
130
|
+
* @returns Array of node ids that can be dispatched for execution immediately.
|
|
131
|
+
*/
|
|
132
|
+
getReadyNodes(completedNodeIds, skippedNodeIds = []) {
|
|
133
|
+
// Build a unified "done" set that includes the START sentinel so that nodes
|
|
134
|
+
// whose only predecessor is START are treated as immediately satisfiable.
|
|
135
|
+
const done = new Set([START, ...completedNodeIds, ...skippedNodeIds]);
|
|
136
|
+
const ready = [];
|
|
137
|
+
for (const nodeId of this.nodeIds) {
|
|
138
|
+
// Skip nodes that are already done.
|
|
139
|
+
if (done.has(nodeId))
|
|
140
|
+
continue;
|
|
141
|
+
const preds = this.predecessors.get(nodeId) ?? [];
|
|
142
|
+
if (preds.every(p => done.has(p))) {
|
|
143
|
+
ready.push(nodeId);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return ready;
|
|
147
|
+
}
|
|
148
|
+
// ---------------------------------------------------------------------------
|
|
149
|
+
// Reachability analysis
|
|
150
|
+
// ---------------------------------------------------------------------------
|
|
151
|
+
/**
|
|
152
|
+
* Returns the ids of all real nodes that are not reachable from the START
|
|
153
|
+
* sentinel via a BFS traversal of the adjacency list.
|
|
154
|
+
*
|
|
155
|
+
* Unreachable (orphan) nodes indicate a structural authoring error: they can
|
|
156
|
+
* never execute because no execution path leads to them. The runtime may
|
|
157
|
+
* choose to warn, error, or prune these nodes before starting a run.
|
|
158
|
+
*
|
|
159
|
+
* @returns Array of node ids that cannot be reached from START; empty for a
|
|
160
|
+
* well-formed graph.
|
|
161
|
+
*/
|
|
162
|
+
getUnreachableNodes() {
|
|
163
|
+
const visited = new Set();
|
|
164
|
+
const queue = [START];
|
|
165
|
+
while (queue.length > 0) {
|
|
166
|
+
const current = queue.shift();
|
|
167
|
+
if (visited.has(current))
|
|
168
|
+
continue;
|
|
169
|
+
visited.add(current);
|
|
170
|
+
for (const neighbour of this.adjacency.get(current) ?? []) {
|
|
171
|
+
if (!visited.has(neighbour)) {
|
|
172
|
+
queue.push(neighbour);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// Return real nodes that BFS never visited.
|
|
177
|
+
return [...this.nodeIds].filter(id => !visited.has(id));
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
//# sourceMappingURL=NodeScheduler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NodeScheduler.js","sourceRoot":"","sources":["../../../src/orchestration/runtime/NodeScheduler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file StateManager.ts
|
|
3
|
+
* @description Manages the mutable {@link GraphState} partitions for an active graph run.
|
|
4
|
+
*
|
|
5
|
+
* Responsibilities:
|
|
6
|
+
* - Creating a clean initial state from caller-supplied input.
|
|
7
|
+
* - Applying node output patches to the `scratch` partition, honoring per-field
|
|
8
|
+
* {@link StateReducers} for deterministic conflict resolution.
|
|
9
|
+
* - Applying node output patches to the `artifacts` partition (last-write-wins by default).
|
|
10
|
+
* - Merging states produced by parallel branches using the same reducer logic.
|
|
11
|
+
* - Tracking the ordered list of visited node ids and the global iteration counter.
|
|
12
|
+
*
|
|
13
|
+
* `StateManager` is intentionally stateless between calls — it receives the current
|
|
14
|
+
* {@link GraphState} as an argument and returns a *new* object; it never mutates in place.
|
|
15
|
+
* This makes it straightforward to unit-test and safe to use in concurrent contexts.
|
|
16
|
+
*/
|
|
17
|
+
import type { StateReducers, GraphState } from '../ir/types.js';
|
|
18
|
+
/**
|
|
19
|
+
* Manages the {@link GraphState} partitions (`input`, `scratch`, `artifacts`,
|
|
20
|
+
* `memory`, `diagnostics`) for a single graph run.
|
|
21
|
+
*
|
|
22
|
+
* All methods return a *new* `GraphState` object; the original is never mutated.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* const manager = new StateManager({ 'scratch.messages': 'concat' });
|
|
27
|
+
* let state = manager.initialize({ prompt: 'Hello' });
|
|
28
|
+
* state = manager.updateScratch(state, { messages: ['first'] });
|
|
29
|
+
* state = manager.updateScratch(state, { messages: ['second'] });
|
|
30
|
+
* // state.scratch.messages === ['first', 'second']
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export declare class StateManager {
|
|
34
|
+
private readonly reducers;
|
|
35
|
+
/**
|
|
36
|
+
* @param reducers - Field-level reducer configuration keyed by dot-notation paths
|
|
37
|
+
* (e.g. `'scratch.messages'`). Determines how conflicting values
|
|
38
|
+
* are merged during {@link updateScratch} and {@link mergeParallelBranches}.
|
|
39
|
+
*/
|
|
40
|
+
constructor(reducers: StateReducers);
|
|
41
|
+
/**
|
|
42
|
+
* Create a clean initial {@link GraphState} from the caller-supplied `input` value.
|
|
43
|
+
*
|
|
44
|
+
* The `input` partition is frozen with {@link Object.freeze} so that no node can
|
|
45
|
+
* accidentally mutate it. All other partitions start empty.
|
|
46
|
+
*
|
|
47
|
+
* @param input - Arbitrary value provided by the graph caller; becomes `state.input`.
|
|
48
|
+
* @returns A fully initialised `GraphState` ready for the first node execution.
|
|
49
|
+
*/
|
|
50
|
+
initialize(input: unknown): GraphState;
|
|
51
|
+
/**
|
|
52
|
+
* Apply a `patch` to the `scratch` partition, honoring any registered reducers.
|
|
53
|
+
*
|
|
54
|
+
* For each key in `patch`:
|
|
55
|
+
* - If a reducer is registered at `scratch.<key>` **and** the key already exists
|
|
56
|
+
* in the current scratch, the reducer is called to merge the existing and incoming
|
|
57
|
+
* values.
|
|
58
|
+
* - Otherwise the incoming value simply overwrites (last-write-wins semantics).
|
|
59
|
+
*
|
|
60
|
+
* @param state - Current graph state (not mutated).
|
|
61
|
+
* @param patch - Partial scratch update emitted by a completed node.
|
|
62
|
+
* @returns New `GraphState` with the merged scratch partition.
|
|
63
|
+
*/
|
|
64
|
+
updateScratch(state: GraphState, patch: Record<string, unknown>): GraphState;
|
|
65
|
+
/**
|
|
66
|
+
* Apply a `patch` to the `artifacts` partition using last-write-wins semantics.
|
|
67
|
+
*
|
|
68
|
+
* Artifact fields are intended for caller-facing outputs and are not subject to
|
|
69
|
+
* reducer logic in this method. If you need reducer-aware artifact merging, use
|
|
70
|
+
* {@link mergeParallelBranches} instead.
|
|
71
|
+
*
|
|
72
|
+
* @param state - Current graph state (not mutated).
|
|
73
|
+
* @param patch - Partial artifacts update emitted by a completed node.
|
|
74
|
+
* @returns New `GraphState` with the updated artifacts partition.
|
|
75
|
+
*/
|
|
76
|
+
updateArtifacts(state: GraphState, patch: Record<string, unknown>): GraphState;
|
|
77
|
+
/**
|
|
78
|
+
* Record that execution has entered `nodeId`.
|
|
79
|
+
*
|
|
80
|
+
* Updates `currentNodeId`, appends to `visitedNodes`, and increments `iteration`.
|
|
81
|
+
*
|
|
82
|
+
* @param state - Current graph state (not mutated).
|
|
83
|
+
* @param nodeId - Id of the node that is about to execute.
|
|
84
|
+
* @returns New `GraphState` reflecting the visit.
|
|
85
|
+
*/
|
|
86
|
+
recordNodeVisit(state: GraphState, nodeId: string): GraphState;
|
|
87
|
+
/**
|
|
88
|
+
* Merge the `scratch` partitions of one or more parallel branch states back into
|
|
89
|
+
* a single `GraphState`.
|
|
90
|
+
*
|
|
91
|
+
* The algorithm walks every key present in any branch's scratch object and applies
|
|
92
|
+
* the registered reducer for that key (if any) against the accumulator. When no
|
|
93
|
+
* reducer is registered, the last branch's value wins.
|
|
94
|
+
*
|
|
95
|
+
* The `artifacts`, `memory`, `diagnostics`, `visitedNodes`, and `iteration` fields
|
|
96
|
+
* of `baseState` are preserved unchanged — the caller is responsible for merging
|
|
97
|
+
* those separately if needed.
|
|
98
|
+
*
|
|
99
|
+
* @param baseState - State prior to the parallel fan-out (provides the baseline scratch).
|
|
100
|
+
* @param branchStates - States produced by each parallel branch.
|
|
101
|
+
* @returns New `GraphState` with the merged scratch partition.
|
|
102
|
+
*/
|
|
103
|
+
mergeParallelBranches(baseState: GraphState, branchStates: GraphState[]): GraphState;
|
|
104
|
+
/**
|
|
105
|
+
* Dispatch to a {@link BuiltinReducer} strategy or call a custom {@link ReducerFn}.
|
|
106
|
+
*
|
|
107
|
+
* @param reducer - The reducer to apply.
|
|
108
|
+
* @param existing - Value currently stored in `GraphState`.
|
|
109
|
+
* @param incoming - New value emitted by the most recently completed node.
|
|
110
|
+
* @returns The merged value.
|
|
111
|
+
*/
|
|
112
|
+
private applyReducer;
|
|
113
|
+
/**
|
|
114
|
+
* Construct an empty {@link MemoryView} used during state initialisation.
|
|
115
|
+
*/
|
|
116
|
+
private emptyMemoryView;
|
|
117
|
+
/**
|
|
118
|
+
* Construct a zeroed {@link DiagnosticsView} used during state initialisation.
|
|
119
|
+
*/
|
|
120
|
+
private emptyDiagnostics;
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=StateManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StateManager.d.ts","sourceRoot":"","sources":["../../../src/orchestration/runtime/StateManager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EACV,aAAa,EAGb,UAAU,EAGX,MAAM,gBAAgB,CAAC;AAMxB;;;;;;;;;;;;;;GAcG;AACH,qBAAa,YAAY;IAMX,OAAO,CAAC,QAAQ,CAAC,QAAQ;IALrC;;;;OAIG;gBAC0B,QAAQ,EAAE,aAAa;IAMpD;;;;;;;;OAQG;IACH,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,UAAU;IAatC;;;;;;;;;;;;OAYG;IACH,aAAa,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,UAAU;IAmB5E;;;;;;;;;;OAUG;IACH,eAAe,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,UAAU;IAO9E;;;;;;;;OAQG;IACH,eAAe,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,UAAU;IAS9D;;;;;;;;;;;;;;;OAeG;IACH,qBAAqB,CAAC,SAAS,EAAE,UAAU,EAAE,YAAY,EAAE,UAAU,EAAE,GAAG,UAAU;IAyBpF;;;;;;;OAOG;IACH,OAAO,CAAC,YAAY;IAwDpB;;OAEG;IACH,OAAO,CAAC,eAAe;IASvB;;OAEG;IACH,OAAO,CAAC,gBAAgB;CAYzB"}
|