@salesforce/magen-mcp-workflow 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 (134) hide show
  1. package/README.md +168 -0
  2. package/dist/checkpointing/index.d.ts +3 -0
  3. package/dist/checkpointing/index.js +10 -0
  4. package/dist/checkpointing/index.js.map +1 -0
  5. package/dist/checkpointing/jsonCheckpointer.d.ts +19 -0
  6. package/dist/checkpointing/jsonCheckpointer.js +164 -0
  7. package/dist/checkpointing/jsonCheckpointer.js.map +1 -0
  8. package/dist/checkpointing/statePersistence.d.ts +26 -0
  9. package/dist/checkpointing/statePersistence.js +117 -0
  10. package/dist/checkpointing/statePersistence.js.map +1 -0
  11. package/dist/checkpointing/workflowStateManager.d.ts +99 -0
  12. package/dist/checkpointing/workflowStateManager.js +206 -0
  13. package/dist/checkpointing/workflowStateManager.js.map +1 -0
  14. package/dist/common/fileSystem.d.ts +113 -0
  15. package/dist/common/fileSystem.js +62 -0
  16. package/dist/common/fileSystem.js.map +1 -0
  17. package/dist/common/metadata.d.ts +93 -0
  18. package/dist/common/metadata.js +40 -0
  19. package/dist/common/metadata.js.map +1 -0
  20. package/dist/common/propertyMetadata.d.ts +58 -0
  21. package/dist/common/propertyMetadata.js +8 -0
  22. package/dist/common/propertyMetadata.js.map +1 -0
  23. package/dist/common/types.d.ts +16 -0
  24. package/dist/common/types.js +8 -0
  25. package/dist/common/types.js.map +1 -0
  26. package/dist/index.d.ts +15 -0
  27. package/dist/index.js +37 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/logging/logger.d.ts +56 -0
  30. package/dist/logging/logger.js +113 -0
  31. package/dist/logging/logger.js.map +1 -0
  32. package/dist/nodes/abstractBaseNode.d.ts +27 -0
  33. package/dist/nodes/abstractBaseNode.js +34 -0
  34. package/dist/nodes/abstractBaseNode.js.map +1 -0
  35. package/dist/nodes/abstractToolNode.d.ts +32 -0
  36. package/dist/nodes/abstractToolNode.js +44 -0
  37. package/dist/nodes/abstractToolNode.js.map +1 -0
  38. package/dist/nodes/getUserInput/factory.d.ts +42 -0
  39. package/dist/nodes/getUserInput/factory.js +64 -0
  40. package/dist/nodes/getUserInput/factory.js.map +1 -0
  41. package/dist/nodes/getUserInput/index.d.ts +2 -0
  42. package/dist/nodes/getUserInput/index.js +3 -0
  43. package/dist/nodes/getUserInput/index.js.map +1 -0
  44. package/dist/nodes/getUserInput/node.d.ts +68 -0
  45. package/dist/nodes/getUserInput/node.js +41 -0
  46. package/dist/nodes/getUserInput/node.js.map +1 -0
  47. package/dist/nodes/index.d.ts +5 -0
  48. package/dist/nodes/index.js +12 -0
  49. package/dist/nodes/index.js.map +1 -0
  50. package/dist/nodes/toolExecutor.d.ts +22 -0
  51. package/dist/nodes/toolExecutor.js +19 -0
  52. package/dist/nodes/toolExecutor.js.map +1 -0
  53. package/dist/nodes/userInputExtraction/factory.d.ts +42 -0
  54. package/dist/nodes/userInputExtraction/factory.js +55 -0
  55. package/dist/nodes/userInputExtraction/factory.js.map +1 -0
  56. package/dist/nodes/userInputExtraction/index.d.ts +2 -0
  57. package/dist/nodes/userInputExtraction/index.js +3 -0
  58. package/dist/nodes/userInputExtraction/index.js.map +1 -0
  59. package/dist/nodes/userInputExtraction/node.d.ts +60 -0
  60. package/dist/nodes/userInputExtraction/node.js +24 -0
  61. package/dist/nodes/userInputExtraction/node.js.map +1 -0
  62. package/dist/routers/checkPropertiesFulfilledRouter.d.ts +74 -0
  63. package/dist/routers/checkPropertiesFulfilledRouter.js +106 -0
  64. package/dist/routers/checkPropertiesFulfilledRouter.js.map +1 -0
  65. package/dist/routers/index.d.ts +1 -0
  66. package/dist/routers/index.js +8 -0
  67. package/dist/routers/index.js.map +1 -0
  68. package/dist/services/abstractService.d.ts +71 -0
  69. package/dist/services/abstractService.js +83 -0
  70. package/dist/services/abstractService.js.map +1 -0
  71. package/dist/services/getInputService.d.ts +43 -0
  72. package/dist/services/getInputService.js +48 -0
  73. package/dist/services/getInputService.js.map +1 -0
  74. package/dist/services/index.d.ts +3 -0
  75. package/dist/services/index.js +10 -0
  76. package/dist/services/index.js.map +1 -0
  77. package/dist/services/inputExtractionService.d.ts +46 -0
  78. package/dist/services/inputExtractionService.js +133 -0
  79. package/dist/services/inputExtractionService.js.map +1 -0
  80. package/dist/storage/wellKnownDirectory.d.ts +79 -0
  81. package/dist/storage/wellKnownDirectory.js +121 -0
  82. package/dist/storage/wellKnownDirectory.js.map +1 -0
  83. package/dist/tools/base/abstractTool.d.ts +61 -0
  84. package/dist/tools/base/abstractTool.js +87 -0
  85. package/dist/tools/base/abstractTool.js.map +1 -0
  86. package/dist/tools/base/abstractWorkflowTool.d.ts +34 -0
  87. package/dist/tools/base/abstractWorkflowTool.js +94 -0
  88. package/dist/tools/base/abstractWorkflowTool.js.map +1 -0
  89. package/dist/tools/base/index.d.ts +2 -0
  90. package/dist/tools/base/index.js +9 -0
  91. package/dist/tools/base/index.js.map +1 -0
  92. package/dist/tools/orchestrator/config.d.ts +55 -0
  93. package/dist/tools/orchestrator/config.js +8 -0
  94. package/dist/tools/orchestrator/config.js.map +1 -0
  95. package/dist/tools/orchestrator/index.d.ts +3 -0
  96. package/dist/tools/orchestrator/index.js +9 -0
  97. package/dist/tools/orchestrator/index.js.map +1 -0
  98. package/dist/tools/orchestrator/metadata.d.ts +55 -0
  99. package/dist/tools/orchestrator/metadata.js +49 -0
  100. package/dist/tools/orchestrator/metadata.js.map +1 -0
  101. package/dist/tools/orchestrator/orchestratorTool.d.ts +39 -0
  102. package/dist/tools/orchestrator/orchestratorTool.js +186 -0
  103. package/dist/tools/orchestrator/orchestratorTool.js.map +1 -0
  104. package/dist/tools/utilities/getInput/factory.d.ts +43 -0
  105. package/dist/tools/utilities/getInput/factory.js +32 -0
  106. package/dist/tools/utilities/getInput/factory.js.map +1 -0
  107. package/dist/tools/utilities/getInput/index.d.ts +3 -0
  108. package/dist/tools/utilities/getInput/index.js +10 -0
  109. package/dist/tools/utilities/getInput/index.js.map +1 -0
  110. package/dist/tools/utilities/getInput/metadata.d.ts +78 -0
  111. package/dist/tools/utilities/getInput/metadata.js +43 -0
  112. package/dist/tools/utilities/getInput/metadata.js.map +1 -0
  113. package/dist/tools/utilities/getInput/tool.d.ts +89 -0
  114. package/dist/tools/utilities/getInput/tool.js +69 -0
  115. package/dist/tools/utilities/getInput/tool.js.map +1 -0
  116. package/dist/tools/utilities/index.d.ts +2 -0
  117. package/dist/tools/utilities/index.js +9 -0
  118. package/dist/tools/utilities/index.js.map +1 -0
  119. package/dist/tools/utilities/inputExtraction/factory.d.ts +43 -0
  120. package/dist/tools/utilities/inputExtraction/factory.js +32 -0
  121. package/dist/tools/utilities/inputExtraction/factory.js.map +1 -0
  122. package/dist/tools/utilities/inputExtraction/index.d.ts +3 -0
  123. package/dist/tools/utilities/inputExtraction/index.js +10 -0
  124. package/dist/tools/utilities/inputExtraction/index.js.map +1 -0
  125. package/dist/tools/utilities/inputExtraction/metadata.d.ts +66 -0
  126. package/dist/tools/utilities/inputExtraction/metadata.js +52 -0
  127. package/dist/tools/utilities/inputExtraction/metadata.js.map +1 -0
  128. package/dist/tools/utilities/inputExtraction/tool.d.ts +82 -0
  129. package/dist/tools/utilities/inputExtraction/tool.js +71 -0
  130. package/dist/tools/utilities/inputExtraction/tool.js.map +1 -0
  131. package/dist/utils/toolExecutionUtils.d.ts +55 -0
  132. package/dist/utils/toolExecutionUtils.js +70 -0
  133. package/dist/utils/toolExecutionUtils.js.map +1 -0
  134. package/package.json +42 -0
