@inf-minds/kernel 0.0.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.
Files changed (46) hide show
  1. package/dist/condition-evaluator.d.ts +69 -0
  2. package/dist/condition-evaluator.d.ts.map +1 -0
  3. package/dist/condition-evaluator.js +120 -0
  4. package/dist/condition-evaluator.js.map +1 -0
  5. package/dist/graph-executor.d.ts +63 -0
  6. package/dist/graph-executor.d.ts.map +1 -0
  7. package/dist/graph-executor.js +245 -0
  8. package/dist/graph-executor.js.map +1 -0
  9. package/dist/index.d.ts +7 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +13 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/kernel.d.ts +45 -0
  14. package/dist/kernel.d.ts.map +1 -0
  15. package/dist/kernel.js +202 -0
  16. package/dist/kernel.js.map +1 -0
  17. package/dist/registries/index.d.ts +3 -0
  18. package/dist/registries/index.d.ts.map +1 -0
  19. package/dist/registries/index.js +5 -0
  20. package/dist/registries/index.js.map +1 -0
  21. package/dist/registries/mind-registry.d.ts +11 -0
  22. package/dist/registries/mind-registry.d.ts.map +1 -0
  23. package/dist/registries/mind-registry.js +31 -0
  24. package/dist/registries/mind-registry.js.map +1 -0
  25. package/dist/registries/mode-registry.d.ts +11 -0
  26. package/dist/registries/mode-registry.d.ts.map +1 -0
  27. package/dist/registries/mode-registry.js +31 -0
  28. package/dist/registries/mode-registry.js.map +1 -0
  29. package/dist/session.d.ts +123 -0
  30. package/dist/session.d.ts.map +1 -0
  31. package/dist/session.js +403 -0
  32. package/dist/session.js.map +1 -0
  33. package/dist/types.d.ts +288 -0
  34. package/dist/types.d.ts.map +1 -0
  35. package/dist/types.js +14 -0
  36. package/dist/types.js.map +1 -0
  37. package/package.json +37 -0
  38. package/src/condition-evaluator.ts +168 -0
  39. package/src/graph-executor.ts +315 -0
  40. package/src/index.ts +50 -0
  41. package/src/kernel.ts +242 -0
  42. package/src/registries/index.ts +5 -0
  43. package/src/registries/mind-registry.ts +38 -0
  44. package/src/registries/mode-registry.ts +38 -0
  45. package/src/session.ts +541 -0
  46. package/src/types.ts +280 -0
