@phi-code-admin/phi-code 0.76.16 → 0.77.1

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 (81) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/dist/core/agent-session.d.ts.map +1 -1
  3. package/dist/core/agent-session.js +20 -3
  4. package/dist/core/agent-session.js.map +1 -1
  5. package/dist/core/api-key-store.d.ts +1 -1
  6. package/dist/core/api-key-store.d.ts.map +1 -1
  7. package/dist/core/api-key-store.js +5 -2
  8. package/dist/core/api-key-store.js.map +1 -1
  9. package/dist/core/auth-storage.d.ts.map +1 -1
  10. package/dist/core/auth-storage.js +3 -3
  11. package/dist/core/auth-storage.js.map +1 -1
  12. package/dist/core/bash-executor.d.ts.map +1 -1
  13. package/dist/core/bash-executor.js +4 -1
  14. package/dist/core/bash-executor.js.map +1 -1
  15. package/dist/core/compaction/branch-summarization.d.ts.map +1 -1
  16. package/dist/core/compaction/branch-summarization.js +7 -2
  17. package/dist/core/compaction/branch-summarization.js.map +1 -1
  18. package/dist/core/compaction/compaction.d.ts.map +1 -1
  19. package/dist/core/compaction/compaction.js +4 -0
  20. package/dist/core/compaction/compaction.js.map +1 -1
  21. package/dist/core/keybindings.d.ts +5 -0
  22. package/dist/core/keybindings.d.ts.map +1 -1
  23. package/dist/core/keybindings.js +4 -0
  24. package/dist/core/keybindings.js.map +1 -1
  25. package/dist/core/model-registry.d.ts.map +1 -1
  26. package/dist/core/model-registry.js +6 -2
  27. package/dist/core/model-registry.js.map +1 -1
  28. package/dist/core/model-resolver.d.ts.map +1 -1
  29. package/dist/core/model-resolver.js +9 -3
  30. package/dist/core/model-resolver.js.map +1 -1
  31. package/dist/core/session-manager.d.ts.map +1 -1
  32. package/dist/core/session-manager.js +36 -5
  33. package/dist/core/session-manager.js.map +1 -1
  34. package/dist/core/settings-manager.d.ts.map +1 -1
  35. package/dist/core/settings-manager.js +15 -4
  36. package/dist/core/settings-manager.js.map +1 -1
  37. package/dist/core/tools/bash.d.ts.map +1 -1
  38. package/dist/core/tools/bash.js +2 -0
  39. package/dist/core/tools/bash.js.map +1 -1
  40. package/dist/core/tools/find.d.ts.map +1 -1
  41. package/dist/core/tools/find.js +3 -1
  42. package/dist/core/tools/find.js.map +1 -1
  43. package/dist/core/tools/output-accumulator.d.ts +1 -0
  44. package/dist/core/tools/output-accumulator.d.ts.map +1 -1
  45. package/dist/core/tools/output-accumulator.js +12 -1
  46. package/dist/core/tools/output-accumulator.js.map +1 -1
  47. package/dist/core/tools/read.d.ts.map +1 -1
  48. package/dist/core/tools/read.js +3 -1
  49. package/dist/core/tools/read.js.map +1 -1
  50. package/dist/migrations.d.ts.map +1 -1
  51. package/dist/migrations.js +27 -7
  52. package/dist/migrations.js.map +1 -1
  53. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  54. package/dist/modes/interactive/components/custom-editor.js +16 -1
  55. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  56. package/dist/modes/interactive/components/diff.d.ts.map +1 -1
  57. package/dist/modes/interactive/components/diff.js +4 -2
  58. package/dist/modes/interactive/components/diff.js.map +1 -1
  59. package/dist/modes/interactive/components/footer.d.ts +9 -0
  60. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  61. package/dist/modes/interactive/components/footer.js +29 -4
  62. package/dist/modes/interactive/components/footer.js.map +1 -1
  63. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  64. package/dist/modes/interactive/components/login-dialog.js +10 -4
  65. package/dist/modes/interactive/components/login-dialog.js.map +1 -1
  66. package/dist/modes/interactive/interactive-mode.d.ts +7 -0
  67. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  68. package/dist/modes/interactive/interactive-mode.js +80 -8
  69. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  70. package/dist/utils/sleep.d.ts.map +1 -1
  71. package/dist/utils/sleep.js +7 -3
  72. package/dist/utils/sleep.js.map +1 -1
  73. package/extensions/phi/benchmark.ts +9 -2
  74. package/extensions/phi/init.ts +4 -1
  75. package/extensions/phi/keys.ts +13 -0
  76. package/extensions/phi/memory.ts +4 -1
  77. package/extensions/phi/orchestrator.ts +115 -25
  78. package/extensions/phi/providers/opencode-go.ts +39 -13
  79. package/extensions/phi/setup.ts +8 -1
  80. package/extensions/phi/web-search.ts +15 -3
  81. package/package.json +1 -1
