agentic-api 2.0.491 → 2.0.592

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 (57) hide show
  1. package/README.md +37 -34
  2. package/dist/src/agents/job.runner.d.ts +130 -0
  3. package/dist/src/agents/job.runner.js +339 -0
  4. package/dist/src/agents/reducer.core.d.ts +11 -1
  5. package/dist/src/agents/reducer.core.js +76 -86
  6. package/dist/src/agents/reducer.d.ts +1 -0
  7. package/dist/src/agents/reducer.factory.d.ts +46 -0
  8. package/dist/src/agents/reducer.factory.js +154 -0
  9. package/dist/src/agents/reducer.js +1 -0
  10. package/dist/src/agents/simulator.d.ts +26 -1
  11. package/dist/src/agents/simulator.dashboard.d.ts +140 -0
  12. package/dist/src/agents/simulator.dashboard.js +344 -0
  13. package/dist/src/agents/simulator.js +56 -0
  14. package/dist/src/agents/simulator.types.d.ts +38 -6
  15. package/dist/src/agents/simulator.utils.d.ts +22 -1
  16. package/dist/src/agents/simulator.utils.js +27 -0
  17. package/dist/src/execute/helpers.js +2 -2
  18. package/dist/src/execute/modelconfig.d.ts +21 -11
  19. package/dist/src/execute/modelconfig.js +29 -13
  20. package/dist/src/execute/responses.js +8 -7
  21. package/dist/src/index.d.ts +6 -1
  22. package/dist/src/index.js +21 -1
  23. package/dist/src/llm/config.d.ts +25 -0
  24. package/dist/src/llm/config.js +38 -0
  25. package/dist/src/llm/index.d.ts +48 -0
  26. package/dist/src/llm/index.js +115 -0
  27. package/dist/src/llm/openai.d.ts +6 -0
  28. package/dist/src/llm/openai.js +154 -0
  29. package/dist/src/llm/pricing.d.ts +26 -0
  30. package/dist/src/llm/pricing.js +129 -0
  31. package/dist/src/llm/xai.d.ts +17 -0
  32. package/dist/src/llm/xai.js +90 -0
  33. package/dist/src/pricing.llm.d.ts +3 -15
  34. package/dist/src/pricing.llm.js +10 -251
  35. package/dist/src/prompts.d.ts +0 -1
  36. package/dist/src/prompts.js +51 -118
  37. package/dist/src/rag/embeddings.d.ts +5 -1
  38. package/dist/src/rag/embeddings.js +15 -5
  39. package/dist/src/rag/parser.js +1 -1
  40. package/dist/src/rag/rag.manager.d.ts +44 -6
  41. package/dist/src/rag/rag.manager.js +138 -49
  42. package/dist/src/rag/types.d.ts +2 -0
  43. package/dist/src/rag/usecase.js +8 -11
  44. package/dist/src/rules/git/git.health.js +59 -4
  45. package/dist/src/rules/git/repo.d.ts +11 -4
  46. package/dist/src/rules/git/repo.js +64 -18
  47. package/dist/src/rules/git/repo.pr.d.ts +8 -0
  48. package/dist/src/rules/git/repo.pr.js +45 -1
  49. package/dist/src/rules/git/repo.tools.d.ts +5 -1
  50. package/dist/src/rules/git/repo.tools.js +54 -7
  51. package/dist/src/rules/types.d.ts +14 -0
  52. package/dist/src/rules/utils.matter.d.ts +0 -20
  53. package/dist/src/rules/utils.matter.js +42 -74
  54. package/dist/src/scrapper.js +2 -2
  55. package/dist/src/utils.d.ts +0 -8
  56. package/dist/src/utils.js +1 -28
  57. package/package.json +1 -1
package/README.md CHANGED
@@ -66,15 +66,24 @@ npm install @agentic-api
66
66
 
67
67
  ## 💡 Quick Start
68
68
 
