@ashx-j/lunr 0.2.7 → 0.2.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 (164) hide show
  1. package/dist/builtin-extensions/ashxj-spinners.d.ts +7 -0
  2. package/dist/builtin-extensions/ashxj-spinners.d.ts.map +1 -1
  3. package/dist/builtin-extensions/ashxj-spinners.js +40 -3
  4. package/dist/builtin-extensions/ashxj-spinners.js.map +1 -1
  5. package/dist/builtin-extensions/ashxj-tui.d.ts +4 -0
  6. package/dist/builtin-extensions/ashxj-tui.d.ts.map +1 -1
  7. package/dist/builtin-extensions/ashxj-tui.js +44 -0
  8. package/dist/builtin-extensions/ashxj-tui.js.map +1 -1
  9. package/dist/builtin-extensions/lunr-behavior.d.ts +2 -1
  10. package/dist/builtin-extensions/lunr-behavior.d.ts.map +1 -1
  11. package/dist/builtin-extensions/lunr-behavior.js +26 -14
  12. package/dist/builtin-extensions/lunr-behavior.js.map +1 -1
  13. package/dist/builtin-extensions/lunr-todos.d.ts +4 -7
  14. package/dist/builtin-extensions/lunr-todos.d.ts.map +1 -1
  15. package/dist/builtin-extensions/lunr-todos.js +18 -10
  16. package/dist/builtin-extensions/lunr-todos.js.map +1 -1
  17. package/dist/builtin-extensions/pi-subagents/src/agents/agent-management.d.ts.map +1 -1
  18. package/dist/builtin-extensions/pi-subagents/src/agents/agent-management.js +3 -3
  19. package/dist/builtin-extensions/pi-subagents/src/agents/agent-management.js.map +1 -1
  20. package/dist/builtin-extensions/pi-subagents/src/extension/control-notices.d.ts.map +1 -1
  21. package/dist/builtin-extensions/pi-subagents/src/extension/control-notices.js +2 -2
  22. package/dist/builtin-extensions/pi-subagents/src/extension/control-notices.js.map +1 -1
  23. package/dist/builtin-extensions/pi-subagents/src/extension/index.d.ts.map +1 -1
  24. package/dist/builtin-extensions/pi-subagents/src/extension/index.js +1 -7
  25. package/dist/builtin-extensions/pi-subagents/src/extension/index.js.map +1 -1
  26. package/dist/builtin-extensions/pi-subagents/src/extension/schemas.d.ts.map +1 -1
  27. package/dist/builtin-extensions/pi-subagents/src/extension/schemas.js +2 -2
  28. package/dist/builtin-extensions/pi-subagents/src/extension/schemas.js.map +1 -1
  29. package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.d.ts +2 -2
  30. package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.d.ts.map +1 -1
  31. package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.js +3 -4
  32. package/dist/builtin-extensions/pi-subagents/src/extension/tool-description.js.map +1 -1
  33. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.d.ts.map +1 -1
  34. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js +21 -0
  35. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/execution.js.map +1 -1
  36. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.d.ts.map +1 -1
  37. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js +6 -18
  38. package/dist/builtin-extensions/pi-subagents/src/runs/foreground/subagent-executor.js.map +1 -1
  39. package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.d.ts.map +1 -1
  40. package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.js +13 -5
  41. package/dist/builtin-extensions/pi-subagents/src/runs/shared/model-fallback.js.map +1 -1
  42. package/dist/builtin-extensions/pi-subagents/src/shared/lunr-child-context.d.ts +21 -0
  43. package/dist/builtin-extensions/pi-subagents/src/shared/lunr-child-context.d.ts.map +1 -0
  44. package/dist/builtin-extensions/pi-subagents/src/shared/lunr-child-context.js +22 -0
  45. package/dist/builtin-extensions/pi-subagents/src/shared/lunr-child-context.js.map +1 -0
  46. package/dist/builtin-extensions/pi-subagents/src/shared/types.d.ts +1 -0
  47. package/dist/builtin-extensions/pi-subagents/src/shared/types.d.ts.map +1 -1
  48. package/dist/builtin-extensions/pi-subagents/src/shared/types.js.map +1 -1
  49. package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.d.ts.map +1 -1
  50. package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.js +2 -1
  51. package/dist/builtin-extensions/pi-subagents/src/slash/prompt-workflows.js.map +1 -1
  52. package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.d.ts.map +1 -1
  53. package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.js +10 -18
  54. package/dist/builtin-extensions/pi-subagents/src/slash/slash-commands.js.map +1 -1
  55. package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts +3 -0
  56. package/dist/builtin-extensions/pi-subagents/src/tui/render.d.ts.map +1 -1
  57. package/dist/builtin-extensions/pi-subagents/src/tui/render.js +9 -11
  58. package/dist/builtin-extensions/pi-subagents/src/tui/render.js.map +1 -1
  59. package/dist/builtin-extensions/pi-web-access/render-search-error.d.ts.map +1 -1
  60. package/dist/builtin-extensions/pi-web-access/render-search-error.js +1 -1
  61. package/dist/builtin-extensions/pi-web-access/render-search-error.js.map +1 -1
  62. package/dist/catalog/models.json +1 -1
  63. package/dist/catalog/providers/github-copilot.json +1 -1
  64. package/dist/catalog/providers/google-vertex.json +1 -1
  65. package/dist/catalog/providers/google.json +1 -1
  66. package/dist/catalog/providers/nvidia.json +1 -1
  67. package/dist/catalog/providers/opencode-go.json +1 -1
  68. package/dist/catalog/providers/opencode.json +1 -1
  69. package/dist/catalog/providers/openrouter.json +1 -1
  70. package/dist/catalog/providers/vercel-ai-gateway.json +1 -1
  71. package/dist/catalog/publication.json +3 -3
  72. package/dist/cli/update-cli.d.ts +16 -0
  73. package/dist/cli/update-cli.d.ts.map +1 -0
  74. package/dist/cli/update-cli.js +83 -0
  75. package/dist/cli/update-cli.js.map +1 -0
  76. package/dist/cli.d.ts.map +1 -1
  77. package/dist/cli.js +6 -0
  78. package/dist/cli.js.map +1 -1
  79. package/dist/core/behavior-preset.d.ts +60 -0
  80. package/dist/core/behavior-preset.d.ts.map +1 -0
  81. package/dist/core/behavior-preset.js +257 -0
  82. package/dist/core/behavior-preset.js.map +1 -0
  83. package/dist/core/customize.d.ts +3 -0
  84. package/dist/core/customize.d.ts.map +1 -1
  85. package/dist/core/customize.js +9 -0
  86. package/dist/core/customize.js.map +1 -1
  87. package/dist/core/footer-data-provider.d.ts +12 -1
  88. package/dist/core/footer-data-provider.d.ts.map +1 -1
  89. package/dist/core/footer-data-provider.js +70 -5
  90. package/dist/core/footer-data-provider.js.map +1 -1
  91. package/dist/core/keybindings.d.ts +2 -2
  92. package/dist/core/keybindings.d.ts.map +1 -1
  93. package/dist/core/keybindings.js +1 -1
  94. package/dist/core/keybindings.js.map +1 -1
  95. package/dist/core/model-tiers.d.ts +9 -0
  96. package/dist/core/model-tiers.d.ts.map +1 -1
  97. package/dist/core/model-tiers.js +16 -0
  98. package/dist/core/model-tiers.js.map +1 -1
  99. package/dist/core/rollback.d.ts +1 -1
  100. package/dist/core/rollback.d.ts.map +1 -1
  101. package/dist/core/rollback.js +1 -1
  102. package/dist/core/rollback.js.map +1 -1
  103. package/dist/core/settings-manager.d.ts +23 -0
  104. package/dist/core/settings-manager.d.ts.map +1 -1
  105. package/dist/core/settings-manager.js +66 -0
  106. package/dist/core/settings-manager.js.map +1 -1
  107. package/dist/core/slash-commands.d.ts.map +1 -1
  108. package/dist/core/slash-commands.js +2 -1
  109. package/dist/core/slash-commands.js.map +1 -1
  110. package/dist/core/tools/bash.d.ts.map +1 -1
  111. package/dist/core/tools/bash.js +1 -3
  112. package/dist/core/tools/bash.js.map +1 -1
  113. package/dist/core/update-check.d.ts +32 -0
  114. package/dist/core/update-check.d.ts.map +1 -0
  115. package/dist/core/update-check.js +112 -0
  116. package/dist/core/update-check.js.map +1 -0
  117. package/dist/core/usage-service.d.ts +24 -1
  118. package/dist/core/usage-service.d.ts.map +1 -1
  119. package/dist/core/usage-service.js +91 -14
  120. package/dist/core/usage-service.js.map +1 -1
  121. package/dist/gateway/agent-bridge.d.ts.map +1 -1
  122. package/dist/gateway/agent-bridge.js +5 -1
  123. package/dist/gateway/agent-bridge.js.map +1 -1
  124. package/dist/gateway/cron.d.ts.map +1 -1
  125. package/dist/gateway/cron.js +5 -1
  126. package/dist/gateway/cron.js.map +1 -1
  127. package/dist/main.d.ts.map +1 -1
  128. package/dist/main.js +13 -1
  129. package/dist/main.js.map +1 -1
  130. package/dist/modes/interactive/components/assistant-message.d.ts +10 -1
  131. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  132. package/dist/modes/interactive/components/assistant-message.js +154 -68
  133. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  134. package/dist/modes/interactive/components/settings-selector.d.ts +13 -0
  135. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  136. package/dist/modes/interactive/components/settings-selector.js +157 -10
  137. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  138. package/dist/modes/interactive/components/thinking-summary.d.ts +4 -4
  139. package/dist/modes/interactive/components/thinking-summary.d.ts.map +1 -1
  140. package/dist/modes/interactive/components/thinking-summary.js +7 -5
  141. package/dist/modes/interactive/components/thinking-summary.js.map +1 -1
  142. package/dist/modes/interactive/components/thinking-tail.d.ts.map +1 -1
  143. package/dist/modes/interactive/components/thinking-tail.js +5 -3
  144. package/dist/modes/interactive/components/thinking-tail.js.map +1 -1
  145. package/dist/modes/interactive/components/tool-execution.d.ts +1 -0
  146. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  147. package/dist/modes/interactive/components/tool-execution.js +9 -0
  148. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  149. package/dist/modes/interactive/interactive-mode.d.ts +6 -1
  150. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  151. package/dist/modes/interactive/interactive-mode.js +124 -43
  152. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  153. package/docs/keybindings.md +1 -1
  154. package/docs/providers.md +2 -0
  155. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  156. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  157. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  158. package/examples/extensions/gondolin/package-lock.json +2 -2
  159. package/examples/extensions/gondolin/package.json +1 -1
  160. package/examples/extensions/sandbox/package-lock.json +2 -2
  161. package/examples/extensions/sandbox/package.json +1 -1
  162. package/examples/extensions/with-deps/package-lock.json +2 -2
  163. package/examples/extensions/with-deps/package.json +1 -1
  164. package/package.json +4 -4
