@flomatai/core 0.1.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/dist/agent.d.ts +92 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +137 -0
- package/dist/agent.js.map +1 -0
- package/dist/cli-utils.d.ts +41 -0
- package/dist/cli-utils.d.ts.map +1 -0
- package/dist/cli-utils.js +64 -0
- package/dist/cli-utils.js.map +1 -0
- package/dist/errors.d.ts +52 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +105 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -0
- package/dist/llm-provider.d.ts +29 -0
- package/dist/llm-provider.d.ts.map +1 -0
- package/dist/llm-provider.js +44 -0
- package/dist/llm-provider.js.map +1 -0
- package/dist/logger.d.ts +32 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +75 -0
- package/dist/logger.js.map +1 -0
- package/dist/mock-llm.d.ts +70 -0
- package/dist/mock-llm.d.ts.map +1 -0
- package/dist/mock-llm.js +385 -0
- package/dist/mock-llm.js.map +1 -0
- package/dist/orchestrator-helpers.d.ts +20 -0
- package/dist/orchestrator-helpers.d.ts.map +1 -0
- package/dist/orchestrator-helpers.js +38 -0
- package/dist/orchestrator-helpers.js.map +1 -0
- package/dist/orchestrator.d.ts +124 -0
- package/dist/orchestrator.d.ts.map +1 -0
- package/dist/orchestrator.js +349 -0
- package/dist/orchestrator.js.map +1 -0
- package/dist/pipeline-registry.d.ts +120 -0
- package/dist/pipeline-registry.d.ts.map +1 -0
- package/dist/pipeline-registry.js +171 -0
- package/dist/pipeline-registry.js.map +1 -0
- package/dist/pipeline.d.ts +122 -0
- package/dist/pipeline.d.ts.map +1 -0
- package/dist/pipeline.js +152 -0
- package/dist/pipeline.js.map +1 -0
- package/dist/skill.d.ts +112 -0
- package/dist/skill.d.ts.map +1 -0
- package/dist/skill.js +12 -0
- package/dist/skill.js.map +1 -0
- package/dist/skills/io-skill.d.ts +49 -0
- package/dist/skills/io-skill.d.ts.map +1 -0
- package/dist/skills/io-skill.js +103 -0
- package/dist/skills/io-skill.js.map +1 -0
- package/dist/skills/llm-skill.d.ts +64 -0
- package/dist/skills/llm-skill.d.ts.map +1 -0
- package/dist/skills/llm-skill.js +112 -0
- package/dist/skills/llm-skill.js.map +1 -0
- package/dist/skills/transform-skill.d.ts +27 -0
- package/dist/skills/transform-skill.d.ts.map +1 -0
- package/dist/skills/transform-skill.js +32 -0
- package/dist/skills/transform-skill.js.map +1 -0
- package/dist/state/file-store.d.ts +25 -0
- package/dist/state/file-store.d.ts.map +1 -0
- package/dist/state/file-store.js +92 -0
- package/dist/state/file-store.js.map +1 -0
- package/dist/state/memory-store.d.ts +24 -0
- package/dist/state/memory-store.d.ts.map +1 -0
- package/dist/state/memory-store.js +65 -0
- package/dist/state/memory-store.js.map +1 -0
- package/dist/state/types.d.ts +40 -0
- package/dist/state/types.d.ts.map +1 -0
- package/dist/state/types.js +8 -0
- package/dist/state/types.js.map +1 -0
- package/dist/strategies/custom.d.ts +12 -0
- package/dist/strategies/custom.d.ts.map +1 -0
- package/dist/strategies/custom.js +14 -0
- package/dist/strategies/custom.js.map +1 -0
- package/dist/strategies/plan-and-execute.d.ts +27 -0
- package/dist/strategies/plan-and-execute.d.ts.map +1 -0
- package/dist/strategies/plan-and-execute.js +195 -0
- package/dist/strategies/plan-and-execute.js.map +1 -0
- package/dist/strategies/react.d.ts +27 -0
- package/dist/strategies/react.d.ts.map +1 -0
- package/dist/strategies/react.js +172 -0
- package/dist/strategies/react.js.map +1 -0
- package/dist/strategies/router.d.ts +11 -0
- package/dist/strategies/router.d.ts.map +1 -0
- package/dist/strategies/router.js +70 -0
- package/dist/strategies/router.js.map +1 -0
- package/dist/strategies/sequential.d.ts +12 -0
- package/dist/strategies/sequential.d.ts.map +1 -0
- package/dist/strategies/sequential.js +39 -0
- package/dist/strategies/sequential.js.map +1 -0
- package/dist/strategies/types.d.ts +62 -0
- package/dist/strategies/types.d.ts.map +1 -0
- package/dist/strategies/types.js +5 -0
- package/dist/strategies/types.js.map +1 -0
- package/dist/types.d.ts +83 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/package.json +28 -0
- package/src/agent.ts +243 -0
- package/src/cli-utils.ts +73 -0
- package/src/errors.ts +146 -0
- package/src/index.ts +124 -0
- package/src/llm-provider.ts +88 -0
- package/src/logger.ts +97 -0
- package/src/mock-llm.ts +433 -0
- package/src/orchestrator-helpers.ts +40 -0
- package/src/orchestrator.ts +522 -0
- package/src/pipeline-registry.ts +253 -0
- package/src/pipeline.ts +265 -0
- package/src/skill.ts +127 -0
- package/src/skills/io-skill.ts +133 -0
- package/src/skills/llm-skill.ts +207 -0
- package/src/skills/transform-skill.ts +61 -0
- package/src/state/file-store.ts +119 -0
- package/src/state/memory-store.ts +82 -0
- package/src/state/types.ts +53 -0
- package/src/strategies/custom.ts +24 -0
- package/src/strategies/plan-and-execute.ts +268 -0
- package/src/strategies/react.ts +239 -0
- package/src/strategies/router.ts +101 -0
- package/src/strategies/sequential.ts +55 -0
- package/src/strategies/types.ts +97 -0
- package/src/types.ts +102 -0
- package/tsconfig.json +9 -0
package/src/agent.ts
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent — an intelligent orchestrator that composes Skills via a Strategy.
|
|
3
|
+
*
|
|
4
|
+
* Agents can spawn sub-agents (hierarchical up to configurable depth).
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { Skill, SkillContext } from './skill.js';
|
|
8
|
+
import type { LLMRegistry } from './llm-provider.js';
|
|
9
|
+
import type { StateStore } from './state/types.js';
|
|
10
|
+
import type { Logger } from './logger.js';
|
|
11
|
+
import { SequentialStrategy } from './strategies/sequential.js';
|
|
12
|
+
import { RouterStrategy } from './strategies/router.js';
|
|
13
|
+
import { PlanAndExecuteStrategy, type PlanAndExecuteOptions } from './strategies/plan-and-execute.js';
|
|
14
|
+
import { ReActStrategy, type ReActOptions } from './strategies/react.js';
|
|
15
|
+
import { CustomStrategy } from './strategies/custom.js';
|
|
16
|
+
import type {
|
|
17
|
+
Strategy,
|
|
18
|
+
StrategyName,
|
|
19
|
+
AgentResult,
|
|
20
|
+
AgentContext,
|
|
21
|
+
AgentRunner,
|
|
22
|
+
SpawnAgentConfig,
|
|
23
|
+
CustomStrategyFn,
|
|
24
|
+
} from './strategies/types.js';
|
|
25
|
+
import { AgentMaxDepthError } from './errors.js';
|
|
26
|
+
import { resolveLLM } from './llm-provider.js';
|
|
27
|
+
|
|
28
|
+
// ── Agent Config ──────────────────────────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
export interface AgentConfig {
|
|
31
|
+
/** Unique name for this agent. */
|
|
32
|
+
name: string;
|
|
33
|
+
/**
|
|
34
|
+
* The agent's role/persona, used as the system prompt for decision-making.
|
|
35
|
+
* Be specific about what this agent does and what constraints it follows.
|
|
36
|
+
*/
|
|
37
|
+
role: string;
|
|
38
|
+
/** Skills this agent can use. */
|
|
39
|
+
skills: Skill[];
|
|
40
|
+
/**
|
|
41
|
+
* Which strategy to use for skill orchestration.
|
|
42
|
+
* @default 'sequential'
|
|
43
|
+
*/
|
|
44
|
+
strategy?: StrategyName;
|
|
45
|
+
/**
|
|
46
|
+
* The LLM registry key to use for decision-making.
|
|
47
|
+
* @default 'default'
|
|
48
|
+
*/
|
|
49
|
+
llm?: string;
|
|
50
|
+
/**
|
|
51
|
+
* Max iterations for 'react' strategy.
|
|
52
|
+
* @default 10
|
|
53
|
+
*/
|
|
54
|
+
maxIterations?: number;
|
|
55
|
+
/**
|
|
56
|
+
* Options for 'plan-and-execute' strategy.
|
|
57
|
+
*/
|
|
58
|
+
planAndExecuteOptions?: PlanAndExecuteOptions;
|
|
59
|
+
/**
|
|
60
|
+
* Options for 'react' strategy.
|
|
61
|
+
*/
|
|
62
|
+
reactOptions?: ReActOptions;
|
|
63
|
+
/**
|
|
64
|
+
* Custom strategy function (used when strategy='custom').
|
|
65
|
+
*/
|
|
66
|
+
customStrategy?: CustomStrategyFn;
|
|
67
|
+
/**
|
|
68
|
+
* Maximum depth of sub-agent spawning (default: inherited from orchestrator).
|
|
69
|
+
*/
|
|
70
|
+
maxDepth?: number;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// ── Agent ─────────────────────────────────────────────────────────────────────
|
|
74
|
+
|
|
75
|
+
export class Agent implements AgentRunner {
|
|
76
|
+
readonly config: AgentConfig;
|
|
77
|
+
private readonly strategy: Strategy;
|
|
78
|
+
|
|
79
|
+
constructor(config: AgentConfig) {
|
|
80
|
+
this.config = config;
|
|
81
|
+
this.strategy = resolveStrategy(config);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async run(
|
|
85
|
+
input: unknown,
|
|
86
|
+
ctx: AgentContext,
|
|
87
|
+
): Promise<AgentResult> {
|
|
88
|
+
ctx.logger.info(`Agent "${this.config.name}" starting`);
|
|
89
|
+
const result = await this.strategy.execute(this.config.skills, input, ctx);
|
|
90
|
+
ctx.logger.info(
|
|
91
|
+
`Agent "${this.config.name}" completed in ${result.durationMs}ms (${result.tokensUsed} tokens)`,
|
|
92
|
+
);
|
|
93
|
+
return result;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Create an AgentContext for standalone execution (outside a pipeline). */
|
|
97
|
+
createContext(options: {
|
|
98
|
+
llmRegistry: LLMRegistry;
|
|
99
|
+
state: StateStore;
|
|
100
|
+
logger: Logger;
|
|
101
|
+
config?: Record<string, unknown>;
|
|
102
|
+
runId?: string;
|
|
103
|
+
abortController?: AbortController;
|
|
104
|
+
emit?: (event: string, data: unknown) => void;
|
|
105
|
+
depth?: number;
|
|
106
|
+
maxDepth?: number;
|
|
107
|
+
}): AgentContext {
|
|
108
|
+
return buildAgentContext({
|
|
109
|
+
agentName: this.config.name,
|
|
110
|
+
agentRole: this.config.role,
|
|
111
|
+
llmKey: this.config.llm ?? 'default',
|
|
112
|
+
llmRegistry: options.llmRegistry,
|
|
113
|
+
state: options.state,
|
|
114
|
+
logger: options.logger.child(this.config.name),
|
|
115
|
+
config: options.config ?? {},
|
|
116
|
+
runId: options.runId ?? `agent-${Date.now()}`,
|
|
117
|
+
abortSignal: options.abortController?.signal ?? new AbortController().signal,
|
|
118
|
+
emit: options.emit ?? (() => {}),
|
|
119
|
+
depth: options.depth ?? 0,
|
|
120
|
+
maxDepth: options.maxDepth ?? 5,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// ── Factory ───────────────────────────────────────────────────────────────────
|
|
126
|
+
|
|
127
|
+
export const createAgent = (config: AgentConfig): Agent => new Agent(config);
|
|
128
|
+
|
|
129
|
+
/** Ergonomic namespace: Agent.create(...) */
|
|
130
|
+
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
131
|
+
export namespace AgentFactory {
|
|
132
|
+
export const create = createAgent;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ── Internal: Context Builder ─────────────────────────────────────────────────
|
|
136
|
+
|
|
137
|
+
export function buildAgentContext(params: {
|
|
138
|
+
agentName: string;
|
|
139
|
+
agentRole: string;
|
|
140
|
+
llmKey: string;
|
|
141
|
+
llmRegistry: LLMRegistry;
|
|
142
|
+
state: StateStore;
|
|
143
|
+
logger: Logger;
|
|
144
|
+
config: Record<string, unknown>;
|
|
145
|
+
runId: string;
|
|
146
|
+
abortSignal: AbortSignal;
|
|
147
|
+
emit: (event: string, data: unknown) => void;
|
|
148
|
+
depth: number;
|
|
149
|
+
maxDepth: number;
|
|
150
|
+
}): AgentContext {
|
|
151
|
+
let totalTokens = 0;
|
|
152
|
+
|
|
153
|
+
const ctx: AgentContext = {
|
|
154
|
+
agentName: params.agentName,
|
|
155
|
+
agentRole: params.agentRole,
|
|
156
|
+
llm: resolveLLM(params.llmRegistry, params.llmKey),
|
|
157
|
+
getLLM: (key: string) => resolveLLM(params.llmRegistry, key),
|
|
158
|
+
logger: params.logger,
|
|
159
|
+
state: params.state,
|
|
160
|
+
config: params.config,
|
|
161
|
+
abortSignal: params.abortSignal,
|
|
162
|
+
runId: params.runId,
|
|
163
|
+
depth: params.depth,
|
|
164
|
+
maxDepth: params.maxDepth,
|
|
165
|
+
get totalTokens() { return totalTokens; },
|
|
166
|
+
addTokens(n: number) { totalTokens += n; },
|
|
167
|
+
emit: params.emit,
|
|
168
|
+
|
|
169
|
+
toSkillContext(): SkillContext {
|
|
170
|
+
return {
|
|
171
|
+
llm: ctx.llm,
|
|
172
|
+
logger: ctx.logger,
|
|
173
|
+
state: ctx.state,
|
|
174
|
+
emit: ctx.emit,
|
|
175
|
+
config: ctx.config,
|
|
176
|
+
abortSignal: ctx.abortSignal,
|
|
177
|
+
runId: ctx.runId,
|
|
178
|
+
getLLM: ctx.getLLM,
|
|
179
|
+
};
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
spawnAgent(spawnConfig: SpawnAgentConfig): AgentRunner {
|
|
183
|
+
if (params.depth >= params.maxDepth) {
|
|
184
|
+
throw new AgentMaxDepthError(spawnConfig.name, params.maxDepth);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const childAgent = new Agent({
|
|
188
|
+
name: spawnConfig.name,
|
|
189
|
+
role: spawnConfig.role,
|
|
190
|
+
skills: spawnConfig.skills,
|
|
191
|
+
strategy: spawnConfig.strategy,
|
|
192
|
+
llm: spawnConfig.llm ?? params.llmKey,
|
|
193
|
+
maxIterations: spawnConfig.maxIterations,
|
|
194
|
+
customStrategy: spawnConfig.customStrategy,
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
const childCtx = buildAgentContext({
|
|
198
|
+
...params,
|
|
199
|
+
agentName: spawnConfig.name,
|
|
200
|
+
agentRole: spawnConfig.role,
|
|
201
|
+
llmKey: spawnConfig.llm ?? params.llmKey,
|
|
202
|
+
logger: params.logger.child(spawnConfig.name),
|
|
203
|
+
depth: params.depth + 1,
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
params.emit('agent:spawn', {
|
|
207
|
+
parent: params.agentName,
|
|
208
|
+
child: spawnConfig.name,
|
|
209
|
+
depth: params.depth + 1,
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
return {
|
|
213
|
+
run: (input: unknown) => childAgent.run(input, childCtx),
|
|
214
|
+
};
|
|
215
|
+
},
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
return ctx;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// ── Internal: Strategy Resolution ─────────────────────────────────────────────
|
|
222
|
+
|
|
223
|
+
function resolveStrategy(config: AgentConfig): Strategy {
|
|
224
|
+
const strategyName = config.strategy ?? 'sequential';
|
|
225
|
+
|
|
226
|
+
switch (strategyName) {
|
|
227
|
+
case 'sequential':
|
|
228
|
+
return new SequentialStrategy();
|
|
229
|
+
case 'router':
|
|
230
|
+
return new RouterStrategy();
|
|
231
|
+
case 'plan-and-execute':
|
|
232
|
+
return new PlanAndExecuteStrategy(config.planAndExecuteOptions);
|
|
233
|
+
case 'react':
|
|
234
|
+
return new ReActStrategy(config.reactOptions);
|
|
235
|
+
case 'custom':
|
|
236
|
+
if (!config.customStrategy) {
|
|
237
|
+
throw new Error(`Agent "${config.name}": strategy='custom' requires customStrategy function`);
|
|
238
|
+
}
|
|
239
|
+
return new CustomStrategy(config.customStrategy);
|
|
240
|
+
default:
|
|
241
|
+
throw new Error(`Unknown agent strategy: "${strategyName as string}"`);
|
|
242
|
+
}
|
|
243
|
+
}
|
package/src/cli-utils.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* cli-utils — lightweight CLI argument and input helpers.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from the repeated boilerplate found in every example's run.ts.
|
|
5
|
+
* Covers the three patterns used across all examples:
|
|
6
|
+
* • Named flag parsing: --flag value
|
|
7
|
+
* • Boolean flag check: --flag
|
|
8
|
+
* • Text input reading: --file path | stdin | env var | fallback string
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Parse a named CLI flag and return its value.
|
|
13
|
+
*
|
|
14
|
+
* @example getArg('--topic') // node run.js --topic "foo" → "foo"
|
|
15
|
+
*/
|
|
16
|
+
export function getArg(flag: string): string | undefined {
|
|
17
|
+
const idx = process.argv.indexOf(flag);
|
|
18
|
+
return idx !== -1 ? process.argv[idx + 1] : undefined;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Check whether a boolean CLI flag is present.
|
|
23
|
+
*
|
|
24
|
+
* @example hasFlag('--mock') // node run.js --mock → true
|
|
25
|
+
*/
|
|
26
|
+
export function hasFlag(flag: string): boolean {
|
|
27
|
+
return process.argv.includes(flag);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Read a text document from the first available source:
|
|
32
|
+
* 1. `--file <path>` CLI flag
|
|
33
|
+
* 2. `envVar` environment variable (if provided)
|
|
34
|
+
* 3. stdin (when not a TTY)
|
|
35
|
+
* 4. `fallback` string (demo/default content)
|
|
36
|
+
*
|
|
37
|
+
* Logs a one-line message indicating which source was used.
|
|
38
|
+
*
|
|
39
|
+
* @param options.envVar Environment variable name to check (e.g. 'DOCS').
|
|
40
|
+
* @param options.fallback Demo text to use when no other source is available.
|
|
41
|
+
* @param options.label Human-readable name for the input (e.g. 'documentation').
|
|
42
|
+
* Used in log messages. Defaults to 'input'.
|
|
43
|
+
*/
|
|
44
|
+
export async function readTextInput(options: {
|
|
45
|
+
envVar?: string;
|
|
46
|
+
fallback?: string;
|
|
47
|
+
label?: string;
|
|
48
|
+
}): Promise<string> {
|
|
49
|
+
const { envVar, fallback = '', label = 'input' } = options;
|
|
50
|
+
|
|
51
|
+
const filePath = getArg('--file');
|
|
52
|
+
if (filePath) {
|
|
53
|
+
const { readFile } = await import('fs/promises');
|
|
54
|
+
const text = await readFile(filePath, 'utf-8');
|
|
55
|
+
console.log(`Reading ${label} from: ${filePath}`);
|
|
56
|
+
return text;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (envVar && process.env[envVar]) {
|
|
60
|
+
console.log(`Reading ${label} from ${envVar} env var`);
|
|
61
|
+
return process.env[envVar]!;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (!process.stdin.isTTY) {
|
|
65
|
+
const chunks: Buffer[] = [];
|
|
66
|
+
for await (const chunk of process.stdin) chunks.push(chunk as Buffer);
|
|
67
|
+
console.log(`Reading ${label} from stdin`);
|
|
68
|
+
return Buffer.concat(chunks).toString('utf-8');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
console.log(`Using demo ${label} (pass --file <path> for real ${label})`);
|
|
72
|
+
return fallback;
|
|
73
|
+
}
|
package/src/errors.ts
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* flomatai error hierarchy.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export class FlomatAIError extends Error {
|
|
6
|
+
readonly code: string;
|
|
7
|
+
readonly details?: unknown;
|
|
8
|
+
|
|
9
|
+
constructor(message: string, code: string, details?: unknown) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = 'FlomatAIError';
|
|
12
|
+
this.code = code;
|
|
13
|
+
this.details = details;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// ── Skill Errors ─────────────────────────────────────────────────────────────
|
|
18
|
+
|
|
19
|
+
export class SkillError extends FlomatAIError {
|
|
20
|
+
readonly skillName: string;
|
|
21
|
+
|
|
22
|
+
constructor(skillName: string, message: string, details?: unknown) {
|
|
23
|
+
super(message, 'SKILL_ERROR', details);
|
|
24
|
+
this.name = 'SkillError';
|
|
25
|
+
this.skillName = skillName;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export class SkillValidationError extends FlomatAIError {
|
|
30
|
+
readonly skillName: string;
|
|
31
|
+
readonly field: 'input' | 'output';
|
|
32
|
+
|
|
33
|
+
constructor(
|
|
34
|
+
skillName: string,
|
|
35
|
+
field: 'input' | 'output',
|
|
36
|
+
message: string,
|
|
37
|
+
details?: unknown,
|
|
38
|
+
) {
|
|
39
|
+
super(message, 'SKILL_VALIDATION_ERROR', details);
|
|
40
|
+
this.name = 'SkillValidationError';
|
|
41
|
+
this.skillName = skillName;
|
|
42
|
+
this.field = field;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export class SkillTimeoutError extends SkillError {
|
|
47
|
+
readonly timeoutMs: number;
|
|
48
|
+
|
|
49
|
+
constructor(skillName: string, timeoutMs: number) {
|
|
50
|
+
super(skillName, `Skill "${skillName}" timed out after ${timeoutMs}ms`);
|
|
51
|
+
this.name = 'SkillTimeoutError';
|
|
52
|
+
this.timeoutMs = timeoutMs;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// ── Pipeline Errors ──────────────────────────────────────────────────────────
|
|
57
|
+
|
|
58
|
+
export class PipelineError extends FlomatAIError {
|
|
59
|
+
readonly pipelineName: string;
|
|
60
|
+
readonly stepName?: string;
|
|
61
|
+
|
|
62
|
+
constructor(pipelineName: string, message: string, stepName?: string, details?: unknown) {
|
|
63
|
+
super(message, 'PIPELINE_ERROR', details);
|
|
64
|
+
this.name = 'PipelineError';
|
|
65
|
+
this.pipelineName = pipelineName;
|
|
66
|
+
this.stepName = stepName;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export class StepMaxRetriesError extends PipelineError {
|
|
71
|
+
constructor(pipelineName: string, stepName: string, attempts: number, cause: Error) {
|
|
72
|
+
super(
|
|
73
|
+
pipelineName,
|
|
74
|
+
`Step "${stepName}" failed after ${attempts} attempts: ${cause.message}`,
|
|
75
|
+
stepName,
|
|
76
|
+
{ cause },
|
|
77
|
+
);
|
|
78
|
+
this.name = 'StepMaxRetriesError';
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ── Agent Errors ─────────────────────────────────────────────────────────────
|
|
83
|
+
|
|
84
|
+
export class AgentError extends FlomatAIError {
|
|
85
|
+
readonly agentName: string;
|
|
86
|
+
|
|
87
|
+
constructor(agentName: string, message: string, details?: unknown) {
|
|
88
|
+
super(message, 'AGENT_ERROR', details);
|
|
89
|
+
this.name = 'AgentError';
|
|
90
|
+
this.agentName = agentName;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export class AgentMaxIterationsError extends AgentError {
|
|
95
|
+
constructor(agentName: string, maxIterations: number) {
|
|
96
|
+
super(
|
|
97
|
+
agentName,
|
|
98
|
+
`Agent "${agentName}" hit max iterations (${maxIterations}) without finishing`,
|
|
99
|
+
);
|
|
100
|
+
this.name = 'AgentMaxIterationsError';
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export class AgentMaxDepthError extends AgentError {
|
|
105
|
+
constructor(agentName: string, maxDepth: number) {
|
|
106
|
+
super(agentName, `Agent hierarchy exceeded max depth (${maxDepth})`);
|
|
107
|
+
this.name = 'AgentMaxDepthError';
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ── LLM Errors ───────────────────────────────────────────────────────────────
|
|
112
|
+
|
|
113
|
+
export class LLMError extends FlomatAIError {
|
|
114
|
+
readonly providerName: string;
|
|
115
|
+
readonly statusCode?: number;
|
|
116
|
+
|
|
117
|
+
constructor(providerName: string, message: string, statusCode?: number, details?: unknown) {
|
|
118
|
+
super(message, 'LLM_ERROR', details);
|
|
119
|
+
this.name = 'LLMError';
|
|
120
|
+
this.providerName = providerName;
|
|
121
|
+
this.statusCode = statusCode;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export class LLMRateLimitError extends LLMError {
|
|
126
|
+
readonly retryAfterMs?: number;
|
|
127
|
+
|
|
128
|
+
constructor(providerName: string, retryAfterMs?: number) {
|
|
129
|
+
super(
|
|
130
|
+
providerName,
|
|
131
|
+
`Rate limit exceeded for provider "${providerName}"${retryAfterMs ? `. Retry after ${retryAfterMs}ms` : ''}`,
|
|
132
|
+
429,
|
|
133
|
+
);
|
|
134
|
+
this.name = 'LLMRateLimitError';
|
|
135
|
+
this.retryAfterMs = retryAfterMs;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// ── State Errors ─────────────────────────────────────────────────────────────
|
|
140
|
+
|
|
141
|
+
export class StateError extends FlomatAIError {
|
|
142
|
+
constructor(message: string, details?: unknown) {
|
|
143
|
+
super(message, 'STATE_ERROR', details);
|
|
144
|
+
this.name = 'StateError';
|
|
145
|
+
}
|
|
146
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @flomatai/core — public API
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// ── Types ─────────────────────────────────────────────────────────────────────
|
|
6
|
+
export type {
|
|
7
|
+
Message,
|
|
8
|
+
MessageRole,
|
|
9
|
+
LLMOptions,
|
|
10
|
+
LLMResponse,
|
|
11
|
+
LLMChunk,
|
|
12
|
+
ToolDefinition,
|
|
13
|
+
ToolCall,
|
|
14
|
+
PipelineRun,
|
|
15
|
+
StepRecord,
|
|
16
|
+
RunStatus,
|
|
17
|
+
} from './types.js';
|
|
18
|
+
|
|
19
|
+
// ── Errors ────────────────────────────────────────────────────────────────────
|
|
20
|
+
export {
|
|
21
|
+
FlomatAIError,
|
|
22
|
+
SkillError,
|
|
23
|
+
SkillValidationError,
|
|
24
|
+
SkillTimeoutError,
|
|
25
|
+
PipelineError,
|
|
26
|
+
StepMaxRetriesError,
|
|
27
|
+
AgentError,
|
|
28
|
+
AgentMaxIterationsError,
|
|
29
|
+
AgentMaxDepthError,
|
|
30
|
+
LLMError,
|
|
31
|
+
LLMRateLimitError,
|
|
32
|
+
StateError,
|
|
33
|
+
} from './errors.js';
|
|
34
|
+
|
|
35
|
+
// ── Logger ────────────────────────────────────────────────────────────────────
|
|
36
|
+
export { Logger, logger, type LogLevel, type LogEntry, type LogTransport } from './logger.js';
|
|
37
|
+
|
|
38
|
+
// ── LLM Provider ──────────────────────────────────────────────────────────────
|
|
39
|
+
export type { LLMProvider, LLMRegistry } from './llm-provider.js';
|
|
40
|
+
export { resolveLLM, withRetry, withTimeout } from './llm-provider.js';
|
|
41
|
+
|
|
42
|
+
// ── Skill ─────────────────────────────────────────────────────────────────────
|
|
43
|
+
export type { Skill, SkillMeta, SkillContext, SkillResult, MCPClientLike } from './skill.js';
|
|
44
|
+
|
|
45
|
+
// ── Skill Builders ────────────────────────────────────────────────────────────
|
|
46
|
+
export { LLMSkill, createLLMSkill, type LLMSkillConfig } from './skills/llm-skill.js';
|
|
47
|
+
export { TransformSkill, createTransformSkill, type TransformSkillConfig } from './skills/transform-skill.js';
|
|
48
|
+
export {
|
|
49
|
+
IOSkill,
|
|
50
|
+
createHttpGetSkill,
|
|
51
|
+
createHttpPostSkill,
|
|
52
|
+
createReadFileSkill,
|
|
53
|
+
createWriteFileSkill,
|
|
54
|
+
} from './skills/io-skill.js';
|
|
55
|
+
|
|
56
|
+
// ── Pipeline ──────────────────────────────────────────────────────────────────
|
|
57
|
+
export {
|
|
58
|
+
Pipeline,
|
|
59
|
+
PipelineBuilder,
|
|
60
|
+
resolvePath,
|
|
61
|
+
type BuiltPipeline,
|
|
62
|
+
type PipelineStep,
|
|
63
|
+
type StepInputContext,
|
|
64
|
+
type StepKind,
|
|
65
|
+
} from './pipeline.js';
|
|
66
|
+
|
|
67
|
+
// ── Pipeline Registry ─────────────────────────────────────────────────────────
|
|
68
|
+
export {
|
|
69
|
+
PipelineRegistry,
|
|
70
|
+
loadPipeline,
|
|
71
|
+
createRegistryFromConfig,
|
|
72
|
+
type PipelineSource,
|
|
73
|
+
type RegisteredPipeline,
|
|
74
|
+
type PipelinePackageMeta,
|
|
75
|
+
type PipelinePackageExports,
|
|
76
|
+
} from './pipeline-registry.js';
|
|
77
|
+
|
|
78
|
+
// ── State ─────────────────────────────────────────────────────────────────────
|
|
79
|
+
export type { StateStore, RunFilter } from './state/types.js';
|
|
80
|
+
export { MemoryStore } from './state/memory-store.js';
|
|
81
|
+
export { FileStore } from './state/file-store.js';
|
|
82
|
+
|
|
83
|
+
// ── Agent ─────────────────────────────────────────────────────────────────────
|
|
84
|
+
export { Agent, createAgent, type AgentConfig } from './agent.js';
|
|
85
|
+
export type {
|
|
86
|
+
AgentContext,
|
|
87
|
+
AgentResult,
|
|
88
|
+
AgentTraceEntry,
|
|
89
|
+
AgentRunner,
|
|
90
|
+
SpawnAgentConfig,
|
|
91
|
+
StrategyName,
|
|
92
|
+
CustomStrategyFn,
|
|
93
|
+
Strategy,
|
|
94
|
+
} from './strategies/types.js';
|
|
95
|
+
|
|
96
|
+
// Strategy classes (for custom usage)
|
|
97
|
+
export { SequentialStrategy } from './strategies/sequential.js';
|
|
98
|
+
export { RouterStrategy } from './strategies/router.js';
|
|
99
|
+
export { PlanAndExecuteStrategy, type PlanAndExecuteOptions } from './strategies/plan-and-execute.js';
|
|
100
|
+
export { ReActStrategy, type ReActOptions } from './strategies/react.js';
|
|
101
|
+
export { CustomStrategy } from './strategies/custom.js';
|
|
102
|
+
|
|
103
|
+
// ── Orchestrator ──────────────────────────────────────────────────────────────
|
|
104
|
+
export {
|
|
105
|
+
Orchestrator,
|
|
106
|
+
type OrchestratorConfig,
|
|
107
|
+
type OrchestratorHooks,
|
|
108
|
+
type RunOptions,
|
|
109
|
+
} from './orchestrator.js';
|
|
110
|
+
|
|
111
|
+
// ── Orchestrator helpers ──────────────────────────────────────────────────────
|
|
112
|
+
export { createConsoleHooks } from './orchestrator-helpers.js';
|
|
113
|
+
|
|
114
|
+
// ── CLI utilities ─────────────────────────────────────────────────────────────
|
|
115
|
+
export { getArg, hasFlag, readTextInput } from './cli-utils.js';
|
|
116
|
+
|
|
117
|
+
// ── Testing / Mock ────────────────────────────────────────────────────────────
|
|
118
|
+
export {
|
|
119
|
+
MockLLMProvider,
|
|
120
|
+
createTestLLM,
|
|
121
|
+
createMockLLMRegistry,
|
|
122
|
+
createTestOrchestrator,
|
|
123
|
+
type MockResponse,
|
|
124
|
+
} from './mock-llm.js';
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LLMProvider abstraction — the universal interface for all AI model providers.
|
|
3
|
+
*
|
|
4
|
+
* Implementations live in @flomatai/provider-* packages.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { Message, LLMOptions, LLMResponse, LLMChunk } from './types.js';
|
|
8
|
+
|
|
9
|
+
// ── Core Provider Interface ───────────────────────────────────────────────────
|
|
10
|
+
|
|
11
|
+
export interface LLMProvider {
|
|
12
|
+
/** Identifier for this provider (e.g. 'anthropic', 'openai', 'ollama'). */
|
|
13
|
+
readonly name: string;
|
|
14
|
+
/** The model being used (e.g. 'claude-3-5-sonnet-20241022'). */
|
|
15
|
+
readonly model: string;
|
|
16
|
+
/**
|
|
17
|
+
* Send a chat completion request and await the full response.
|
|
18
|
+
*/
|
|
19
|
+
chat(messages: Message[], options?: LLMOptions): Promise<LLMResponse>;
|
|
20
|
+
/**
|
|
21
|
+
* Stream a chat completion. Yields chunks as they arrive.
|
|
22
|
+
*/
|
|
23
|
+
stream(messages: Message[], options?: LLMOptions): AsyncIterable<LLMChunk>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// ── LLM Registry ─────────────────────────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* A named registry of LLM providers.
|
|
30
|
+
* The 'default' key is used when no specific provider is requested.
|
|
31
|
+
*/
|
|
32
|
+
export type LLMRegistry = Record<string, LLMProvider>;
|
|
33
|
+
|
|
34
|
+
export function resolveLLM(registry: LLMRegistry, key?: string): LLMProvider {
|
|
35
|
+
const resolved = key
|
|
36
|
+
? (registry[key] ?? registry['default'])
|
|
37
|
+
: registry['default'];
|
|
38
|
+
|
|
39
|
+
if (!resolved) {
|
|
40
|
+
throw new Error(
|
|
41
|
+
`LLM provider "${key ?? 'default'}" not found in registry. ` +
|
|
42
|
+
`Available: ${Object.keys(registry).join(', ')}`,
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
return resolved;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// ── Utility: retry wrapper for LLM calls ─────────────────────────────────────
|
|
49
|
+
|
|
50
|
+
export async function withRetry<T>(
|
|
51
|
+
fn: () => Promise<T>,
|
|
52
|
+
retries: number,
|
|
53
|
+
onRetry?: (attempt: number, error: Error) => void,
|
|
54
|
+
): Promise<T> {
|
|
55
|
+
let lastError: Error = new Error('Unknown error');
|
|
56
|
+
for (let attempt = 1; attempt <= retries + 1; attempt++) {
|
|
57
|
+
try {
|
|
58
|
+
return await fn();
|
|
59
|
+
} catch (err) {
|
|
60
|
+
lastError = err instanceof Error ? err : new Error(String(err));
|
|
61
|
+
if (attempt <= retries) {
|
|
62
|
+
// Exponential backoff: 500ms, 1000ms, 2000ms, ...
|
|
63
|
+
const delay = Math.min(500 * Math.pow(2, attempt - 1), 10_000);
|
|
64
|
+
onRetry?.(attempt, lastError);
|
|
65
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
throw lastError;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ── Utility: timeout wrapper ──────────────────────────────────────────────────
|
|
73
|
+
|
|
74
|
+
export async function withTimeout<T>(
|
|
75
|
+
fn: () => Promise<T>,
|
|
76
|
+
timeoutMs: number,
|
|
77
|
+
label: string,
|
|
78
|
+
): Promise<T> {
|
|
79
|
+
return new Promise<T>((resolve, reject) => {
|
|
80
|
+
const timer = setTimeout(
|
|
81
|
+
() => reject(new Error(`"${label}" timed out after ${timeoutMs}ms`)),
|
|
82
|
+
timeoutMs,
|
|
83
|
+
);
|
|
84
|
+
fn()
|
|
85
|
+
.then((result) => { clearTimeout(timer); resolve(result); })
|
|
86
|
+
.catch((err) => { clearTimeout(timer); reject(err); });
|
|
87
|
+
});
|
|
88
|
+
}
|