package/README.md ADDED
@@ -0,0 +1,168 @@
1
+ # @salesforce/magen-mcp-workflow
2
+
3
+ A reusable workflow orchestration framework for building deterministic, multi-step workflows with LangGraph and the Model Context Protocol (MCP).
4
+
5
+ ## Overview
6
+
7
+ `mcp-workflow` is a **framework for MCP server authors** who need to orchestrate complex, multi-step processes involving LLM interactions. It provides the infrastructure for workflow execution, state management, and MCP tool coordination, while allowing consumers to define their own domain-specific logic.
8
+
9
+ ### What This Framework Provides
10
+
11
+ ✅ **Infrastructure**:
12
+
13
+ - `OrchestratorTool`: Manages workflow execution and coordinates MCP tool invocations
14
+ - Base classes for MCP tools (`AbstractTool`, `AbstractWorkflowTool`)
15
+ - Base classes for workflow nodes (`BaseNode`, `AbstractToolNode`)
16
+ - State persistence and checkpointing (via `.magen/` directory)
17
+ - Logging infrastructure
18
+ - Dependency injection patterns for testability
19
+
20
+ ### What Consumers Provide
21
+
22
+ ❌ **Domain Logic**:
23
+
24
+ - Your own `StateGraph` definition (workflow structure)
25
+ - Your own state annotations (`Annotation.Root`)
26
+ - Your own workflow nodes (business operations)
27
+ - Your own MCP server tools (domain capabilities)
28
+ - Your own MCP server instance
29
+
30
+ ## Installation
31
+
32
+ ```bash
33
+ npm install @salesforce/magen-mcp-workflow
34
+ ```
35
+
36
+ ## Quick Start
37
+
38
+ ```typescript
39
+ import { Annotation, StateGraph, START, END } from '@langchain/langgraph';
40
+ import { OrchestratorTool, OrchestratorConfig, BaseNode } from '@salesforce/magen-mcp-workflow';
41
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
42
+
43
+ // 1. Define your state
44
+ const MyWorkflowState = Annotation.Root({
45
+ userMessage: Annotation<string>,
46
+ response: Annotation<string>,
47
+ });
48
+
49
+ type State = typeof MyWorkflowState.State;
50
+
51
+ // 2. Create workflow nodes
52
+ class ProcessMessageNode extends BaseNode<State> {
53
+ constructor() {
54
+ super('ProcessMessage');
55
+ }
56
+
57
+ execute = (state: State): Partial<State> => {
58
+ return {
59
+ response: `Processed: ${state.userMessage}`,
60
+ };
61
+ };
62
+ }
63
+
64
+ // 3. Build the workflow (uncompiled StateGraph)
65
+ const processNode = new ProcessMessageNode();
66
+ const workflow = new StateGraph(MyWorkflowState)
67
+ .addNode(processNode.name, processNode.execute)
68
+ .addEdge(START, processNode.name)
69
+ .addEdge(processNode.name, END);
70
+
71
+ // 4. Configure the orchestrator
72
+ const orchestratorConfig: OrchestratorConfig<typeof MyWorkflowState> = {
73
+ toolId: 'my-orchestrator',
74
+ title: 'My Orchestrator',
75
+ description: 'Orchestrates my workflow',
76
+ workflow, // Pass uncompiled StateGraph
77
+ // context defaults to { environment: 'production' }
78
+ };
79
+
80
+ // 5. Create and register the orchestrator
81
+ const server = new McpServer({ name: 'my-server', version: '1.0.0' });
82
+ const orchestrator = new OrchestratorTool(server, orchestratorConfig);
83
+ orchestrator.register({ readOnlyHint: false });
84
+ ```
85
+
86
+ ## Core Concepts
87
+
88
+ ### The `.magen` Directory
89
+
90
+ By convention, workflow state and logs are stored in a well-known directory (`.magen/`):
91
+
92
+ - **Workflow state**: `~/.magen/workflow-state.json`
93
+ - **Workflow logs**: `~/.magen/workflow_logs.json`
94
+
95
+ This can be overridden via the `PROJECT_PATH` environment variable:
96
+
97
+ ```bash
98
+ export PROJECT_PATH=/path/to/project
99
+ # State stored in: /path/to/project/.magen/
100
+ ```
101
+
102
+ ### Environment Contexts
103
+
104
+ The orchestrator supports two execution environments:
105
+
106
+ - **`production`** (default): Uses `JsonCheckpointSaver` with `.magen/` directory persistence
107
+ - **`test`**: Uses `MemorySaver` for in-memory checkpointing (no file I/O)
108
+
109
+ ```typescript
110
+ const config: OrchestratorConfig<typeof MyWorkflowState> = {
111
+ // ...
112
+ context: { environment: 'test' }, // For testing
113
+ };
114
+ ```
115
+
116
+ ### Workflow State Management
117
+
118
+ Workflows maintain session continuity across stateless MCP tool invocations using lightweight state data (`thread_id`). The orchestrator handles:
119
+
120
+ - Starting new workflow sessions
121
+ - Resuming in-progress workflows
122
+ - Persisting state between invocations
123
+ - Managing LangGraph checkpoints
124
+
125
+ ## Architecture
126
+
127
+ ```
128
+ ┌─────────────────────────────────────────────────────────────┐
129
+ │ Your MCP Server │
130
+ │ ┌──────────────────────────────────────────────────────┐ │
131
+ │ │ Domain Logic │ │
132
+ │ │ - StateGraph (workflow structure) │ │
133
+ │ │ - Nodes (business operations) │ │
134
+ │ │ - Tools (domain capabilities) │ │
135
+ │ └──────────────────────────────────────────────────────┘ │
136
+ │ │ │
137
+ │ │ depends on │
138
+ │ ▼ │
139
+ │ ┌──────────────────────────────────────────────────────┐ │
140
+ │ │ @salesforce/magen-mcp-workflow │ │
141
+ │ │ - OrchestratorTool │ │
142
+ │ │ - Base Classes │ │
143
+ │ │ - Checkpointing │ │
144
+ │ └──────────────────────────────────────────────────────┘ │
145
+ └─────────────────────────────────────────────────────────────┘
146
+ ```
147
+
148
+ ## Design Philosophy
149
+
150
+ This is a **framework, not a library**. Every API is designed with consumers in mind:
151
+
152
+ 1. **Configurability**: Consumers configure the workflow engine with their workflows
153
+ 2. **Genericity**: All components are generic over state types
154
+ 3. **Encapsulation**: Implementation details are hidden
155
+ 4. **Convention over Configuration**: Sensible defaults with escape hatches
156
+ 5. **Testability**: Dependency injection patterns throughout
157
+
158
+ ## Documentation
159
+
160
+ For comprehensive documentation, examples, and API references, see the [documentation directory](../../docs/README.md).
161
+
162
+ ## Contributing
163
+
164
+ This package is part of the `mobile-mcp-tools` monorepo. See the [main README](../../README.md) for contribution guidelines.
165
+
166
+ ## License
167
+
168
+ MIT
@@ -0,0 +1,3 @@
1
+ export { JsonCheckpointSaver } from './jsonCheckpointer.js';
2
+ export { WorkflowStatePersistence } from './statePersistence.js';
3
+ export { WorkflowStateManager, type WorkflowStateManagerConfig, type WorkflowEnvironment, } from './workflowStateManager.js';
@@ -0,0 +1,10 @@
1
+ /*
2
+ * Copyright (c) 2025, salesforce.com, inc.
3
+ * All rights reserved.
4
+ * SPDX-License-Identifier: MIT
5
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
6
+ */
7
+ export { JsonCheckpointSaver } from './jsonCheckpointer.js';
8
+ export { WorkflowStatePersistence } from './statePersistence.js';
9
+ export { WorkflowStateManager, } from './workflowStateManager.js';
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/checkpointing/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,EACL,oBAAoB,GAGrB,MAAM,2BAA2B,CAAC"}
@@ -0,0 +1,19 @@
1
+ import { RunnableConfig } from '@langchain/core/runnables';
2
+ import { BaseCheckpointSaver, Checkpoint, CheckpointMetadata, CheckpointTuple } from '@langchain/langgraph';
3
+ import { CheckpointListOptions, PendingWrite } from '@langchain/langgraph-checkpoint';
4
+ export declare class JsonCheckpointSaver extends BaseCheckpointSaver {
5
+ private state;
6
+ getTuple(config: RunnableConfig): Promise<CheckpointTuple | undefined>;
7
+ put(config: RunnableConfig, checkpoint: Checkpoint, metadata: CheckpointMetadata): Promise<RunnableConfig>;
8
+ putWrites(config: RunnableConfig, writes: PendingWrite[], taskId: string): Promise<void>;
9
+ list(config: RunnableConfig, options?: CheckpointListOptions): AsyncGenerator<CheckpointTuple>;
10
+ deleteThread(threadId: string): Promise<void>;
11
+ /**
12
+ * Exports the entire state of the checkpointer as a single JSON string.
13
+ */
14
+ exportState(): Promise<string>;
15
+ /**
16
+ * Imports and overwrites the entire state of the checkpointer.
17
+ */
18
+ importState(jsonState: string): Promise<void>;
19
+ }
@@ -0,0 +1,164 @@
1
+ /*
2
+ * Copyright (c) 2025, salesforce.com, inc.
3
+ * All rights reserved.
4
+ * SPDX-License-Identifier: MIT
5
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
6
+ */
7
+ import { BaseCheckpointSaver, } from '@langchain/langgraph';
8
+ import { WRITES_IDX_MAP, } from '@langchain/langgraph-checkpoint';
9
+ function createCheckpointKey(config) {
10
+ const threadId = config.configurable?.thread_id;
11
+ const checkpointId = config.configurable?.checkpoint_id;
12
+ if (!threadId || !checkpointId) {
13
+ throw new Error(`Invalid config, missing thread_id or checkpoint_id: ${JSON.stringify(config.configurable)}`);
14
+ }
15
+ return `${threadId}:${checkpointId}`;
16
+ }
17
+ export class JsonCheckpointSaver extends BaseCheckpointSaver {
18
+ state = { version: 1, storage: {}, writes: {} };
19
+ async getTuple(config) {
20
+ const threadId = config.configurable?.thread_id;
21
+ if (!threadId) {
22
+ throw new Error('thread_id not found in config');
23
+ }
24
+ const checkpoints = this.state.storage[threadId];
25
+ if (!checkpoints || checkpoints.length === 0) {
26
+ return undefined;
27
+ }
28
+ // This logic correctly gets the latest checkpoint.
29
+ // In this implementation, the latest is always at index 0.
30
+ const latest = checkpoints[0];
31
+ const checkpointPromise = this.serde.loadsTyped('json', Buffer.from(latest.checkpoint, 'base64'));
32
+ const metadataPromise = this.serde.loadsTyped('json', Buffer.from(latest.metadata, 'base64'));
33
+ const [checkpoint, metadata] = await Promise.all([checkpointPromise, metadataPromise]);
34
+ // Rehydrate pending writes associated with this checkpoint
35
+ const pendingWrites = [];
36
+ const checkpointKey = createCheckpointKey({
37
+ configurable: { thread_id: threadId, checkpoint_id: checkpoint.id },
38
+ });
39
+ const savedWrites = this.state.writes[checkpointKey];
40
+ if (savedWrites) {
41
+ const parsedWrites = JSON.parse(savedWrites);
42
+ for (const [taskId, channel, valueBase64] of Object.values(parsedWrites)) {
43
+ pendingWrites.push([
44
+ taskId,
45
+ channel,
46
+ await this.serde.loadsTyped('json', Buffer.from(valueBase64, 'base64')),
47
+ ]);
48
+ }
49
+ }
50
+ const checkpointTuple = {
51
+ config,
52
+ checkpoint,
53
+ metadata,
54
+ pendingWrites,
55
+ };
56
+ if (latest.parentId) {
57
+ checkpointTuple.parentConfig = {
58
+ configurable: { thread_id: threadId, checkpoint_id: latest.parentId },
59
+ };
60
+ }
61
+ return checkpointTuple;
62
+ }
63
+ async put(config, checkpoint, metadata) {
64
+ const threadId = config.configurable?.thread_id;
65
+ if (!threadId) {
66
+ throw new Error('thread_id not found in config');
67
+ }
68
+ if (!this.state.storage[threadId]) {
69
+ this.state.storage[threadId] = [];
70
+ }
71
+ const checkpointPromise = this.serde.dumpsTyped(checkpoint);
72
+ const metadataPromise = this.serde.dumpsTyped(metadata);
73
+ const [[, checkpointBytes], [, metadataBytes]] = await Promise.all([
74
+ checkpointPromise,
75
+ metadataPromise,
76
+ ]);
77
+ const parentId = config.configurable?.checkpoint_id;
78
+ // unshift() adds to the beginning, ensuring the latest is always at index 0.
79
+ this.state.storage[threadId].unshift({
80
+ checkpoint: Buffer.from(checkpointBytes).toString('base64'),
81
+ metadata: Buffer.from(metadataBytes).toString('base64'),
82
+ parentId: parentId,
83
+ });
84
+ return {
85
+ configurable: { thread_id: threadId, checkpoint_id: checkpoint.id },
86
+ };
87
+ }
88
+ async putWrites(config, writes, taskId) {
89
+ const key = createCheckpointKey(config);
90
+ const existingWritesString = this.state.writes[key];
91
+ const existingWrites = existingWritesString
92
+ ? JSON.parse(existingWritesString)
93
+ : {};
94
+ for (const [index, [channel, value]] of writes.entries()) {
95
+ const [, serializedValue] = await this.serde.dumpsTyped(value);
96
+ const valueBase64 = Buffer.from(serializedValue).toString('base64');
97
+ const innerKey = `${taskId}:${WRITES_IDX_MAP[channel] ?? index}`;
98
+ existingWrites[innerKey] = [taskId, channel, valueBase64];
99
+ }
100
+ this.state.writes[key] = JSON.stringify(existingWrites);
101
+ }
102
+ async *list(config, options) {
103
+ const threadId = config.configurable?.thread_id;
104
+ if (!threadId) {
105
+ return;
106
+ }
107
+ const checkpoints = this.state.storage[threadId] ?? [];
108
+ let count = 0;
109
+ for (const saved of checkpoints) {
110
+ const metadataPromise = this.serde.loadsTyped('json', Buffer.from(saved.metadata, 'base64'));
111
+ const metadata = (await metadataPromise);
112
+ // Apply filter if provided
113
+ if (options?.filter &&
114
+ !Object.entries(options.filter).every(([key, value]) => metadata[key] === value)) {
115
+ continue;
116
+ }
117
+ // Apply limit if provided
118
+ if (options?.limit && count >= options.limit) {
119
+ return;
120
+ }
121
+ count++;
122
+ const checkpoint = (await this.serde.loadsTyped('json', Buffer.from(saved.checkpoint, 'base64')));
123
+ const tuple = {
124
+ config: {
125
+ configurable: { thread_id: threadId, checkpoint_id: checkpoint.id },
126
+ },
127
+ checkpoint,
128
+ metadata,
129
+ };
130
+ if (saved.parentId) {
131
+ tuple.parentConfig = {
132
+ configurable: { thread_id: threadId, checkpoint_id: saved.parentId },
133
+ };
134
+ }
135
+ yield tuple;
136
+ }
137
+ }
138
+ async deleteThread(threadId) {
139
+ delete this.state.storage[threadId];
140
+ for (const key of Object.keys(this.state.writes)) {
141
+ if (key.startsWith(`${threadId}:`)) {
142
+ delete this.state.writes[key];
143
+ }
144
+ }
145
+ }
146
+ /**
147
+ * Exports the entire state of the checkpointer as a single JSON string.
148
+ */
149
+ async exportState() {
150
+ return JSON.stringify(this.state);
151
+ }
152
+ /**
153
+ * Imports and overwrites the entire state of the checkpointer.
154
+ */
155
+ async importState(jsonState) {
156
+ const parsedState = JSON.parse(jsonState);
157
+ // Basic check for future migration logic
158
+ if (!parsedState.version) {
159
+ parsedState.version = 1;
160
+ }
161
+ this.state = parsedState;
162
+ }
163
+ }
164
+ //# sourceMappingURL=jsonCheckpointer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsonCheckpointer.js","sourceRoot":"","sources":["../../src/checkpointing/jsonCheckpointer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Utility class for persisting and recalling LangGraph checkpointer state
3
+ */
4
+ export declare class WorkflowStatePersistence {
5
+ private readonly storePath;
6
+ private readonly logger;
7
+ constructor(storePath: string);
8
+ /**
9
+ * Reads the serialized state from disk if it exists
10
+ * @returns The serialized state string, or undefined if the file doesn't exist or is invalid
11
+ */
12
+ readState(): Promise<string | undefined>;
13
+ /**
14
+ * Writes the serialized state to disk
15
+ * @param serializedState The state to persist
16
+ */
17
+ writeState(serializedState: string): Promise<void>;
18
+ /**
19
+ * Deletes the persisted state file
20
+ */
21
+ clearState(): Promise<void>;
22
+ /**
23
+ * Checks if a persisted state file exists
24
+ */
25
+ stateExists(): Promise<boolean>;
26
+ }
@@ -0,0 +1,117 @@
1
+ /*
2
+ * Copyright (c) 2025, salesforce.com, inc.
3
+ * All rights reserved.
4
+ * SPDX-License-Identifier: MIT
5
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
6
+ */
7
+ import * as fs from 'fs/promises';
8
+ import * as path from 'path';
9
+ import { createComponentLogger } from '../logging/logger.js';
10
+ /**
11
+ * Utility class for persisting and recalling LangGraph checkpointer state
12
+ */
13
+ export class WorkflowStatePersistence {
14
+ storePath;
15
+ logger;
16
+ constructor(storePath) {
17
+ this.storePath = storePath;
18
+ this.logger = createComponentLogger('WorkflowStatePersistence');
19
+ }
20
+ /**
21
+ * Reads the serialized state from disk if it exists
22
+ * @returns The serialized state string, or undefined if the file doesn't exist or is invalid
23
+ */
24
+ async readState() {
25
+ try {
26
+ // Check if the file exists
27
+ await fs.access(this.storePath);
28
+ this.logger.info(`Reading checkpointer state from: ${this.storePath}`);
29
+ // Read the file content
30
+ const content = await fs.readFile(this.storePath, 'utf-8');
31
+ // Validate that it's valid JSON
32
+ JSON.parse(content);
33
+ this.logger.debug('Successfully read and validated checkpointer state', {
34
+ stateSize: content.length,
35
+ path: this.storePath,
36
+ });
37
+ return content;
38
+ }
39
+ catch (error) {
40
+ if (error.code === 'ENOENT') {
41
+ this.logger.info(`Checkpointer state file not found: ${this.storePath}. Starting with fresh state.`);
42
+ return undefined;
43
+ }
44
+ else if (error instanceof SyntaxError) {
45
+ this.logger.warn(`Invalid JSON in state file: ${this.storePath}. Starting with fresh state.`, error);
46
+ return undefined;
47
+ }
48
+ else {
49
+ this.logger.error(`Failed to read checkpointer state from: ${this.storePath}`, error);
50
+ return undefined;
51
+ }
52
+ }
53
+ }
54
+ /**
55
+ * Writes the serialized state to disk
56
+ * @param serializedState The state to persist
57
+ */
58
+ async writeState(serializedState) {
59
+ try {
60
+ // Ensure the directory exists
61
+ const storeDir = path.dirname(this.storePath);
62
+ await fs.mkdir(storeDir, { recursive: true });
63
+ // Validate that the state is valid JSON before writing
64
+ JSON.parse(serializedState);
65
+ this.logger.info(`Saving checkpointer state to: ${this.storePath}`);
66
+ // Write the state to a temporary file first, then rename for atomicity
67
+ const tempPath = `${this.storePath}.tmp`;
68
+ await fs.writeFile(tempPath, serializedState, 'utf-8');
69
+ await fs.rename(tempPath, this.storePath);
70
+ this.logger.debug('Successfully saved checkpointer state', {
71
+ stateSize: serializedState.length,
72
+ path: this.storePath,
73
+ });
74
+ }
75
+ catch (error) {
76
+ if (error instanceof SyntaxError) {
77
+ this.logger.error(`Invalid JSON state provided for persistence. State not saved.`, error);
78
+ throw new Error(`Invalid serialized state: ${error.message}`);
79
+ }
80
+ else {
81
+ this.logger.error(`Failed to save checkpointer state to: ${this.storePath}`, error);
82
+ throw error;
83
+ }
84
+ }
85
+ }
86
+ /**
87
+ * Deletes the persisted state file
88
+ */
89
+ async clearState() {
90
+ try {
91
+ await fs.unlink(this.storePath);
92
+ this.logger.info(`Cleared checkpointer state file: ${this.storePath}`);
93
+ }
94
+ catch (error) {
95
+ if (error.code === 'ENOENT') {
96
+ this.logger.debug(`State file already doesn't exist: ${this.storePath}`);
97
+ }
98
+ else {
99
+ this.logger.error(`Failed to clear state file: ${this.storePath}`, error);
100
+ throw error;
101
+ }
102
+ }
103
+ }
104
+ /**
105
+ * Checks if a persisted state file exists
106
+ */
107
+ async stateExists() {
108
+ try {
109
+ const stat = await fs.stat(this.storePath);
110
+ return stat.isFile();
111
+ }
112
+ catch {
113
+ return false;
114
+ }
115
+ }
116
+ }
117
+ //# sourceMappingURL=statePersistence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"statePersistence.js","sourceRoot":"","sources":["../../src/checkpointing/statePersistence.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,99 @@
1
+ import { BaseCheckpointSaver } from '@langchain/langgraph';
2
+ import { Logger } from '../logging/logger.js';
3
+ import { FileSystemOperations } from '../common/fileSystem.js';
4
+ /**
5
+ * Workflow execution environment
6
+ */
7
+ export type WorkflowEnvironment = 'production' | 'test';
8
+ /**
9
+ * Configuration for WorkflowStateManager
10
+ */
11
+ export interface WorkflowStateManagerConfig {
12
+ /**
13
+ * Execution environment - determines checkpointing strategy
14
+ * - 'production': Uses JsonCheckpointSaver with .magen/ directory persistence
15
+ * - 'test': Uses MemorySaver for isolated, in-memory state (no file I/O)
16
+ * Default: 'production'
17
+ */
18
+ environment?: WorkflowEnvironment;
19
+ /**
20
+ * Optional project path for well-known directory (.magen/)
21
+ * Used to customize the location of workflow state files
22
+ * Defaults to os.homedir() if not specified
23
+ */
24
+ projectPath?: string;
25
+ /**
26
+ * Optional filesystem operations implementation for dependency injection
27
+ * Defaults to NodeFileSystemOperations for production use
28
+ */
29
+ fileSystemOperations?: FileSystemOperations;
30
+ /**
31
+ * Optional logger for state management operations
32
+ */
33
+ logger?: Logger;
34
+ }
35
+ /**
36
+ * Manages workflow state persistence and checkpointer lifecycle
37
+ *
38
+ * This service encapsulates all workflow state management responsibilities:
39
+ * - Creating and configuring checkpointers (MemorySaver vs JsonCheckpointSaver)
40
+ * - Loading existing state from disk
41
+ * - Saving state to disk
42
+ * - Managing well-known directory paths
43
+ *
44
+ * This separation allows the orchestrator to focus on workflow execution logic
45
+ * while delegating all state management concerns to this service.
46
+ */
47
+ export declare class WorkflowStateManager {
48
+ private readonly logger;
49
+ private readonly environment;
50
+ private readonly wellKnownDirectoryManager;
51
+ private readonly fileSystemOperations;
52
+ constructor(config?: WorkflowStateManagerConfig);
53
+ /**
54
+ * Creates a checkpointer configured for the current environment
55
+ *
56
+ * For 'test' environment:
57
+ * - Returns MemorySaver (in-memory, no file I/O)
58
+ *
59
+ * For 'production' environment:
60
+ * - Returns JsonCheckpointSaver
61
+ * - Automatically loads existing state from disk if available
62
+ * - Creates fresh checkpointer if no state exists
63
+ *
64
+ * @returns A configured checkpointer ready for use
65
+ */
66
+ createCheckpointer(): Promise<BaseCheckpointSaver>;
67
+ /**
68
+ * Saves checkpointer state to disk (production mode only)
69
+ *
70
+ * Only applies to JsonCheckpointSaver. MemorySaver (used in test mode)
71
+ * intentionally does not persist state.
72
+ *
73
+ * @param checkpointer - The checkpointer to save
74
+ * @throws Error if test environment unexpectedly has JsonCheckpointSaver
75
+ */
76
+ saveCheckpointerState(checkpointer: BaseCheckpointSaver): Promise<void>;
77
+ /**
78
+ * Reads the serialized state from disk if it exists
79
+ * @returns The serialized state string, or undefined if the file doesn't exist or is invalid
80
+ */
81
+ private readState;
82
+ /**
83
+ * Writes the serialized state to disk
84
+ * @param serializedState The state to persist
85
+ */
86
+ private writeState;
87
+ /**
88
+ * Deletes the persisted state file
89
+ */
90
+ clearState(): Promise<void>;
91
+ /**
92
+ * Checks if a persisted state file exists
93
+ */
94
+ stateExists(): Promise<boolean>;
95
+ /**
96
+ * Gets the file path for workflow state storage
97
+ */
98
+ private getStatePath;
99
+ }