@kortyx/runtime 0.2.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +20 -0
  3. package/dist/checkpointer.d.ts +2 -2
  4. package/dist/checkpointer.d.ts.map +1 -1
  5. package/dist/checkpointer.js +11 -5
  6. package/dist/config/load-kortyx-config.js +5 -5
  7. package/dist/framework/adapter.d.ts +21 -0
  8. package/dist/framework/adapter.d.ts.map +1 -0
  9. package/dist/framework/adapter.js +74 -0
  10. package/dist/framework/in-memory-checkpointer.d.ts +9 -0
  11. package/dist/framework/in-memory-checkpointer.d.ts.map +1 -0
  12. package/dist/framework/in-memory-checkpointer.js +134 -0
  13. package/dist/framework/pending-requests.d.ts +33 -0
  14. package/dist/framework/pending-requests.d.ts.map +1 -0
  15. package/dist/framework/pending-requests.js +32 -0
  16. package/dist/framework/redis/pending-request-store.d.ts +8 -0
  17. package/dist/framework/redis/pending-request-store.d.ts.map +1 -0
  18. package/dist/framework/redis/pending-request-store.js +30 -0
  19. package/dist/framework/redis/redis-checkpointer.d.ts +13 -0
  20. package/dist/framework/redis/redis-checkpointer.d.ts.map +1 -0
  21. package/dist/framework/redis/redis-checkpointer.js +159 -0
  22. package/dist/framework/redis/redis-client.d.ts +13 -0
  23. package/dist/framework/redis/redis-client.d.ts.map +1 -0
  24. package/dist/framework/redis/redis-client.js +181 -0
  25. package/dist/framework/redis/redis-store.d.ts +20 -0
  26. package/dist/framework/redis/redis-store.d.ts.map +1 -0
  27. package/dist/framework/redis/redis-store.js +107 -0
  28. package/dist/graph/create-langgraph.d.ts +4 -2
  29. package/dist/graph/create-langgraph.d.ts.map +1 -1
  30. package/dist/graph/create-langgraph.js +56 -34
  31. package/dist/index.d.ts +4 -0
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +4 -0
  34. package/dist/node-loader.d.ts +7 -0
  35. package/dist/node-loader.d.ts.map +1 -0
  36. package/dist/node-loader.js +58 -0
  37. package/dist/node-registry.d.ts +7 -0
  38. package/dist/node-registry.d.ts.map +1 -0
  39. package/dist/node-registry.js +27 -0
  40. package/dist/registry/file-registry.d.ts.map +1 -1
  41. package/dist/registry/file-registry.js +25 -2
  42. package/dist/registry/in-memory-registry.d.ts +6 -0
  43. package/dist/registry/in-memory-registry.d.ts.map +1 -0
  44. package/dist/registry/in-memory-registry.js +25 -0
  45. package/dist/registry/interface.d.ts +2 -2
  46. package/dist/registry/interface.d.ts.map +1 -1
  47. package/dist/state/build-initial-state.d.ts +1 -1
  48. package/dist/state/build-initial-state.d.ts.map +1 -1
  49. package/dist/state/build-initial-state.js +6 -2
  50. package/package.json +40 -18
  51. package/src/checkpointer.ts +0 -13
  52. package/src/config/load-kortyx-config.ts +0 -46
  53. package/src/graph/create-langgraph.ts +0 -437
  54. package/src/index.ts +0 -8
  55. package/src/interrupt/tokens.ts +0 -9
  56. package/src/registry/file-registry.ts +0 -124
  57. package/src/registry/interface.ts +0 -13
  58. package/src/state/build-initial-state.ts +0 -30
  59. package/tsconfig.build.json +0 -25
  60. package/tsconfig.build.tsbuildinfo +0 -1
  61. package/tsconfig.json +0 -29
