@kortyx/agent 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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.4.0](https://github.com/kortyx-io/Kortyx/compare/agent-v0.3.0...agent-v0.4.0) (2026-02-07)
4
+
5
+
6
+ ### Features
7
+
8
+ * **runtime:** framework persistence adapter ([8383ad2](https://github.com/kortyx-io/Kortyx/commit/8383ad2d73754ccf4daf33e414e15c3ce44df8cb))
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * **agent:** remove explicit any in chat flow ([fcd701c](https://github.com/kortyx-io/Kortyx/commit/fcd701cfb6cb5f037900bbf0b396230603d16877))
14
+
15
+ ## [0.3.0](https://github.com/kortyx-io/kortyx/compare/agent-v0.2.2...agent-v0.3.0) (2026-01-25)
16
+
17
+
18
+ ### Features
19
+
20
+ * add new example project ([dae4ad0](https://github.com/kortyx-io/kortyx/commit/dae4ad00f2e987ecb71f863bfc776ea7e19e2430))
21
+
22
+
23
+ ### Bug Fixes
24
+
25
+ * package scripts and dist ([0ed75d3](https://github.com/kortyx-io/kortyx/commit/0ed75d362230a5eddc83b269d14cb787fbf84fb8))
26
+
3
27
  ## [0.2.2](https://github.com/kortyx-io/kortyx/compare/agent-v0.2.1...agent-v0.2.2) (2026-01-22)
4
28
 
5
29
 
package/README.md ADDED
@@ -0,0 +1,20 @@
1
+ # @kortyx/agent
2
+
3
+ Agent composition utilities for Kortyx.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @kortyx/agent
9
+ ```
10
+
11
+ ## Documentation
12
+
13
+ This repo is under active development. For the latest package list and links, see the monorepo README:
14
+
15
+ - `README.md`
16
+
17
+ ## License
18
+
19
+ Apache-2.0
20
+
@@ -1 +1 @@
1
- {"version":3,"file":"create-agent.d.ts","sourceRoot":"","sources":["../../src/chat/create-agent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAGtE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAGtD,MAAM,WAAW,eAAe,CAC9B,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACtC,OAAO,CACP,SAAQ,IAAI,CACV,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,UAAU,GAAG,SAAS,GAAG,gBAAgB,GAAG,kBAAkB,CAC/D;IACD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,cAAc,CAAC,EAAE,gBAAgB,CAAC;IAClC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,WAAW,CACzB,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACtC,OAAO,GAAG,OAAO,EACjB,IAAI,EAAE,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC;4BA6CN,WAAW,EAAE,YAAY,OAAO;EAyBjE"}
1
+ {"version":3,"file":"create-agent.d.ts","sourceRoot":"","sources":["../../src/chat/create-agent.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,YAAY,EACZ,gBAAgB,EACjB,MAAM,iBAAiB,CAAC;AAMzB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAGtD,MAAM,WAAW,eAAe,CAC9B,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACtC,OAAO,CACP,SAAQ,IAAI,CACV,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,UAAU,GAAG,SAAS,GAAG,gBAAgB,GAAG,kBAAkB,CAC/D;IACD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,cAAc,CAAC,EAAE,gBAAgB,CAAC;IAClC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAgB,WAAW,CACzB,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACtC,OAAO,GAAG,OAAO,EACjB,IAAI,EAAE,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC;4BAoDN,WAAW,EAAE,YAAY,OAAO;EAiCjE"}
@@ -1,11 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createAgent = createAgent;
4
+ const node_path_1 = require("node:path");
4
5
  const runtime_1 = require("@kortyx/runtime");
5
- const path_1 = require("path");
6
6
  const process_chat_1 = require("./process-chat");
7
7
  function createAgent(args) {
8
- const { workflowsDir, workflowRegistry, selectWorkflow, fallbackWorkflowId, config, configPath, ...baseArgs } = args;
8
+ const { workflowsDir, workflowRegistry, selectWorkflow, fallbackWorkflowId, config, configPath, defaultWorkflowId, frameworkAdapter, ...baseArgs } = args;
9
+ const resolvedDefaultWorkflowId = defaultWorkflowId ?? config?.fallbackWorkflowId ?? fallbackWorkflowId;
10
+ const resolvedFrameworkAdapter = frameworkAdapter ?? (0, runtime_1.createFrameworkAdapterFromEnv)();
9
11
  const resolvedCwd = process.cwd();
10
12
  const registryPromise = (async () => {
11
13
  if (workflowRegistry)
@@ -21,7 +23,7 @@ function createAgent(args) {
21
23
  ...(configPath ? { configPath } : {}),
22
24
  };
23
25
  const loadedConfig = config ?? (await (0, runtime_1.loadKortyxConfig)(loadConfigArgs));
24
- const resolvedWorkflowsDir = loadedConfig?.workflowsDir ?? (0, path_1.resolve)(resolvedCwd, "src", "workflows");
26
+ const resolvedWorkflowsDir = loadedConfig?.workflowsDir ?? (0, node_path_1.resolve)(resolvedCwd, "src", "workflows");
25
27
  const registryOptions = {
26
28
  workflowsDir: resolvedWorkflowsDir,
27
29
  fallbackId: loadedConfig?.fallbackWorkflowId ??
@@ -41,9 +43,13 @@ function createAgent(args) {
41
43
  if (selectWorkflow) {
42
44
  return (0, process_chat_1.processChat)({
43
45
  ...baseArgs,
46
+ ...(resolvedDefaultWorkflowId
47
+ ? { defaultWorkflowId: resolvedDefaultWorkflowId }
48
+ : {}),
44
49
  messages,
45
50
  options,
46
51
  selectWorkflow,
52
+ frameworkAdapter: resolvedFrameworkAdapter,
47
53
  });
48
54
  }
49
55
  const registry = await registryPromise;
@@ -52,9 +58,13 @@ function createAgent(args) {
52
58
  }
53
59
  return (0, process_chat_1.processChat)({
54
60
  ...baseArgs,
61
+ ...(resolvedDefaultWorkflowId
62
+ ? { defaultWorkflowId: resolvedDefaultWorkflowId }
63
+ : {}),
55
64
  messages,
56
65
  options,
57
66
  workflowRegistry: registry,
67
+ frameworkAdapter: resolvedFrameworkAdapter,
58
68
  });
59
69
  },
60
70
  };
@@ -1,6 +1,6 @@
1
1
  import type { MemoryAdapter } from "@kortyx/memory";
2
2
  import type { GetProviderFn } from "@kortyx/providers";
3
- import type { WorkflowRegistry } from "@kortyx/runtime";
3
+ import type { FrameworkAdapter, WorkflowRegistry } from "@kortyx/runtime";
4
4
  import type { ApplyResumeSelection } from "../interrupt/resume-handler";
5
5
  import type { SelectWorkflowFn } from "../orchestrator";
6
6
  import type { ChatMessage } from "../types/chat-message";
@@ -15,11 +15,12 @@ export interface ProcessChatArgs<Config extends Record<string, unknown>, Options
15
15
  loadRuntimeConfig: (options?: Options) => Config | Promise<Config>;
16
16
  selectWorkflow?: SelectWorkflowFn;
17
17
  workflowRegistry?: WorkflowRegistry;
18
+ frameworkAdapter?: FrameworkAdapter;
18
19
  getProvider: GetProviderFn;
19
20
  initializeProviders?: InitializeProvidersFn<Config>;
20
- memoryAdapter: MemoryAdapter;
21
+ memoryAdapter?: MemoryAdapter;
21
22
  applyResumeSelection?: ApplyResumeSelection;
22
23
  }
23
- export declare function processChat<Config extends Record<string, unknown>, Options = unknown>({ messages, options, sessionId, defaultWorkflowId, loadRuntimeConfig, selectWorkflow, workflowRegistry, getProvider, initializeProviders, memoryAdapter, applyResumeSelection, }: ProcessChatArgs<Config, Options>): Promise<Response>;
24
+ export declare function processChat<Config extends Record<string, unknown>, Options = unknown>({ messages, options, sessionId, defaultWorkflowId, loadRuntimeConfig, selectWorkflow, workflowRegistry, frameworkAdapter, getProvider, initializeProviders, memoryAdapter, applyResumeSelection, }: ProcessChatArgs<Config, Options>): Promise<Response>;
24
25
  export {};
25
26
  //# sourceMappingURL=process-chat.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"process-chat.d.ts","sourceRoot":"","sources":["../../src/chat/process-chat.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAGxD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAExE,OAAO,KAAK,EAAgB,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEtE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAGzD,KAAK,qBAAqB,CAAC,MAAM,IAAI,CACnC,QAAQ,EAAE,MAAM,SAAS;IAAE,EAAE,EAAE,MAAM,CAAC,CAAA;CAAE,GAAG,CAAC,GAAG,OAAO,KACnD,IAAI,CAAC;AAEV,MAAM,WAAW,eAAe,CAC9B,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACtC,OAAO;IAEP,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,iBAAiB,EAAE,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACnE,cAAc,CAAC,EAAE,gBAAgB,CAAC;IAClC,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,WAAW,EAAE,aAAa,CAAC;IAC3B,mBAAmB,CAAC,EAAE,qBAAqB,CAAC,MAAM,CAAC,CAAC;IACpD,aAAa,EAAE,aAAa,CAAC;IAC7B,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;CAC7C;AAED,wBAAsB,WAAW,CAC/B,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACtC,OAAO,GAAG,OAAO,EACjB,EACA,QAAQ,EACR,OAAO,EACP,SAAS,EACT,iBAAiB,EACjB,iBAAiB,EACjB,cAAc,EACd,gBAAgB,EAChB,WAAW,EACX,mBAAmB,EACnB,aAAa,EACb,oBAAoB,GACrB,EAAE,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,CAkFtD"}
1
+ {"version":3,"file":"process-chat.d.ts","sourceRoot":"","sources":["../../src/chat/process-chat.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAO1E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAKxE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAExD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAGzD,KAAK,qBAAqB,CAAC,MAAM,IAAI,CACnC,QAAQ,EAAE,MAAM,SAAS;IAAE,EAAE,EAAE,MAAM,CAAC,CAAA;CAAE,GAAG,CAAC,GAAG,OAAO,KACnD,IAAI,CAAC;AAEV,MAAM,WAAW,eAAe,CAC9B,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACtC,OAAO;IAEP,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,iBAAiB,EAAE,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACnE,cAAc,CAAC,EAAE,gBAAgB,CAAC;IAClC,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,WAAW,EAAE,aAAa,CAAC;IAC3B,mBAAmB,CAAC,EAAE,qBAAqB,CAAC,MAAM,CAAC,CAAC;IACpD,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;CAC7C;AAED,wBAAsB,WAAW,CAC/B,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACtC,OAAO,GAAG,OAAO,EACjB,EACA,QAAQ,EACR,OAAO,EACP,SAAS,EACT,iBAAiB,EACjB,iBAAiB,EACjB,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,WAAW,EACX,mBAAmB,EACnB,aAAa,EACb,oBAAoB,GACrB,EAAE,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC,CAyGtD"}
@@ -6,15 +6,21 @@ const stream_1 = require("@kortyx/stream");
6
6
  const resume_handler_1 = require("../interrupt/resume-handler");
7
7
  const orchestrator_1 = require("../orchestrator");
8
8
  const extract_latest_message_1 = require("../utils/extract-latest-message");
9
- async function processChat({ messages, options, sessionId, defaultWorkflowId, loadRuntimeConfig, selectWorkflow, workflowRegistry, getProvider, initializeProviders, memoryAdapter, applyResumeSelection, }) {
9
+ async function processChat({ messages, options, sessionId, defaultWorkflowId, loadRuntimeConfig, selectWorkflow, workflowRegistry, frameworkAdapter, getProvider, initializeProviders, memoryAdapter, applyResumeSelection, }) {
10
10
  const config = await loadRuntimeConfig(options);
11
11
  if (initializeProviders) {
12
- initializeProviders(config?.ai);
12
+ const ai = config.ai;
13
+ initializeProviders((ai && typeof ai === "object" && !Array.isArray(ai)
14
+ ? ai
15
+ : {}));
13
16
  }
14
17
  const runtimeConfig = {
15
18
  ...config,
16
19
  getProvider,
17
20
  ...(memoryAdapter ? { memoryAdapter } : {}),
21
+ ...(frameworkAdapter
22
+ ? { checkpointer: frameworkAdapter.checkpointer }
23
+ : {}),
18
24
  };
19
25
  const workflowSelector = selectWorkflow ??
20
26
  (workflowRegistry ? (id) => workflowRegistry.select(id) : null);
@@ -27,43 +33,61 @@ async function processChat({ messages, options, sessionId, defaultWorkflowId, lo
27
33
  const last = messages[messages.length - 1];
28
34
  const input = (0, extract_latest_message_1.extractLatestUserMessage)(messages);
29
35
  const previousMessages = messages.slice(0, -1);
30
- const storedState = await memoryAdapter.load(resolvedSessionId);
31
36
  const memory = {
32
- ...(storedState?.memory ?? {}),
33
- ...(previousMessages.length > 0 && {
34
- conversationMessages: previousMessages,
35
- }),
37
+ ...(previousMessages.length > 0
38
+ ? { conversationMessages: previousMessages }
39
+ : {}),
36
40
  };
41
+ const isResumeRequest = Boolean((0, resume_handler_1.parseResumeMeta)(last));
42
+ const requestedWorkflowId = (() => {
43
+ if (!options)
44
+ return undefined;
45
+ if (typeof options !== "object")
46
+ return undefined;
47
+ const record = options;
48
+ const wfId = record.workflowId;
49
+ if (typeof wfId === "string")
50
+ return wfId;
51
+ const wf = record.workflow;
52
+ if (typeof wf === "string")
53
+ return wf;
54
+ return undefined;
55
+ })();
56
+ if (!isResumeRequest && requestedWorkflowId) {
57
+ if (requestedWorkflowId.trim() === "")
58
+ delete memory.currentWorkflow;
59
+ else
60
+ memory.currentWorkflow = requestedWorkflowId;
61
+ }
37
62
  const baseState = await (0, runtime_1.buildInitialGraphState)({
38
63
  input,
39
64
  config: runtimeConfig,
40
65
  memory,
41
66
  ...(defaultWorkflowId ? { defaultWorkflowId } : {}),
42
67
  });
43
- const saveMemory = async (activeSessionId, state) => {
44
- await memoryAdapter.save(activeSessionId, state);
45
- };
46
68
  const resumeStream = await (0, resume_handler_1.tryPrepareResumeStream)({
47
69
  lastMessage: last,
48
70
  sessionId: resolvedSessionId,
49
71
  config: runtimeConfig,
50
- saveMemory,
51
72
  selectWorkflow: workflowSelector,
73
+ ...(frameworkAdapter ? { frameworkAdapter } : {}),
52
74
  ...(defaultWorkflowId ? { defaultWorkflowId } : {}),
53
75
  ...(applyResumeSelection ? { applyResumeSelection } : {}),
54
76
  });
55
77
  if (resumeStream)
56
78
  return (0, stream_1.createStreamResponse)(resumeStream);
79
+ const runId = (0, runtime_1.makeRequestId)("run");
57
80
  const currentWorkflow = baseState.currentWorkflow;
58
81
  const selectedWorkflow = await workflowSelector(currentWorkflow);
59
82
  const graph = await (0, runtime_1.createLangGraph)(selectedWorkflow, runtimeConfig);
60
83
  const orchestratedStream = await (0, orchestrator_1.orchestrateGraphStream)({
61
84
  sessionId: resolvedSessionId,
85
+ runId,
62
86
  graph,
63
87
  state: { ...baseState, currentWorkflow },
64
88
  config: runtimeConfig,
65
- saveMemory,
66
89
  selectWorkflow: workflowSelector,
90
+ ...(frameworkAdapter ? { frameworkAdapter } : {}),
67
91
  });
68
92
  return (0, stream_1.createStreamResponse)(orchestratedStream);
69
93
  }
@@ -1,6 +1,6 @@
1
- import { type PendingRequestRecord } from "@kortyx/memory";
1
+ import type { FrameworkAdapter, PendingRequestRecord } from "@kortyx/runtime";
2
2
  import type { StreamChunk } from "@kortyx/stream";
3
- import type { SaveMemoryFn, SelectWorkflowFn } from "../orchestrator";
3
+ import type { SelectWorkflowFn } from "../orchestrator";
4
4
  import type { ChatMessage } from "../types/chat-message";
5
5
  export interface ResumeMeta {
6
6
  token: string;
@@ -17,11 +17,11 @@ interface TryResumeArgs {
17
17
  lastMessage: ChatMessage | undefined;
18
18
  sessionId: string;
19
19
  config: Record<string, unknown>;
20
- saveMemory?: SaveMemoryFn;
21
20
  selectWorkflow: SelectWorkflowFn;
22
21
  defaultWorkflowId?: string;
23
22
  applyResumeSelection?: ApplyResumeSelection;
23
+ frameworkAdapter?: FrameworkAdapter;
24
24
  }
25
- export declare function tryPrepareResumeStream({ lastMessage, sessionId, config, saveMemory, selectWorkflow, defaultWorkflowId, applyResumeSelection, }: TryResumeArgs): Promise<AsyncIterable<StreamChunk> | null>;
25
+ export declare function tryPrepareResumeStream({ lastMessage, sessionId, config, selectWorkflow, defaultWorkflowId, applyResumeSelection, frameworkAdapter, }: TryResumeArgs): Promise<AsyncIterable<StreamChunk> | null>;
26
26
  export {};
27
27
  //# sourceMappingURL=resume-handler.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"resume-handler.d.ts","sourceRoot":"","sources":["../../src/interrupt/resume-handler.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,oBAAoB,EAC1B,MAAM,gBAAgB,CAAC;AAExB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEtE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEzD,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,MAAM,oBAAoB,GAAG,CAAC,IAAI,EAAE;IACxC,OAAO,EAAE,oBAAoB,CAAC;IAC9B,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;AAEjD,wBAAgB,eAAe,CAC7B,GAAG,EAAE,WAAW,GAAG,SAAS,GAC3B,UAAU,GAAG,IAAI,CAmBnB;AAED,UAAU,aAAa;IACrB,WAAW,EAAE,WAAW,GAAG,SAAS,CAAC;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,UAAU,CAAC,EAAE,YAAY,CAAC;IAC1B,cAAc,EAAE,gBAAgB,CAAC;IACjC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;CAC7C;AAED,wBAAsB,sBAAsB,CAAC,EAC3C,WAAW,EACX,SAAS,EACT,MAAM,EACN,UAAU,EACV,cAAc,EACd,iBAAiB,EACjB,oBAAoB,GACrB,EAAE,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,WAAW,CAAC,GAAG,IAAI,CAAC,CAgF5D"}
1
+ {"version":3,"file":"resume-handler.d.ts","sourceRoot":"","sources":["../../src/interrupt/resume-handler.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,gBAAgB,EAChB,oBAAoB,EAErB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAExD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEzD,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,MAAM,oBAAoB,GAAG,CAAC,IAAI,EAAE;IACxC,OAAO,EAAE,oBAAoB,CAAC;IAC9B,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;AAKjD,wBAAgB,eAAe,CAC7B,GAAG,EAAE,WAAW,GAAG,SAAS,GAC3B,UAAU,GAAG,IAAI,CAuBnB;AAED,UAAU,aAAa;IACrB,WAAW,EAAE,WAAW,GAAG,SAAS,CAAC;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,cAAc,EAAE,gBAAgB,CAAC;IACjC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;IAC5C,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;CACrC;AAED,wBAAsB,sBAAsB,CAAC,EAC3C,WAAW,EACX,SAAS,EACT,MAAM,EACN,cAAc,EACd,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,GACjB,EAAE,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,WAAW,CAAC,GAAG,IAAI,CAAC,CA6F5D"}
@@ -2,42 +2,48 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.parseResumeMeta = parseResumeMeta;
4
4
  exports.tryPrepareResumeStream = tryPrepareResumeStream;
5
- const memory_1 = require("@kortyx/memory");
6
5
  const runtime_1 = require("@kortyx/runtime");
7
6
  const orchestrator_1 = require("../orchestrator");
7
+ const isRecord = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
8
8
  function parseResumeMeta(msg) {
9
9
  if (!msg || !msg.metadata)
10
10
  return null;
11
- const raw = msg.metadata?.resume;
12
- if (!raw)
11
+ const raw = msg.metadata.resume;
12
+ if (!isRecord(raw))
13
13
  return null;
14
14
  const token = typeof raw.token === "string" ? raw.token : "";
15
15
  const requestId = typeof raw.requestId === "string" ? raw.requestId : "";
16
- const cancel = Boolean(raw.cancel);
16
+ const cancel = raw.cancel === true;
17
17
  let selected = [];
18
- if (typeof raw.selected === "string")
19
- selected = [raw.selected];
20
- else if (Array.isArray(raw.selected))
21
- selected = raw.selected.map((x) => String(x));
22
- else if (raw?.choice?.id)
23
- selected = [String(raw.choice.id)];
24
- else if (Array.isArray(raw?.choices))
25
- selected = raw.choices.map((c) => String(c.id));
18
+ const rawSelected = raw.selected;
19
+ if (typeof rawSelected === "string")
20
+ selected = [rawSelected];
21
+ else if (Array.isArray(rawSelected))
22
+ selected = rawSelected.map(String);
23
+ else if (isRecord(raw.choice) && typeof raw.choice.id === "string")
24
+ selected = [raw.choice.id];
25
+ else if (Array.isArray(raw.choices))
26
+ selected = raw.choices
27
+ .map((c) => (isRecord(c) ? c.id : undefined))
28
+ .filter((id) => typeof id === "string");
26
29
  if (!token || !requestId)
27
30
  return null;
28
31
  return { token, requestId, selected, cancel };
29
32
  }
30
- async function tryPrepareResumeStream({ lastMessage, sessionId, config, saveMemory, selectWorkflow, defaultWorkflowId, applyResumeSelection, }) {
33
+ async function tryPrepareResumeStream({ lastMessage, sessionId, config, selectWorkflow, defaultWorkflowId, applyResumeSelection, frameworkAdapter, }) {
31
34
  const meta = parseResumeMeta(lastMessage);
32
35
  if (!meta)
33
36
  return null;
34
- const pending = (0, memory_1.getPendingRequest)(meta.token);
37
+ const store = frameworkAdapter?.pendingRequests;
38
+ if (!store)
39
+ return null;
40
+ const pending = await store.get(meta.token);
35
41
  if (!pending || pending.requestId !== meta.requestId) {
36
42
  console.log(`[resume] pending not found or mismatched. token=${meta.token} requestId=${meta.requestId}`);
37
43
  return null;
38
44
  }
39
45
  if (meta.cancel) {
40
- (0, memory_1.deletePendingRequest)(pending.token);
46
+ await store.delete(pending.token);
41
47
  return null;
42
48
  }
43
49
  console.log(`[resume] token=${meta.token} requestId=${meta.requestId} selected=${JSON.stringify(meta.selected)} sessionId=${sessionId}`);
@@ -46,33 +52,41 @@ async function tryPrepareResumeStream({ lastMessage, sessionId, config, saveMemo
46
52
  : meta.selected?.length
47
53
  ? { coordinates: String(meta.selected[0]) }
48
54
  : {};
49
- const resumeDataPatch = resumeData && typeof resumeData === "object" ? resumeData : {};
55
+ const resumeDataPatch = isRecord(resumeData) ? resumeData : {};
56
+ const pendingData = isRecord(pending.state?.data) ? pending.state?.data : {};
57
+ const workflowId = typeof pending.workflow === "string" && pending.workflow.trim()
58
+ ? pending.workflow
59
+ : typeof defaultWorkflowId === "string" && defaultWorkflowId.trim()
60
+ ? defaultWorkflowId
61
+ : "job-search";
50
62
  const resumedState = {
51
63
  input: "",
52
64
  lastNode: "__start__",
53
- currentWorkflow: pending.workflow ||
54
- defaultWorkflowId ||
55
- "job-search",
56
- config: config,
65
+ currentWorkflow: workflowId,
66
+ config,
67
+ memory: {},
57
68
  conversationHistory: [],
58
69
  awaitingHumanInput: false,
59
70
  data: {
60
- ...(pending.state?.data ?? {}),
71
+ ...pendingData,
61
72
  ...resumeDataPatch,
62
73
  },
63
74
  };
64
75
  const wf = await selectWorkflow(resumedState.currentWorkflow);
65
- const resumeValue = meta.selected?.length
66
- ? String(meta.selected[0])
67
- : undefined;
76
+ const resumeValue = meta.selected?.length && pending.schema.kind === "multi-choice"
77
+ ? meta.selected.map((x) => String(x))
78
+ : meta.selected?.length
79
+ ? String(meta.selected[0])
80
+ : undefined;
68
81
  const resumedGraph = await (0, runtime_1.createLangGraph)(wf, {
69
82
  ...config,
70
83
  resume: true,
71
84
  ...(resumeValue !== undefined ? { resumeValue } : {}),
72
85
  });
73
- (0, memory_1.deletePendingRequest)(pending.token);
86
+ await store.delete(pending.token);
74
87
  const args = {
75
88
  sessionId,
89
+ runId: pending.runId,
76
90
  graph: resumedGraph,
77
91
  state: resumedState,
78
92
  config: {
@@ -81,9 +95,8 @@ async function tryPrepareResumeStream({ lastMessage, sessionId, config, saveMemo
81
95
  ...(resumeValue !== undefined ? { resumeValue } : {}),
82
96
  },
83
97
  selectWorkflow,
98
+ ...(frameworkAdapter ? { frameworkAdapter } : {}),
84
99
  };
85
- if (saveMemory)
86
- args.saveMemory = saveMemory;
87
100
  const stream = await (0, orchestrator_1.orchestrateGraphStream)(args);
88
101
  return stream;
89
102
  }
@@ -1,5 +1,6 @@
1
1
  import type { GraphState, WorkflowDefinition } from "@kortyx/core";
2
- export type SelectWorkflowFn = (workflowId: string) => Promise<WorkflowDefinition<any, any>>;
2
+ import { type FrameworkAdapter } from "@kortyx/runtime";
3
+ export type SelectWorkflowFn = (workflowId: string) => Promise<WorkflowDefinition>;
3
4
  export type SaveMemoryFn = (sessionId: string, state: GraphState) => Promise<void>;
4
5
  export interface CompiledGraphLike {
5
6
  config?: Record<string, unknown>;
@@ -10,11 +11,12 @@ export interface CompiledGraphLike {
10
11
  }
11
12
  export interface OrchestrateArgs {
12
13
  sessionId?: string;
14
+ runId: string;
13
15
  graph: CompiledGraphLike;
14
16
  state: GraphState;
15
17
  config: Record<string, unknown>;
16
- saveMemory?: SaveMemoryFn;
17
18
  selectWorkflow: SelectWorkflowFn;
19
+ frameworkAdapter?: FrameworkAdapter;
18
20
  }
19
- export declare function orchestrateGraphStream({ sessionId, graph, state, config, saveMemory, selectWorkflow, }: OrchestrateArgs): Promise<NodeJS.ReadableStream>;
21
+ export declare function orchestrateGraphStream({ sessionId, runId, graph, state, config, selectWorkflow, frameworkAdapter, }: OrchestrateArgs): Promise<NodeJS.ReadableStream>;
20
22
  //# sourceMappingURL=orchestrator.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../src/orchestrator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,kBAAkB,EAAc,MAAM,cAAc,CAAC;AAgB/E,MAAM,MAAM,gBAAgB,GAAG,CAC7B,UAAU,EAAE,MAAM,KACf,OAAO,CAAC,kBAAkB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;AAE3C,MAAM,MAAM,YAAY,GAAG,CACzB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,UAAU,KACd,OAAO,CAAC,IAAI,CAAC,CAAC;AAEnB,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,YAAY,EAAE,CACZ,KAAK,EAAE,UAAU,EACjB,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,KACnE,aAAa,CAAC,OAAO,CAAC,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;CACvD;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,iBAAiB,CAAC;IACzB,KAAK,EAAE,UAAU,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,UAAU,CAAC,EAAE,YAAY,CAAC;IAC1B,cAAc,EAAE,gBAAgB,CAAC;CAClC;AAQD,wBAAsB,sBAAsB,CAAC,EAC3C,SAAS,EACT,KAAK,EACL,KAAK,EACL,MAAM,EACN,UAAU,EACV,cAAc,GACf,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC,CA4dlD"}
1
+ {"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../src/orchestrator.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,kBAAkB,EAAc,MAAM,cAAc,CAAC;AAC/E,OAAO,EAEL,KAAK,gBAAgB,EAKtB,MAAM,iBAAiB,CAAC;AAKzB,MAAM,MAAM,gBAAgB,GAAG,CAC7B,UAAU,EAAE,MAAM,KACf,OAAO,CAAC,kBAAkB,CAAC,CAAC;AAEjC,MAAM,MAAM,YAAY,GAAG,CACzB,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,UAAU,KACd,OAAO,CAAC,IAAI,CAAC,CAAC;AAEnB,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,YAAY,EAAE,CACZ,KAAK,EAAE,UAAU,EACjB,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,KACnE,aAAa,CAAC,OAAO,CAAC,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;CACvD;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,iBAAiB,CAAC;IACzB,KAAK,EAAE,UAAU,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,cAAc,EAAE,gBAAgB,CAAC;IACjC,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;CACrC;AAQD,wBAAsB,sBAAsB,CAAC,EAC3C,SAAS,EACT,KAAK,EACL,KAAK,EACL,KAAK,EACL,MAAM,EACN,cAAc,EACd,gBAAgB,GACjB,EAAE,eAAe,GAAG,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC,CAkgBlD"}
@@ -1,16 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.orchestrateGraphStream = orchestrateGraphStream;
4
- const memory_1 = require("@kortyx/memory");
4
+ const node_stream_1 = require("node:stream");
5
5
  const runtime_1 = require("@kortyx/runtime");
6
6
  const langgraph_1 = require("@langchain/langgraph");
7
- const stream_1 = require("stream");
8
7
  const transform_graph_stream_for_ui_1 = require("./stream/transform-graph-stream-for-ui");
9
- async function orchestrateGraphStream({ sessionId, graph, state, config, saveMemory, selectWorkflow, }) {
10
- const out = new stream_1.PassThrough({ objectMode: true });
8
+ async function orchestrateGraphStream({ sessionId, runId, graph, state, config, selectWorkflow, frameworkAdapter, }) {
9
+ const out = new node_stream_1.PassThrough({ objectMode: true });
11
10
  let currentGraph = graph;
12
11
  let currentState = state;
13
12
  let finished = false;
13
+ const namespacesUsed = new Set();
14
14
  try {
15
15
  const sid = config?.session?.id;
16
16
  if (sid && typeof sid === "string") {
@@ -28,6 +28,8 @@ async function orchestrateGraphStream({ sessionId, graph, state, config, saveMem
28
28
  let pendingRecordToken = null;
29
29
  let activeIsResume = false;
30
30
  let wroteHumanInput = false;
31
+ const pendingStore = frameworkAdapter?.pendingRequests;
32
+ const pendingTtlMs = frameworkAdapter?.ttlMs ?? 15 * 60 * 1000;
31
33
  const forwardEmit = (event, payload) => {
32
34
  if (event === "error") {
33
35
  const msg = String(payload?.message ?? "Unexpected error");
@@ -124,6 +126,7 @@ async function orchestrateGraphStream({ sessionId, graph, state, config, saveMem
124
126
  token,
125
127
  requestId,
126
128
  sessionId: sessionId,
129
+ runId,
127
130
  workflow: local.workflow || currentState.currentWorkflow,
128
131
  node: local.node || "",
129
132
  state: { ...currentState, awaitingHumanInput: true },
@@ -147,9 +150,13 @@ async function orchestrateGraphStream({ sessionId, graph, state, config, saveMem
147
150
  value: o.value,
148
151
  })),
149
152
  createdAt: Date.now(),
150
- ttlMs: 15 * 60 * 1000,
153
+ ttlMs: pendingTtlMs,
151
154
  };
152
- (0, memory_1.savePendingRequest)(record);
155
+ if (pendingStore) {
156
+ pendingStore.save(record).catch((e) => {
157
+ console.error("[orchestrator] failed to save pending request", e);
158
+ });
159
+ }
153
160
  out.write({
154
161
  type: "interrupt",
155
162
  requestId: record.requestId,
@@ -179,9 +186,11 @@ async function orchestrateGraphStream({ sessionId, graph, state, config, saveMem
179
186
  const threadId = currentGraph.config?.session?.id ||
180
187
  sessionId ||
181
188
  "anonymous-session";
189
+ const checkpointNs = String(currentState.currentWorkflow || "default");
190
+ namespacesUsed.add(checkpointNs);
182
191
  out.write({
183
192
  type: "status",
184
- message: `🧵 thread_id=${threadId} workflow=${currentState.currentWorkflow}`,
193
+ message: `🧵 thread_id=${threadId} run_id=${runId} workflow=${currentState.currentWorkflow}`,
185
194
  });
186
195
  const isResume = Boolean(currentGraph.config?.resume);
187
196
  activeIsResume = isResume;
@@ -197,14 +206,14 @@ async function orchestrateGraphStream({ sessionId, graph, state, config, saveMem
197
206
  const runtimeStream = currentGraph.streamEvents(invokeState, {
198
207
  version: "v2",
199
208
  configurable: {
200
- thread_id: threadId,
201
- checkpoint_ns: String(currentState.currentWorkflow || "default"),
209
+ thread_id: runId,
210
+ checkpoint_ns: checkpointNs,
202
211
  },
203
212
  });
204
213
  try {
205
214
  out.write({
206
215
  type: "status",
207
- message: `▶️ streamEvents invoke: resume=${Boolean(currentGraph.config?.resume)} thread_id=${threadId} ns=${String(currentState.currentWorkflow || "default")}`,
216
+ message: `▶️ streamEvents invoke: resume=${Boolean(currentGraph.config?.resume)} thread_id=${threadId} run_id=${runId} ns=${String(currentState.currentWorkflow || "default")}`,
208
217
  });
209
218
  }
210
219
  catch { }
@@ -235,6 +244,7 @@ async function orchestrateGraphStream({ sessionId, graph, state, config, saveMem
235
244
  token,
236
245
  requestId,
237
246
  sessionId: sessionId,
247
+ runId,
238
248
  workflow: currentState.currentWorkflow,
239
249
  node: node || "",
240
250
  state: {
@@ -261,9 +271,11 @@ async function orchestrateGraphStream({ sessionId, graph, state, config, saveMem
261
271
  value: o.value,
262
272
  })),
263
273
  createdAt: Date.now(),
264
- ttlMs: 15 * 60 * 1000,
274
+ ttlMs: pendingTtlMs,
265
275
  };
266
- (0, memory_1.savePendingRequest)(record);
276
+ if (pendingStore) {
277
+ await pendingStore.save(record);
278
+ }
267
279
  out.write({
268
280
  type: "interrupt",
269
281
  requestId,
@@ -306,9 +318,6 @@ async function orchestrateGraphStream({ sessionId, graph, state, config, saveMem
306
318
  else {
307
319
  out.write(chunk);
308
320
  }
309
- if (saveMemory && sessionId && chunk.type !== "status") {
310
- await saveMemory(sessionId, { ...currentState });
311
- }
312
321
  if (chunk.type === "transition") {
313
322
  loopTransitionTo = String(chunk.transitionTo || "");
314
323
  loopTransitionPayload = chunk.payload ?? {};
@@ -338,10 +347,9 @@ async function orchestrateGraphStream({ sessionId, graph, state, config, saveMem
338
347
  ...(workflowFinalState?.data ?? currentState.data ?? {}),
339
348
  ...(transitionPayload ?? {}),
340
349
  };
341
- const newInput = typeof transitionPayload?.rawInput ===
342
- "string"
343
- ? transitionPayload
344
- .rawInput
350
+ const rawInputFromPayload = transitionPayload?.rawInput;
351
+ const newInput = typeof rawInputFromPayload === "string"
352
+ ? rawInputFromPayload
345
353
  : currentState.input;
346
354
  currentState = {
347
355
  ...currentState,
@@ -350,9 +358,6 @@ async function orchestrateGraphStream({ sessionId, graph, state, config, saveMem
350
358
  data: mergedData,
351
359
  ui: {},
352
360
  };
353
- if (saveMemory && sessionId) {
354
- await saveMemory(sessionId, currentState);
355
- }
356
361
  currentGraph = nextGraph;
357
362
  continue;
358
363
  }
@@ -367,9 +372,30 @@ async function orchestrateGraphStream({ sessionId, graph, state, config, saveMem
367
372
  }
368
373
  if (workflowFinalState) {
369
374
  if (workflowFinalState && pendingRecordToken) {
370
- (0, memory_1.updatePendingRequest)(pendingRecordToken, {
371
- state: workflowFinalState,
372
- });
375
+ if (pendingStore) {
376
+ await pendingStore.update(pendingRecordToken, {
377
+ state: workflowFinalState,
378
+ });
379
+ }
380
+ }
381
+ const shouldKeepFrameworkState = Boolean(pendingRecordToken) ||
382
+ Boolean(workflowFinalState?.awaitingHumanInput);
383
+ if (!shouldKeepFrameworkState) {
384
+ try {
385
+ if (frameworkAdapter?.cleanupRun) {
386
+ await frameworkAdapter.cleanupRun(runId, Array.from(namespacesUsed));
387
+ }
388
+ else {
389
+ const cp = currentGraph.config
390
+ ?.checkpointer;
391
+ if (cp?.deleteThread) {
392
+ await cp.deleteThread(runId);
393
+ }
394
+ }
395
+ }
396
+ catch (e) {
397
+ console.error("[orchestrator] framework cleanup failed", e);
398
+ }
373
399
  }
374
400
  finished = true;
375
401
  out.write({ type: "done", data: workflowFinalState });