@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.
- package/dist/condition-evaluator.d.ts +69 -0
- package/dist/condition-evaluator.d.ts.map +1 -0
- package/dist/condition-evaluator.js +120 -0
- package/dist/condition-evaluator.js.map +1 -0
- package/dist/graph-executor.d.ts +63 -0
- package/dist/graph-executor.d.ts.map +1 -0
- package/dist/graph-executor.js +245 -0
- package/dist/graph-executor.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/kernel.d.ts +45 -0
- package/dist/kernel.d.ts.map +1 -0
- package/dist/kernel.js +202 -0
- package/dist/kernel.js.map +1 -0
- package/dist/registries/index.d.ts +3 -0
- package/dist/registries/index.d.ts.map +1 -0
- package/dist/registries/index.js +5 -0
- package/dist/registries/index.js.map +1 -0
- package/dist/registries/mind-registry.d.ts +11 -0
- package/dist/registries/mind-registry.d.ts.map +1 -0
- package/dist/registries/mind-registry.js +31 -0
- package/dist/registries/mind-registry.js.map +1 -0
- package/dist/registries/mode-registry.d.ts +11 -0
- package/dist/registries/mode-registry.d.ts.map +1 -0
- package/dist/registries/mode-registry.js +31 -0
- package/dist/registries/mode-registry.js.map +1 -0
- package/dist/session.d.ts +123 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +403 -0
- package/dist/session.js.map +1 -0
- package/dist/types.d.ts +288 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +14 -0
- package/dist/types.js.map +1 -0
- package/package.json +37 -0
- package/src/condition-evaluator.ts +168 -0
- package/src/graph-executor.ts +315 -0
- package/src/index.ts +50 -0
- package/src/kernel.ts +242 -0
- package/src/registries/index.ts +5 -0
- package/src/registries/mind-registry.ts +38 -0
- package/src/registries/mode-registry.ts +38 -0
- package/src/session.ts +541 -0
- 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
|
+
}
|