@elyracode/coding-agent 0.9.8 → 0.9.9

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 (35) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/cli/doctor-cli.d.ts +8 -0
  3. package/dist/cli/doctor-cli.d.ts.map +1 -0
  4. package/dist/cli/doctor-cli.js +81 -0
  5. package/dist/cli/doctor-cli.js.map +1 -0
  6. package/dist/core/agent-session.d.ts +35 -0
  7. package/dist/core/agent-session.d.ts.map +1 -1
  8. package/dist/core/agent-session.js +85 -24
  9. package/dist/core/agent-session.js.map +1 -1
  10. package/dist/core/settings-manager.d.ts +11 -0
  11. package/dist/core/settings-manager.d.ts.map +1 -1
  12. package/dist/core/settings-manager.js +3 -0
  13. package/dist/core/settings-manager.js.map +1 -1
  14. package/dist/core/slash-commands.d.ts.map +1 -1
  15. package/dist/core/slash-commands.js +1 -0
  16. package/dist/core/slash-commands.js.map +1 -1
  17. package/dist/core/smart-router.d.ts +37 -5
  18. package/dist/core/smart-router.d.ts.map +1 -1
  19. package/dist/core/smart-router.js +98 -19
  20. package/dist/core/smart-router.js.map +1 -1
  21. package/dist/main.d.ts.map +1 -1
  22. package/dist/main.js +4 -0
  23. package/dist/main.js.map +1 -1
  24. package/dist/modes/interactive/interactive-mode.d.ts +1 -0
  25. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  26. package/dist/modes/interactive/interactive-mode.js +58 -0
  27. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  28. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  29. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  30. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  31. package/examples/extensions/sandbox/package-lock.json +2 -2
  32. package/examples/extensions/sandbox/package.json +1 -1
  33. package/examples/extensions/with-deps/package-lock.json +2 -2
  34. package/examples/extensions/with-deps/package.json +1 -1
  35. package/package.json +5 -5
@@ -2246,6 +2246,11 @@ export class InteractiveMode {
2246
2246
  await this.handleReplayCommand(text.slice(7).trim());
2247
2247
  return;
2248
2248
  }
2249
+ if (text === "/route") {
2250
+ this.editor.setText("");
2251
+ await this.handleRouteCommand();
2252
+ return;
2253
+ }
2249
2254
  // Handle bash command (! for normal, !! for excluded from context)
