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/dist/index.js CHANGED
@@ -1925,8 +1925,13 @@ ${block}` : block;
1925
1925
  var ContinuationPlanner = class {
1926
1926
  plan;
1927
1927
  outputs = /* @__PURE__ */ new Map();
1928
- constructor(plan) {
1928
+ retryCount = /* @__PURE__ */ new Map();
1929
+ onStepSkipped;
1930
+ onStepFailed;
1931
+ constructor(plan, callbacks) {
1929
1932
  this.plan = { ...plan, steps: plan.steps.map((s) => ({ ...s })) };
1933
+ this.onStepSkipped = callbacks?.onStepSkipped;
1934
+ this.onStepFailed = callbacks?.onStepFailed;
1930
1935
  }
1931
1936
  // -------------------------------------------------------------------------
1932
1937
  // State queries
@@ -2007,17 +2012,31 @@ var ContinuationPlanner = class {
2007
2012
  /**
2008
2013
  * Marks a step as failed and propagates `skipped` to all transitively dependent steps.
2009
2014
  */
2010
- markStepFailed(stepId) {
2015
+ markStepFailed(stepId, reason = "step failed") {
2011
2016
  this._updateStep(stepId, { status: "failed" });
2017
+ this.onStepFailed?.(stepId, reason);
2012
2018
  this._skipDependants(stepId);
2013
2019
  }
2014
2020
  /**
2015
2021
  * Marks a step as skipped (e.g., condition not met) and propagates to its dependants.
2016
2022
  */
2017
- markStepSkipped(stepId) {
2023
+ markStepSkipped(stepId, reason = "condition not met") {
2018
2024
  this._updateStep(stepId, { status: "skipped" });
2025
+ this.onStepSkipped?.(stepId, reason);
2019
2026
  this._skipDependants(stepId);
2020
2027
  }
2028
+ /**
2029
+ * Resets a step back to `pending` for a retry attempt.
2030
+ * Increments the internal retry counter for the step.
2031
+ */
2032
+ markStepPending(stepId) {
2033
+ this._updateStep(stepId, { status: "pending" });
2034
+ this.retryCount.set(stepId, (this.retryCount.get(stepId) ?? 0) + 1);
2035
+ }
2036
+ /** Returns how many times a step has been retried. */
2037
+ getRetryCount(stepId) {
2038
+ return this.retryCount.get(stepId) ?? 0;
2039
+ }
2021
2040
  // -------------------------------------------------------------------------
2022
2041
  // Output / input mapping helpers
2023
2042
  // -------------------------------------------------------------------------
@@ -2059,6 +2078,7 @@ var ContinuationPlanner = class {
2059
2078
  for (const step of this.plan.steps) {
2060
2079
  if (step.status === "pending" && step.dependsOn.some((d) => toSkip.has(d))) {
2061
2080
  this._updateStep(step.stepId, { status: "skipped" });
2081
+ this.onStepSkipped?.(step.stepId, `dependency '${failedStepId}' failed or was skipped`);
2062
2082
  toSkip.add(step.stepId);
2063
2083
  changed = true;
2064
2084
  }
@@ -2587,6 +2607,21 @@ var SessionManager = class {
2587
2607
  }
2588
2608
  this.media = new MediaBridge(this.adapter);
2589
2609
  this.stepCounter = new StepCounter(config.maxSteps ?? 20);
2610
+ if (config.maxSteps !== void 0 && config.maxIterations === void 0) {
2611
+ const defaultMaxIts = 10;
2612
+ if (config.maxSteps > defaultMaxIts) {
2613
+ this.logger.warn(
2614
+ `[Config] maxSteps=${config.maxSteps} is set but maxIterations is not. The default maxIterations=${defaultMaxIts} may stop the agent before maxSteps is reached. Consider setting maxIterations to at least Math.ceil(maxSteps / avgToolCallsPerTurn).`
2615
+ );
2616
+ }
2617
+ }
2618
+ if (config.maxSteps !== void 0 && config.maxIterations !== void 0) {
2619
+ if (config.maxSteps > config.maxIterations * 10) {
2620
+ this.logger.warn(
2621
+ `[Config] maxSteps (${config.maxSteps}) is much larger than maxIterations (${config.maxIterations}). The agent will be stopped by maxIterations before maxSteps is ever reached.`
2622
+ );
2623
+ }
2624
+ }
2590
2625
  this.toolResponseProcessor = config.toolResponseProcessor instanceof ToolResponseProcessor ? config.toolResponseProcessor : config.toolResponseProcessor ? config.toolResponseProcessor : new ToolResponseProcessor();
2591
2626
  for (const strategy of config.compressionStrategies || []) {
2592
2627
  this.contextManager.registerStrategy(strategy);
@@ -2786,16 +2821,39 @@ var SessionManager = class {
2786
2821
  * ```
2787
2822
  */
2788
2823
  setPlan(steps, strategy = "sequential") {
2789
- this.continuationPlanner = new ContinuationPlanner({
2790
- steps,
2791
- currentStepIndex: 0,
2792
- strategy
2793
- });
2824
+ this.continuationPlanner = new ContinuationPlanner(
2825
+ { steps, currentStepIndex: 0, strategy },
2826
+ {
2827
+ onStepFailed: (stepId, reason) => this.emitTrace("planning", "step_failed", { stepId, reason }),
2828
+ onStepSkipped: (stepId, reason) => this.emitTrace("planning", "step_skipped", { stepId, reason })
2829
+ }
2830
+ );
2794
2831
  this.context.metadata["continuationPlan"] = this.continuationPlanner.getPlan();
2795
2832
  this.logger.debug(`[ContinuationPlanner] Plan set with ${steps.length} steps (strategy: ${strategy})`);
2796
2833
  this.emitTrace("planning", "plan_set", { stepCount: steps.length, strategy });
2797
2834
  }