69
+ ### Configuration `.env`
70
+
71
+ ```bash
72
+ # Provider LLM (openai | xai)
73
+ LLM_PROVIDER=openai
74
+
75
+ # Clés API
76
+ OPENAI_API_KEY=sk-... # Requis pour OpenAI + embeddings + whisper
77
+ XAI_API_KEY=xai-... # Requis si LLM_PROVIDER=xai
78
+ ```
79
+
80
+ ### Usage
81
+
69
82
  ```typescript
70
- import OpenAI from "openai";
71
- import { executeAgentSet } from '@agentic-api';
72
- import { AgenticContext } from '@agentic-api';
73
- import { AgentStateGraph } from '@agentic-api';
83
+ import { llmInstance, executeAgentSet, AgenticContext, AgentStateGraph } from '@agentic-api';
74
84
 
75
- const openai = new OpenAI({
76
- apiKey: process.env.OPENAI_API_KEY,
77
- });
85
+ // Initialiser le LLM (utilise LLM_PROVIDER depuis .env)
86
+ llmInstance();
78
87
 
79
88
  // Create context with user information
80
89
  const context: AgenticContext = {
@@ -357,41 +366,35 @@ const structuredResult = await mapper.reduce(config, structuredCallback, {
357
366
 
358
367
  Advanced testing framework for agent behavior validation with scenario-based simulations.
359
368
 
360
- - **Scenario-Based Testing**: Define complex test scenarios with goals, personas, and expected outcomes
361
- - **Conversational Simulation**: Simulate realistic user interactions with agents
362
- - **Automatic Validation**: Built-in success/failure detection and error reporting
369
+ - **Clean API**: Separated `scenario` (context) and `testCase` (test parameters)
370
+ - **Oneshot by Default**: `maxExchanges=1` for simple single-response tests
371
+ - **Automatic Tool Validation**: Built-in validation with `expectedTools`
363
372
  - **Exchange Limiting**: Control simulation length with configurable exchange limits
364
373
 
365
374
  📖 **[Complete Agent Simulator Documentation →](./docs/README-AGENT-SIMULATOR.md)**
366
375
 
367
376
  ```typescript
368
- import { AgentSimulator, SimulationScenario } from '@agentic-api';
369
-
370
- // Define test scenario
371
- const scenario: SimulationScenario = {
372
- testGoals: "Verify that the agent can help with haiku creation",
373
- testEnd: "Agent provides a complete haiku poem",
374
- testPersona: "A poetry enthusiast seeking creative assistance",
375
- testQuery: "I want to write a haiku about nature. Can you help me?",
376
- testResult: "Agent successfully guides haiku creation process",
377
- testError: "Agent refuses to help or provides incorrect format"
378
- };
377
+ import { AgentSimulator, PERSONA_PATIENT } from '@agentic-api';
379
378
 
380
379
  // Configure simulator
381
380
  const simulator = new AgentSimulator({
382
381
  agents: [haikuAgent, welcomeAgent],
383
382
  start: "welcome",
384
- verbose: true,
385
- instructionEx: "Focus on creative writing assistance"
383
+ verbose: true
386
384
  });
387
385
 