@@ -125,6 +125,10 @@ export class InteractiveMode {
125
125
  version;
126
126
  isInitialized = false;
127
127
  onInputCallback;
128
+ // Input mode: "act" sends messages to the agent directly; "plan" routes the next
129
+ // message through the /plan orchestrator (5 sequential phases, one model per phase).
130
+ // Toggled with Tab (when the editor is empty); shown by the footer Act/Plan indicator.
131
+ inputMode = "act";
128
132
  loadingAnimation = undefined;
129
133
  workingMessage = undefined;
130
134
  workingVisible = true;
@@ -234,6 +238,7 @@ export class InteractiveMode {
234
238
  this.footerDataProvider = new FooterDataProvider(this.sessionManager.getCwd());
235
239
  this.footer = new FooterComponent(this.session, this.footerDataProvider);
236
240
  this.footer.setAutoCompactEnabled(this.session.autoCompactionEnabled);
241
+ this.footer.setMode(this.inputMode);
237
242
  // Load hide thinking block setting
238
243
  this.hideThinkingBlock = this.settingsManager.getHideThinkingBlock();
239
244
  // Register themes from resource loader and initialize
@@ -927,8 +932,11 @@ export class InteractiveMode {
927
932
  if (exact)
928
933
  return exact;
929
934
  let current = p;
930
- while (current.includes("/")) {
931
- current = current.substring(0, current.lastIndexOf("/"));
935
+ // Walk up parent directories independently of the path separator so resource
936
+ // scope labels also resolve on Windows (backslash-separated paths).
937
+ while (current.includes("/") || current.includes("\\")) {
938
+ const idx = Math.max(current.lastIndexOf("/"), current.lastIndexOf("\\"));
939
+ current = current.substring(0, idx);
932
940
  const parent = sourceInfos.get(current);
933
941
  if (parent)
934
942
  return parent;
@@ -1260,6 +1268,9 @@ export class InteractiveMode {
1260
1268
  this.streamingComponent = undefined;
1261
1269
  this.streamingMessage = undefined;
1262
1270
  this.pendingTools.clear();
1271
+ // Drop deferred bash components from the previous session: otherwise the next
1272
+ // submit re-injects a stale component into the new session's chat.
1273
+ this.pendingBashComponents = [];
1263
1274
  this.renderInitialMessages();
1264
1275
  }
1265
1276
  /**
@@ -1942,6 +1953,7 @@ export class InteractiveMode {
1942
1953
  this.defaultEditor.onCtrlD = () => this.handleCtrlD();
1943
1954
  this.defaultEditor.onAction("app.suspend", () => this.handleCtrlZ());
1944
1955
  this.defaultEditor.onAction("app.thinking.cycle", () => this.cycleThinkingLevel());
1956
+ this.defaultEditor.onAction("app.mode.toggle", () => this.toggleInputMode());
1945
1957
  this.defaultEditor.onAction("app.model.cycleForward", () => this.cycleModel("forward"));
1946
1958
  this.defaultEditor.onAction("app.model.cycleBackward", () => this.cycleModel("backward"));
1947
1959
  // Global debug handler on TUI (works regardless of focus)
@@ -1993,6 +2005,12 @@ export class InteractiveMode {
1993
2005
  text = text.trim();
1994
2006
  if (!text)
1995
2007
  return;
2008
+ // In Plan mode, route plain prose through the /plan orchestrator. Slash
2009
+ // commands and bash (! / !!) still run normally, so the user can keep
2010
+ // driving the session while staying in Plan mode.
2011
+ if (this.inputMode === "plan" && !text.startsWith("/") && !text.startsWith("!")) {
2012
+ text = `/plan ${text}`;
2013
+ }
1996
2014
  // Handle commands
1997
2015
  if (text === "/settings") {
1998
2016
  this.showSettingsSelector();
@@ -2833,6 +2851,30 @@ export class InteractiveMode {
2833
2851
  }
2834
2852
  this.ui.requestRender();
2835
2853
  }
2854
+ /**
2855
+ * Toggle between Act mode (messages go straight to the agent) and Plan mode
2856
+ * (the next message is routed through the /plan orchestrator). Bound to Tab when
2857
+ * the editor is empty. The active mode is reflected by the footer Act/Plan indicator.
2858
+ */
2859
+ toggleInputMode() {
2860
+ const next = this.inputMode === "act" ? "plan" : "act";
2861
+ // Plan mode depends on the /plan orchestrator extension. If it is not loaded
2862
+ // (e.g. extensions disabled), stay in Act mode and explain why.
2863
+ if (next === "plan" && !this.session.extensionRunner.getCommand("plan")) {
2864
+ this.showWarning("Plan mode unavailable: the /plan orchestrator extension is not loaded.");
2865
+ return;
2866
+ }
2867
+ this.inputMode = next;
2868
+ this.footer.setMode(next);
2869
+ this.footer.invalidate();
2870
+ if (next === "plan") {
2871
+ this.showStatus("Plan mode ON — your next message launches the /plan orchestrator (5 phases, one model per phase). Tab on an empty prompt to return to Act.");
2872
+ }
2873
+ else {
2874
+ this.showStatus("Act mode ON — messages go straight to the agent. Tab on an empty prompt for Plan mode.");
2875
+ }
2876
+ this.ui.requestRender();
2877
+ }
2836
2878
  cycleThinkingLevel() {
2837
2879
  const newLevel = this.session.cycleThinkingLevel();
2838
2880
  if (newLevel === undefined) {
@@ -2882,6 +2924,11 @@ export class InteractiveMode {
2882
2924
  toggleThinkingBlockVisibility() {
2883
2925
  this.hideThinkingBlock = !this.hideThinkingBlock;
2884
2926
  this.settingsManager.setHideThinkingBlock(this.hideThinkingBlock);
2927
+ // Preserve in-flight tool components across the rebuild. rebuildChatFromMessages()
2928
+ // clears pendingTools and only repopulates from persisted messages, so a tool that
2929
+ // has not produced its result yet would be orphaned and its later updates/result
2930
+ // lost. Keep the existing components (with their accumulated output) to re-attach.
2931
+ const inFlightTools = new Map(this.pendingTools);
2885
2932
  // Rebuild chat from session messages
2886
2933
  this.chatContainer.clear();
2887
2934
  this.rebuildChatFromMessages();
@@ -2890,6 +2937,17 @@ export class InteractiveMode {
2890
2937
  this.streamingComponent.setHideThinkingBlock(this.hideThinkingBlock);
2891
2938
  this.streamingComponent.updateContent(this.streamingMessage);
2892
2939
  this.chatContainer.addChild(this.streamingComponent);
2940
+ // Re-attach in-flight tool components after the streaming message so their
2941
+ // subsequent updates and results still resolve via pendingTools.
2942
+ for (const content of this.streamingMessage.content) {
2943
+ if (content.type === "toolCall") {
2944
+ const existing = inFlightTools.get(content.id);
2945
+ if (existing && !this.pendingTools.has(content.id)) {
2946
+ this.chatContainer.addChild(existing);
2947
+ this.pendingTools.set(content.id, existing);
2948
+ }
2949
+ }
2950
+ }
2893
2951
  }
2894
2952
  this.showStatus(`Thinking blocks: ${this.hideThinkingBlock ? "hidden" : "visible"}`);
2895
2953
  }
@@ -2907,15 +2965,23 @@ export class InteractiveMode {
2907
2965
  fs.writeFileSync(tmpFile, currentText, "utf-8");
2908
2966
  // Stop TUI to release terminal
2909
2967
  this.ui.stop();
2910
- // Split by space to support editor arguments (e.g., "code --wait")
2911
- const [editor, ...editorArgs] = editorCmd.split(" ");
2968
+ // Parse the editor command respecting quoted tokens so editor paths that
2969
+ // contain spaces (common on Windows, e.g. '"C:\\Program Files\\...\\Code.exe"
2970
+ // --wait') are not split apart. A naive split(" ") would break them.
2971
+ const tokens = editorCmd.match(/"[^"]*"|\S+/g) ?? [editorCmd];
2972
+ const [editor, ...editorArgs] = tokens.map((t) => t.replace(/^"|"$/g, ""));
2912
2973
  // Spawn editor synchronously with inherited stdio for interactive editing
2913
2974
  const result = spawnSync(editor, [...editorArgs, tmpFile], {
2914
2975
  stdio: "inherit",
2915
2976
  shell: process.platform === "win32",
2916
2977
  });
2917
- // On successful exit (status 0), replace editor content
2918
- if (result.status === 0) {
2978
+ // Surface a launch failure (binary not found / not executable) instead of
2979
+ // silently doing nothing; result.status is null in that case.
2980
+ if (result.error) {
2981
+ this.showWarning(`Failed to launch editor: ${result.error.message}`);
2982
+ }
2983
+ else if (result.status === 0) {
2984
+ // On successful exit, replace editor content.
2919
2985
  const newContent = fs.readFileSync(tmpFile, "utf-8").replace(/\n$/, "");
2920
2986
  this.editor.setText(newContent);
2921
2987
  }
@@ -3013,6 +3079,11 @@ export class InteractiveMode {
3013
3079
  const hintText = theme.fg("dim", `↳ ${dequeueHint} to edit all queued messages`);
3014
3080
  this.pendingMessagesContainer.addChild(new TruncatedText(hintText, 1, 0));
3015
3081
  }
3082
+ // Re-attach deferred bash components: the clear() above detached them, but they
3083
+ // may still be streaming output and must stay visible until flushed to chat.
3084
+ for (const component of this.pendingBashComponents) {
3085
+ this.pendingMessagesContainer.addChild(component);
3086
+ }
3016
3087
  }
3017
3088
  restoreQueuedMessagesToEditor(options) {
3018
3089
  const { steering, followUp } = this.clearAllQueues();
@@ -4172,8 +4243,9 @@ export class InteractiveMode {
4172
4243
  this.showError("GitHub CLI (gh) is not installed. Install it from https://cli.github.com/");
4173
4244
  return;
4174
4245
  }
4175
- // Export to a temp file
4176
- const tmpFile = path.join(os.tmpdir(), "session.html");
4246
+ // Export to a temp file with a unique name so concurrent phi instances
4247
+ // running /share do not overwrite or delete each other's export.
4248
+ const tmpFile = path.join(os.tmpdir(), `pi-session-${crypto.randomUUID()}.html`);
4177
4249
  try {
4178
4250
  await this.session.exportToHtml(tmpFile);
4179
4251
  }