2798
2835
  // -----------------------------------------------------------------------
2836
+ // Plan inspection API
2837
+ // -----------------------------------------------------------------------
2838
+ /**
2839
+ * Returns a snapshot of the current continuation plan, or `null` if no plan
2840
+ * has been set via `setPlan()`.
2841
+ *
2842
+ * Use this after `run()` to inspect which steps completed, failed, or were skipped.
2843
+ *
2844
+ * @since 1.4.4
2845
+ *
2846
+ * @example
2847
+ * ```typescript
2848
+ * await session.run('Run the pipeline');
2849
+ * const plan = session.getPlan();
2850
+ * const failed = plan?.steps.filter(s => s.status === 'failed');
2851
+ * ```
2852
+ */
2853
+ getPlan() {
2854
+ return this.continuationPlanner ? this.continuationPlanner.getPlan() : null;
2855
+ }
2856
+ // -----------------------------------------------------------------------
2799
2857
  // Goal planning API
2800
2858
  // -----------------------------------------------------------------------
2801
2859
  /**
@@ -2887,7 +2945,7 @@ Respond ONLY with valid JSON (no markdown, no explanations):
2887
2945
  const response = await this.adapter.complete({
2888
2946
  model: this.config.model,
2889
2947
  messages: [{ role: "user", content: planningPrompt }],
2890
- maxTokens: this.config.maxCompletionTokens ?? 2e3
2948
+ maxTokens: this.config.maxCompletionTokens ?? 4e3
2891
2949
  });
2892
2950
  const raw = response.content.replace(/```json|```/g, "").trim();
2893
2951
  const parsed = JSON.parse(raw);
@@ -3204,6 +3262,36 @@ ${planStatus}`;
3204
3262
  (s) => s.toolName === tc.name && s.status === "running"
3205
3263
  );
3206
3264
  if (runningStep) {
3265
+ if (runningStep.verify) {
3266
+ const maxRetries = runningStep.verify.maxRetries ?? 0;
3267
+ const retryCount = this.continuationPlanner.getRetryCount(runningStep.stepId);
3268
+ let verdict;
3269
+ try {
3270
+ verdict = await runningStep.verify.check(content, args);
3271
+ } catch (verifyErr) {
3272
+ verdict = { status: "fail", reason: `Verifier threw: ${verifyErr.message}` };
3273
+ }
3274
+ if (verdict.status === "fail" || verdict.status === "retry" && retryCount >= maxRetries) {
3275
+ this.continuationPlanner.markStepFailed(runningStep.stepId);
3276
+ this.context.metadata["continuationPlan"] = this.continuationPlanner.getPlan();
3277
+ this.emitTrace("planning", "step_failed", {
3278
+ stepId: runningStep.stepId,
3279
+ reason: verdict.reason ?? "verifier returned fail",
3280
+ retryCount
3281
+ });
3282
+ return content;
3283
+ }
3284
+ if (verdict.status === "retry") {
3285
+ this.continuationPlanner.markStepPending(runningStep.stepId);
3286
+ this.context.metadata["continuationPlan"] = this.continuationPlanner.getPlan();
3287
+ this.emitTrace("planning", "step_retry", {
3288
+ stepId: runningStep.stepId,
3289
+ reason: verdict.reason,
3290
+ retryCount: this.continuationPlanner.getRetryCount(runningStep.stepId)
3291
+ });
3292
+ return content;
3293
+ }
3294
+ }
3207
3295
  this.continuationPlanner.markStepDone(runningStep.stepId, content);
3208
3296
  if (runningStep.outputKey) {
3209
3297
  const outputs = this.context.metadata["toolOutputs"] ?? {};
@@ -3274,7 +3362,7 @@ ${planStatus}`;
3274
3362
  const maxIts = this.config.maxIterations || 10;
3275
3363
  this.iterations = 0;
3276
3364
  this.stepCounter = new StepCounter(this.config.maxSteps ?? 20);
3277
- const maxCompletionTokens = this.config.maxCompletionTokens ?? 2e3;
3365
+ const maxCompletionTokens = this.config.maxCompletionTokens ?? 4e3;
3278
3366
  while (this.iterations < maxIts) {
3279
3367
  this.iterations++;
3280
3368
  this.logger.debug(`ReAct Iteration ${this.iterations}/${maxIts}`);
@@ -3471,7 +3559,7 @@ ${planStatus}`;
3471
3559
  const maxIts = this.config.maxIterations || 10;
3472
3560
  this.iterations = 0;
3473
3561
  this.stepCounter = new StepCounter(this.config.maxSteps ?? 20);
3474
- const maxCompletionTokens = this.config.maxCompletionTokens ?? 2e3;
3562
+ const maxCompletionTokens = this.config.maxCompletionTokens ?? 4e3;
3475
3563
  while (this.iterations < maxIts) {
3476
3564
  this.iterations++;
3477
3565
  this.logger.debug(`[stream] ReAct Iteration ${this.iterations}/${maxIts}`);