@@ -1,437 +0,0 @@
1
- import type {
2
- BaseNode,
3
- Edge,
4
- GraphState,
5
- NodeConfig,
6
- NodeContext,
7
- NodeMap,
8
- NodeResult,
9
- WorkflowDefinition,
10
- } from "@kortyx/core";
11
- import { runWithHookContext } from "@kortyx/hooks";
12
- import type { MemoryAdapter } from "@kortyx/memory";
13
- import type { GetProviderFn } from "@kortyx/providers";
14
- import { contentToText, deepMergeWithArrayOverwrite } from "@kortyx/utils";
15
- import {
16
- type AIMessageChunk,
17
- HumanMessage,
18
- SystemMessage,
19
- } from "@langchain/core/messages";
20
- import { Annotation, interrupt, StateGraph } from "@langchain/langgraph";
21
- import { getCheckpointer } from "../checkpointer";
22
-
23
- export interface GraphRuntimeConfig {
24
- emit?: (event: string, payload: unknown) => void;
25
- onCheckpoint?: (args: { nodeId: string; state: GraphState }) => void;
26
- memoryAdapter?: MemoryAdapter;
27
- /**
28
- * Provider factory used by ctx.speak to obtain a streaming model.
29
- * Use @kortyx/providers or implement your own GetProviderFn.
30
- */
31
- getProvider?: GetProviderFn;
32
- [key: string]: unknown;
33
- }
34
-
35
- export async function createLangGraph<
36
- const N extends NodeMap,
37
- const E extends readonly Edge<
38
- (keyof N & string) | "__start__",
39
- (keyof N & string) | "__end__"
40
- >[],
41
- >(workflow: WorkflowDefinition<N, E>, config: GraphRuntimeConfig) {
42
- const StateAnnotation = Annotation.Root({
43
- input: Annotation<string>,
44
- output: Annotation<string>,
45
- lastNode: Annotation<string>,
46
- lastCondition: Annotation<string>,
47
- lastIntent: Annotation<string>,
48
- memory: Annotation<Record<string, unknown>>({
49
- reducer: (l, r) => deepMergeWithArrayOverwrite(l ?? {}, r ?? {}),
50
- default: () => ({}),
51
- }),
52
- config: Annotation<unknown>,
53
- transitionTo: Annotation<string | null>,
54
- data: Annotation<Record<string, unknown>>({
55
- reducer: (l, r) => deepMergeWithArrayOverwrite(l ?? {}, r ?? {}),
56
- default: () => ({}),
57
- }),
58
- conversationHistory: Annotation<
59
- Array<{ node: string; message: string; timestamp: string }>
60
- >({
61
- reducer: (l, r) => [...(l ?? []), ...(r ?? [])],
62
- default: () => [],
63
- }),
64
- awaitingHumanInput: Annotation<boolean>,
65
- });
66
-
67
- const builder = new StateGraph(StateAnnotation);
68
- const edgeApi = builder as unknown as {
69
- addEdge: (from: string, to: string) => void;
70
- addConditionalEdges: (
71
- from: string,
72
- fn: (s: GraphState) => string,
73
- mapping: Record<string, string>,
74
- ) => void;
75
- compile: () => any;
76
- };
77
- const workflowName = workflow.name;
78
-
79
- // Ensure an emit function exists so ctx.emit can always call without optional chaining
80
- type WithEmit = GraphRuntimeConfig & {
81
- emit: (event: string, payload: unknown) => void;
82
- };
83
- const runtimeConfig = config as WithEmit;
84
- if (!runtimeConfig.emit) {
85
- runtimeConfig.emit = () => {};
86
- }
87
-
88
- // Register inline nodes (TS workflows)
89
- const nodesMap = workflow.nodes as N;
90
-
91
- for (const [nodeId, node] of Object.entries(
92
- nodesMap as Record<string, BaseNode>,
93
- )) {
94
- const nodeConfig: NodeConfig = node.config ?? ({} as NodeConfig);
95
- const behavior = nodeConfig.behavior ?? {};
96
-
97
- builder.addNode(nodeId, async (state: GraphState) => {
98
- const ctx = {
99
- graph: { name: workflowName, node: nodeId },
100
- config: nodeConfig,
101
- emit: (event: string, payload: unknown) => {
102
- runtimeConfig.emit(event, payload);
103
- },
104
- error: (message: string) => {
105
- runtimeConfig.emit("error", {
106
- node: nodeId,
107
- message,
108
- });
109
- },
110
- awaitInterrupt: (interruptConfig: any) => {
111
- const { kind, question } = interruptConfig;
112
- const isMulti =
113
- kind === "multi-choice" ||
114
- (interruptConfig.kind !== "text" &&
115
- interruptConfig.multiple === true);
116
- const payload: any = {
117
- kind,
118
- multiple: isMulti,
119
- question,
120
- ...(interruptConfig.kind !== "text"
121
- ? { options: interruptConfig.options }
122
- : {}),
123
- };
124
- runtimeConfig.emit("interrupt", {
125
- node: nodeId,
126
- workflow: workflowName,
127
- input: payload,
128
- });
129
- const resumed = interrupt(payload) as unknown;
130
- if (isMulti) {
131
- if (Array.isArray(resumed)) {
132
- return (resumed as any[])
133
- .map((v) =>
134
- typeof v === "string"
135
- ? v
136
- : v && typeof (v as any).id === "string"
137
- ? (v as any).id
138
- : "",
139
- )
140
- .filter(Boolean) as string[];
141
- }
142
- if (typeof resumed === "string") return [resumed];
143
- return [];
144
- }
145
- if (typeof resumed === "string") return resumed;
146
- if (resumed && typeof (resumed as any).id === "string")
147
- return String((resumed as any).id);
148
- return "";
149
- },
150
- speak: async (
151
- args: Parameters<NodeContext["speak"]>[0],
152
- ): Promise<string> => {
153
- const providerId =
154
- args.model?.provider ?? nodeConfig.model?.provider ?? "google";
155
- const modelName =
156
- args.model?.name ?? nodeConfig.model?.name ?? "gemini-2.5-flash";
157
- const temperature =
158
- args.model?.temperature ?? nodeConfig.model?.temperature ?? 0.3;
159
-
160
- const getProvider = runtimeConfig.getProvider;
161
- if (!getProvider) {
162
- throw new Error(
163
- "Runtime config is missing getProvider; wire a provider factory into @kortyx/runtime.",
164
- );
165
- }
166
-
167
- const model = getProvider(providerId, modelName, {
168
- temperature,
169
- streaming: true,
170
- });
171
-
172
- const messages = [
173
- ...(args.system ? [new SystemMessage(args.system)] : []),
174
- new HumanMessage(args.user ?? ""),
175
- ];
176
-
177
- const minChars = args.stream?.minChars ?? 24;
178
- const flushMs = args.stream?.flushMs ?? 100;
179
- const segmentChars = args.stream?.segmentChars ?? 60;
180
- let final = "";
181
- let buffer = "";
182
- let timer: ReturnType<typeof setTimeout> | null = null;
183
- const t0 = Date.now();
184
- let seenFirst = false;
185
-
186
- const isSilent = false;
187
- const flush = () => {
188
- if (!buffer) return;
189
- if (!isSilent)
190
- ctx.emit("text-delta", { node: nodeId, delta: buffer });
191
- buffer = "";
192
- timer = null;
193
- };
194
-
195
- if (!isSilent) ctx.emit("text-start", { node: nodeId });
196
-
197
- const stream = await model.stream(messages);
198
- for await (const chunk of stream as AsyncIterable<AIMessageChunk>) {
199
- const part = contentToText(chunk.content as unknown);
200
- if (!part) continue;
201
- if (!seenFirst) {
202
- seenFirst = true;
203
- const ttft = Date.now() - t0;
204
- ctx.emit("status", {
205
- node: nodeId,
206
- message: `⏱️ TTFT ${ttft}ms`,
207
- });
208
- }
209
- final += part;
210
-
211
- for (let i = 0; i < part.length; i += segmentChars) {
212
- const seg = part.slice(i, i + segmentChars);
213
- buffer += seg;
214
- if (buffer.length >= minChars) {
215
- flush();
216
- } else if (!timer) {
217
- timer = setTimeout(flush, flushMs);
218
- }
219
- }
220
- }
221
- if (timer) clearTimeout(timer);
222
- flush();
223
-
224
- if (!isSilent) ctx.emit("text-end", { node: nodeId });
225
- const t1 = Date.now();
226
- if (seenFirst)
227
- ctx.emit("status", {
228
- node: nodeId,
229
- message: `✅ Stream done in ${t1 - t0}ms`,
230
- });
231
- return final;
232
- },
233
- } satisfies NodeContext;
234
-
235
- if (behavior.checkpoint && config?.onCheckpoint) {
236
- config.onCheckpoint({ nodeId, state });
237
- }
238
-
239
- const maxAttempts = behavior.retry?.maxAttempts ?? 1;
240
- let attempt = 0;
241
- let nodeResult: NodeResult | undefined;
242
- let hookMemoryUpdates: Record<string, unknown> | null = null;
243
-
244
- while (attempt < maxAttempts) {
245
- try {
246
- attempt++;
247
- const hookContext = {
248
- node: ctx,
249
- state,
250
- ...(runtimeConfig.getProvider
251
- ? { getProvider: runtimeConfig.getProvider }
252
- : {}),
253
- ...(runtimeConfig.memoryAdapter
254
- ? { memoryAdapter: runtimeConfig.memoryAdapter }
255
- : {}),
256
- };
257
- const hookRun = await runWithHookContext(hookContext, () =>
258
- node.run(state, ctx),
259
- );
260
- nodeResult = hookRun.result as NodeResult;
261
- hookMemoryUpdates = hookRun.memoryUpdates;
262
- break;
263
- } catch (err) {
264
- const hasMore = attempt < maxAttempts;
265
- const delayMs = behavior.retry?.delayMs ?? 0;
266
- if (hasMore && delayMs > 0) {
267
- await new Promise((r) => setTimeout(r, delayMs));
268
- continue;
269
- }
270
- if (!hasMore) throw err;
271
- }
272
- }
273
-
274
- const res: NodeResult = (nodeResult ?? {}) as NodeResult;
275
- if (hookMemoryUpdates) {
276
- res.infra = {
277
- ...(res.infra ?? {}),
278
- memory: deepMergeWithArrayOverwrite(
279
- ((res.infra?.memory ?? {}) as Record<string, unknown>) || {},
280
- hookMemoryUpdates,
281
- ),
282
- };
283
- }
284
- const uiMessage = res.ui?.message;
285
- const shouldRespond =
286
- typeof uiMessage === "string" && uiMessage.trim().length > 0;
287
-
288
- if (shouldRespond) {
289
- ctx.emit("message", { node: nodeId, content: uiMessage });
290
- }
291
-
292
- if (res.transitionTo) {
293
- ctx.emit("transition", {
294
- transitionTo: res.transitionTo,
295
- payload: res.data ?? {},
296
- });
297
- }
298
-
299
- const updates: Record<string, unknown> = {};
300
-
301
- if (typeof res.condition === "string")
302
- updates.lastCondition = res.condition;
303
- if (typeof res.intent === "string") updates.lastIntent = res.intent;
304
-
305
- if (res.data && typeof res.data === "object") {
306
- updates.data = deepMergeWithArrayOverwrite(
307
- (state.data ?? {}) as Record<string, unknown>,
308
- res.data as Record<string, unknown>,
309
- );
310
- }
311
- if (res.infra?.memory && typeof res.infra.memory === "object") {
312
- updates.memory = deepMergeWithArrayOverwrite(
313
- (state.memory ?? {}) as Record<string, unknown>,
314
- res.infra.memory as Record<string, unknown>,
315
- );
316
- }
317
-
318
- if (res.ui) {
319
- updates.ui = {
320
- message:
321
- typeof res.ui.message === "string"
322
- ? res.ui.message
323
- : (state.ui?.message ?? ""),
324
- structured: deepMergeWithArrayOverwrite(
325
- (state.ui?.structured ?? {}) as Record<string, unknown>,
326
- (res.ui.structured ?? {}) as Record<string, unknown>,
327
- ),
328
- };
329
- }
330
-
331
- if (shouldRespond && uiMessage) {
332
- updates.conversationHistory = [
333
- ...(state.conversationHistory ?? []),
334
- {
335
- node: nodeId,
336
- message: uiMessage,
337
- timestamp: new Date().toISOString(),
338
- },
339
- ];
340
- }
341
-
342
- if (res.ui?.structured) {
343
- const payload = res.ui.structured as Record<string, unknown>;
344
- const inferredType =
345
- payload && typeof payload === "object" && "jobs" in payload
346
- ? "jobs"
347
- : typeof (payload as { summary?: unknown }).summary === "string" ||
348
- (payload as { topJobs?: unknown }).topJobs
349
- ? "summary"
350
- : "generic";
351
- ctx.emit("structured_data", {
352
- node: nodeId,
353
- dataType: inferredType,
354
- data: payload,
355
- });
356
- }
357
-
358
- return updates as any;
359
- });
360
- }
361
-
362
- type EdgeTriple = readonly [string, string, { when?: string }?];
363
- const condGroups: Record<string, Array<{ when: string; to: string }>> = {};
364
- const plainEdges: Array<[string, string]> = [];
365
-
366
- for (const edge of workflow.edges as E) {
367
- const [from, to, condition] = edge as unknown as EdgeTriple;
368
- if (condition && typeof condition.when === "string" && condition.when) {
369
- if (!condGroups[from]) condGroups[from] = [];
370
- condGroups[from].push({ when: condition.when, to });
371
- } else {
372
- plainEdges.push([from, to]);
373
- }
374
- }
375
-
376
- for (const [from, to] of plainEdges) {
377
- edgeApi.addEdge(from, to);
378
- }
379
-
380
- for (const [from, rules] of Object.entries(condGroups)) {
381
- const mapping: Record<string, string> = Object.fromEntries(
382
- rules.map((r) => [r.when, r.to]),
383
- );
384
- const fallbackKey = `__default__:${from}`;
385
- mapping[fallbackKey] = "__end__";
386
- edgeApi.addConditionalEdges(
387
- from,
388
- (s: GraphState) => {
389
- const trigger = s.lastCondition ?? s.lastIntent;
390
- if (trigger && Object.hasOwn(mapping, trigger as string)) {
391
- return trigger as string;
392
- }
393
- return fallbackKey;
394
- },
395
- mapping,
396
- );
397
- }
398
-
399
- const sessionId = (config as any)?.session?.id ?? "__default__";
400
- const checkpointer = getCheckpointer(sessionId);
401
- const graph = (builder as any).compile({ checkpointer });
402
-
403
- interface GraphExtensions {
404
- name: string;
405
- config: GraphRuntimeConfig;
406
- resume(state: GraphState, input: unknown): Promise<GraphState>;
407
- timeTravelTo(state: GraphState, checkpointId: string): Promise<GraphState>;
408
- }
409
- type RuntimeGraph = typeof graph & GraphExtensions;
410
-
411
- const runtimeGraph = graph as RuntimeGraph;
412
- runtimeGraph.name = workflow.name;
413
- runtimeGraph.config = config;
414
-
415
- runtimeGraph.resume = async (state: GraphState, input: unknown) => {
416
- const resumed = { ...state, input, awaitingHumanInput: false };
417
- return graph.invoke(resumed as any) as unknown as GraphState;
418
- };
419
-
420
- runtimeGraph.timeTravelTo = async (
421
- state: GraphState,
422
- checkpointId: string,
423
- ) => {
424
- const checkpoints = (
425
- state.memory as { checkpoints?: Record<string, { snapshot: unknown }> }
426
- )?.checkpoints;
427
- const snapshot = checkpoints?.[checkpointId];
428
- if (!snapshot) throw new Error(`Checkpoint '${checkpointId}' not found`);
429
- return graph.invoke(
430
- snapshot.snapshot as unknown as Partial<
431
- (typeof StateAnnotation)["State"]
432
- >,
433
- ) as unknown as GraphState;
434
- };
435
-
436
- return runtimeGraph;
437
- }
package/src/index.ts DELETED
@@ -1,8 +0,0 @@
1
- // release-test: 2026-01-22
2
- export * from "./checkpointer";
3
- export * from "./config/load-kortyx-config";
4
- export * from "./graph/create-langgraph";
5
- export * from "./interrupt/tokens";
6
- export * from "./registry/file-registry";
7
- export * from "./registry/interface";
8
- export * from "./state/build-initial-state";
@@ -1,9 +0,0 @@
1
- import { v7 as uuid } from "uuid";
2
-
3
- export function makeResumeToken(): string {
4
- return uuid();
5
- }
6
-
7
- export function makeRequestId(prefix = "req"): string {
8
- return `${prefix}-${uuid()}`;
9
- }
@@ -1,124 +0,0 @@
1
- import type { Edge, NodeMap, WorkflowDefinition } from "@kortyx/core";
2
- import { readdir } from "fs/promises";
3
- import { join } from "path";
4
- import type { WorkflowRegistry } from "./interface";
5
-
6
- export interface FileWorkflowRegistryOptions {
7
- workflowsDir: string;
8
- fallbackId?: string;
9
- cache?: boolean;
10
- extensions?: string[];
11
- }
12
-
13
- type AnyWorkflow = WorkflowDefinition<NodeMap, readonly Edge<string, string>[]>;
14
-
15
- const DEFAULT_EXTENSIONS = [
16
- ".workflow.ts",
17
- ".workflow.mts",
18
- ".workflow.js",
19
- ".workflow.mjs",
20
- ];
21
-
22
- function isWorkflowFile(name: string, extensions: string[]) {
23
- return extensions.some((ext) => name.endsWith(ext));
24
- }
25
-
26
- export function createFileWorkflowRegistry(
27
- options: FileWorkflowRegistryOptions,
28
- ): WorkflowRegistry {
29
- const workflowsDir = options.workflowsDir;
30
- const fallbackId = options.fallbackId ?? "general-chat";
31
- const extensions = options.extensions ?? DEFAULT_EXTENSIONS;
32
- const shouldCache = options.cache ?? true;
33
-
34
- let workflowCache: Record<string, AnyWorkflow> | null = null;
35
-
36
- async function loadWorkflows(): Promise<Record<string, AnyWorkflow>> {
37
- if (shouldCache && workflowCache) return workflowCache;
38
-
39
- const files = await readdir(workflowsDir, { withFileTypes: true });
40
- const workflowFiles = files
41
- .filter((entry) => entry.isFile())
42
- .map((entry) => entry.name)
43
- .filter((file) => isWorkflowFile(file, extensions));
44
-
45
- const workflows: Record<string, AnyWorkflow> = {};
46
-
47
- for (const file of workflowFiles) {
48
- try {
49
- const modulePath = join(workflowsDir, file);
50
- const module = await import(modulePath);
51
- const workflowExport = Object.values(module).find(
52
- (
53
- exp,
54
- ): exp is WorkflowDefinition<
55
- NodeMap,
56
- readonly Edge<string, string>[]
57
- > => {
58
- const obj = exp as {
59
- name?: unknown;
60
- nodes?: unknown;
61
- edges?: unknown;
62
- };
63
- return (
64
- exp !== null &&
65
- typeof exp === "object" &&
66
- typeof obj.name === "string" &&
67
- typeof obj.nodes === "object" &&
68
- obj.nodes !== null &&
69
- Array.isArray(obj.edges)
70
- );
71
- },
72
- );
73
-
74
- if (workflowExport) {
75
- workflows[workflowExport.name] = workflowExport;
76
- }
77
- } catch (error) {
78
- console.warn(`Failed to load workflow from ${file}:`, error);
79
- }
80
- }
81
-
82
- if (shouldCache) workflowCache = workflows;
83
- return workflows;
84
- }
85
-
86
- async function list(): Promise<AnyWorkflow[]> {
87
- const workflows = await loadWorkflows();
88
- return Object.values(workflows);
89
- }
90
-
91
- async function get(id: string): Promise<AnyWorkflow | null> {
92
- const workflows = await loadWorkflows();
93
- return workflows[id] ?? null;
94
- }
95
-
96
- async function select(
97
- id: string,
98
- selectOptions?: { fallbackId?: string },
99
- ): Promise<AnyWorkflow> {
100
- const workflows = await loadWorkflows();
101
- const fallback = selectOptions?.fallbackId ?? fallbackId;
102
- const selected = workflows[id] ?? (fallback ? workflows[fallback] : null);
103
-
104
- if (!selected) {
105
- throw new Error(
106
- `No workflow found for id "${id}" and no fallback "${fallback}" workflow available`,
107
- );
108
- }
109
-
110
- return selected;
111
- }
112
-
113
- async function refresh() {
114
- workflowCache = null;
115
- await loadWorkflows();
116
- }
117
-
118
- return {
119
- list,
120
- get,
121
- select,
122
- refresh,
123
- } satisfies WorkflowRegistry;
124
- }
@@ -1,13 +0,0 @@
1
- import type { Edge, NodeMap, WorkflowDefinition } from "@kortyx/core";
2
-
3
- type AnyWorkflow = WorkflowDefinition<NodeMap, readonly Edge<string, string>[]>;
4
-
5
- export interface WorkflowRegistry {
6
- list: () => Promise<AnyWorkflow[]>;
7
- get: (id: string) => Promise<AnyWorkflow | null>;
8
- select: (
9
- id: string,
10
- options?: { fallbackId?: string },
11
- ) => Promise<AnyWorkflow>;
12
- refresh?: () => Promise<void>;
13
- }
@@ -1,30 +0,0 @@
1
- import type { GraphState, MemoryEnvelope } from "@kortyx/core";
2
-
3
- /**
4
- * Prepares the initial runtime graph state from chat context, memory, and runtime config.
5
- * This stays deliberately generic in the config type so apps can shape it.
6
- */
7
- export interface InitialStateArgs<Config = unknown> {
8
- input: string;
9
- memory: MemoryEnvelope;
10
- config: Config;
11
- defaultWorkflowId?: string;
12
- }
13
-
14
- export async function buildInitialGraphState<Config>({
15
- input,
16
- memory,
17
- config,
18
- defaultWorkflowId = "frontdesk",
19
- }: InitialStateArgs<Config>): Promise<GraphState> {
20
- return {
21
- input,
22
- lastNode: "__start__",
23
- memory,
24
- config: config as unknown,
25
- conversationHistory: [],
26
- currentWorkflow:
27
- (memory.currentWorkflow as any) || (defaultWorkflowId as any),
28
- awaitingHumanInput: false,
29
- } as GraphState;
30
- }
@@ -1,25 +0,0 @@
1
- {
2
- "extends": "./tsconfig.json",
3
- "compilerOptions": {
4
- "module": "NodeNext",
5
- "moduleResolution": "NodeNext",
6
- "declaration": true,
7
- "declarationMap": true,
8
- "sourceMap": false,
9
- "skipLibCheck": true,
10
- "skipDefaultLibCheck": true,
11
- "incremental": true,
12
- "noEmit": false,
13
- "outDir": "./dist",
14
- "rootDir": "./src",
15
- "strict": false,
16
- "exactOptionalPropertyTypes": false,
17
- "noImplicitReturns": false,
18
- "noUnusedLocals": false,
19
- "noUnusedParameters": false,
20
- "noUncheckedIndexedAccess": false,
21
- "isolatedModules": true,
22
- "baseUrl": ".",
23
- "paths": {}
24
- }
25
- }