@@ -0,0 +1,315 @@
1
+ // ABOUTME: Graph executor for coordination mode execution
2
+ // ABOUTME: Handles node scheduling, edge traversal, and state management
3
+
4
+ import type { CoordinationMode } from '@inf-minds/coordination-modes';
5
+ import type { GraphState, GraphNodeState } from '@inf-minds/jobs';
6
+ import { evaluateCondition, type ConditionContext } from './condition-evaluator.js';
7
+
8
+ /**
9
+ * Result of determining the next nodes to execute.
10
+ */
11
+ export interface NextNodesResult {
12
+ /** Node IDs to start executing */
13
+ nodesToStart: string[];
14
+ /** Node IDs that were skipped (condition not met) */
15
+ skippedNodes: string[];
16
+ /** Whether the session should complete */
17
+ shouldComplete: boolean;
18
+ /** Final output if session should complete */
19
+ finalOutput?: unknown;
20
+ }
21
+
22
+ /**
23
+ * Options for determining next nodes.
24
+ */
25
+ export interface DetermineNextNodesOptions {
26
+ /** The coordination mode */
27
+ mode: CoordinationMode;
28
+ /** Current graph state */
29
+ graphState: GraphState;
30
+ /** Node that just completed (or null if starting) */
31
+ completedNodeId: string | null;
32
+ /** Output from the completed node */
33
+ completedOutput: unknown;
34
+ }
35
+
36
+ /**
37
+ * Determine which nodes should be executed next after a node completes.
38
+ *
39
+ * This function:
40
+ * 1. Finds outgoing edges from the completed node
41
+ * 2. Evaluates edge conditions
42
+ * 3. Handles loop logic (exit conditions and max iterations)
43
+ * 4. Checks if session should complete
44
+ */
45
+ export function determineNextNodes(options: DetermineNextNodesOptions): NextNodesResult {
46
+ const { mode, graphState, completedNodeId, completedOutput } = options;
47
+ const nodesToStart: string[] = [];
48
+ const skippedNodes: string[] = [];
49
+
50
+ // If no completed node, we're starting - go to entry node
51
+ if (!completedNodeId) {
52
+ return {
53
+ nodesToStart: [mode.graph.entryNode],
54
+ skippedNodes: [],
55
+ shouldComplete: false,
56
+ };
57
+ }
58
+
59
+ // Get the completed node definition
60
+ const completedNode = mode.graph.nodes[completedNodeId];
61
+ if (!completedNode) {
62
+ return {
63
+ nodesToStart: [],
64
+ skippedNodes: [],
65
+ shouldComplete: true,
66
+ finalOutput: { error: `Node ${completedNodeId} not found in graph` },
67
+ };
68
+ }
69
+
70
+ // Check if this is an exit node
71
+ const isExitNode = mode.graph.exitNodes.includes(completedNodeId);
72
+
73
+ // Count iterations for loop handling
74
+ const currentIteration = graphState.nodes[completedNodeId]?.jobId
75
+ ? countIterations(graphState, completedNodeId)
76
+ : 0;
77
+
78
+ // Build condition context (reused for edge and exit conditions)
79
+ const context: ConditionContext = {
80
+ output: completedOutput,
81
+ graphState: {
82
+ completedNodes: graphState.completedNodes,
83
+ activeNodes: graphState.activeNodes,
84
+ nodeOutputs: extractNodeOutputs(graphState),
85
+ },
86
+ iteration: currentIteration,
87
+ };
88
+
89
+ // Find outgoing edges
90
+ const outgoingEdges = mode.graph.edges.filter((e) => e.from === completedNodeId);
91
+
92
+ // Evaluate conditions for each outgoing edge FIRST
93
+ for (const edge of outgoingEdges) {
94
+ if (edge.condition) {
95
+ const evalResult = evaluateCondition(edge.condition, context);
96
+
97
+ if (evalResult.result) {
98
+ nodesToStart.push(edge.to);
99
+ } else {
100
+ skippedNodes.push(edge.to);
101
+ }
102
+ } else {
103
+ // No condition - always traverse
104
+ nodesToStart.push(edge.to);
105
+ }
106
+ }
107
+
108
+ // If we have nodes to traverse, go to them (regardless of exit condition)
109
+ if (nodesToStart.length > 0) {
110
+ return {
111
+ nodesToStart,
112
+ skippedNodes,
113
+ shouldComplete: false,
114
+ };
115
+ }
116
+
117
+ // No edges to traverse - check exit condition for potential looping
118
+ if (completedNode.exitCondition) {
119
+ const exitResult = evaluateCondition(completedNode.exitCondition, context);
120
+
121
+ // If exit condition is NOT met and under max iterations, loop back
122
+ if (!exitResult.result) {
123
+ const maxIterations = completedNode.maxIterations ?? Infinity;
124
+ if (currentIteration < maxIterations) {
125
+ // Loop: re-execute this node
126
+ return {
127
+ nodesToStart: [completedNodeId],
128
+ skippedNodes,
129
+ shouldComplete: false,
130
+ };
131
+ }
132
+ }
133
+ // Exit condition met or max iterations reached - session should complete
134
+ }
135
+
136
+ // No edges taken and either:
137
+ // - No exit condition (so we just complete)
138
+ // - Exit condition is met
139
+ // - Max iterations reached
140
+ if (isExitNode || outgoingEdges.length === 0) {
141
+ return {
142
+ nodesToStart: [],
143
+ skippedNodes,
144
+ shouldComplete: true,
145
+ finalOutput: completedOutput,
146
+ };
147
+ }
148
+
149
+ // Dead end but not an exit node - this is a graph issue
150
+ return {
151
+ nodesToStart: [],
152
+ skippedNodes,
153
+ shouldComplete: true,
154
+ finalOutput: { error: `Node ${completedNodeId} has no traversable edges and is not an exit node` },
155
+ };
156
+ }
157
+
158
+ /**
159
+ * Count how many times a node has been executed in this session.
160
+ */
161
+ function countIterations(graphState: GraphState, nodeId: string): number {
162
+ // In the current model, we track iterations through job count
163
+ // For now, count how many times the node appears in completedNodes
164
+ // A more robust implementation would track this in the node state
165
+ return graphState.completedNodes.filter((id) => id === nodeId).length + 1;
166
+ }
167
+
168
+ /**
169
+ * Extract node outputs from graph state.
170
+ */
171
+ function extractNodeOutputs(graphState: GraphState): Record<string, unknown> {
172
+ const outputs: Record<string, unknown> = {};
173
+ for (const [nodeId, nodeState] of Object.entries(graphState.nodes)) {
174
+ if (nodeState.output !== undefined) {
175
+ outputs[nodeId] = nodeState.output;
176
+ }
177
+ }
178
+ return outputs;
179
+ }
180
+
181
+ /**
182
+ * Update graph state when a node starts.
183
+ */
184
+ export function updateStateForNodeStart(
185
+ graphState: GraphState,
186
+ nodeId: string,
187
+ jobId: string
188
+ ): GraphState {
189
+ const nodeState: GraphNodeState = graphState.nodes[nodeId] ?? {
190
+ nodeId,
191
+ status: 'pending',
192
+ };
193
+
194
+ return {
195
+ ...graphState,
196
+ nodes: {
197
+ ...graphState.nodes,
198
+ [nodeId]: {
199
+ ...nodeState,
200
+ status: 'active',
201
+ jobId,
202
+ startedAt: new Date().toISOString(),
203
+ },
204
+ },
205
+ activeNodes: [...graphState.activeNodes.filter((id) => id !== nodeId), nodeId],
206
+ };
207
+ }
208
+
209
+ /**
210
+ * Update graph state when a node completes.
211
+ */
212
+ export function updateStateForNodeComplete(
213
+ graphState: GraphState,
214
+ nodeId: string,
215
+ output: unknown
216
+ ): GraphState {
217
+ const nodeState = graphState.nodes[nodeId];
218
+ if (!nodeState) {
219
+ return graphState;
220
+ }
221
+
222
+ return {
223
+ ...graphState,
224
+ nodes: {
225
+ ...graphState.nodes,
226
+ [nodeId]: {
227
+ ...nodeState,
228
+ status: 'completed',
229
+ output,
230
+ completedAt: new Date().toISOString(),
231
+ },
232
+ },
233
+ activeNodes: graphState.activeNodes.filter((id) => id !== nodeId),
234
+ completedNodes: [...graphState.completedNodes, nodeId],
235
+ };
236
+ }
237
+
238
+ /**
239
+ * Update graph state when a node fails.
240
+ */
241
+ export function updateStateForNodeFailure(
242
+ graphState: GraphState,
243
+ nodeId: string,
244
+ error: string
245
+ ): GraphState {
246
+ const nodeState = graphState.nodes[nodeId];
247
+ if (!nodeState) {
248
+ return graphState;
249
+ }
250
+
251
+ return {
252
+ ...graphState,
253
+ nodes: {
254
+ ...graphState.nodes,
255
+ [nodeId]: {
256
+ ...nodeState,
257
+ status: 'failed',
258
+ error,
259
+ completedAt: new Date().toISOString(),
260
+ },
261
+ },
262
+ activeNodes: graphState.activeNodes.filter((id) => id !== nodeId),
263
+ failedNodes: [...graphState.failedNodes, nodeId],
264
+ };
265
+ }
266
+
267
+ /**
268
+ * Update graph state when a node is skipped.
269
+ */
270
+ export function updateStateForNodeSkipped(
271
+ graphState: GraphState,
272
+ nodeId: string,
273
+ reason: string
274
+ ): GraphState {
275
+ const nodeState: GraphNodeState = graphState.nodes[nodeId] ?? {
276
+ nodeId,
277
+ status: 'pending',
278
+ };
279
+
280
+ return {
281
+ ...graphState,
282
+ nodes: {
283
+ ...graphState.nodes,
284
+ [nodeId]: {
285
+ ...nodeState,
286
+ status: 'skipped',
287
+ error: reason,
288
+ completedAt: new Date().toISOString(),
289
+ },
290
+ },
291
+ skippedNodes: [...graphState.skippedNodes, nodeId],
292
+ };
293
+ }
294
+
295
+ /**
296
+ * Mark graph state as completed.
297
+ */
298
+ export function markGraphComplete(graphState: GraphState): GraphState {
299
+ return {
300
+ ...graphState,
301
+ status: 'completed',
302
+ completedAt: new Date().toISOString(),
303
+ };
304
+ }
305
+
306
+ /**
307
+ * Mark graph state as failed.
308
+ */
309
+ export function markGraphFailed(graphState: GraphState): GraphState {
310
+ return {
311
+ ...graphState,
312
+ status: 'failed',
313
+ completedAt: new Date().toISOString(),
314
+ };
315
+ }
package/src/index.ts ADDED
@@ -0,0 +1,50 @@
1
+ // ABOUTME: Kernel package entry point
2
+ // ABOUTME: Exports kernel session orchestration and graph execution
3
+
4
+ // Type exports
5
+ export type {
6
+ SessionStatus,
7
+ Unsubscribe,
8
+ SessionFilter,
9
+ CreateSessionOptions,
10
+ SessionEvent,
11
+ KernelSession,
12
+ Kernel,
13
+ KernelOptions,
14
+ MindRegistry,
15
+ ModeRegistry,
16
+ StartNodeOptions,
17
+ NodeCompletionOptions,
18
+ GraphState,
19
+ } from './types.js';
20
+
21
+ // Constant exports
22
+ export { SESSION_STATUS } from './types.js';
23
+
24
+ // Registry exports
25
+ export { createMindRegistry, createModeRegistry } from './registries/index.js';
26
+
27
+ // Kernel factory
28
+ export { createKernel } from './kernel.js';
29
+
30
+ // Condition evaluator
31
+ export {
32
+ evaluateCondition,
33
+ isValidConditionExpression,
34
+ COMMON_CONDITIONS,
35
+ type ConditionContext,
36
+ type EvaluationResult,
37
+ } from './condition-evaluator.js';
38
+
39
+ // Graph executor utilities
40
+ export {
41
+ determineNextNodes,
42
+ updateStateForNodeStart,
43
+ updateStateForNodeComplete,
44
+ updateStateForNodeFailure,
45
+ updateStateForNodeSkipped,
46
+ markGraphComplete,
47
+ markGraphFailed,
48
+ type NextNodesResult,
49
+ type DetermineNextNodesOptions,
50
+ } from './graph-executor.js';
package/src/kernel.ts ADDED
@@ -0,0 +1,242 @@
1
+ // ABOUTME: Kernel factory for creating kernel instances
2
+ // ABOUTME: The kernel manages sessions, minds, and coordination modes
3
+
4
+ import { validateGraph, type CoordinationMode } from '@inf-minds/coordination-modes';
5
+ import type { MindConfig } from '@inf-minds/mindkit';
6
+ import { JOB_TYPE, type Job } from '@inf-minds/jobs';
7
+
8
+ import type {
9
+ Kernel,
10
+ KernelOptions,
11
+ KernelSession,
12
+ CreateSessionOptions,
13
+ SessionFilter,
14
+ GraphState,
15
+ } from './types.js';
16
+ import { createMindRegistry, createModeRegistry } from './registries/index.js';
17
+ import { KernelSessionImpl } from './session.js';
18
+
19
+ /**
20
+ * Create a new kernel instance.
21
+ *
22
+ * The kernel is the central orchestrator for multi-mind coordination.
23
+ * It manages:
24
+ * - Mind configurations (registered by name)
25
+ * - Coordination modes (execution graphs)
26
+ * - Sessions (executing instances of modes)
27
+ *
28
+ * @param options - Kernel configuration options
29
+ * @returns A new Kernel instance
30
+ *
31
+ * @example
32
+ * ```ts
33
+ * const kernel = createKernel({
34
+ * jobManager,
35
+ * eventAppender,
36
+ * artifactManager,
37
+ * });
38
+ *
39
+ * // Register minds
40
+ * kernel.registerMind('researcher', {
41
+ * model: 'claude-sonnet-4-20250514',
42
+ * systemPrompt: 'You are a research assistant...',
43
+ * });
44
+ *
45
+ * // Register modes
46
+ * kernel.registerMode(createPipelineMode({
47
+ * steps: [
48
+ * { id: 'research', mind: { type: 'registered', mindType: 'researcher' } },
49
+ * { id: 'analyze', mind: { type: 'registered', mindType: 'analyzer' } },
50
+ * ],
51
+ * }));
52
+ *
53
+ * // Create and run a session
54
+ * const session = await kernel.createSession({
55
+ * mode: 'pipeline',
56
+ * input: { topic: 'quantum computing' },
57
+ * accountId: 'acc-123',
58
+ * });
59
+ * ```
60
+ */
61
+ export function createKernel(options: KernelOptions): Kernel {
62
+ const { jobManager, eventAppender, artifactManager, defaultArtifactRouter } = options;
63
+
64
+ // Create registries
65
+ const mindRegistry = createMindRegistry();
66
+ const modeRegistry = createModeRegistry();
67
+
68
+ /**
69
+ * Resolve a mode from either a name or inline definition.
70
+ */
71
+ function resolveMode(modeRef: string | CoordinationMode): CoordinationMode {
72
+ if (typeof modeRef === 'string') {
73
+ const mode = modeRegistry.get(modeRef);
74
+ if (!mode) {
75
+ throw new Error(`Mode '${modeRef}' not found in registry`);
76
+ }
77
+ return mode;
78
+ }
79
+ return modeRef;
80
+ }
81
+
82
+ /**
83
+ * Create initial graph state for a new session.
84
+ */
85
+ function createInitialGraphState(mode: CoordinationMode): GraphState {
86
+ const nodes: Record<string, { nodeId: string; status: 'pending' }> = {};
87
+
88
+ for (const nodeId of Object.keys(mode.graph.nodes)) {
89
+ nodes[nodeId] = {
90
+ nodeId,
91
+ status: 'pending',
92
+ };
93
+ }
94
+
95
+ return {
96
+ nodes,
97
+ activeNodes: [],
98
+ completedNodes: [],
99
+ failedNodes: [],
100
+ skippedNodes: [],
101
+ startedAt: new Date().toISOString(),
102
+ status: 'running',
103
+ };
104
+ }
105
+
106
+ /**
107
+ * Wrap a job as a KernelSession.
108
+ */
109
+ async function wrapJobAsSession(job: Job): Promise<KernelSession> {
110
+ // Parse the job input to get mode name
111
+ const jobInput = job.input as { mode?: string; userInput?: unknown } | undefined;
112
+ const modeName = jobInput?.mode ?? 'unknown';
113
+
114
+ // Get the mode definition if available
115
+ const mode = modeRegistry.get(modeName);
116
+
117
+ return new KernelSessionImpl({
118
+ job,
119
+ mode,
120
+ modeName,
121
+ jobManager,
122
+ eventAppender,
123
+ artifactManager,
124
+ artifactRouter: defaultArtifactRouter,
125
+ mindRegistry,
126
+ });
127
+ }
128
+
129
+ return {
130
+ async createSession(sessionOptions: CreateSessionOptions): Promise<KernelSession> {
131
+ // Resolve the coordination mode
132
+ const mode = resolveMode(sessionOptions.mode);
133
+
134
+ // Validate the graph
135
+ const validation = validateGraph(mode.graph);
136
+ if (!validation.valid) {
137
+ throw new Error(`Invalid graph: ${validation.errors.join(', ')}`);
138
+ }
139
+
140
+ // Warn about issues (but don't fail)
141
+ if (validation.warnings.length > 0) {
142
+ console.warn(`Graph warnings: ${validation.warnings.join(', ')}`);
143
+ }
144
+
145
+ // Create initial graph state
146
+ const graphState = createInitialGraphState(mode);
147
+
148
+ // Generate artifact base path for this session
149
+ const sessionId = crypto.randomUUID();
150
+ const artifactBasePath = `/sessions/${sessionId}`;
151
+
152
+ // Create the parent job
153
+ const job = await jobManager.create({
154
+ type: JOB_TYPE.KERNEL_SESSION,
155
+ accountId: sessionOptions.accountId,
156
+ input: {
157
+ mode: mode.name,
158
+ userInput: sessionOptions.input,
159
+ metadata: sessionOptions.metadata,
160
+ },
161
+ graphState,
162
+ artifactBasePath,
163
+ });
164
+
165
+ // Create and return the session wrapper
166
+ const artifactRouter = sessionOptions.artifactRouter ?? defaultArtifactRouter;
167
+
168
+ return new KernelSessionImpl({
169
+ job,
170
+ mode,
171
+ modeName: mode.name,
172
+ jobManager,
173
+ eventAppender,
174
+ artifactManager,
175
+ artifactRouter,
176
+ mindRegistry,
177
+ });
178
+ },
179
+
180
+ async getSession(sessionId: string): Promise<KernelSession | null> {
181
+ const job = await jobManager.get(sessionId);
182
+
183
+ if (!job) {
184
+ return null;
185
+ }
186
+
187
+ // Verify it's a kernel-session job
188
+ if (job.type !== JOB_TYPE.KERNEL_SESSION) {
189
+ return null;
190
+ }
191
+
192
+ return wrapJobAsSession(job);
193
+ },
194
+
195
+ async listSessions(filter?: SessionFilter): Promise<KernelSession[]> {
196
+ // Use job manager to list kernel-session jobs
197
+ const result = await jobManager.list({
198
+ accountId: filter?.accountId ?? '', // Required by job manager
199
+ type: JOB_TYPE.KERNEL_SESSION,
200
+ status: filter?.status as Parameters<typeof jobManager.list>[0]['status'],
201
+ limit: filter?.limit,
202
+ offset: filter?.offset,
203
+ });
204
+
205
+ // Filter by mode if specified
206
+ let jobs = result.jobs;
207
+ if (filter?.mode) {
208
+ jobs = jobs.filter((job) => {
209
+ const input = job.input as { mode?: string } | undefined;
210
+ return input?.mode === filter.mode;
211
+ });
212
+ }
213
+
214
+ // Wrap jobs as sessions
215
+ return Promise.all(jobs.map(wrapJobAsSession));
216
+ },
217
+
218
+ registerMind(name: string, config: MindConfig): void {
219
+ mindRegistry.register(name, config);
220
+ },
221
+
222
+ getMind(name: string): MindConfig | undefined {
223
+ return mindRegistry.get(name);
224
+ },
225
+
226
+ registerMode(mode: CoordinationMode): void {
227
+ modeRegistry.register(mode);
228
+ },
229
+
230
+ getMode(name: string): CoordinationMode | undefined {
231
+ return modeRegistry.get(name);
232
+ },
233
+
234
+ listMindTypes(): string[] {
235
+ return mindRegistry.list();
236
+ },
237
+
238
+ listModeNames(): string[] {
239
+ return modeRegistry.list();
240
+ },
241
+ };
242
+ }
@@ -0,0 +1,5 @@
1
+ // ABOUTME: Registry exports
2
+ // ABOUTME: Exports mind and mode registry factories
3
+
4
+ export { createMindRegistry } from './mind-registry.js';
5
+ export { createModeRegistry } from './mode-registry.js';
@@ -0,0 +1,38 @@
1
+ // ABOUTME: Mind registry for storing named mind configurations
2
+ // ABOUTME: Allows registering and retrieving mind configs by name
3
+
4
+ import type { MindConfig } from '@inf-minds/mindkit';
5
+ import type { MindRegistry } from '../types.js';
6
+
7
+ /**
8
+ * Create a new mind registry.
9
+ *
10
+ * The registry stores named mind configurations that can be referenced
11
+ * in coordination mode graph nodes.
12
+ *
13
+ * @returns A new MindRegistry instance
14
+ */
15
+ export function createMindRegistry(): MindRegistry {
16
+ const minds = new Map<string, MindConfig>();
17
+
18
+ return {
19
+ register(name: string, config: MindConfig): void {
20
+ if (minds.has(name)) {
21
+ throw new Error(`Mind '${name}' is already registered`);
22
+ }
23
+ minds.set(name, config);
24
+ },
25
+
26
+ get(name: string): MindConfig | undefined {
27
+ return minds.get(name);
28
+ },
29
+
30
+ has(name: string): boolean {
31
+ return minds.has(name);
32
+ },
33
+
34
+ list(): string[] {
35
+ return Array.from(minds.keys());
36
+ },
37
+ };
38
+ }
@@ -0,0 +1,38 @@
1
+ // ABOUTME: Mode registry for storing named coordination modes
2
+ // ABOUTME: Allows registering and retrieving modes by name
3
+
4
+ import type { CoordinationMode } from '@inf-minds/coordination-modes';
5
+ import type { ModeRegistry } from '../types.js';
6
+
7
+ /**
8
+ * Create a new mode registry.
9
+ *
10
+ * The registry stores coordination modes that can be used when
11
+ * creating kernel sessions.
12
+ *
13
+ * @returns A new ModeRegistry instance
14
+ */
15
+ export function createModeRegistry(): ModeRegistry {
16
+ const modes = new Map<string, CoordinationMode>();
17
+
18
+ return {
19
+ register(mode: CoordinationMode): void {
20
+ if (modes.has(mode.name)) {
21
+ throw new Error(`Mode '${mode.name}' is already registered`);
22
+ }
23
+ modes.set(mode.name, mode);
24
+ },
25
+
26
+ get(name: string): CoordinationMode | undefined {
27
+ return modes.get(name);
28
+ },
29
+
30
+ has(name: string): boolean {
31
+ return modes.has(name);
32
+ },
33
+
34
+ list(): string[] {
35
+ return Array.from(modes.keys());
36
+ },
37
+ };
38
+ }