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