@@ -6,12 +6,14 @@ import * as crypto from "node:crypto";
6
6
  import * as fs from "node:fs";
7
7
  import * as os from "node:os";
8
8
  import * as path from "node:path";
9
+ import { getSupportedThinkingLevels, } from "@ashx-j/lunr-ai/compat";
9
10
  import { CombinedAutocompleteProvider, Container, fuzzyFilter, isFocusable, Markdown, matchesKey, ProcessTerminal, Spacer, setKeybindings, Text, TruncatedText, TUI, visibleWidth, } from "@ashx-j/lunr-tui";
10
11
  import chalk from "chalk";
11
12
  import { spawn, spawnSync } from "child_process";
12
13
  import { APP_NAME, APP_TITLE, appendDebugLog, CONFIG_DIR_NAME, getAgentDir, getAuthPath, getDebugLogPath, getDocsPath, getShareViewerUrl, VERSION, } from "../../config.js";
13
14
  import { parseSkillBlock } from "../../core/agent-session.js";
14
15
  import { SessionImportFileNotFoundError } from "../../core/agent-session-runtime.js";
16
+ import { applyBehaviorPreset, reconcileBehaviorPreset } from "../../core/behavior-preset.js";
15
17
  import { CACHE_TTL_MS, collectCacheMisses, computeCacheWaste, detectCacheMiss, } from "../../core/cache-stats.js";
