lemura 1.4.3 → 1.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,52 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.4.4] - 2026-05-21
9
+
10
+ ### Added
11
+
12
+ - **`StepVerifier` on `ContinuationStep`** (`verify?: StepVerifier`): Optional semantic verifier called after a tool executes to confirm the sub-goal is actually satisfied — independent of the LLM's own assessment. Returns `pass`, `fail`, or `retry`. A `retry` verdict resets the step to `pending` and re-queues it for the next iteration; `fail` triggers BFS propagation to dependant steps. Supports `maxRetries?: number` (default: `0`) before a `retry` verdict is forced to `fail`. Verifier exceptions are caught and treated as `fail`.
13
+
14
+ - **`StepVerifierResult` interface**: `{ status: 'pass' | 'fail' | 'retry'; reason?: string }` — the return type of `StepVerifier.check()`.
15
+
16
+ - **`ContinuationPlanner.markStepPending(stepId)`**: Resets a step to `pending` for a retry attempt and increments its internal retry counter.
17
+
18
+ - **`ContinuationPlanner.getRetryCount(stepId)`**: Returns how many times a step has been retried.
19
+
20
+ - **`SessionManager.getPlan()`**: New public method returning a snapshot of the current `ContinuationPlan` (or `null` if no plan is active). Use this after `run()` for post-mortem inspection of step statuses without relying on `onTrace`.
21
+
22
+ - **`TraceEvent.type: 'verification'`**: New trace type. Emitted as `step_retry` when a verifier returns `retry`, and supplements the existing `step_failed` / `step_skipped` events now emitted by the planner.
23
+
24
+ - **`onStepFailed` / `onStepSkipped` callbacks on `ContinuationPlanner`**: Internal callbacks wired at `setPlan()` time to emit `planning/step_failed` and `planning/step_skipped` trace events — including BFS-propagated skips, which were previously invisible to `onTrace`.
25
+
26
+ ### Changed
27
+
28
+ - **`maxCompletionTokens` default raised from `2_000` to `4_000`**: The previous default was too low for complex reasoning chains, causing silent mid-thought truncations. Fully backward-compatible — any explicit `maxCompletionTokens` in existing configs is unchanged.
29
+
30
+ - **`ContinuationPlanner.markStepFailed(stepId, reason?)`** and **`markStepSkipped(stepId, reason?)`**: Both methods now accept an optional `reason` string surfaced in trace events and BFS-propagated skip messages.
31
+
32
+ ### Fixed
33
+
34
+ - **BFS-propagated step skips were invisible to `onTrace`**: When a step failed and its dependants were automatically skipped, no trace events were emitted for the skipped steps. The new `onStepSkipped` callback fires for every BFS-propagated skip, making silent plan collapses visible.
35
+
36
+ - **No way to inspect plan state after `run()`**: `continuationPlanner` was private with no public accessor. The new `getPlan()` method exposes a safe snapshot for post-run debugging.
37
+
38
+ ### Other
39
+
40
+ - **Config coherence warning** (`maxSteps` vs `maxIterations`): The `SessionManager` constructor now logs a `warn`-level message when `maxSteps` is explicitly set without a matching `maxIterations`, or when `maxSteps` is so large relative to `maxIterations` that it can never be reached. No behavior change — purely diagnostic, fully backward-compatible.
41
+
42
+ ## [1.4.3] - 2026-05-14
43
+
44
+ ### Changed
45
+
46
+ - **npm package metadata**: Added `repository`, `bugs`, `homepage`, `keywords`, `author`, `license`, `engines`, `sideEffects`, and `files` fields to `package.json` to meet npm publishing standards.
47
+
48
+ ## [1.4.2] - 2026-05-14
49
+
50
+ ### Fixed
51
+
52
+ - **OpenAI wire format for tool calls**: `OpenAICompatibleAdapter.toOpenAIMessages()` now converts lemura's internal camelCase `toolCalls` (assistant turns) and `name`-keyed tool results (tool turns) to the proper OpenAI wire format (`tool_calls` / `tool_call_id`). Previously, providers enforcing strict OpenAI compatibility (e.g. Cerebras) would reject these messages with a 400/422 error.
53
+
8
54
  ## [1.4.1] - 2026-05-07
9
55
 
10
56
  ### Added
