@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
package/src/session.ts ADDED
@@ -0,0 +1,541 @@
1
+ // ABOUTME: KernelSession implementation
2
+ // ABOUTME: Wraps a kernel-session job and manages graph execution
3
+
4
+ import type { CoordinationMode } from '@inf-minds/coordination-modes';
5
+ import type {
6
+ JobManager,
7
+ EventAppender,
8
+ ArtifactManager,
9
+ ArtifactRef,
10
+ Job,
11
+ GraphState,
12
+ } from '@inf-minds/jobs';
13
+ import type { ArtifactRouter, VisibilityHints } from '@inf-minds/jobs';
14
+ import { JOB_TYPE } from '@inf-minds/jobs';
15
+
16
+ import type {
17
+ KernelSession,
18
+ SessionStatus,
19
+ SessionEvent,
20
+ Unsubscribe,
21
+ MindRegistry,
22
+ } from './types.js';
23
+ import { SESSION_STATUS } from './types.js';
24
+ import {
25
+ determineNextNodes,
26
+ updateStateForNodeStart,
27
+ updateStateForNodeComplete,
28
+ updateStateForNodeFailure,
29
+ updateStateForNodeSkipped,
30
+ markGraphComplete,
31
+ markGraphFailed,
32
+ } from './graph-executor.js';
33
+
34
+ /**
35
+ * Options for creating a KernelSessionImpl.
36
+ */
37
+ export interface KernelSessionImplOptions {
38
+ /** The parent kernel-session job */
39
+ job: Job;
40
+ /** The coordination mode (may be undefined if restored from job) */
41
+ mode?: CoordinationMode;
42
+ /** Mode name for display */
43
+ modeName: string;
44
+ /** JobManager for job lifecycle */
45
+ jobManager: JobManager;
46
+ /** EventAppender for event streaming */
47
+ eventAppender: EventAppender;
48
+ /** ArtifactManager for artifact operations */
49
+ artifactManager: ArtifactManager;
50
+ /** ArtifactRouter for routing decisions */
51
+ artifactRouter?: ArtifactRouter;
52
+ /** MindRegistry for resolving registered minds */
53
+ mindRegistry: MindRegistry;
54
+ }
55
+
56
+ /**
57
+ * Implementation of KernelSession.
58
+ *
59
+ * Manages the lifecycle of a kernel session, including:
60
+ * - Graph execution state
61
+ * - Node scheduling
62
+ * - Artifact routing
63
+ * - Event emission
64
+ */
65
+ export class KernelSessionImpl implements KernelSession {
66
+ private readonly _job: Job;
67
+ private readonly _mode?: CoordinationMode;
68
+ private readonly _modeName: string;
69
+ private readonly _jobManager: JobManager;
70
+ private readonly _eventAppender: EventAppender;
71
+ private readonly _artifactManager: ArtifactManager;
72
+ private readonly _artifactRouter?: ArtifactRouter;
73
+ private readonly _mindRegistry: MindRegistry;
74
+ private readonly _subscribers: Set<(event: SessionEvent) => void>;
75
+ private _currentGraphState: GraphState;
76
+
77
+ constructor(options: KernelSessionImplOptions) {
78
+ this._job = options.job;
79
+ this._mode = options.mode;
80
+ this._modeName = options.modeName;
81
+ this._jobManager = options.jobManager;
82
+ this._eventAppender = options.eventAppender;
83
+ this._artifactManager = options.artifactManager;
84
+ this._artifactRouter = options.artifactRouter;
85
+ this._mindRegistry = options.mindRegistry;
86
+ this._subscribers = new Set();
87
+ this._currentGraphState = (options.job.graphState as GraphState) ?? {
88
+ nodes: {},
89
+ activeNodes: [],
90
+ completedNodes: [],
91
+ failedNodes: [],
92
+ skippedNodes: [],
93
+ startedAt: new Date().toISOString(),
94
+ status: 'running',
95
+ };
96
+ }
97
+
98
+ get id(): string {
99
+ return this._job.id;
100
+ }
101
+
102
+ get mode(): string {
103
+ return this._modeName;
104
+ }
105
+
106
+ get status(): SessionStatus {
107
+ // Map job status to session status
108
+ switch (this._job.status) {
109
+ case 'pending':
110
+ return SESSION_STATUS.PENDING;
111
+ case 'running':
112
+ return SESSION_STATUS.RUNNING;
113
+ case 'completed':
114
+ return SESSION_STATUS.COMPLETED;
115
+ case 'failed':
116
+ return SESSION_STATUS.FAILED;
117
+ case 'cancelled':
118
+ return SESSION_STATUS.CANCELLED;
119
+ default:
120
+ return SESSION_STATUS.PENDING;
121
+ }
122
+ }
123
+
124
+ get graphState(): GraphState {
125
+ return this._currentGraphState;
126
+ }
127
+
128
+ get artifactBasePath(): string {
129
+ return this._job.artifactBasePath ?? `/sessions/${this._job.id}`;
130
+ }
131
+
132
+ get accountId(): string {
133
+ return this._job.accountId;
134
+ }
135
+
136
+ /**
137
+ * Get the coordination mode for this session.
138
+ * May be undefined if the session was restored from a job without the mode.
139
+ */
140
+ get coordinationMode(): CoordinationMode | undefined {
141
+ return this._mode;
142
+ }
143
+
144
+ /**
145
+ * Get the artifact router for this session.
146
+ */
147
+ get artifactRouter(): ArtifactRouter | undefined {
148
+ return this._artifactRouter;
149
+ }
150
+
151
+ /**
152
+ * Get the mind registry for resolving mind configs.
153
+ */
154
+ get mindRegistryRef(): MindRegistry {
155
+ return this._mindRegistry;
156
+ }
157
+
158
+ async pause(): Promise<void> {
159
+ // Pause the session by updating the job
160
+ // Active nodes will complete but no new nodes will start
161
+ await this._emitEvent({ type: 'session_paused', timestamp: new Date() });
162
+ // In a full implementation, this would update the job status
163
+ // and prevent new node executions
164
+ throw new Error('pause() not yet implemented');
165
+ }
166
+
167
+ async resume(): Promise<void> {
168
+ // Resume a paused session
169
+ await this._emitEvent({ type: 'session_resumed', timestamp: new Date() });
170
+ // In a full implementation, this would restart graph traversal
171
+ throw new Error('resume() not yet implemented');
172
+ }
173
+
174
+ async cancel(): Promise<void> {
175
+ // Cancel the session
176
+ await this._jobManager.cancel(this._job.id);
177
+ await this._emitEvent({ type: 'session_cancelled', timestamp: new Date() });
178
+ }
179
+
180
+ subscribe(handler: (event: SessionEvent) => void): Unsubscribe {
181
+ this._subscribers.add(handler);
182
+ return () => {
183
+ this._subscribers.delete(handler);
184
+ };
185
+ }
186
+
187
+ async getArtifacts(path?: string): Promise<ArtifactRef[]> {
188
+ // List artifacts for this session
189
+ const artifacts = await this._artifactManager.listBySession(this._job.id);
190
+
191
+ if (path) {
192
+ // Filter by path prefix
193
+ return artifacts.filter((a) => a.name.startsWith(path));
194
+ }
195
+
196
+ return artifacts;
197
+ }
198
+
199
+ getNodeOutput(nodeId: string): unknown | null {
200
+ const nodeState = this.graphState.nodes[nodeId];
201
+ if (nodeState?.status === 'completed') {
202
+ return nodeState.output ?? null;
203
+ }
204
+ return null;
205
+ }
206
+
207
+ async waitForCompletion(): Promise<unknown> {
208
+ // Poll for completion
209
+ // In a real implementation, this would use events or long polling
210
+ const maxAttempts = 1000;
211
+ const pollInterval = 100;
212
+
213
+ for (let i = 0; i < maxAttempts; i++) {
214
+ const job = await this._jobManager.get(this._job.id);
215
+ if (!job) {
216
+ throw new Error(`Session ${this._job.id} not found`);
217
+ }
218
+
219
+ if (job.status === 'completed') {
220
+ return job.output;
221
+ }
222
+
223
+ if (job.status === 'failed') {
224
+ throw new Error(`Session failed: ${job.error ?? 'Unknown error'}`);
225
+ }
226
+
227
+ if (job.status === 'cancelled') {
228
+ throw new Error('Session was cancelled');
229
+ }
230
+
231
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
232
+ }
233
+
234
+ throw new Error('Session timed out waiting for completion');
235
+ }
236
+
237
+ /**
238
+ * Emit a session event to all subscribers.
239
+ */
240
+ private async _emitEvent(event: SessionEvent): Promise<void> {
241
+ // Persist to event stream
242
+ await this._eventAppender.append({
243
+ jobId: this._job.id,
244
+ type: event.type,
245
+ payload: event,
246
+ });
247
+
248
+ // Notify subscribers
249
+ for (const handler of this._subscribers) {
250
+ try {
251
+ handler(event);
252
+ } catch (error) {
253
+ console.error('Error in session event handler:', error);
254
+ }
255
+ }
256
+ }
257
+
258
+ /**
259
+ * Start the session by executing the entry node.
260
+ */
261
+ async start(): Promise<void> {
262
+ if (!this._mode) {
263
+ throw new Error('Cannot start session without coordination mode');
264
+ }
265
+
266
+ // Emit session started event
267
+ await this._emitEvent({
268
+ type: 'session_started',
269
+ sessionId: this.id,
270
+ mode: this._modeName,
271
+ timestamp: new Date(),
272
+ });
273
+
274
+ // Determine first nodes to execute (entry node)
275
+ const nextNodes = determineNextNodes({
276
+ mode: this._mode,
277
+ graphState: this._currentGraphState,
278
+ completedNodeId: null,
279
+ completedOutput: null,
280
+ });
281
+
282
+ // Start each node
283
+ for (const nodeId of nextNodes.nodesToStart) {
284
+ await this._startNode(nodeId, this._getUserInput());
285
+ }
286
+ }
287
+
288
+ /**
289
+ * Handle completion of a node execution.
290
+ * Called by the coordinator when a mind-execution job completes.
291
+ */
292
+ async handleNodeComplete(
293
+ nodeId: string,
294
+ output: unknown,
295
+ artifacts?: Array<{ ref: ArtifactRef; hints: VisibilityHints }>
296
+ ): Promise<void> {
297
+ if (!this._mode) {
298
+ throw new Error('Cannot handle node completion without coordination mode');
299
+ }
300
+
301
+ // Update graph state
302
+ this._currentGraphState = updateStateForNodeComplete(
303
+ this._currentGraphState,
304
+ nodeId,
305
+ output
306
+ );
307
+
308
+ // Route artifacts if present
309
+ if (artifacts && this._artifactRouter) {
310
+ const routingContext = {
311
+ sessionId: this.id,
312
+ nodeId,
313
+ mindId: this._getNodeMindType(nodeId),
314
+ graphState: this._currentGraphState,
315
+ accountId: this.accountId,
316
+ };
317
+
318
+ await this._artifactRouter.routeOutputArtifacts(
319
+ artifacts.map((a) => ({ artifact: a.ref, hints: a.hints })),
320
+ routingContext
321
+ );
322
+ }
323
+
324
+ // Emit node completed event
325
+ await this._emitEvent({
326
+ type: 'node_completed',
327
+ nodeId,
328
+ output,
329
+ timestamp: new Date(),
330
+ });
331
+
332
+ // Determine next nodes
333
+ const nextNodes = determineNextNodes({
334
+ mode: this._mode,
335
+ graphState: this._currentGraphState,
336
+ completedNodeId: nodeId,
337
+ completedOutput: output,
338
+ });
339
+
340
+ // Handle skipped nodes
341
+ for (const skippedId of nextNodes.skippedNodes) {
342
+ this._currentGraphState = updateStateForNodeSkipped(
343
+ this._currentGraphState,
344
+ skippedId,
345
+ 'Condition not met'
346
+ );
347
+
348
+ await this._emitEvent({
349
+ type: 'node_skipped',
350
+ nodeId: skippedId,
351
+ reason: 'Condition not met',
352
+ timestamp: new Date(),
353
+ });
354
+ }
355
+
356
+ // Check if session should complete
357
+ if (nextNodes.shouldComplete) {
358
+ await this._completeSession(nextNodes.finalOutput);
359
+ return;
360
+ }
361
+
362
+ // Start next nodes
363
+ for (const nextNodeId of nextNodes.nodesToStart) {
364
+ // Emit graph advanced event
365
+ await this._emitEvent({
366
+ type: 'graph_advanced',
367
+ fromNode: nodeId,
368
+ toNode: nextNodeId,
369
+ timestamp: new Date(),
370
+ });
371
+
372
+ await this._startNode(nextNodeId, output);
373
+ }
374
+ }
375
+
376
+ /**
377
+ * Handle failure of a node execution.
378
+ * Called by the coordinator when a mind-execution job fails.
379
+ */
380
+ async handleNodeFailure(nodeId: string, error: string): Promise<void> {
381
+ // Update graph state
382
+ this._currentGraphState = updateStateForNodeFailure(
383
+ this._currentGraphState,
384
+ nodeId,
385
+ error
386
+ );
387
+
388
+ // Emit node failed event
389
+ await this._emitEvent({
390
+ type: 'node_failed',
391
+ nodeId,
392
+ error,
393
+ timestamp: new Date(),
394
+ });
395
+
396
+ // For now, fail the entire session on any node failure
397
+ // A more sophisticated implementation could support error handling modes
398
+ await this._failSession(error);
399
+ }
400
+
401
+ /**
402
+ * Start execution of a specific node.
403
+ */
404
+ private async _startNode(nodeId: string, input: unknown): Promise<void> {
405
+ if (!this._mode) {
406
+ throw new Error('Cannot start node without coordination mode');
407
+ }
408
+
409
+ const node = this._mode.graph.nodes[nodeId];
410
+ if (!node) {
411
+ throw new Error(`Node ${nodeId} not found in graph`);
412
+ }
413
+
414
+ // Resolve mind config
415
+ const mindConfig = this._resolveMindConfig(node);
416
+
417
+ // Get input artifacts if router is available
418
+ let inputArtifacts: ArtifactRef[] = [];
419
+ if (this._artifactRouter) {
420
+ const routingContext = {
421
+ sessionId: this.id,
422
+ nodeId,
423
+ mindId: node.mind.mindType ?? nodeId,
424
+ graphState: this._currentGraphState,
425
+ accountId: this.accountId,
426
+ };
427
+
428
+ inputArtifacts = await this._artifactRouter.resolveInputArtifacts(routingContext);
429
+ }
430
+
431
+ // Create child job for mind execution
432
+ const childJob = await this._jobManager.create({
433
+ type: JOB_TYPE.MIND_EXECUTION,
434
+ accountId: this.accountId,
435
+ parentJobId: this.id,
436
+ nodeId,
437
+ input: {
438
+ nodeId,
439
+ mindConfig,
440
+ task: input,
441
+ artifacts: inputArtifacts,
442
+ iteration: this._getNodeIteration(nodeId),
443
+ },
444
+ });
445
+
446
+ // Update graph state
447
+ this._currentGraphState = updateStateForNodeStart(
448
+ this._currentGraphState,
449
+ nodeId,
450
+ childJob.id
451
+ );
452
+
453
+ // Emit node started event
454
+ await this._emitEvent({
455
+ type: 'node_started',
456
+ nodeId,
457
+ mindType: node.mind.mindType ?? nodeId,
458
+ jobId: childJob.id,
459
+ timestamp: new Date(),
460
+ });
461
+ }
462
+
463
+ /**
464
+ * Complete the session with final output.
465
+ */
466
+ private async _completeSession(output: unknown): Promise<void> {
467
+ this._currentGraphState = markGraphComplete(this._currentGraphState);
468
+
469
+ await this._jobManager.complete(this.id, output);
470
+
471
+ await this._emitEvent({
472
+ type: 'session_completed',
473
+ output,
474
+ timestamp: new Date(),
475
+ });
476
+ }
477
+
478
+ /**
479
+ * Fail the session with error.
480
+ */
481
+ private async _failSession(error: string): Promise<void> {
482
+ this._currentGraphState = markGraphFailed(this._currentGraphState);
483
+
484
+ await this._jobManager.fail(this.id, error);
485
+
486
+ await this._emitEvent({
487
+ type: 'session_failed',
488
+ error,
489
+ timestamp: new Date(),
490
+ });
491
+ }
492
+
493
+ /**
494
+ * Get the user input from the job.
495
+ */
496
+ private _getUserInput(): unknown {
497
+ const jobInput = this._job.input as { userInput?: unknown } | undefined;
498
+ return jobInput?.userInput;
499
+ }
500
+
501
+ /**
502
+ * Get the mind type for a node.
503
+ */
504
+ private _getNodeMindType(nodeId: string): string {
505
+ if (!this._mode) {
506
+ return nodeId;
507
+ }
508
+ const node = this._mode.graph.nodes[nodeId];
509
+ return node?.mind.mindType ?? nodeId;
510
+ }
511
+
512
+ /**
513
+ * Get the current iteration count for a node.
514
+ */
515
+ private _getNodeIteration(nodeId: string): number {
516
+ // Count how many times this node has been started
517
+ const completedCount = this._currentGraphState.completedNodes.filter(
518
+ (id) => id === nodeId
519
+ ).length;
520
+ return completedCount + 1;
521
+ }
522
+
523
+ /**
524
+ * Resolve mind config from a node's mind reference.
525
+ */
526
+ private _resolveMindConfig(node: { mind: { type: string; mindType?: string; config?: unknown } }): unknown {
527
+ if (node.mind.type === 'inline') {
528
+ return node.mind.config;
529
+ }
530
+
531
+ if (node.mind.type === 'registered' && node.mind.mindType) {
532
+ const config = this._mindRegistry.get(node.mind.mindType);
533
+ if (!config) {
534
+ throw new Error(`Mind type '${node.mind.mindType}' not found in registry`);
535
+ }
536
+ return config;
537
+ }
538
+
539
+ throw new Error(`Invalid mind reference type: ${node.mind.type}`);
540
+ }
541
+ }