groundswell 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/.claude/settings.local.json +9 -0
- package/.claude/system_prompts/task-breakdown.md +100 -0
- package/PRPs/001-hierarchical-workflow-engine.md +2438 -0
- package/PRPs/PRDs/001-hierarchical-workflow-engine.md +543 -0
- package/PRPs/PRDs/002-agent-prompt.md +390 -0
- package/PRPs/PRDs/003-agent-prompt.md +943 -0
- package/PRPs/PRDs/004-agent-prompt.md +1136 -0
- package/PRPs/PRDs/tasks-001.json +492 -0
- package/PRPs/README.md +83 -0
- package/PRPs/templates/prp_base.md +222 -0
- package/README.md +218 -0
- package/docs/agent.md +422 -0
- package/docs/prompt.md +419 -0
- package/docs/workflow.md +600 -0
- package/examples/README.md +244 -0
- package/examples/examples/01-basic-workflow.ts +100 -0
- package/examples/examples/02-decorator-options.ts +217 -0
- package/examples/examples/03-parent-child.ts +241 -0
- package/examples/examples/04-observers-debugger.ts +340 -0
- package/examples/examples/05-error-handling.ts +387 -0
- package/examples/examples/06-concurrent-tasks.ts +352 -0
- package/examples/examples/07-agent-loops.ts +432 -0
- package/examples/examples/08-sdk-features.ts +667 -0
- package/examples/examples/09-reflection.ts +573 -0
- package/examples/examples/10-introspection.ts +550 -0
- package/examples/index.ts +143 -0
- package/examples/utils/helpers.ts +57 -0
- package/llms_full.txt +5890 -0
- package/package.json +63 -0
- package/plan/P1P2/PRP.md +527 -0
- package/plan/P1P2/research/LRU_CACHE_BEST_PRACTICES.md +1929 -0
- package/plan/P1P2/research/LRU_CACHE_CODE_PATTERNS.md +857 -0
- package/plan/P1P2/research/LRU_CACHE_INTEGRATION_GUIDE.md +738 -0
- package/plan/P1P2/research/LRU_CACHE_RESEARCH_INDEX.md +424 -0
- package/plan/P1P2/research/REFLECTION_INDEX.md +291 -0
- package/plan/P1P2/research/REFLECTION_RESEARCH_REPORT.md +1342 -0
- package/plan/P1P2/research/RESEARCH_SUMMARY.md +342 -0
- package/plan/P1P2/research/anthropic-sdk.md +174 -0
- package/plan/P1P2/research/async-local-storage.md +200 -0
- package/plan/P1P2/research/reflection-code-patterns.md +1205 -0
- package/plan/P1P2/research/reflection-decision-matrix.md +421 -0
- package/plan/P1P2/research/reflection-implementation-guide.md +1341 -0
- package/plan/P1P2/research/reflection-integration-guide.md +834 -0
- package/plan/P1P2/research/reflection-patterns.md +1468 -0
- package/plan/P1P2/research/reflection-quick-reference.md +558 -0
- package/plan/P1P2/research/zod-schema.md +152 -0
- package/plan/P3P4/PRP.md +1388 -0
- package/plan/P3P4/research/caching-lru.md +116 -0
- package/plan/P3P4/research/introspection-tools.md +177 -0
- package/plan/P3P4/research/reflection-patterns.md +117 -0
- package/plan/P4P5/PRP.md +1136 -0
- package/plan/P4P5/research/RESEARCH_SUMMARY.md +151 -0
- package/plan/architecture/external_deps.md +358 -0
- package/plan/architecture/system_context.md +242 -0
- package/plan/backlog.json +867 -0
- package/plan/research/INTROSPECTION_RESEARCH_SUMMARY.md +378 -0
- package/plan/research/README-INTROSPECTION.md +352 -0
- package/plan/research/agent-introspection-patterns.md +1085 -0
- package/plan/research/introspection-security-guide.md +928 -0
- package/plan/research/introspection-tool-examples.md +875 -0
- package/scripts/generate-llms-full.ts +206 -0
- package/src/__tests__/integration/agent-workflow.test.ts +256 -0
- package/src/__tests__/integration/tree-mirroring.test.ts +114 -0
- package/src/__tests__/unit/agent.test.ts +169 -0
- package/src/__tests__/unit/cache-key.test.ts +182 -0
- package/src/__tests__/unit/cache.test.ts +172 -0
- package/src/__tests__/unit/context.test.ts +138 -0
- package/src/__tests__/unit/decorators.test.ts +100 -0
- package/src/__tests__/unit/introspection-tools.test.ts +277 -0
- package/src/__tests__/unit/prompt.test.ts +135 -0
- package/src/__tests__/unit/reflection.test.ts +210 -0
- package/src/__tests__/unit/tree-debugger.test.ts +85 -0
- package/src/__tests__/unit/workflow.test.ts +81 -0
- package/src/cache/cache-key.ts +244 -0
- package/src/cache/cache.ts +236 -0
- package/src/cache/index.ts +8 -0
- package/src/core/agent.ts +573 -0
- package/src/core/context.ts +119 -0
- package/src/core/event-tree.ts +260 -0
- package/src/core/factory.ts +123 -0
- package/src/core/index.ts +17 -0
- package/src/core/logger.ts +87 -0
- package/src/core/mcp-handler.ts +184 -0
- package/src/core/prompt.ts +150 -0
- package/src/core/workflow-context.ts +349 -0
- package/src/core/workflow.ts +302 -0
- package/src/debugger/index.ts +1 -0
- package/src/debugger/tree-debugger.ts +210 -0
- package/src/decorators/index.ts +3 -0
- package/src/decorators/observed-state.ts +95 -0
- package/src/decorators/step.ts +139 -0
- package/src/decorators/task.ts +96 -0
- package/src/examples/index.ts +2 -0
- package/src/examples/tdd-orchestrator.ts +65 -0
- package/src/examples/test-cycle-workflow.ts +64 -0
- package/src/index.ts +140 -0
- package/src/reflection/index.ts +5 -0
- package/src/reflection/reflection.ts +407 -0
- package/src/tools/index.ts +36 -0
- package/src/tools/introspection.ts +464 -0
- package/src/types/agent.ts +90 -0
- package/src/types/decorators.ts +25 -0
- package/src/types/error-strategy.ts +13 -0
- package/src/types/error.ts +20 -0
- package/src/types/events.ts +74 -0
- package/src/types/index.ts +55 -0
- package/src/types/logging.ts +24 -0
- package/src/types/observer.ts +18 -0
- package/src/types/prompt.ts +40 -0
- package/src/types/reflection.ts +117 -0
- package/src/types/sdk-primitives.ts +128 -0
- package/src/types/snapshot.ts +14 -0
- package/src/types/workflow-context.ts +163 -0
- package/src/types/workflow.ts +37 -0
- package/src/utils/id.ts +11 -0
- package/src/utils/index.ts +3 -0
- package/src/utils/observable.ts +77 -0
- package/tasks.json +0 -0
- package/tsconfig.json +22 -0
- package/vitest.config.ts +16 -0
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EventTreeHandle - Queryable interface for the workflow event tree
|
|
3
|
+
*
|
|
4
|
+
* Provides methods for traversing and querying the event tree
|
|
5
|
+
* that mirrors workflow execution.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
EventTreeHandle,
|
|
10
|
+
EventNode,
|
|
11
|
+
EventMetrics,
|
|
12
|
+
} from '../types/workflow-context.js';
|
|
13
|
+
import type { WorkflowNode, WorkflowEvent } from '../types/index.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Implementation of EventTreeHandle
|
|
17
|
+
*/
|
|
18
|
+
export class EventTreeHandleImpl implements EventTreeHandle {
|
|
19
|
+
/** Root node of the tree */
|
|
20
|
+
public readonly root: EventNode;
|
|
21
|
+
|
|
22
|
+
/** Index of nodes by ID for fast lookup */
|
|
23
|
+
private nodeIndex: Map<string, EventNode> = new Map();
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Create a new EventTreeHandle from a workflow node
|
|
27
|
+
* @param workflowNode Root workflow node
|
|
28
|
+
*/
|
|
29
|
+
constructor(workflowNode: WorkflowNode) {
|
|
30
|
+
this.root = this.buildEventNode(workflowNode);
|
|
31
|
+
this.buildIndex(this.root);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Get a node by ID
|
|
36
|
+
* @param id Node ID to find
|
|
37
|
+
*/
|
|
38
|
+
public getNode(id: string): EventNode | undefined {
|
|
39
|
+
return this.nodeIndex.get(id);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Get all children of a node
|
|
44
|
+
* @param id Parent node ID
|
|
45
|
+
*/
|
|
46
|
+
public getChildren(id: string): EventNode[] {
|
|
47
|
+
const node = this.nodeIndex.get(id);
|
|
48
|
+
return node?.children ?? [];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Get all ancestors of a node (from node up to root, excluding the node itself)
|
|
53
|
+
* @param id Node ID
|
|
54
|
+
*/
|
|
55
|
+
public getAncestors(id: string): EventNode[] {
|
|
56
|
+
const ancestors: EventNode[] = [];
|
|
57
|
+
const node = this.nodeIndex.get(id);
|
|
58
|
+
|
|
59
|
+
if (!node || !node.parentId) {
|
|
60
|
+
return ancestors;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
let currentId = node.parentId;
|
|
64
|
+
while (currentId) {
|
|
65
|
+
const parent = this.nodeIndex.get(currentId);
|
|
66
|
+
if (!parent) break;
|
|
67
|
+
ancestors.push(parent);
|
|
68
|
+
currentId = parent.parentId ?? '';
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return ancestors;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Export tree as JSON
|
|
76
|
+
*/
|
|
77
|
+
public toJSON(): EventNode {
|
|
78
|
+
return this.cloneNode(this.root);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Rebuild the tree from an updated workflow node
|
|
83
|
+
* @param workflowNode Updated workflow node
|
|
84
|
+
*/
|
|
85
|
+
public rebuild(workflowNode: WorkflowNode): void {
|
|
86
|
+
const newRoot = this.buildEventNode(workflowNode);
|
|
87
|
+
(this.root as { children: EventNode[] }).children = newRoot.children;
|
|
88
|
+
Object.assign(this.root, {
|
|
89
|
+
type: newRoot.type,
|
|
90
|
+
name: newRoot.name,
|
|
91
|
+
timestamp: newRoot.timestamp,
|
|
92
|
+
metrics: newRoot.metrics,
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
this.nodeIndex.clear();
|
|
96
|
+
this.buildIndex(this.root);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Build an EventNode from a WorkflowNode
|
|
101
|
+
*/
|
|
102
|
+
private buildEventNode(
|
|
103
|
+
wfNode: WorkflowNode,
|
|
104
|
+
parentId?: string
|
|
105
|
+
): EventNode {
|
|
106
|
+
const eventNode: EventNode = {
|
|
107
|
+
id: wfNode.id,
|
|
108
|
+
type: 'workflow',
|
|
109
|
+
timestamp: Date.now(),
|
|
110
|
+
name: wfNode.name,
|
|
111
|
+
parentId,
|
|
112
|
+
children: [],
|
|
113
|
+
metrics: this.extractMetrics(wfNode),
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
// Add event nodes from workflow events
|
|
117
|
+
for (const event of wfNode.events) {
|
|
118
|
+
const childNode = this.eventToNode(event, wfNode.id);
|
|
119
|
+
if (childNode) {
|
|
120
|
+
eventNode.children.push(childNode);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Add child workflow nodes
|
|
125
|
+
for (const child of wfNode.children) {
|
|
126
|
+
eventNode.children.push(this.buildEventNode(child, wfNode.id));
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return eventNode;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Convert a WorkflowEvent to an EventNode
|
|
134
|
+
*/
|
|
135
|
+
private eventToNode(
|
|
136
|
+
event: WorkflowEvent,
|
|
137
|
+
parentId: string
|
|
138
|
+
): EventNode | null {
|
|
139
|
+
const baseNode: Omit<EventNode, 'type' | 'payload'> = {
|
|
140
|
+
id: `${parentId}-${event.type}-${Date.now()}`,
|
|
141
|
+
timestamp: Date.now(),
|
|
142
|
+
parentId,
|
|
143
|
+
children: [],
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
switch (event.type) {
|
|
147
|
+
case 'stepStart':
|
|
148
|
+
return {
|
|
149
|
+
...baseNode,
|
|
150
|
+
type: 'step',
|
|
151
|
+
name: event.step,
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
case 'stepEnd':
|
|
155
|
+
return {
|
|
156
|
+
...baseNode,
|
|
157
|
+
type: 'stepComplete',
|
|
158
|
+
name: event.step,
|
|
159
|
+
metrics: { duration: event.duration },
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
case 'agentPromptStart':
|
|
163
|
+
return {
|
|
164
|
+
...baseNode,
|
|
165
|
+
id: `${event.agentId}-prompt-${event.promptId}`,
|
|
166
|
+
type: 'agentPrompt',
|
|
167
|
+
name: event.agentName,
|
|
168
|
+
payload: { promptId: event.promptId },
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
case 'agentPromptEnd':
|
|
172
|
+
return {
|
|
173
|
+
...baseNode,
|
|
174
|
+
id: `${event.agentId}-promptEnd-${event.promptId}`,
|
|
175
|
+
type: 'agentPromptComplete',
|
|
176
|
+
name: event.agentName,
|
|
177
|
+
metrics: {
|
|
178
|
+
duration: event.duration,
|
|
179
|
+
tokenUsage: event.tokenUsage
|
|
180
|
+
? {
|
|
181
|
+
input: event.tokenUsage.input_tokens,
|
|
182
|
+
output: event.tokenUsage.output_tokens,
|
|
183
|
+
}
|
|
184
|
+
: undefined,
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
case 'toolInvocation':
|
|
189
|
+
return {
|
|
190
|
+
...baseNode,
|
|
191
|
+
type: 'tool',
|
|
192
|
+
name: event.toolName,
|
|
193
|
+
payload: { input: event.input, output: event.output },
|
|
194
|
+
metrics: { duration: event.duration },
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
case 'error':
|
|
198
|
+
return {
|
|
199
|
+
...baseNode,
|
|
200
|
+
type: 'error',
|
|
201
|
+
name: event.error.message,
|
|
202
|
+
payload: event.error,
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
default:
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Extract metrics from a workflow node
|
|
212
|
+
*/
|
|
213
|
+
private extractMetrics(wfNode: WorkflowNode): EventMetrics | undefined {
|
|
214
|
+
// Calculate total duration from step events
|
|
215
|
+
const stepEndEvents = wfNode.events.filter(
|
|
216
|
+
(e) => e.type === 'stepEnd'
|
|
217
|
+
) as Array<{ type: 'stepEnd'; duration: number }>;
|
|
218
|
+
|
|
219
|
+
if (stepEndEvents.length === 0) {
|
|
220
|
+
return undefined;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const totalDuration = stepEndEvents.reduce(
|
|
224
|
+
(sum, e) => sum + e.duration,
|
|
225
|
+
0
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
return { duration: totalDuration };
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Build index of all nodes
|
|
233
|
+
*/
|
|
234
|
+
private buildIndex(node: EventNode): void {
|
|
235
|
+
this.nodeIndex.set(node.id, node);
|
|
236
|
+
for (const child of node.children) {
|
|
237
|
+
this.buildIndex(child);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Deep clone a node for JSON export
|
|
243
|
+
*/
|
|
244
|
+
private cloneNode(node: EventNode): EventNode {
|
|
245
|
+
return {
|
|
246
|
+
...node,
|
|
247
|
+
children: node.children.map((c) => this.cloneNode(c)),
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Create an EventTreeHandle from a workflow node
|
|
254
|
+
* @param workflowNode Root workflow node
|
|
255
|
+
*/
|
|
256
|
+
export function createEventTreeHandle(
|
|
257
|
+
workflowNode: WorkflowNode
|
|
258
|
+
): EventTreeHandle {
|
|
259
|
+
return new EventTreeHandleImpl(workflowNode);
|
|
260
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Factory Functions - Convenience functions for dynamic workflow, agent, and prompt creation
|
|
3
|
+
*
|
|
4
|
+
* These functions provide a simple API for creating workflow components dynamically
|
|
5
|
+
* at runtime, useful for metaprogramming and agent-driven workflow construction.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { Workflow, type WorkflowExecutor } from './workflow.js';
|
|
9
|
+
import { Agent } from './agent.js';
|
|
10
|
+
import { Prompt } from './prompt.js';
|
|
11
|
+
import type { WorkflowConfig } from '../types/workflow-context.js';
|
|
12
|
+
import type { AgentConfig, PromptConfig } from '../types/index.js';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Create a new Workflow instance
|
|
16
|
+
*
|
|
17
|
+
* @param config Workflow configuration
|
|
18
|
+
* @param executor Executor function that receives WorkflowContext
|
|
19
|
+
* @returns Configured Workflow instance
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* const workflow = createWorkflow(
|
|
24
|
+
* { name: 'DataPipeline', enableReflection: true },
|
|
25
|
+
* async (ctx) => {
|
|
26
|
+
* const data = await ctx.step('fetch', () => fetchData());
|
|
27
|
+
* const processed = await ctx.step('process', () => processData(data));
|
|
28
|
+
* return processed;
|
|
29
|
+
* }
|
|
30
|
+
* );
|
|
31
|
+
*
|
|
32
|
+
* const result = await workflow.run();
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export function createWorkflow<T>(
|
|
36
|
+
config: WorkflowConfig,
|
|
37
|
+
executor: WorkflowExecutor<T>
|
|
38
|
+
): Workflow<T> {
|
|
39
|
+
return new Workflow(config, executor);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Create a new Agent instance
|
|
44
|
+
*
|
|
45
|
+
* @param config Agent configuration
|
|
46
|
+
* @returns Configured Agent instance
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```ts
|
|
50
|
+
* const agent = createAgent({
|
|
51
|
+
* name: 'DataAnalyst',
|
|
52
|
+
* system: 'You analyze data and provide insights.',
|
|
53
|
+
* enableCache: true,
|
|
54
|
+
* enableReflection: true,
|
|
55
|
+
* });
|
|
56
|
+
*
|
|
57
|
+
* const result = await agent.prompt(analysisPrompt);
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
export function createAgent(config: AgentConfig): Agent {
|
|
61
|
+
return new Agent(config);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Create a new Prompt instance
|
|
66
|
+
*
|
|
67
|
+
* @param config Prompt configuration
|
|
68
|
+
* @returns Configured Prompt instance
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```ts
|
|
72
|
+
* const prompt = createPrompt({
|
|
73
|
+
* user: 'Analyze this data',
|
|
74
|
+
* data: { values: [1, 2, 3, 4, 5] },
|
|
75
|
+
* responseFormat: z.object({
|
|
76
|
+
* summary: z.string(),
|
|
77
|
+
* insights: z.array(z.string()),
|
|
78
|
+
* }),
|
|
79
|
+
* });
|
|
80
|
+
*
|
|
81
|
+
* const result = await agent.prompt(prompt);
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
export function createPrompt<T>(config: PromptConfig<T>): Prompt<T> {
|
|
85
|
+
return new Prompt(config);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Create a workflow with an inline executor (shorthand)
|
|
90
|
+
*
|
|
91
|
+
* @param name Workflow name
|
|
92
|
+
* @param executor Executor function
|
|
93
|
+
* @returns Configured Workflow instance
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```ts
|
|
97
|
+
* const workflow = quickWorkflow('ProcessData', async (ctx) => {
|
|
98
|
+
* return await ctx.step('process', () => processData());
|
|
99
|
+
* });
|
|
100
|
+
* ```
|
|
101
|
+
*/
|
|
102
|
+
export function quickWorkflow<T>(
|
|
103
|
+
name: string,
|
|
104
|
+
executor: WorkflowExecutor<T>
|
|
105
|
+
): Workflow<T> {
|
|
106
|
+
return new Workflow({ name }, executor);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Create a simple agent with minimal configuration (shorthand)
|
|
111
|
+
*
|
|
112
|
+
* @param name Agent name
|
|
113
|
+
* @param system System prompt
|
|
114
|
+
* @returns Configured Agent instance
|
|
115
|
+
*
|
|
116
|
+
* @example
|
|
117
|
+
* ```ts
|
|
118
|
+
* const agent = quickAgent('Helper', 'You are a helpful assistant.');
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
121
|
+
export function quickAgent(name: string, system?: string): Agent {
|
|
122
|
+
return new Agent({ name, system });
|
|
123
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export { WorkflowLogger } from './logger.js';
|
|
2
|
+
export { Workflow, type WorkflowExecutor } from './workflow.js';
|
|
3
|
+
export { Agent, type PromptResult } from './agent.js';
|
|
4
|
+
export { Prompt } from './prompt.js';
|
|
5
|
+
export { MCPHandler, type ToolExecutor } from './mcp-handler.js';
|
|
6
|
+
export { EventTreeHandleImpl, createEventTreeHandle } from './event-tree.js';
|
|
7
|
+
export { WorkflowContextImpl, createWorkflowContext } from './workflow-context.js';
|
|
8
|
+
export {
|
|
9
|
+
getExecutionContext,
|
|
10
|
+
requireExecutionContext,
|
|
11
|
+
runInContext,
|
|
12
|
+
runInContextSync,
|
|
13
|
+
hasExecutionContext,
|
|
14
|
+
createChildContext,
|
|
15
|
+
agentExecutionStorage,
|
|
16
|
+
type AgentExecutionContext,
|
|
17
|
+
} from './context.js';
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { WorkflowNode, LogEntry, LogLevel, WorkflowObserver } from '../types/index.js';
|
|
2
|
+
import { generateId } from '../utils/id.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Logger that emits log entries to workflow node and observers
|
|
6
|
+
*/
|
|
7
|
+
export class WorkflowLogger {
|
|
8
|
+
private readonly parentLogId?: string;
|
|
9
|
+
|
|
10
|
+
constructor(
|
|
11
|
+
private readonly node: WorkflowNode,
|
|
12
|
+
private readonly observers: WorkflowObserver[],
|
|
13
|
+
parentLogId?: string
|
|
14
|
+
) {
|
|
15
|
+
this.parentLogId = parentLogId;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Emit a log entry to the node and all observers
|
|
20
|
+
*/
|
|
21
|
+
private emit(entry: LogEntry): void {
|
|
22
|
+
this.node.logs.push(entry);
|
|
23
|
+
for (const obs of this.observers) {
|
|
24
|
+
try {
|
|
25
|
+
obs.onLog(entry);
|
|
26
|
+
} catch (err) {
|
|
27
|
+
console.error('Observer onLog error:', err);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Create a log entry with the given level
|
|
34
|
+
*/
|
|
35
|
+
private log(level: LogLevel, message: string, data?: unknown): void {
|
|
36
|
+
const entry: LogEntry = {
|
|
37
|
+
id: generateId(),
|
|
38
|
+
workflowId: this.node.id,
|
|
39
|
+
timestamp: Date.now(),
|
|
40
|
+
level,
|
|
41
|
+
message,
|
|
42
|
+
data,
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// Add parent log ID if this is a child logger
|
|
46
|
+
if (this.parentLogId) {
|
|
47
|
+
entry.parentLogId = this.parentLogId;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
this.emit(entry);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Log a debug message
|
|
55
|
+
*/
|
|
56
|
+
debug(message: string, data?: unknown): void {
|
|
57
|
+
this.log('debug', message, data);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Log an info message
|
|
62
|
+
*/
|
|
63
|
+
info(message: string, data?: unknown): void {
|
|
64
|
+
this.log('info', message, data);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Log a warning message
|
|
69
|
+
*/
|
|
70
|
+
warn(message: string, data?: unknown): void {
|
|
71
|
+
this.log('warn', message, data);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Log an error message
|
|
76
|
+
*/
|
|
77
|
+
error(message: string, data?: unknown): void {
|
|
78
|
+
this.log('error', message, data);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Create a child logger that includes parentLogId
|
|
83
|
+
*/
|
|
84
|
+
child(parentLogId: string): WorkflowLogger {
|
|
85
|
+
return new WorkflowLogger(this.node, this.observers, parentLogId);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP Handler - Manages MCP server connections and tool execution
|
|
3
|
+
*
|
|
4
|
+
* Provides integration with MCP (Model Context Protocol) servers,
|
|
5
|
+
* converting MCP tools to Anthropic Tool format.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { MCPServer, Tool, ToolResult } from '../types/index.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Tool executor function type
|
|
12
|
+
*/
|
|
13
|
+
export type ToolExecutor = (input: unknown) => Promise<unknown>;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Registered tool with its executor
|
|
17
|
+
*/
|
|
18
|
+
interface RegisteredTool {
|
|
19
|
+
tool: Tool;
|
|
20
|
+
executor: ToolExecutor;
|
|
21
|
+
serverName: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* MCPHandler - Manages MCP server registration and tool execution
|
|
26
|
+
*
|
|
27
|
+
* Currently supports:
|
|
28
|
+
* - inprocess transport: Direct tool registration
|
|
29
|
+
* - stdio transport: Documented for future implementation
|
|
30
|
+
*/
|
|
31
|
+
export class MCPHandler {
|
|
32
|
+
/** Registered MCP servers */
|
|
33
|
+
private servers: Map<string, MCPServer> = new Map();
|
|
34
|
+
|
|
35
|
+
/** Registered tools from all servers */
|
|
36
|
+
private registeredTools: Map<string, RegisteredTool> = new Map();
|
|
37
|
+
|
|
38
|
+
/** Custom tool executors for inprocess servers */
|
|
39
|
+
private toolExecutors: Map<string, ToolExecutor> = new Map();
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Register an MCP server
|
|
43
|
+
* @param server MCP server configuration
|
|
44
|
+
*/
|
|
45
|
+
public registerServer(server: MCPServer): void {
|
|
46
|
+
if (this.servers.has(server.name)) {
|
|
47
|
+
throw new Error(`MCP server '${server.name}' is already registered`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
this.servers.set(server.name, server);
|
|
51
|
+
|
|
52
|
+
// Register tools from the server
|
|
53
|
+
if (server.tools) {
|
|
54
|
+
for (const tool of server.tools) {
|
|
55
|
+
const fullName = `${server.name}__${tool.name}`;
|
|
56
|
+
this.registeredTools.set(fullName, {
|
|
57
|
+
tool: { ...tool, name: fullName },
|
|
58
|
+
executor: this.createToolExecutor(server, tool),
|
|
59
|
+
serverName: server.name,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Unregister an MCP server
|
|
67
|
+
* @param name Server name to unregister
|
|
68
|
+
*/
|
|
69
|
+
public unregisterServer(name: string): void {
|
|
70
|
+
const server = this.servers.get(name);
|
|
71
|
+
if (server?.tools) {
|
|
72
|
+
for (const tool of server.tools) {
|
|
73
|
+
const fullName = `${name}__${tool.name}`;
|
|
74
|
+
this.registeredTools.delete(fullName);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
this.servers.delete(name);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Register a custom tool executor for an inprocess tool
|
|
82
|
+
* @param serverName Server name
|
|
83
|
+
* @param toolName Tool name
|
|
84
|
+
* @param executor Executor function
|
|
85
|
+
*/
|
|
86
|
+
public registerToolExecutor(
|
|
87
|
+
serverName: string,
|
|
88
|
+
toolName: string,
|
|
89
|
+
executor: ToolExecutor
|
|
90
|
+
): void {
|
|
91
|
+
const fullName = `${serverName}__${toolName}`;
|
|
92
|
+
this.toolExecutors.set(fullName, executor);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Get all tools from registered servers in Anthropic format
|
|
97
|
+
* @returns Array of Tool definitions
|
|
98
|
+
*/
|
|
99
|
+
public getTools(): Tool[] {
|
|
100
|
+
return Array.from(this.registeredTools.values()).map((rt) => rt.tool);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Execute a tool by name
|
|
105
|
+
* @param toolName Full tool name (serverName__toolName)
|
|
106
|
+
* @param input Tool input
|
|
107
|
+
* @returns Tool result
|
|
108
|
+
*/
|
|
109
|
+
public async executeTool(
|
|
110
|
+
toolName: string,
|
|
111
|
+
input: unknown
|
|
112
|
+
): Promise<ToolResult> {
|
|
113
|
+
const registered = this.registeredTools.get(toolName);
|
|
114
|
+
if (!registered) {
|
|
115
|
+
return {
|
|
116
|
+
type: 'tool_result',
|
|
117
|
+
tool_use_id: '',
|
|
118
|
+
content: `Tool '${toolName}' not found`,
|
|
119
|
+
is_error: true,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
const result = await registered.executor(input);
|
|
125
|
+
return {
|
|
126
|
+
type: 'tool_result',
|
|
127
|
+
tool_use_id: '',
|
|
128
|
+
content: typeof result === 'string' ? result : JSON.stringify(result),
|
|
129
|
+
};
|
|
130
|
+
} catch (error) {
|
|
131
|
+
const message = error instanceof Error ? error.message : 'Unknown error';
|
|
132
|
+
return {
|
|
133
|
+
type: 'tool_result',
|
|
134
|
+
tool_use_id: '',
|
|
135
|
+
content: `Tool execution failed: ${message}`,
|
|
136
|
+
is_error: true,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Check if a tool is registered
|
|
143
|
+
* @param toolName Tool name to check
|
|
144
|
+
*/
|
|
145
|
+
public hasTool(toolName: string): boolean {
|
|
146
|
+
return this.registeredTools.has(toolName);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Get registered server names
|
|
151
|
+
*/
|
|
152
|
+
public getServerNames(): string[] {
|
|
153
|
+
return Array.from(this.servers.keys());
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Create a tool executor based on transport type
|
|
158
|
+
*/
|
|
159
|
+
private createToolExecutor(server: MCPServer, tool: Tool): ToolExecutor {
|
|
160
|
+
const fullName = `${server.name}__${tool.name}`;
|
|
161
|
+
|
|
162
|
+
if (server.transport === 'inprocess') {
|
|
163
|
+
// For inprocess, look for registered executor
|
|
164
|
+
return async (input: unknown) => {
|
|
165
|
+
const executor = this.toolExecutors.get(fullName);
|
|
166
|
+
if (!executor) {
|
|
167
|
+
throw new Error(
|
|
168
|
+
`No executor registered for inprocess tool '${fullName}'. ` +
|
|
169
|
+
`Use registerToolExecutor() to provide an executor.`
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
return executor(input);
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// stdio transport - placeholder for future implementation
|
|
177
|
+
return async (_input: unknown) => {
|
|
178
|
+
throw new Error(
|
|
179
|
+
`stdio transport for MCP server '${server.name}' is not yet implemented. ` +
|
|
180
|
+
`Use inprocess transport or register a custom executor.`
|
|
181
|
+
);
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
}
|