@@ -221,7 +221,7 @@ interface SessionConfig {
221
221
  skillTokenBudget?: number;
222
222
  /**
223
223
  * Maximum tokens the provider may generate per completion call.
224
- * Defaults to 2 000 when not set. This is separate from `maxTokens`
224
+ * Defaults to 4 000 when not set. This is separate from `maxTokens`
225
225
  * which controls the total context window size.
226
226
  */
227
227
  maxCompletionTokens?: number;
@@ -271,7 +271,7 @@ interface SessionConfig {
271
271
  /** Rich trace event for observability */
272
272
  interface TraceEvent {
273
273
  sessionId?: string;
274
- type: 'planning' | 'budget' | 'tool_call' | 'tool_result' | 'thinking' | 'system' | 'compression' | 'error' | 'skill';
274
+ type: 'planning' | 'budget' | 'tool_call' | 'tool_result' | 'thinking' | 'system' | 'compression' | 'error' | 'skill' | 'verification';
275
275
  name: string;
276
276
  input?: any;
277
277
  output?: any;
@@ -221,7 +221,7 @@ interface SessionConfig {
221
221
  skillTokenBudget?: number;
222
222
  /**
223
223
  * Maximum tokens the provider may generate per completion call.
224
- * Defaults to 2 000 when not set. This is separate from `maxTokens`
224
+ * Defaults to 4 000 when not set. This is separate from `maxTokens`
225
225
  * which controls the total context window size.
226
226
  */
227
227
  maxCompletionTokens?: number;
@@ -271,7 +271,7 @@ interface SessionConfig {
271
271
  /** Rich trace event for observability */
272
272
  interface TraceEvent {
273
273
  sessionId?: string;
274
- type: 'planning' | 'budget' | 'tool_call' | 'tool_result' | 'thinking' | 'system' | 'compression' | 'error' | 'skill';
274
+ type: 'planning' | 'budget' | 'tool_call' | 'tool_result' | 'thinking' | 'system' | 'compression' | 'error' | 'skill' | 'verification';
275
275
  name: string;
276
276
  input?: any;
277
277
  output?: any;
package/dist/index.d.mts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { a as IProviderAdapter, T as TranscriptionRequest, d as TranscriptionResponse, e as SynthesisRequest, A as AudioChunk, V as VisionRequest, f as VisionResponse, g as ImageGenRequest, h as ImageGenResponse, M as ModelInfo, C as ContextWindow, i as Turn, j as ContentBlock, I as IToolDefinition } from './adapters-BhTAnrOM.mjs';
2
2
  export { k as CompletionChunk, l as CompletionRequest, m as CompletionResponse, b as IContextStrategy, c as IScratchpadAdapter, n as IStorageAdapter, N as NormalizedMessage, o as STMItem, p as STMRegistryConfig, S as ShortTermMemoryRegistry, q as TokenUsage, r as ToolCall, s as ToolContext, t as ToolResult } from './adapters-BhTAnrOM.mjs';
3
- import { S as SessionConfig, I as IToolResponseProcessor, T as ToolResponseEvaluation, M as MCPServerConfig, a as MCPToolDefinition } from './agent-DfN5nNXc.mjs';
4
- export { b as MCPJsonRpcRequest, c as MCPJsonRpcResponse, d as MCPTransportType, e as MediaConfig, f as ToolDecision, g as ToolExecutionBudget, h as ToolFirewallConfig, i as ToolFirewallRule, j as TraceEvent } from './agent-DfN5nNXc.mjs';
3
+ import { S as SessionConfig, I as IToolResponseProcessor, T as ToolResponseEvaluation, M as MCPServerConfig, a as MCPToolDefinition } from './agent-81ZUiP-T.mjs';
4
+ export { b as MCPJsonRpcRequest, c as MCPJsonRpcResponse, d as MCPTransportType, e as MediaConfig, f as ToolDecision, g as ToolExecutionBudget, h as ToolFirewallConfig, i as ToolFirewallRule, j as TraceEvent } from './agent-81ZUiP-T.mjs';
5
5
  export { LemuraAdapterError, LemuraContextOverflowError, LemuraError, LemuraMCPConnectionError, LemuraMCPError, LemuraMCPTimeoutError, LemuraMaxIterationsError, LemuraSkillInjectionError, LemuraToolNotFoundError, LemuraToolTimeoutError, LemuraToolValidationError } from './types/index.mjs';
6
6
  import { I as ILogger } from './logger-DxvKliuk.mjs';
7
7
  export { L as LogLevel, a as LogMetadata, S as Severity } from './logger-DxvKliuk.mjs';
@@ -30,67 +30,6 @@ declare class MediaBridge {
30
30
  supportsVision(): boolean;
31
31
  }
32
32
 
33
- interface Goal {
34
- id: string;
35
- statement: string;
36
- /** High-level sub-goals decomposed from the main statement */
37
- decomposition: string[];
38
- successCriteria: string[];
39
- injectionFrequency: 'always' | 'every_N_turns' | 'on_compression';
40
- injectionPosition: 'system_prompt' | 'pre_turn';
41
- /** Sub-goals already completed — updated via `markSubGoalDone()` */
42
- completedSubGoals?: string[];
43
- }
44
- /**
45
- * GoalInjector keeps the original task objective visible throughout the ReAct loop,
46
- * preventing goal drift after many tool calls and context compressions.
47
- *
48
- * Usage in SessionManager:
49
- * - For `system_prompt` position: call `injectInto(prompt)` which appends the goal block.
50
- * - For `pre_turn` position: call `getFormattedBlock()` and push as a system message.
51
- */
52
- declare class GoalInjector {
53
- private goal;
54
- private turnsSinceInjection;
55
- constructor(goal: Goal);
56
- /**
57
- * Returns the formatted `[CURRENT GOAL]` block string — without caring about
58
- * where it will be placed. Callers decide whether to append to a system prompt
59
- * or push as a separate message.
60
- */
61
- getFormattedBlock(): string;
62
- /**
63
- * Appends the goal block to the given prompt string (for `system_prompt` position).
64
- * For `pre_turn` position, use `getFormattedBlock()` directly.
65
- *
66
- * @param prompt - The existing system prompt to append to.
67
- */
68
- injectInto(prompt: string): string;
69
- /**
70
- * Returns true when the goal should be re-injected this turn,
71
- * based on `injectionFrequency`.
72
- *
73
- * @param turnIndex - The current turn index in the ReAct loop (0-based)
74
- * @param compressionOccurred - Whether context was compressed this iteration
75
- * @param injectionN - The N for 'every_N_turns' frequency (default: 3)
76
- */
77
- shouldInjectThisTurn(turnIndex: number, compressionOccurred?: boolean, injectionN?: number): boolean;
78
- /**
79
- * Updates the goal with new sub-goal decomposition and success criteria,
80
- * typically populated by the mini-planning LLM call.
81
- */
82
- updateDecomposition(decomposition: string[], successCriteria?: string[]): void;
83
- /**
84
- * Marks a sub-goal as completed so it moves to the "completed" section
85
- * in subsequent injections.
86
- */
87
- markSubGoalDone(subGoal: string): void;
88
- /** Returns a snapshot of the current goal state (safe to store in context.metadata). */
89
- getGoal(): Goal;
90
- /** Increments the internal turn counter (used for `every_N_turns` frequency). */
91
- incrementTurn(): void;
92
- }
93
-
94
33
  /**
95
34
  * An optional condition that gates a step's execution on the output of a prior step.
96
35
  * When the condition is not met, the step is automatically marked `skipped`.
@@ -101,6 +40,44 @@ interface StepCondition {
101
40
  /** Substring that must be present in the prior step's output to allow this step to run */
102
41
  outputContains: string;
103
42
  }
43
+ /**
44
+ * Result returned by a `StepVerifier.check` function.
45
+ * - `pass` — the sub-goal is achieved; the step is marked `done`.
46
+ * - `fail` — the sub-goal failed; the step is marked `failed` and BFS propagates to dependants.
47
+ * - `retry` — the output is unsatisfactory but retriable; the step is reset to `pending`.
48
+ */
49
+ interface StepVerifierResult {
50
+ status: 'pass' | 'fail' | 'retry';
51
+ reason?: string;
52
+ }
53
+ /**
54
+ * Optional semantic verifier attached to a `ContinuationStep`.
55
+ * Called after the tool executes successfully to confirm the sub-goal is actually met.
56
+ *
57
+ * @example
58
+ * verify: {
59
+ * maxRetries: 2,
60
+ * check: (output) => {
61
+ * const data = JSON.parse(output);
62
+ * return data.rows?.length > 0
63
+ * ? { status: 'pass' }
64
+ * : { status: 'retry', reason: 'Empty result set' };
65
+ * }
66
+ * }
67
+ */
68
+ interface StepVerifier {
69
+ /**
70
+ * Inspects the tool output and decides whether the sub-goal is satisfied.
71
+ * @param output - Serialised tool result string
72
+ * @param args - The resolved arguments that were passed to the tool
73
+ */
74
+ check: (output: string, args: Record<string, unknown>) => Promise<StepVerifierResult> | StepVerifierResult;
75
+ /**
76
+ * Maximum number of `retry` verdicts allowed before the step is forced to `failed`.
77
+ * Defaults to 0 (no retries — a `retry` verdict immediately becomes `failed`).
78
+ */
79
+ maxRetries?: number;
80
+ }
104
81
  interface ContinuationStep {
105
82
  stepId: string;
106
83
  toolName: string;
@@ -124,6 +101,14 @@ interface ContinuationStep {
124
101
  * contains the given substring. When the condition is not met, the step is skipped.
125
102
  */
126
103
  condition?: StepCondition;
104
+ /**
105
+ * Optional semantic verifier: called after the tool executes to confirm the
106
+ * sub-goal is actually satisfied. Supports `pass / fail / retry` verdicts
107
+ * with a configurable `maxRetries` count.
108
+ *
109
+ * @since 1.4.4
110
+ */
111
+ verify?: StepVerifier;
127
112
  }
128
113
  interface ContinuationPlan {
129
114
  steps: ContinuationStep[];
@@ -152,7 +137,13 @@ interface ContinuationPlan {
152
137
  declare class ContinuationPlanner {
153
138
  private plan;
154
139
  private outputs;
155
- constructor(plan: ContinuationPlan);
140
+ private retryCount;
141
+ private onStepSkipped;
142
+ private onStepFailed;
143
+ constructor(plan: ContinuationPlan, callbacks?: {
144
+ onStepSkipped?: (stepId: string, reason: string) => void;
145
+ onStepFailed?: (stepId: string, reason: string) => void;
146
+ });
156
147
  /** Returns the current plan (deep copy) */
157
148
  getPlan(): ContinuationPlan;
158
149
  /** Returns a human-readable status string with icons (injected before each iteration) */
@@ -172,11 +163,18 @@ declare class ContinuationPlanner {
172
163
  /**
173
164
  * Marks a step as failed and propagates `skipped` to all transitively dependent steps.
174
165
  */
175
- markStepFailed(stepId: string): void;
166
+ markStepFailed(stepId: string, reason?: string): void;
176
167
  /**
177
168
  * Marks a step as skipped (e.g., condition not met) and propagates to its dependants.
178
169
  */
179
- markStepSkipped(stepId: string): void;
170
+ markStepSkipped(stepId: string, reason?: string): void;
171
+ /**
172
+ * Resets a step back to `pending` for a retry attempt.
173
+ * Increments the internal retry counter for the step.
174
+ */
175
+ markStepPending(stepId: string): void;
176
+ /** Returns how many times a step has been retried. */
177
+ getRetryCount(stepId: string): number;
180
178
  /** Retrieves an output stored by `outputKey` from a completed step */
181
179
  getOutput(key: string): string | undefined;
182
180
  /**
@@ -190,6 +188,67 @@ declare class ContinuationPlanner {
190
188
  private _advanceIndex;
191
189
  }
192
190
 
191
+ interface Goal {
192
+ id: string;
193
+ statement: string;
194
+ /** High-level sub-goals decomposed from the main statement */
195
+ decomposition: string[];
196
+ successCriteria: string[];
197
+ injectionFrequency: 'always' | 'every_N_turns' | 'on_compression';
198
+ injectionPosition: 'system_prompt' | 'pre_turn';
199
+ /** Sub-goals already completed — updated via `markSubGoalDone()` */
200
+ completedSubGoals?: string[];
201
+ }
202
+ /**
203
+ * GoalInjector keeps the original task objective visible throughout the ReAct loop,
204
+ * preventing goal drift after many tool calls and context compressions.
205
+ *
206
+ * Usage in SessionManager:
207
+ * - For `system_prompt` position: call `injectInto(prompt)` which appends the goal block.
208
+ * - For `pre_turn` position: call `getFormattedBlock()` and push as a system message.
209
+ */
210
+ declare class GoalInjector {
211
+ private goal;
212
+ private turnsSinceInjection;
213
+ constructor(goal: Goal);
214
+ /**
215
+ * Returns the formatted `[CURRENT GOAL]` block string — without caring about
216
+ * where it will be placed. Callers decide whether to append to a system prompt
217
+ * or push as a separate message.
218
+ */
219
+ getFormattedBlock(): string;
220
+ /**
221
+ * Appends the goal block to the given prompt string (for `system_prompt` position).
222
+ * For `pre_turn` position, use `getFormattedBlock()` directly.
223
+ *
224
+ * @param prompt - The existing system prompt to append to.
225
+ */
226
+ injectInto(prompt: string): string;
227
+ /**
228
+ * Returns true when the goal should be re-injected this turn,
229
+ * based on `injectionFrequency`.
230
+ *
231
+ * @param turnIndex - The current turn index in the ReAct loop (0-based)
232
+ * @param compressionOccurred - Whether context was compressed this iteration
233
+ * @param injectionN - The N for 'every_N_turns' frequency (default: 3)
234
+ */
235
+ shouldInjectThisTurn(turnIndex: number, compressionOccurred?: boolean, injectionN?: number): boolean;
236
+ /**
237
+ * Updates the goal with new sub-goal decomposition and success criteria,
238
+ * typically populated by the mini-planning LLM call.
239
+ */
240
+ updateDecomposition(decomposition: string[], successCriteria?: string[]): void;
241
+ /**
242
+ * Marks a sub-goal as completed so it moves to the "completed" section
243
+ * in subsequent injections.
244
+ */
245
+ markSubGoalDone(subGoal: string): void;
246
+ /** Returns a snapshot of the current goal state (safe to store in context.metadata). */
247
+ getGoal(): Goal;
248
+ /** Increments the internal turn counter (used for `every_N_turns` frequency). */
249
+ incrementTurn(): void;
250
+ }
251
+
193
252
  /**
194
253
  * Core entry point for lemura agent sessions.
195
254
  *
@@ -320,6 +379,22 @@ declare class SessionManager {
320
379
  * ```
321
380
  */
322
381
  setPlan(steps: ContinuationStep[], strategy?: ContinuationPlan['strategy']): void;
382
+ /**
383
+ * Returns a snapshot of the current continuation plan, or `null` if no plan
384
+ * has been set via `setPlan()`.
385
+ *
386
+ * Use this after `run()` to inspect which steps completed, failed, or were skipped.
387
+ *
388
+ * @since 1.4.4
389
+ *
390
+ * @example
391
+ * ```typescript
392
+ * await session.run('Run the pipeline');
393
+ * const plan = session.getPlan();
394
+ * const failed = plan?.steps.filter(s => s.status === 'failed');
395
+ * ```
396
+ */
397
+ getPlan(): ContinuationPlan | null;
323
398
  /**
324
399
  * Manually sets the agent's goal, bypassing the automatic mini-planning LLM call.
325
400
  *
@@ -612,4 +687,4 @@ declare class MCPClientRegistry {
612
687
  private _bridge;
613
688
  }
614
689
 
615
- export { AudioChunk, ContentBlock, ContextWindow, type ContinuationPlan, ContinuationPlanner, type ContinuationStep, FinalResponseFormatter, type Goal, GoalInjector, ILogger, IProviderAdapter, IToolDefinition, IToolResponseProcessor, ImageGenRequest, ImageGenResponse, MCPClient, MCPClientRegistry, MCPServerConfig, MCPToolDefinition, MediaBridge, ModelInfo, SessionConfig, SessionManager, SkillInjector, type StepCondition, StepCounter, SynthesisRequest, ToolRegistry, ToolResponseEvaluation, ToolResponseProcessor, type ToolResponseProcessorConfig, TranscriptionRequest, TranscriptionResponse, Turn, VisionRequest, VisionResponse };
690
+ export { AudioChunk, ContentBlock, ContextWindow, type ContinuationPlan, ContinuationPlanner, type ContinuationStep, FinalResponseFormatter, type Goal, GoalInjector, ILogger, IProviderAdapter, IToolDefinition, IToolResponseProcessor, ImageGenRequest, ImageGenResponse, MCPClient, MCPClientRegistry, MCPServerConfig, MCPToolDefinition, MediaBridge, ModelInfo, SessionConfig, SessionManager, SkillInjector, type StepCondition, StepCounter, type StepVerifier, type StepVerifierResult, SynthesisRequest, ToolRegistry, ToolResponseEvaluation, ToolResponseProcessor, type ToolResponseProcessorConfig, TranscriptionRequest, TranscriptionResponse, Turn, VisionRequest, VisionResponse };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { a as IProviderAdapter, T as TranscriptionRequest, d as TranscriptionResponse, e as SynthesisRequest, A as AudioChunk, V as VisionRequest, f as VisionResponse, g as ImageGenRequest, h as ImageGenResponse, M as ModelInfo, C as ContextWindow, i as Turn, j as ContentBlock, I as IToolDefinition } from './adapters-CVcfWf85.js';
2
2
  export { k as CompletionChunk, l as CompletionRequest, m as CompletionResponse, b as IContextStrategy, c as IScratchpadAdapter, n as IStorageAdapter, N as NormalizedMessage, o as STMItem, p as STMRegistryConfig, S as ShortTermMemoryRegistry, q as TokenUsage, r as ToolCall, s as ToolContext, t as ToolResult } from './adapters-CVcfWf85.js';
3
- import { S as SessionConfig, I as IToolResponseProcessor, T as ToolResponseEvaluation, M as MCPServerConfig, a as MCPToolDefinition } from './agent-DTcDIKIn.js';
4
- export { b as MCPJsonRpcRequest, c as MCPJsonRpcResponse, d as MCPTransportType, e as MediaConfig, f as ToolDecision, g as ToolExecutionBudget, h as ToolFirewallConfig, i as ToolFirewallRule, j as TraceEvent } from './agent-DTcDIKIn.js';
3
+ import { S as SessionConfig, I as IToolResponseProcessor, T as ToolResponseEvaluation, M as MCPServerConfig, a as MCPToolDefinition } from './agent-BH_uE2nT.js';
4
+ export { b as MCPJsonRpcRequest, c as MCPJsonRpcResponse, d as MCPTransportType, e as MediaConfig, f as ToolDecision, g as ToolExecutionBudget, h as ToolFirewallConfig, i as ToolFirewallRule, j as TraceEvent } from './agent-BH_uE2nT.js';
5
5
  export { LemuraAdapterError, LemuraContextOverflowError, LemuraError, LemuraMCPConnectionError, LemuraMCPError, LemuraMCPTimeoutError, LemuraMaxIterationsError, LemuraSkillInjectionError, LemuraToolNotFoundError, LemuraToolTimeoutError, LemuraToolValidationError } from './types/index.js';
6
6
  import { I as ILogger } from './logger-DxvKliuk.js';
7
7
  export { L as LogLevel, a as LogMetadata, S as Severity } from './logger-DxvKliuk.js';
@@ -30,67 +30,6 @@ declare class MediaBridge {
30
30
  supportsVision(): boolean;
31
31
  }
32
32
 
33
- interface Goal {
34
- id: string;
35
- statement: string;
36
- /** High-level sub-goals decomposed from the main statement */
37
- decomposition: string[];
38
- successCriteria: string[];
39
- injectionFrequency: 'always' | 'every_N_turns' | 'on_compression';
40
- injectionPosition: 'system_prompt' | 'pre_turn';
41
- /** Sub-goals already completed — updated via `markSubGoalDone()` */
42
- completedSubGoals?: string[];
43
- }
44
- /**
45
- * GoalInjector keeps the original task objective visible throughout the ReAct loop,
46
- * preventing goal drift after many tool calls and context compressions.
47
- *
48
- * Usage in SessionManager:
49
- * - For `system_prompt` position: call `injectInto(prompt)` which appends the goal block.
50
- * - For `pre_turn` position: call `getFormattedBlock()` and push as a system message.
51
- */
52
- declare class GoalInjector {
53
- private goal;
54
- private turnsSinceInjection;
55
- constructor(goal: Goal);
56
- /**
57
- * Returns the formatted `[CURRENT GOAL]` block string — without caring about
58
- * where it will be placed. Callers decide whether to append to a system prompt
59
- * or push as a separate message.
60
- */
61
- getFormattedBlock(): string;
62
- /**
63
- * Appends the goal block to the given prompt string (for `system_prompt` position).
64
- * For `pre_turn` position, use `getFormattedBlock()` directly.
65
- *
66
- * @param prompt - The existing system prompt to append to.
67
- */
68
- injectInto(prompt: string): string;
69
- /**
70
- * Returns true when the goal should be re-injected this turn,
71
- * based on `injectionFrequency`.
72
- *
73
- * @param turnIndex - The current turn index in the ReAct loop (0-based)
74
- * @param compressionOccurred - Whether context was compressed this iteration
75
- * @param injectionN - The N for 'every_N_turns' frequency (default: 3)
76
- */
77
- shouldInjectThisTurn(turnIndex: number, compressionOccurred?: boolean, injectionN?: number): boolean;
78
- /**
79
- * Updates the goal with new sub-goal decomposition and success criteria,
80
- * typically populated by the mini-planning LLM call.
81
- */
82
- updateDecomposition(decomposition: string[], successCriteria?: string[]): void;
83
- /**
84
- * Marks a sub-goal as completed so it moves to the "completed" section
85
- * in subsequent injections.
86
- */
87
- markSubGoalDone(subGoal: string): void;
88
- /** Returns a snapshot of the current goal state (safe to store in context.metadata). */
89
- getGoal(): Goal;
90
- /** Increments the internal turn counter (used for `every_N_turns` frequency). */
91
- incrementTurn(): void;
92
- }
93
-
94
33
  /**
95
34
  * An optional condition that gates a step's execution on the output of a prior step.
96
35
  * When the condition is not met, the step is automatically marked `skipped`.
@@ -101,6 +40,44 @@ interface StepCondition {
101
40
  /** Substring that must be present in the prior step's output to allow this step to run */
102
41
  outputContains: string;
103
42
  }
43
+ /**
44
+ * Result returned by a `StepVerifier.check` function.
45
+ * - `pass` — the sub-goal is achieved; the step is marked `done`.
46
+ * - `fail` — the sub-goal failed; the step is marked `failed` and BFS propagates to dependants.
47
+ * - `retry` — the output is unsatisfactory but retriable; the step is reset to `pending`.
48
+ */
49
+ interface StepVerifierResult {
50
+ status: 'pass' | 'fail' | 'retry';
51
+ reason?: string;
52
+ }
53
+ /**
54
+ * Optional semantic verifier attached to a `ContinuationStep`.
55
+ * Called after the tool executes successfully to confirm the sub-goal is actually met.
56
+ *
57
+ * @example
58
+ * verify: {
59
+ * maxRetries: 2,
60
+ * check: (output) => {
61
+ * const data = JSON.parse(output);
62
+ * return data.rows?.length > 0
63
+ * ? { status: 'pass' }
64
+ * : { status: 'retry', reason: 'Empty result set' };
65
+ * }
66
+ * }
67
+ */
68
+ interface StepVerifier {
69
+ /**
70
+ * Inspects the tool output and decides whether the sub-goal is satisfied.
71
+ * @param output - Serialised tool result string
72
+ * @param args - The resolved arguments that were passed to the tool
73
+ */
74
+ check: (output: string, args: Record<string, unknown>) => Promise<StepVerifierResult> | StepVerifierResult;
75
+ /**
76
+ * Maximum number of `retry` verdicts allowed before the step is forced to `failed`.
77
+ * Defaults to 0 (no retries — a `retry` verdict immediately becomes `failed`).
78
+ */
79
+ maxRetries?: number;
80
+ }
104
81
  interface ContinuationStep {
105
82
  stepId: string;
106
83
  toolName: string;
@@ -124,6 +101,14 @@ interface ContinuationStep {
124
101
  * contains the given substring. When the condition is not met, the step is skipped.
125
102
  */
126
103
  condition?: StepCondition;
104
+ /**
105
+ * Optional semantic verifier: called after the tool executes to confirm the
106
+ * sub-goal is actually satisfied. Supports `pass / fail / retry` verdicts
107
+ * with a configurable `maxRetries` count.
108
+ *
109
+ * @since 1.4.4
110
+ */
111
+ verify?: StepVerifier;
127
112
  }
128
113
  interface ContinuationPlan {
129
114
  steps: ContinuationStep[];
@@ -152,7 +137,13 @@ interface ContinuationPlan {
152
137
  declare class ContinuationPlanner {
153
138
  private plan;
154
139
  private outputs;
155
- constructor(plan: ContinuationPlan);
140
+ private retryCount;
141
+ private onStepSkipped;
142
+ private onStepFailed;
143
+ constructor(plan: ContinuationPlan, callbacks?: {
144
+ onStepSkipped?: (stepId: string, reason: string) => void;
145
+ onStepFailed?: (stepId: string, reason: string) => void;
146
+ });
156
147
  /** Returns the current plan (deep copy) */
157
148
  getPlan(): ContinuationPlan;
158
149
  /** Returns a human-readable status string with icons (injected before each iteration) */
@@ -172,11 +163,18 @@ declare class ContinuationPlanner {
172
163
  /**
173
164
  * Marks a step as failed and propagates `skipped` to all transitively dependent steps.
174
165
  */
175
- markStepFailed(stepId: string): void;
166
+ markStepFailed(stepId: string, reason?: string): void;
176
167
  /**
177
168
  * Marks a step as skipped (e.g., condition not met) and propagates to its dependants.
178
169
  */
179
- markStepSkipped(stepId: string): void;
170
+ markStepSkipped(stepId: string, reason?: string): void;
171
+ /**
172
+ * Resets a step back to `pending` for a retry attempt.
173
+ * Increments the internal retry counter for the step.
174
+ */
175
+ markStepPending(stepId: string): void;
176
+ /** Returns how many times a step has been retried. */
177
+ getRetryCount(stepId: string): number;
180
178
  /** Retrieves an output stored by `outputKey` from a completed step */
181
179
  getOutput(key: string): string | undefined;
182
180
  /**
@@ -190,6 +188,67 @@ declare class ContinuationPlanner {
190
188
  private _advanceIndex;
191
189
  }
192
190
 
191
+ interface Goal {
192
+ id: string;
193
+ statement: string;
194
+ /** High-level sub-goals decomposed from the main statement */
195
+ decomposition: string[];
196
+ successCriteria: string[];
197
+ injectionFrequency: 'always' | 'every_N_turns' | 'on_compression';
198
+ injectionPosition: 'system_prompt' | 'pre_turn';
199
+ /** Sub-goals already completed — updated via `markSubGoalDone()` */
200
+ completedSubGoals?: string[];
201
+ }
202
+ /**
203
+ * GoalInjector keeps the original task objective visible throughout the ReAct loop,
204
+ * preventing goal drift after many tool calls and context compressions.
205
+ *
206
+ * Usage in SessionManager:
207
+ * - For `system_prompt` position: call `injectInto(prompt)` which appends the goal block.
208
+ * - For `pre_turn` position: call `getFormattedBlock()` and push as a system message.
209
+ */
210
+ declare class GoalInjector {
211
+ private goal;
212
+ private turnsSinceInjection;
213
+ constructor(goal: Goal);
214
+ /**
215
+ * Returns the formatted `[CURRENT GOAL]` block string — without caring about
216
+ * where it will be placed. Callers decide whether to append to a system prompt
217
+ * or push as a separate message.
218
+ */
219
+ getFormattedBlock(): string;
220
+ /**
221
+ * Appends the goal block to the given prompt string (for `system_prompt` position).
222
+ * For `pre_turn` position, use `getFormattedBlock()` directly.
223
+ *
224
+ * @param prompt - The existing system prompt to append to.
225
+ */
226
+ injectInto(prompt: string): string;
227
+ /**
228
+ * Returns true when the goal should be re-injected this turn,
229
+ * based on `injectionFrequency`.
230
+ *
231
+ * @param turnIndex - The current turn index in the ReAct loop (0-based)
232
+ * @param compressionOccurred - Whether context was compressed this iteration
233
+ * @param injectionN - The N for 'every_N_turns' frequency (default: 3)
234
+ */
235
+ shouldInjectThisTurn(turnIndex: number, compressionOccurred?: boolean, injectionN?: number): boolean;
236
+ /**
237
+ * Updates the goal with new sub-goal decomposition and success criteria,
238
+ * typically populated by the mini-planning LLM call.
239
+ */
240
+ updateDecomposition(decomposition: string[], successCriteria?: string[]): void;
241
+ /**
242
+ * Marks a sub-goal as completed so it moves to the "completed" section
243
+ * in subsequent injections.
244
+ */
245
+ markSubGoalDone(subGoal: string): void;
246
+ /** Returns a snapshot of the current goal state (safe to store in context.metadata). */
247
+ getGoal(): Goal;
248
+ /** Increments the internal turn counter (used for `every_N_turns` frequency). */
249
+ incrementTurn(): void;
250
+ }
251
+
193
252
  /**
194
253
  * Core entry point for lemura agent sessions.
195
254
  *
@@ -320,6 +379,22 @@ declare class SessionManager {
320
379
  * ```
321
380
  */
322
381
  setPlan(steps: ContinuationStep[], strategy?: ContinuationPlan['strategy']): void;
382
+ /**
383
+ * Returns a snapshot of the current continuation plan, or `null` if no plan
384
+ * has been set via `setPlan()`.
385
+ *
386
+ * Use this after `run()` to inspect which steps completed, failed, or were skipped.
387
+ *
388
+ * @since 1.4.4
389
+ *
390
+ * @example
391
+ * ```typescript
392
+ * await session.run('Run the pipeline');
393
+ * const plan = session.getPlan();
394
+ * const failed = plan?.steps.filter(s => s.status === 'failed');
395
+ * ```
396
+ */
397
+ getPlan(): ContinuationPlan | null;
323
398
  /**
324
399
  * Manually sets the agent's goal, bypassing the automatic mini-planning LLM call.
325
400
  *
@@ -612,4 +687,4 @@ declare class MCPClientRegistry {
612
687
  private _bridge;
613
688
  }
614
689
 
615
- export { AudioChunk, ContentBlock, ContextWindow, type ContinuationPlan, ContinuationPlanner, type ContinuationStep, FinalResponseFormatter, type Goal, GoalInjector, ILogger, IProviderAdapter, IToolDefinition, IToolResponseProcessor, ImageGenRequest, ImageGenResponse, MCPClient, MCPClientRegistry, MCPServerConfig, MCPToolDefinition, MediaBridge, ModelInfo, SessionConfig, SessionManager, SkillInjector, type StepCondition, StepCounter, SynthesisRequest, ToolRegistry, ToolResponseEvaluation, ToolResponseProcessor, type ToolResponseProcessorConfig, TranscriptionRequest, TranscriptionResponse, Turn, VisionRequest, VisionResponse };
690
+ export { AudioChunk, ContentBlock, ContextWindow, type ContinuationPlan, ContinuationPlanner, type ContinuationStep, FinalResponseFormatter, type Goal, GoalInjector, ILogger, IProviderAdapter, IToolDefinition, IToolResponseProcessor, ImageGenRequest, ImageGenResponse, MCPClient, MCPClientRegistry, MCPServerConfig, MCPToolDefinition, MediaBridge, ModelInfo, SessionConfig, SessionManager, SkillInjector, type StepCondition, StepCounter, type StepVerifier, type StepVerifierResult, SynthesisRequest, ToolRegistry, ToolResponseEvaluation, ToolResponseProcessor, type ToolResponseProcessorConfig, TranscriptionRequest, TranscriptionResponse, Turn, VisionRequest, VisionResponse };