2250
2255
  if (text.startsWith("!")) {
2251
2256
  const isExcluded = text.startsWith("!!");
@@ -2298,6 +2303,15 @@ export class InteractiveMode {
2298
2303
  this.unsubscribe = this.session.subscribe(async (event) => {
2299
2304
  await this.handleEvent(event);
2300
2305
  });
2306
+ // Feed the active goal budget into smart routing so it can cap tiers
2307
+ // as the budget depletes.
2308
+ this.session.setRoutingBudgetProvider(() => {
2309
+ const goal = this._activeGoal;
2310
+ if (!goal || goal.budgetUsd === undefined)
2311
+ return undefined;
2312
+ const spent = this.session.getSessionStats().cost - (goal.startCostUsd ?? 0);
2313
+ return spent / goal.budgetUsd;
2314
+ });
2301
2315
  }
2302
2316
  async handleEvent(event) {
2303
2317
  if (!this.isInitialized) {
@@ -2344,6 +2358,11 @@ export class InteractiveMode {
2344
2358
  this.footer.invalidate();
2345
2359
  this.updateEditorBorderColor();
2346
2360
  break;
2361
+ case "smart_routing":
2362
+ this.chatContainer.addChild(new Text(theme.fg("dim", `smart routing: ${event.tier} \u2014 ${event.reason} \u2192 ${event.toModel} (was ${event.fromModel})`), 1, 0));
2363
+ this.footer.invalidate();
2364
+ this.ui.requestRender();
2365
+ break;
2347
2366
  case "message_start":
2348
2367
  if (event.message.role === "custom") {
2349
2368
  this.addMessageToChat(event.message);
@@ -3716,6 +3735,35 @@ export class InteractiveMode {
3716
3735
  this.chatContainer.addChild(new Text(`${theme.bold(theme.fg("accent", "Goal set:"))} ${goalDisplay}\n${theme.fg("muted", `The agent will keep working until this command exits with code 0.${budgetNote} Type /goal to clear.`)}`, 1, 0));
3717
3736
  this.ui.requestRender();
3718
3737
  }
3738
+ async handleRouteCommand() {
3739
+ if (!this.settingsManager.getSmartRouting()) {
3740
+ this.showStatus("Smart routing is disabled. Enable it in /settings.");
3741
+ return;
3742
+ }
3743
+ const preview = await this.session.getRoutingPreview();
3744
+ if (!preview) {
3745
+ this.showStatus("Smart routing is inactive (fewer than two available models).");
3746
+ return;
3747
+ }
3748
+ const lines = [theme.bold("Smart Routing"), ""];
3749
+ lines.push(`${theme.fg("dim", "Next turn tier:")} ${theme.fg("accent", preview.tier)} ${theme.fg("muted", `\u2014 ${preview.reason}`)}`);
3750
+ if (preview.budgetUsedFraction !== undefined) {
3751
+ const pct = Math.round(preview.budgetUsedFraction * 100);
3752
+ lines.push(`${theme.fg("dim", "Goal budget:")} ${pct}% spent${pct >= 75 ? theme.fg("warning", " (tier capping active)") : ""}`);
3753
+ }
3754
+ if (preview.requiresImageInput) {
3755
+ lines.push(theme.fg("dim", "Conversation contains images \u2014 only vision-capable models are eligible."));
3756
+ }
3757
+ lines.push("");
3758
+ for (const pick of preview.picks) {
3759
+ const marker = pick.tier === preview.tier ? theme.fg("accent", "\u25b6") : " ";
3760
+ const pinNote = pick.pinned ? theme.fg("muted", " (pinned)") : "";
3761
+ lines.push(`${marker} ${theme.fg("dim", `${pick.tier}:`.padEnd(10))}${pick.model.name || pick.model.id}${pinNote}`);
3762
+ }
3763
+ this.chatContainer.addChild(new Spacer(1));
3764
+ this.chatContainer.addChild(new Text(lines.join("\n"), 1, 0));
3765
+ this.ui.requestRender();
3766
+ }
3719
3767
  getLastUserMessageText() {
3720
3768
  const messages = this.session.state.messages;
3721
3769
  for (let i = messages.length - 1; i >= 0; i--) {
@@ -4034,6 +4082,16 @@ export class InteractiveMode {
4034
4082
  info += `${theme.fg("dim", "Cache write:")} ${fmt(stats.tokens.cacheWrite)} tokens\n`;
4035
4083
  info += `${theme.fg("dim", "Total:")} ${fmt(stats.tokens.total)} tokens\n\n`;
4036
4084
  info += `${theme.fg("dim", "Estimated cost:")} ${theme.fg("accent", cost)}\n`;
4085
+ // Burn rate: cost per hour since the first message, once there is
4086
+ // enough elapsed time and spend for the projection to mean anything.
4087
+ const firstMessage = this.session.state.messages.find((m) => "timestamp" in m && typeof m.timestamp === "number");
4088
+ if (firstMessage && stats.cost > 0) {
4089
+ const elapsedMs = Date.now() - firstMessage.timestamp;
4090
+ if (elapsedMs > 60_000) {
4091
+ const perHour = stats.cost / (elapsedMs / 3_600_000);
4092
+ info += `${theme.fg("dim", "Burn rate:")} ${theme.fg("accent", `$${perHour.toFixed(2)}/hour`)} ${theme.fg("muted", "at the current pace")}\n`;
4093
+ }
4094
+ }
4037
4095
  if (stats.contextUsage) {
4038
4096
  const ctxTokens = stats.contextUsage.tokens ?? 0;
4039
4097
  const pct = stats.contextUsage.contextWindow > 0 ? Math.round((ctxTokens / stats.contextUsage.contextWindow) * 100) : 0;