388
- // Run simulation
389
- const result = await simulator.executeSimulation({
390
- scenario,
391
- maxExchanges: 10,
392
- onMessage: (message) => {
393
- console.log(`${message.role}: ${message.content}`);
394
- }
386
+ // Define test scenario (context)
387
+ const scenario = {
388
+ goals: "Verify that the agent can help with haiku creation. Agent provides a complete haiku poem.",
389
+ persona: PERSONA_PATIENT
390
+ // result defaults to '{"success": boolean, "explain": string, "error": string}'
391
+ };
392
+
393
+ // Run test case
394
+ const result = await simulator.testCase(scenario, {
395
+ query: "I want to write a haiku about nature. Can you help me?",
396
+ maxExchanges: 5, // defaults to 1 (oneshot)
397
+ expectedTools: { 'transferAgents': { gte: 1 } } // defaults to {}
395
398
  });
396
399
 
397
400
  // Validate results
@@ -406,10 +409,10 @@ if (!result.success) {
406
409
 
407
410
  ### Simulation Features
408
411
 
409
- - **Structured Scenarios**: Define test goals, end conditions, and expected behaviors
410
- - **Persona Simulation**: Simulator adopts specific user personas for realistic testing
411
- - **Error Detection**: Automatic detection of unwanted content or behaviors
412
- - **Exchange Tracking**: Monitor conversation flow and agent performance
412
+ - **Separated Concerns**: `scenario` for context, `testCase` for test parameters
413
+ - **Sensible Defaults**: `maxExchanges=1`, `expectedTools={}`, default result format
414
+ - **Persona Simulation**: Built-in personas (PERSONA_PATIENT, PERSONA_PRESSE, PERSONA_ENERVE)
415
+ - **Tool Validation**: Automatic validation with `equal`, `gte`, `lte` constraints
413
416
  - **Execution Metadata**: Access to token usage, actions, and performance metrics
414
417
 
415
418
  ## 📋 Rules Management System
@@ -0,0 +1,130 @@
1
+ export type JobStatus = 'planned' | 'running' | 'done' | 'failed';
2
+ export type TaskStatus = 'todo' | 'doing' | 'done' | 'failed';
3
+ export interface TaskSpec {
4
+ id: string;
5
+ title: string;
6
+ type?: string;
7
+ dependsOn?: string[];
8
+ input?: any;
9
+ acceptance?: string[];
10
+ }
11
+ export interface TaskResult {
12
+ taskId: string;
13
+ ok: boolean;
14
+ summary: string;
15
+ data?: any;
16
+ artifacts?: {
17
+ kind: string;
18
+ ref: string;
19
+ meta?: any;
20
+ }[];
21
+ error?: string;
22
+ }
23
+ export interface JobPlan {
24
+ jobId: string;
25
+ goal: string;
26
+ beneficiary?: string;
27
+ tasks: TaskSpec[];
28
+ }
29
+ export interface ReducedJobMemory {
30
+ memory: string;
31
+ index: Record<string, any>;
32
+ statusLine?: string;
33
+ }
34
+ export interface JobRunnerState {
35
+ jobId: string;
36
+ plan?: JobPlan;
37
+ jobStatus: JobStatus;
38
+ taskStatus: Record<string, TaskStatus>;
39
+ lastMemory: ReducedJobMemory | null;
40
+ lastError?: string;
41
+ }
42
+ export type PlannerFn = (input: any, seedMemory?: ReducedJobMemory | null) => Promise<JobPlan>;
43
+ export type ExecutorFn = (task: TaskSpec, memory: ReducedJobMemory | null) => Promise<TaskResult>;
44
+ export type ReducerFn = (previous: ReducedJobMemory | null, task: TaskSpec, result: TaskResult) => Promise<ReducedJobMemory>;
45
+ export type JobEventType = 'job_created' | 'plan_ready' | 'task_started' | 'task_done' | 'memory_reduced' | 'job_done' | 'job_failed';
46
+ export interface JobEvent<T = any> {
47
+ type: JobEventType;
48
+ at: string;
49
+ payload?: T;
50
+ }
51
+ export interface JobRunnerConfig {
52
+ planner: PlannerFn;
53
+ executor: ExecutorFn;
54
+ reducer: ReducerFn;
55
+ /**
56
+ * Directory where snapshots and events are persisted.
57
+ * If not provided, persistence is disabled.
58
+ */
59
+ storeDir?: string;
60
+ /**
61
+ * Max attempts per task when schema validation fails or executor throws.
62
+ * (Logical failure with ok=false does not trigger retry)
63
+ */
64
+ maxAttempts?: number;
65
+ onEvent?: (event: JobEvent) => void;
66
+ }
67
+ export interface RunJobOptions {
68
+ jobId?: string;
69
+ seedMemory?: ReducedJobMemory | null;
70
+ resume?: boolean;
71
+ }
72
+ export interface JobRunSuccess {
73
+ ok: true;
74
+ jobId: string;
75
+ plan: JobPlan;
76
+ finalSummary: string;
77
+ artifactsIndex: Record<string, any>;
78
+ memory: ReducedJobMemory | null;
79
+ taskStatus: Record<string, TaskStatus>;
80
+ snapshotFile?: string;
81
+ eventsFile?: string;
82
+ }
83
+ export interface JobRunFailure {
84
+ ok: false;
85
+ jobId: string;
86
+ plan?: JobPlan;
87
+ failedTaskId?: string;
88
+ error: string;
89
+ errorSummary: {
90
+ taskId?: string;
91
+ taskTitle?: string;
92
+ nature: string;
93
+ progress?: string;
94
+ nextAction: string;
95
+ };
96
+ memory: ReducedJobMemory | null;
97
+ taskStatus: Record<string, TaskStatus>;
98
+ snapshotFile?: string;
99
+ eventsFile?: string;
100
+ }
101
+ export type JobRunOutcome = JobRunSuccess | JobRunFailure;
102
+ export declare class JobRunner {
103
+ private readonly storeDir?;
104
+ private readonly maxAttempts;
105
+ private readonly planner;
106
+ private readonly executor;
107
+ private readonly reducer;
108
+ private readonly onEvent?;
109
+ constructor(config: JobRunnerConfig);
110
+ /**
111
+ * Run a job end-to-end (Plan → Execute → Reduce) with persistence and retries.
112
+ */
113
+ run(input: any, options?: RunJobOptions): Promise<JobRunOutcome>;
114
+ private planJob;
115
+ private initTaskStatus;
116
+ private nextReadyTask;
117
+ private executeWithRetry;
118
+ private reduceWithRetry;
119
+ private handleFailure;
120
+ private validateJobPlan;
121
+ private validateTaskResult;
122
+ private validateMemory;
123
+ private persistState;
124
+ private loadState;
125
+ private ensureStoreDir;
126
+ private snapshotPath;
127
+ private eventsPath;
128
+ private logEvent;
129
+ private emit;
130
+ }
@@ -0,0 +1,339 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.JobRunner = void 0;
7
+ const crypto_1 = require("crypto");
8
+ const fs_1 = require("fs");
9
+ const path_1 = __importDefault(require("path"));
10
+ class JobRunner {
11
+ constructor(config) {
12
+ this.planner = config.planner;
13
+ this.executor = config.executor;
14
+ this.reducer = config.reducer;
15
+ this.storeDir = config.storeDir;
16
+ this.maxAttempts = config.maxAttempts || 2;
17
+ this.onEvent = config.onEvent;
18
+ }
19
+ /**
20
+ * Run a job end-to-end (Plan → Execute → Reduce) with persistence and retries.
21
+ */
22
+ async run(input, options) {
23
+ const seedMemory = options?.seedMemory ?? null;
24
+ const jobId = options?.jobId || (0, crypto_1.randomUUID)();
25
+ let state = null;
26
+ if (options?.resume && this.storeDir) {
27
+ state = await this.loadState(jobId);
28
+ }
29
+ if (!state) {
30
+ const plan = await this.planJob(jobId, input, seedMemory);
31
+ state = {
32
+ jobId: plan.jobId,
33
+ plan,
34
+ jobStatus: 'running',
35
+ taskStatus: this.initTaskStatus(plan.tasks),
36
+ lastMemory: seedMemory || null
37
+ };
38
+ await this.persistState(state);
39
+ this.emit({ type: 'job_created', at: new Date().toISOString(), payload: { jobId: state.jobId } });
40
+ }
41
+ if (!state.plan) {
42
+ return {
43
+ ok: false,
44
+ jobId,
45
+ error: 'Missing plan in restored state',
46
+ errorSummary: {
47
+ nature: 'state_corruption',
48
+ nextAction: 'Re-run planner to regenerate plan'
49
+ },
50
+ memory: state.lastMemory,
51
+ taskStatus: state.taskStatus
52
+ };
53
+ }
54
+ this.emit({ type: 'plan_ready', at: new Date().toISOString(), payload: { jobId: state.jobId, tasks: state.plan.tasks.length } });
55
+ const plan = state.plan;
56
+ state.jobStatus = 'running';
57
+ while (true) {
58
+ const nextTask = this.nextReadyTask(plan.tasks, state.taskStatus);
59
+ if (!nextTask)
60
+ break;
61
+ state.taskStatus[nextTask.id] = 'doing';
62
+ await this.persistState(state);
63
+ const execResult = await this.executeWithRetry(nextTask, state.lastMemory);
64
+ if (!execResult.valid) {
65
+ return await this.handleFailure(state, plan, nextTask, execResult.error || 'Execution failed');
66
+ }
67
+ const result = execResult.result;
68
+ this.emit({ type: 'task_done', at: new Date().toISOString(), payload: { jobId: plan.jobId, taskId: nextTask.id, ok: result.ok } });
69
+ const reduced = await this.reduceWithRetry(state.lastMemory, nextTask, result);
70
+ if (!reduced.valid) {
71
+ return await this.handleFailure(state, plan, nextTask, reduced.error || 'Reducer failed');
72
+ }
73
+ state.lastMemory = reduced.memory;
74
+ state.taskStatus[nextTask.id] = result.ok ? 'done' : 'failed';
75
+ await this.persistState(state);
76
+ this.emit({ type: 'memory_reduced', at: new Date().toISOString(), payload: { jobId: plan.jobId, taskId: nextTask.id } });
77
+ if (!result.ok) {
78
+ return await this.handleFailure(state, plan, nextTask, result.error || 'Task reported logical failure');
79
+ }
80
+ }
81
+ state.jobStatus = 'done';
82
+ await this.persistState(state);
83
+ this.emit({ type: 'job_done', at: new Date().toISOString(), payload: { jobId: plan.jobId } });
84
+ return {
85
+ ok: true,
86
+ jobId: plan.jobId,
87
+ plan,
88
+ finalSummary: state.lastMemory?.memory || '',
89
+ artifactsIndex: state.lastMemory?.index || {},
90
+ memory: state.lastMemory,
91
+ taskStatus: state.taskStatus,
92
+ snapshotFile: this.snapshotPath(plan.jobId),
93
+ eventsFile: this.eventsPath(plan.jobId)
94
+ };
95
+ }
96
+ async planJob(jobId, input, seedMemory) {
97
+ const plan = await this.planner(input, seedMemory);
98
+ const finalPlan = {
99
+ ...plan,
100
+ jobId: plan.jobId || jobId
101
+ };
102
+ this.validateJobPlan(finalPlan, true);
103
+ return finalPlan;
104
+ }
105
+ initTaskStatus(tasks) {
106
+ return tasks.reduce((acc, task) => {
107
+ acc[task.id] = 'todo';
108
+ return acc;
109
+ }, {});
110
+ }
111
+ nextReadyTask(tasks, status) {
112
+ return tasks.find(task => {
113
+ if (status[task.id] !== 'todo')
114
+ return false;
115
+ if (!task.dependsOn || task.dependsOn.length === 0)
116
+ return true;
117
+ return task.dependsOn.every(dep => status[dep] === 'done');
118
+ }) || null;
119
+ }
120
+ async executeWithRetry(task, memory) {
121
+ for (let attempt = 1; attempt <= this.maxAttempts; attempt++) {
122
+ this.emit({ type: 'task_started', at: new Date().toISOString(), payload: { taskId: task.id, attempt } });
123
+ try {
124
+ const result = await this.executor(task, memory);
125
+ const errors = this.validateTaskResult(result);
126
+ if (errors.length === 0) {
127
+ return { valid: true, result, attempts: attempt };
128
+ }
129
+ if (attempt >= this.maxAttempts) {
130
+ return { valid: false, error: `TaskResult validation failed: ${errors.join(', ')}`, attempts: attempt };
131
+ }
132
+ }
133
+ catch (err) {
134
+ if (attempt >= this.maxAttempts) {
135
+ return { valid: false, error: err?.message || 'Executor error', attempts: attempt };
136
+ }
137
+ }
138
+ }
139
+ return { valid: false, error: 'Unknown execution failure', attempts: this.maxAttempts };
140
+ }
141
+ async reduceWithRetry(previous, task, result) {
142
+ for (let attempt = 1; attempt <= this.maxAttempts; attempt++) {
143
+ try {
144
+ const nextMemory = await this.reducer(previous, task, result);
145
+ const errors = this.validateMemory(nextMemory);
146
+ if (errors.length === 0) {
147
+ return { valid: true, memory: nextMemory };
148
+ }
149
+ if (attempt >= this.maxAttempts) {
150
+ return { valid: false, error: `Reducer validation failed: ${errors.join(', ')}` };
151
+ }
152
+ }
153
+ catch (err) {
154
+ if (attempt >= this.maxAttempts) {
155
+ return { valid: false, error: err?.message || 'Reducer error' };
156
+ }
157
+ }
158
+ }
159
+ return { valid: false, error: 'Unknown reducer failure' };
160
+ }
161
+ async handleFailure(state, plan, task, reason) {
162
+ state.jobStatus = 'failed';
163
+ state.taskStatus[task.id] = state.taskStatus[task.id] === 'done' ? 'done' : 'failed';
164
+ state.lastError = reason;
165
+ await this.persistState(state);
166
+ const errorSummary = {
167
+ taskId: task.id,
168
+ taskTitle: task.title,
169
+ nature: reason,
170
+ progress: state.lastMemory?.memory,
171
+ nextAction: 'Inspect executor/reducer outputs or planner schema, then retry'
172
+ };
173
+ this.emit({ type: 'job_failed', at: new Date().toISOString(), payload: { jobId: plan.jobId, taskId: task.id, reason } });
174
+ return {
175
+ ok: false,
176
+ jobId: plan.jobId,
177
+ plan,
178
+ failedTaskId: task.id,
179
+ error: reason,
180
+ errorSummary,
181
+ memory: state.lastMemory,
182
+ taskStatus: state.taskStatus,
183
+ snapshotFile: this.snapshotPath(plan.jobId),
184
+ eventsFile: this.eventsPath(plan.jobId)
185
+ };
186
+ }
187
+ validateJobPlan(plan, throwOnError = false) {
188
+ const errors = [];
189
+ if (!plan || typeof plan !== 'object') {
190
+ errors.push('plan must be an object');
191
+ }
192
+ if (!plan.jobId || typeof plan.jobId !== 'string' || !plan.jobId.trim()) {
193
+ errors.push('jobId is required');
194
+ }
195
+ if (!plan.goal || typeof plan.goal !== 'string' || !plan.goal.trim()) {
196
+ errors.push('goal is required');
197
+ }
198
+ if (!Array.isArray(plan.tasks) || plan.tasks.length === 0) {
199
+ errors.push('tasks must be a non-empty array');
200
+ }
201
+ else {
202
+ plan.tasks.forEach((task, idx) => {
203
+ if (!task || typeof task !== 'object') {
204
+ errors.push(`task[${idx}] must be an object`);
205
+ return;
206
+ }
207
+ if (!task.id || typeof task.id !== 'string' || !task.id.trim()) {
208
+ errors.push(`task[${idx}].id is required`);
209
+ }
210
+ if (!task.title || typeof task.title !== 'string' || !task.title.trim()) {
211
+ errors.push(`task[${idx}].title is required`);
212
+ }
213
+ if (task.dependsOn && !Array.isArray(task.dependsOn)) {
214
+ errors.push(`task[${idx}].dependsOn must be an array if provided`);
215
+ }
216
+ if (task.acceptance && !Array.isArray(task.acceptance)) {
217
+ errors.push(`task[${idx}].acceptance must be an array if provided`);
218
+ }
219
+ });
220
+ }
221
+ if (throwOnError && errors.length) {
222
+ throw new Error(`Invalid JobPlan: ${errors.join(', ')}`);
223
+ }
224
+ return errors;
225
+ }
226
+ validateTaskResult(result) {
227
+ const errors = [];
228
+ if (!result || typeof result !== 'object') {
229
+ errors.push('TaskResult must be an object');
230
+ return errors;
231
+ }
232
+ if (!result.taskId || typeof result.taskId !== 'string' || !result.taskId.trim()) {
233
+ errors.push('taskId is required');
234
+ }
235
+ if (typeof result.ok !== 'boolean') {
236
+ errors.push('ok must be boolean');
237
+ }
238
+ if (!result.summary || typeof result.summary !== 'string') {
239
+ errors.push('summary is required');
240
+ }
241
+ if (result.artifacts && !Array.isArray(result.artifacts)) {
242
+ errors.push('artifacts must be an array if provided');
243
+ }
244
+ else if (Array.isArray(result.artifacts)) {
245
+ result.artifacts.forEach((artifact, idx) => {
246
+ if (!artifact || typeof artifact !== 'object') {
247
+ errors.push(`artifacts[${idx}] must be an object`);
248
+ return;
249
+ }
250
+ if (!artifact.kind || !artifact.ref) {
251
+ errors.push(`artifacts[${idx}] requires kind and ref`);
252
+ }
253
+ });
254
+ }
255
+ return errors;
256
+ }
257
+ validateMemory(memory) {
258
+ const errors = [];
259
+ if (!memory || typeof memory !== 'object') {
260
+ errors.push('memory must be an object');
261
+ return errors;
262
+ }
263
+ if (typeof memory.memory !== 'string') {
264
+ errors.push('memory.memory must be a string');
265
+ }
266
+ if (!memory.index || typeof memory.index !== 'object') {
267
+ errors.push('memory.index must be an object');
268
+ }
269
+ if (memory.statusLine && typeof memory.statusLine !== 'string') {
270
+ errors.push('memory.statusLine must be a string if provided');
271
+ }
272
+ return errors;
273
+ }
274
+ async persistState(state) {
275
+ if (!this.storeDir)
276
+ return;
277
+ await this.ensureStoreDir();
278
+ const file = this.snapshotPath(state.jobId);
279
+ if (!file)
280
+ return;
281
+ const snapshot = JSON.stringify(state, null, 2);
282
+ await fs_1.promises.writeFile(file, snapshot, 'utf-8');
283
+ }
284
+ async loadState(jobId) {
285
+ if (!this.storeDir)
286
+ return null;
287
+ const file = this.snapshotPath(jobId);
288
+ if (!file)
289
+ return null;
290
+ try {
291
+ const data = await fs_1.promises.readFile(file, 'utf-8');
292
+ return JSON.parse(data);
293
+ }
294
+ catch {
295
+ return null;
296
+ }
297
+ }
298
+ async ensureStoreDir() {
299
+ if (!this.storeDir)
300
+ return;
301
+ await fs_1.promises.mkdir(this.storeDir, { recursive: true });
302
+ }
303
+ snapshotPath(jobId) {
304
+ if (!this.storeDir)
305
+ return undefined;
306
+ return path_1.default.join(this.storeDir, `${jobId}.json`);
307
+ }
308
+ eventsPath(jobId) {
309
+ if (!this.storeDir)
310
+ return undefined;
311
+ return path_1.default.join(this.storeDir, `${jobId}.events.jsonl`);
312
+ }
313
+ async logEvent(jobId, event) {
314
+ if (!this.storeDir)
315
+ return;
316
+ await this.ensureStoreDir();
317
+ const file = this.eventsPath(jobId);
318
+ if (!file)
319
+ return;
320
+ const line = JSON.stringify(event);
321
+ await fs_1.promises.appendFile(file, `${line}\n`, 'utf-8');
322
+ }
323
+ emit(event) {
324
+ if (!event.at) {
325
+ event.at = new Date().toISOString();
326
+ }
327
+ if (this.onEvent) {
328
+ this.onEvent(event);
329
+ }
330
+ // Persist events only when jobId is present in payload or snapshot is known
331
+ const jobId = (event.payload && event.payload.jobId) || undefined;
332
+ if (jobId && this.storeDir) {
333
+ this.logEvent(jobId, event).catch(() => {
334
+ /* silent */
335
+ });
336
+ }
337
+ }
338
+ }
339
+ exports.JobRunner = JobRunner;
@@ -12,13 +12,23 @@ export interface AgentReducerConfig {
12
12
  /** Default agent name to use if task doesn't specify one */
13
13
  defaultAgent: string;
14
14
  }
15
+ /**
16
+ * Options for MapLLM constructor
17
+ */
18
+ export interface MapLLMOptions {
19
+ /** Whether to execute a final reduce pass after all chunks (default: true) */
20
+ finalReduce?: boolean;
21
+ /** Threshold in bytes to trigger automatic intermediate reduce (optional) */
22
+ reduceThresholdBytes?: number;
23
+ }
15
24
  /**
16
25
  * MapLLM - Orchestrateur principal pour le reduce hiérarchique
17
26
  */
18
27
  export declare class MapLLM {
19
28
  private loader;
20
29
  private agentConfig?;
21
- constructor(loader: NativeLoader);
30
+ private readonly options;
31
+ constructor(loader: NativeLoader, options?: MapLLMOptions);
22
32
  /**
23
33
  * Vérifie si le loader fournit des agents (TaskListLoader)
24
34
  */