16
18
  import { formatCatalogRefreshSummary } from "../../core/catalog-merge.js";
17
19
  import { computeContextBreakdown } from "../../core/context-breakdown.js";
@@ -26,6 +28,8 @@ import { isValidProviderBaseUrl, parseModelIds, slugifyProviderId, upsertCustomP
26
28
  import { defaultModelPerProvider, findExactModelReferenceMatch, resolveModelScope } from "../../core/model-resolver.js";
27
29
  import { refreshModelCandidatesForInit } from "../../core/model-runtime.js";
28
30
  import { getModelTiersBridge } from "../../core/model-tiers.js";
31
+ import { checkForUpdate, markUpdateNotified } from "../../core/update-check.js";
32
+ import { getUsageServiceBridge } from "../../core/usage-service.js";
29
33
  import { registerPermissionModeBridge } from "../../core/permission-mode.js";
30
34
  import { nextModeForGoal, registerPermissionModeControlBridge } from "../../core/permission-mode-control.js";
31
35
  import { AUTO_MODE_ADDENDUM, getPermissionMode, nextPermissionMode, registerApprovalHandler, resetPermissions, restorePermissionModeAfterPlan, setPermissionMode, } from "../../core/permissions.js";
@@ -350,7 +354,7 @@ export class InteractiveMode {
350
354
  resetPermissions(this.settingsManager.getDefaultPermissionMode());
351
355
  // lunr: clear process registry and rollback state
352
356
  processRegistry.clearRegistry();
353
- // lunr: on fork (/rollback, /undo, tree navigation) the snapshots SURVIVE —
357
+ // lunr: on fork (/rollback, tree navigation) the snapshots SURVIVE —
354
358
  // the caller migrates them to the forked session id (migrateRollbackSession).
355
359
  // Any other replacement wipes only the session being torn down.
356
360
  if (reason !== "fork") {
@@ -366,11 +370,6 @@ export class InteractiveMode {
366
370
  this.version = VERSION;
367
371
  this.ui = new TUI(new ProcessTerminal(), this.settingsManager.getShowHardwareCursor());
368
372
  this.ui.setClearOnShrink(this.settingsManager.getClearOnShrink());
369
- this.ui.onTextSelected = (text) => {
370
- void copyToClipboard(text)
371
- .then(() => this.showStatus("Copied."))
372
- .catch((error) => this.showError(error instanceof Error ? error.message : String(error)));
373
- };
374
373
  // lunr: register the permission-mode footer bridge (provider-side: InteractiveMode owns the state).
375
374
  registerPermissionModeBridge(() => getPermissionMode());
376
375
  registerPermissionModeControlBridge({
@@ -594,6 +593,8 @@ export class InteractiveMode {
594
593
  this.ui.start();
595
594
  this.isInitialized = true;
596
595
  time("ui.start");
596
+ void this.maybeNotifyCliUpdate();
597
+ this.startPlanUsagePolling();
597
598
  // lunr: register the permission approval dialog handler so manual mode can prompt.
598
599
  registerApprovalHandler(async (req) => {
599
600
  // lunr: auto-activated agent swarms get their own dialog; on approval the
@@ -670,6 +671,38 @@ export class InteractiveMode {
670
671
  await this.deferredBuiltinAttachPromise;
671
672
  }
672
673
  }
674
+ planUsageTimer;
675
+ startPlanUsagePolling() {
676
+ const bridge = getUsageServiceBridge();
677
+ if (!bridge)
678
+ return;
679
+ bridge.setOnUpdate(() => this.ui.requestRender());
680
+ const tick = () => {
681
+ const provider = this.session.model?.provider;
682
+ if (provider)
683
+ bridge.prefetch(provider);
684
+ };
685
+ tick();
686
+ this.planUsageTimer = setInterval(tick, 60_000);
687
+ this.planUsageTimer.unref?.();
688
+ }
689
+ /** Background npm version check. Never blocks first paint. Workspace installs skip. */
690
+ async maybeNotifyCliUpdate() {
691
+ try {
692
+ const result = await checkForUpdate({
693
+ currentVersion: VERSION,
694
+ agentDir: getAgentDir(),
695
+ offline: process.env.PI_OFFLINE === "1",
696
+ });
697
+ if (!result?.notice)
698
+ return;
699
+ this.showStatus(result.notice);
700
+ markUpdateNotified(getAgentDir(), result.latest);
701
+ }
702
+ catch {
703
+ // Version check is best-effort.
704
+ }
705
+ }
673
706
  /**
674
707
  * Update terminal title with session name and cwd.
675
708
  */
@@ -2122,7 +2155,7 @@ export class InteractiveMode {
2122
2155
  // Global debug handler on TUI (works regardless of focus)
2123
2156
  this.ui.onDebug = () => this.handleDebugCommand();
2124
2157
  this.defaultEditor.onAction("app.model.select", () => void this.showModelSelector());
2125
- this.defaultEditor.onAction("app.tools.expand", () => this.toggleToolOutputExpansion());
2158
+ // lunr: tool/thinking expand is per-item click; ctrl+o is not bound (tree filter still uses ctrl+o).
2126
2159
  this.defaultEditor.onAction("app.thinking.toggle", () => this.toggleThinkingBlockVisibility());
2127
2160
  this.defaultEditor.onAction("app.editor.external", () => this.openExternalEditor());
2128
2161
  this.defaultEditor.onAction("app.message.copy", () => void this.handleCopyCommand());
@@ -2300,6 +2333,11 @@ export class InteractiveMode {
2300
2333
  await this.handleUndoCommand();
2301
2334
  return;
2302
2335
  }
2336
+ if (text === "/edit") {
2337
+ this.editor.setText("");
2338
+ await this.handleEditCommand();
2339
+ return;
2340
+ }
2303
2341
  if (text === "/redo") {
2304
2342
  this.editor.setText("");
2305
2343
  await this.handleRedoCommand();
@@ -2475,7 +2513,7 @@ export class InteractiveMode {
2475
2513
  return;
2476
2514
  const opts = this.getSmoothStreamingOptions();
2477
2515
  const revealed = sliceMessageContent(target, this.streamingDisplayedLength, opts);
2478
- this.streamingComponent.updateContent(revealed);
2516
+ this.streamingComponent.updateContent(revealed, { thinkingSource: target });
2479
2517
  this.syncRevealedStreamingTools(revealed, target);
2480
2518
  this.ui.requestRender();
2481
2519
  }
@@ -2635,7 +2673,7 @@ export class InteractiveMode {
2635
2673
  this.streamingDisplayedLength = 0;
2636
2674
  this.chatContainer.addChild(this.streamingComponent);
2637
2675
  if (this.settingsManager.getSmoothStreaming()) {
2638
- this.streamingComponent.updateContent(sliceMessageContent(this.streamingMessage, 0, this.getSmoothStreamingOptions()));
2676
+ this.streamingComponent.updateContent(sliceMessageContent(this.streamingMessage, 0, this.getSmoothStreamingOptions()), { thinkingSource: this.streamingMessage });
2639
2677
  }
2640
2678
  else {
2641
2679
  this.streamingComponent.updateContent(this.streamingMessage);
@@ -2721,9 +2759,7 @@ export class InteractiveMode {
2721
2759
  case "tool_execution_start": {
2722
2760
  // If smooth streaming still has leading text unrevealed, flush through
2723
2761
  // this tool so the running card is not hidden behind the typewriter.
2724
- if (this.settingsManager.getSmoothStreaming() &&
2725
- this.streamingTargetMessage &&
2726
- this.streamingComponent) {
2762
+ if (this.settingsManager.getSmoothStreaming() && this.streamingTargetMessage && this.streamingComponent) {
2727
2763
  const opts = this.getSmoothStreamingOptions();
2728
2764
  const needed = countGraphemesBeforeToolCall(this.streamingTargetMessage, event.toolCallId, opts);
2729
2765
  if (this.streamingDisplayedLength < needed) {
@@ -3196,6 +3232,11 @@ export class InteractiveMode {
3196
3232
  return;
3197
3233
  this.isShuttingDown = true;
3198
3234
  this.stopSmoothStreaming();
3235
+ if (this.planUsageTimer) {
3236
+ clearInterval(this.planUsageTimer);
3237
+ this.planUsageTimer = undefined;
3238
+ }
3239
+ getUsageServiceBridge()?.setOnUpdate(undefined);
3199
3240
  // Keep signal handlers registered until terminal cleanup has completed.
3200
3241
  // `signal-exit` checks the listener list during the same SIGTERM/SIGHUP
3201
3242
  // dispatch and re-sends the signal if only its own listeners remain.
@@ -3803,6 +3844,7 @@ export class InteractiveMode {
3803
3844
  showTerminalProgress: this.settingsManager.getShowTerminalProgress(),
3804
3845
  modelTiers: this.settingsManager.getModelTiers(),
3805
3846
  memoryCharCap: this.settingsManager.getMemoryCharCap(),
3847
+ behaviorPreset: reconcileBehaviorPreset(this.settingsManager),
3806
3848
  searchCurator: getSearchCuratorSetting(),
3807
3849
  // lunr: TUI customize settings
3808
3850
  gutterRail: this.settingsManager.getGutterRail(),
@@ -3812,6 +3854,10 @@ export class InteractiveMode {
3812
3854
  footerContext: this.settingsManager.getFooterContext(),
3813
3855
  footerTokens: this.settingsManager.getFooterTokens(),
3814
3856
  footerStatuses: this.settingsManager.getFooterStatuses(),
3857
+ footerGit: this.settingsManager.getFooterGit(),
3858
+ footerPlan: this.settingsManager.getFooterPlan(),
3859
+ footerPlanBar: this.settingsManager.getFooterPlanBar(),
3860
+ planUsageWindow: this.settingsManager.getPlanUsageWindow(),
3815
3861
  defaultPermissionMode: this.settingsManager.getDefaultPermissionMode(),
3816
3862
  rollbackEnabled: this.settingsManager.getRollbackEnabled(),
3817
3863
  rollbackTurns: this.settingsManager.getRollbackTurns(),
@@ -3975,9 +4021,28 @@ export class InteractiveMode {
3975
4021
  onModelTierModelChange: (tier, model) => {
3976
4022
  this.settingsManager.setTierModel(tier, model);
3977
4023
  },
4024
+ onModelTierThinkingChange: (tier, level) => {
4025
+ this.settingsManager.setTierThinking(tier, level);
4026
+ },
4027
+ getTierThinkingLevels: (tier) => {
4028
+ const model = this.resolveModelReference(this.settingsManager.getTierModel(tier));
4029
+ if (model)
4030
+ return getSupportedThinkingLevels(model);
4031
+ return this.session.getAvailableThinkingLevels();
4032
+ },
3978
4033
  onMemoryCharCapChange: (cap) => {
3979
4034
  this.settingsManager.setMemoryCharCap(cap);
3980
4035
  },
4036
+ onBehaviorPresetChange: (preset) => {
4037
+ const result = applyBehaviorPreset(this.settingsManager, preset, getAgentDir(), {
4038
+ overwrite: true,
4039
+ });
4040
+ if (result.ok) {
4041
+ this.showStatus(preset === "custom"
4042
+ ? "Behavior preset: custom (file is source of truth)"
4043
+ : `Behavior preset: ${preset}`);
4044
+ }
4045
+ },
3981
4046
  onSearchCuratorChange: (setting) => {
3982
4047
  if (!setSearchCuratorSetting(setting)) {
3983
4048
  this.showError("pi-web-access is not loaded; curator setting unavailable.");
@@ -4005,6 +4070,18 @@ export class InteractiveMode {
4005
4070
  onFooterStatusesChange: (enabled) => {
4006
4071
  this.settingsManager.setFooterStatuses(enabled);
4007
4072
  },
4073
+ onFooterGitChange: (enabled) => {
4074
+ this.settingsManager.setFooterGit(enabled);
4075
+ },
4076
+ onFooterPlanChange: (enabled) => {
4077
+ this.settingsManager.setFooterPlan(enabled);
4078
+ },
4079
+ onFooterPlanBarChange: (enabled) => {
4080
+ this.settingsManager.setFooterPlanBar(enabled);
4081
+ },
4082
+ onPlanUsageWindowChange: (window) => {
4083
+ this.settingsManager.setPlanUsageWindow(window);
4084
+ },
4008
4085
  onDefaultPermissionModeChange: (mode) => {
4009
4086
  this.settingsManager.setDefaultPermissionMode(mode);
4010
4087
  },
@@ -5478,16 +5555,15 @@ export class InteractiveMode {
5478
5555
  this.setExtensionStatus("research", "research ● active");
5479
5556
  await this.sendUserMessageAfterDeferredBuiltins(buildResearchPrompt(question, depth, breadth));
5480
5557
  }
5481
- // lunr: /undo persists the rewind by forking the session to just before the
5482
- // last user message (same fork mechanism /rollback uses, but WITHOUT touching files).
5483
- // The branched session file survives restart/`-c`, so undone turns stay gone.
5484
- // /redo stays ephemeral and cannot redo across a fork, so the redo stack is cleared.
5485
- async handleUndoCommand() {
5558
+ // lunr: /undo and /edit rewind the same session via navigateTree (no fork).
5559
+ // /undo leaves the editor alone; /edit pastes the undone user text. /redo
5560
+ // pops the previous leaf and navigates back.
5561
+ async rewindLastTurn(command) {
5486
5562
  if (this.session.isStreaming) {
5487
- this.showWarning("Wait for the current response to finish before running /undo.");
5488
- return;
5563
+ this.showWarning(`Wait for the current response to finish before running /${command}.`);
5564
+ return undefined;
5489
5565
  }
5490
- // Find the last user message on the current branch (root leaf order)
5566
+ const nothingLabel = command === "undo" ? "Nothing to undo" : "Nothing to edit";
5491
5567
  const branch = this.sessionManager.getBranch();
5492
5568
  let lastUserIndex = -1;
5493
5569
  for (let i = branch.length - 1; i >= 0; i--) {
@@ -5498,17 +5574,16 @@ export class InteractiveMode {
5498
5574
  }
5499
5575
  }
5500
5576
  if (lastUserIndex === -1) {
5501
- this.showStatus("Nothing to undo");
5502
- return;
5577
+ this.showStatus(nothingLabel);
5578
+ return undefined;
5503
5579
  }
5504
5580
  const lastUser = branch[lastUserIndex];
5505
5581
  const leafId = this.sessionManager.getLeafId();
5506
5582
  let targetId;
5507
5583
  if (lastUser.id === leafId) {
5508
- // User message with no response yet (e.g. aborted stream) — undo to its parent.
5509
5584
  if (!lastUser.parentId) {
5510
- this.showStatus("Nothing to undo");
5511
- return;
5585
+ this.showStatus(nothingLabel);
5586
+ return undefined;
5512
5587
  }
5513
5588
  targetId = lastUser.parentId;
5514
5589
  }
@@ -5516,31 +5591,37 @@ export class InteractiveMode {
5516
5591
  targetId = lastUser.id;
5517
5592
  }
5518
5593
  try {
5519
- // Fork to just before the target — writes a branched session file so the
5520
- // rewind survives restart. Unlike /rollback, this must NOT touch files.
5521
- // lunr: fork teardown skips the rollback wipe (reason === "fork"), so carry
5522
- // the surviving snapshots over to the forked session id.
5523
- const oldSid = this.sessionManager.getSessionId();
5524
- const forkResult = await this.runtimeHost.fork(targetId, { position: "before" });
5525
- if (forkResult.cancelled) {
5526
- this.showStatus("Undo cancelled");
5527
- return;
5594
+ const result = await this.session.navigateTree(targetId, {});
5595
+ if (result.cancelled) {
5596
+ this.showStatus(command === "undo" ? "Undo cancelled" : "Edit cancelled");
5597
+ return undefined;
5598
+ }
5599
+ if (leafId) {
5600
+ this.redoStack.push(leafId);
5528
5601
  }
5529
- migrateRollbackSession(oldSid, this.sessionManager.getSessionId());
5530
- // /redo cannot redo across a fork — clear the ephemeral redo stack.
5531
- this.redoStack.length = 0;
5532
5602
  this.chatContainer.clear();
5533
5603
  this.renderInitialMessages();
5534
- if (forkResult.selectedText && !this.editor.getText().trim()) {
5535
- this.editor.setText(forkResult.selectedText);
5536
- }
5537
- this.showStatus("Undone — branched session created (persistent). /redo unavailable after a fork.");
5538
5604
  void this.flushCompactionQueue({ willRetry: false });
5605
+ return { editorText: result.editorText };
5539
5606
  }
5540
5607
  catch (error) {
5541
5608
  this.showError(error instanceof Error ? error.message : String(error));
5609
+ return undefined;
5542
5610
  }
5543
5611
  }
5612
+ async handleUndoCommand() {
5613
+ const result = await this.rewindLastTurn("undo");
5614
+ if (!result)
5615
+ return;
5616
+ this.showStatus("Undone. /redo to restore.");
5617
+ }
5618
+ async handleEditCommand() {
5619
+ const result = await this.rewindLastTurn("edit");
5620
+ if (!result)
5621
+ return;
5622
+ this.editor.setText(result.editorText ?? "");
5623
+ this.showStatus("Editing last message. /redo to restore.");
5624
+ }
5544
5625
  async handleRedoCommand() {
5545
5626
  if (this.session.isStreaming) {
5546
5627
  this.showWarning("Wait for the current response to finish before running /redo.");
@@ -6417,7 +6498,7 @@ export class InteractiveMode {
6417
6498
  const cyclePermissionMode = this.getAppKeyDisplay("app.mode.cycle");
6418
6499
  const cycleModelForward = this.getAppKeyDisplay("app.model.cycleForward");
6419
6500
  const selectModel = this.getAppKeyDisplay("app.model.select");
6420
- const expandTools = this.getAppKeyDisplay("app.tools.expand");
6501
+ const expandTools = "click";
6421
6502
  const toggleThinking = this.getAppKeyDisplay("app.thinking.toggle");
6422
6503
  const externalEditor = this.getAppKeyDisplay("app.editor.external");
6423
6504
  const cycleModelBackward = this.getAppKeyDisplay("app.model.cycleBackward");
@@ -6462,7 +6543,7 @@ export class InteractiveMode {
6462
6543
  | \`${cyclePermissionMode}\` | Cycle permission mode |
6463
6544
  ${cycleThinkingLevel ? `| \`${cycleThinkingLevel}\` | Cycle thinking level |\n` : ""}| \`${cycleModelForward}\` / \`${cycleModelBackward}\` | Cycle models |
6464
6545
  | \`${selectModel}\` | Open model selector |
6465
- | \`${expandTools}\` | Toggle tool output expansion |
6546
+ | \`${expandTools}\` | Expand or collapse a thinking/tool card |
6466
6547
  | \`${toggleThinking}\` | Toggle thinking block visibility |
6467
6548
  | \`${externalEditor}\` | Edit message in external editor |
6468
6549
  | \`${copyMessage}\` | Copy last assistant message |