@oh-my-pi/pi-coding-agent 16.1.7 → 16.1.8

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 (93) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/dist/cli.js +3390 -3377
  3. package/dist/types/cli/session-picker.d.ts +0 -1
  4. package/dist/types/cli/tiny-models-cli.d.ts +2 -0
  5. package/dist/types/collab/protocol.d.ts +20 -1
  6. package/dist/types/config/settings-schema.d.ts +57 -6
  7. package/dist/types/extensibility/legacy-pi-ai-shim.d.ts +5 -1
  8. package/dist/types/internal-urls/filesystem-resource.d.ts +9 -0
  9. package/dist/types/mcp/loader.d.ts +3 -2
  10. package/dist/types/mcp/manager.d.ts +4 -3
  11. package/dist/types/mcp/startup-events.d.ts +21 -4
  12. package/dist/types/mnemopi/config.d.ts +1 -0
  13. package/dist/types/modes/components/agent-hub.d.ts +1 -0
  14. package/dist/types/modes/components/agent-transcript-viewer.d.ts +1 -0
  15. package/dist/types/modes/components/assistant-message.d.ts +5 -1
  16. package/dist/types/modes/components/btw-panel.d.ts +2 -0
  17. package/dist/types/modes/components/chat-transcript-builder.d.ts +1 -0
  18. package/dist/types/modes/components/session-selector.d.ts +0 -2
  19. package/dist/types/modes/controllers/btw-controller.d.ts +2 -0
  20. package/dist/types/modes/controllers/selector-controller.d.ts +1 -0
  21. package/dist/types/modes/controllers/streaming-reveal.d.ts +3 -2
  22. package/dist/types/modes/interactive-mode.d.ts +3 -0
  23. package/dist/types/modes/types.d.ts +3 -0
  24. package/dist/types/session/agent-session.d.ts +5 -3
  25. package/dist/types/session/session-history-format.d.ts +25 -0
  26. package/dist/types/session/snapcompact-inline.d.ts +1 -1
  27. package/dist/types/slash-commands/builtin-registry.d.ts +7 -4
  28. package/dist/types/slash-commands/types.d.ts +2 -0
  29. package/dist/types/system-prompt.d.ts +1 -0
  30. package/dist/types/tiny/models.d.ts +11 -7
  31. package/dist/types/tools/todo.d.ts +1 -0
  32. package/dist/types/utils/thinking-display.d.ts +2 -0
  33. package/package.json +12 -12
  34. package/src/advisor/__tests__/advisor.test.ts +104 -0
  35. package/src/advisor/runtime.ts +38 -2
  36. package/src/cli/session-picker.ts +1 -2
  37. package/src/cli/tiny-models-cli.ts +7 -2
  38. package/src/collab/guest.ts +172 -20
  39. package/src/collab/host.ts +47 -5
  40. package/src/collab/protocol.ts +16 -1
  41. package/src/config/settings-schema.ts +59 -5
  42. package/src/edit/renderer.ts +8 -12
  43. package/src/extensibility/legacy-pi-ai-shim.ts +16 -4
  44. package/src/internal-urls/docs-index.generated.txt +1 -1
  45. package/src/internal-urls/filesystem-resource.ts +34 -0
  46. package/src/internal-urls/local-protocol.ts +7 -1
  47. package/src/internal-urls/memory-protocol.ts +5 -1
  48. package/src/internal-urls/skill-protocol.ts +20 -4
  49. package/src/internal-urls/vault-protocol.ts +5 -2
  50. package/src/main.ts +8 -8
  51. package/src/mcp/loader.ts +4 -3
  52. package/src/mcp/manager.ts +35 -15
  53. package/src/mcp/startup-events.ts +106 -11
  54. package/src/mnemopi/config.ts +2 -0
  55. package/src/mnemopi/state.ts +3 -1
  56. package/src/modes/components/agent-hub.ts +4 -0
  57. package/src/modes/components/agent-transcript-viewer.ts +2 -0
  58. package/src/modes/components/assistant-message.ts +217 -18
  59. package/src/modes/components/btw-panel.ts +15 -3
  60. package/src/modes/components/chat-transcript-builder.ts +8 -2
  61. package/src/modes/components/model-selector.ts +72 -9
  62. package/src/modes/components/omfg-panel.ts +1 -1
  63. package/src/modes/components/session-selector.ts +4 -9
  64. package/src/modes/components/snapcompact-shape-preview.ts +1 -1
  65. package/src/modes/components/tool-execution.ts +10 -2
  66. package/src/modes/controllers/btw-controller.ts +32 -7
  67. package/src/modes/controllers/event-controller.ts +24 -4
  68. package/src/modes/controllers/input-controller.ts +43 -21
  69. package/src/modes/controllers/selector-controller.ts +23 -13
  70. package/src/modes/controllers/streaming-reveal.ts +78 -20
  71. package/src/modes/controllers/todo-command-controller.ts +9 -10
  72. package/src/modes/interactive-mode.ts +83 -8
  73. package/src/modes/types.ts +3 -0
  74. package/src/modes/utils/ui-helpers.ts +1 -0
  75. package/src/prompts/advisor/system.md +1 -1
  76. package/src/prompts/system/side-channel-no-tools.md +3 -0
  77. package/src/sdk.ts +12 -8
  78. package/src/session/agent-session.ts +349 -84
  79. package/src/session/history-storage.ts +15 -16
  80. package/src/session/session-history-format.ts +41 -1
  81. package/src/session/snapcompact-inline.ts +9 -6
  82. package/src/slash-commands/builtin-registry.ts +147 -21
  83. package/src/slash-commands/helpers/todo.ts +12 -6
  84. package/src/slash-commands/types.ts +2 -0
  85. package/src/system-prompt.ts +6 -11
  86. package/src/tiny/models.ts +7 -3
  87. package/src/tiny/title-client.ts +8 -2
  88. package/src/tiny/worker.ts +1 -0
  89. package/src/tools/search.ts +10 -1
  90. package/src/tools/sqlite-reader.ts +59 -3
  91. package/src/tools/todo.ts +6 -0
  92. package/src/tools/write.ts +4 -6
  93. package/src/utils/thinking-display.ts +78 -0
@@ -70,7 +70,12 @@ import type { Goal, GoalModeState } from "../goals/state";
70
70
  import { resolveLocalUrlToPath } from "../internal-urls";
71
71
  import { LSP_STARTUP_EVENT_CHANNEL, type LspStartupEvent } from "../lsp/startup-events";
72
72
  import type { MCPManager } from "../mcp";
73
- import { formatMCPConnectingMessage, isMcpConnectingEvent, MCP_CONNECTING_EVENT_CHANNEL } from "../mcp/startup-events";
73
+ import {
74
+ formatMCPConnectionStatusMessage,
75
+ isMcpConnectionStatusEvent,
76
+ MCP_CONNECTION_STATUS_EVENT_CHANNEL,
77
+ type McpConnectionStatusEvent,
78
+ } from "../mcp/startup-events";
74
79
  import {
75
80
  humanizePlanTitle,
76
81
  type PlanApprovalDetails,
@@ -88,7 +93,7 @@ import type { SessionContext } from "../session/session-context";
88
93
  import { getRecentSessions } from "../session/session-listing";
89
94
  import type { SessionManager } from "../session/session-manager";
90
95
  import type { ShakeMode } from "../session/shake-types";
91
- import { BUILTIN_SLASH_COMMAND_RESERVED_NAMES, BUILTIN_SLASH_COMMANDS } from "../slash-commands/builtin-registry";
96
+ import { BUILTIN_SLASH_COMMAND_RESERVED_NAMES, buildTuiBuiltinSlashCommands } from "../slash-commands/builtin-registry";
92
97
  import { formatDuration } from "../slash-commands/helpers/format";
93
98
  import { STTController, type SttState } from "../stt";
94
99
  import { discoverTitleSystemPromptFile, resolvePromptInput } from "../system-prompt";
@@ -401,6 +406,7 @@ export class InteractiveMode implements InteractiveModeContext {
401
406
  #modelCycleClearTimer: NodeJS.Timeout | undefined;
402
407
  todoPhases: TodoPhase[] = [];
403
408
  hideThinkingBlock = false;
409
+ proseOnlyThinking = true;
404
410
  pendingImages: ImageContent[] = [];
405
411
  pendingImageLinks: (string | undefined)[] = [];
406
412
  compactionQueuedMessages: CompactionQueuedMessage[] = [];
@@ -506,6 +512,14 @@ export class InteractiveMode implements InteractiveModeContext {
506
512
  this.loadingAnimation.stop();
507
513
  this.loadingAnimation = undefined;
508
514
  }
515
+ if (this.autoCompactionLoader) {
516
+ this.autoCompactionLoader.stop();
517
+ this.autoCompactionLoader = undefined;
518
+ }
519
+ if (this.retryLoader) {
520
+ this.retryLoader.stop();
521
+ this.retryLoader = undefined;
522
+ }
509
523
  this.statusContainer.clear();
510
524
  this.pendingMessagesContainer.clear();
511
525
  this.#cancelModelCycleClearTimer();
@@ -526,6 +540,10 @@ export class InteractiveMode implements InteractiveModeContext {
526
540
  #observerRegistry: SessionObserverRegistry;
527
541
  #eventBus?: EventBus;
528
542
  #eventBusUnsubscribers: Array<() => void> = [];
543
+ #mcpStatusOrder: string[] = [];
544
+ #mcpPendingServers = new Set<string>();
545
+ #mcpConnectedServers = new Set<string>();
546
+ #mcpFailedServers = new Map<string, string>();
529
547
  #welcomeComponent?: WelcomeComponent;
530
548
  readonly #chatHost: ChatBlockHost = { requestRender: () => this.ui.requestRender() };
531
549
 
@@ -559,13 +577,12 @@ export class InteractiveMode implements InteractiveModeContext {
559
577
  }),
560
578
  );
561
579
  this.#eventBusUnsubscribers.push(
562
- eventBus.on(MCP_CONNECTING_EVENT_CHANNEL, data => {
563
- if (!isMcpConnectingEvent(data)) {
564
- logger.warn("Ignoring malformed mcp:connecting event", { data });
580
+ eventBus.on(MCP_CONNECTION_STATUS_EVENT_CHANNEL, data => {
581
+ if (!isMcpConnectionStatusEvent(data)) {
582
+ logger.warn("Ignoring malformed mcp:connection-status event", { data });
565
583
  return;
566
584
  }
567
- if (this.settings.get("startup.quiet")) return;
568
- this.showStatus(formatMCPConnectingMessage(data.serverNames));
585
+ this.#handleMcpConnectionStatusEvent(data);
569
586
  }),
570
587
  );
571
588
  }
@@ -618,6 +635,7 @@ export class InteractiveMode implements InteractiveModeContext {
618
635
  this.statusLine.setAutoCompactEnabled(session.autoCompactionEnabled);
619
636
 
620
637
  this.hideThinkingBlock = settings.get("hideThinkingBlock");
638
+ this.proseOnlyThinking = settings.get("proseOnlyThinking");
621
639
 
622
640
  const hookCommands: SlashCommand[] = (
623
641
  this.session.extensionRunner?.getRegisteredCommands(BUILTIN_SLASH_COMMAND_RESERVED_NAMES) ?? []
@@ -643,8 +661,9 @@ export class InteractiveMode implements InteractiveModeContext {
643
661
  }
644
662
  }
645
663
 
664
+ const builtinCommands = buildTuiBuiltinSlashCommands({ ctx: this });
646
665
  // Store pending commands for init() where file commands are loaded async
647
- this.#pendingSlashCommands = [...BUILTIN_SLASH_COMMANDS, ...hookCommands, ...customCommands, ...skillCommandList];
666
+ this.#pendingSlashCommands = [...builtinCommands, ...hookCommands, ...customCommands, ...skillCommandList];
648
667
 
649
668
  this.#uiHelpers = new UiHelpers(this);
650
669
  this.#btwController = new BtwController(this);
@@ -660,6 +679,54 @@ export class InteractiveMode implements InteractiveModeContext {
660
679
  this.#observerRegistry = new SessionObserverRegistry();
661
680
  }
662
681
 
682
+ #handleMcpConnectionStatusEvent(event: McpConnectionStatusEvent): void {
683
+ if (this.settings.get("startup.quiet")) return;
684
+ if (event.type === "connecting") {
685
+ this.#mcpStatusOrder = [];
686
+ this.#mcpPendingServers.clear();
687
+ this.#mcpConnectedServers.clear();
688
+ this.#mcpFailedServers.clear();
689
+ for (const serverName of event.serverNames) {
690
+ this.#trackMcpStatusServer(serverName);
691
+ this.#mcpPendingServers.add(serverName);
692
+ }
693
+ } else if (event.type === "connected") {
694
+ this.#trackMcpStatusServer(event.serverName);
695
+ this.#mcpPendingServers.delete(event.serverName);
696
+ this.#mcpFailedServers.delete(event.serverName);
697
+ this.#mcpConnectedServers.add(event.serverName);
698
+ } else {
699
+ this.#trackMcpStatusServer(event.serverName);
700
+ this.#mcpPendingServers.delete(event.serverName);
701
+ this.#mcpConnectedServers.delete(event.serverName);
702
+ this.#mcpFailedServers.set(event.serverName, event.error);
703
+ }
704
+
705
+ const message = formatMCPConnectionStatusMessage({
706
+ pendingServers: this.#orderedMcpStatusServers(this.#mcpPendingServers),
707
+ connectedServers: this.#orderedMcpStatusServers(this.#mcpConnectedServers),
708
+ failedServers: this.#orderedMcpStatusFailures(),
709
+ });
710
+ if (message) this.showStatus(message);
711
+ }
712
+
713
+ #trackMcpStatusServer(serverName: string): void {
714
+ if (!this.#mcpStatusOrder.includes(serverName)) {
715
+ this.#mcpStatusOrder.push(serverName);
716
+ }
717
+ }
718
+
719
+ #orderedMcpStatusServers(servers: ReadonlySet<string>): string[] {
720
+ return this.#mcpStatusOrder.filter(serverName => servers.has(serverName));
721
+ }
722
+
723
+ #orderedMcpStatusFailures(): Array<{ serverName: string; error: string }> {
724
+ return this.#mcpStatusOrder.flatMap(serverName => {
725
+ const error = this.#mcpFailedServers.get(serverName);
726
+ return error === undefined ? [] : [{ serverName, error }];
727
+ });
728
+ }
729
+
663
730
  playWelcomeIntro(): void {
664
731
  const welcome = this.#welcomeComponent;
665
732
  // Component-scoped: the intro only mutates the welcome box's own rows,
@@ -3769,6 +3836,14 @@ export class InteractiveMode implements InteractiveModeContext {
3769
3836
  return this.#btwController.handleBranch();
3770
3837
  }
3771
3838
 
3839
+ canCopyBtw(): boolean {
3840
+ return this.#btwController.canCopy();
3841
+ }
3842
+
3843
+ handleBtwCopyKey(): Promise<boolean> {
3844
+ return this.#btwController.handleCopy();
3845
+ }
3846
+
3772
3847
  async handleBtwBranch(question: string, assistantMessage: AssistantMessage): Promise<void> {
3773
3848
  try {
3774
3849
  const result = await this.session.branchFromBtw(question, assistantMessage);
@@ -148,6 +148,7 @@ export interface InteractiveModeContext {
148
148
  loopLimit?: LoopLimitRuntime;
149
149
  planModePlanFilePath?: string;
150
150
  hideThinkingBlock: boolean;
151
+ proseOnlyThinking: boolean;
151
152
  pendingImages: ImageContent[];
152
153
  pendingImageLinks: (string | undefined)[];
153
154
  compactionQueuedMessages: CompactionQueuedMessage[];
@@ -348,6 +349,8 @@ export interface InteractiveModeContext {
348
349
  handleBtwEscape(): boolean;
349
350
  handleBtwBranchKey(): Promise<boolean>;
350
351
  canBranchBtw(): boolean;
352
+ canCopyBtw(): boolean;
353
+ handleBtwCopyKey(): Promise<boolean>;
351
354
  handleBtwBranch(question: string, assistantMessage: AssistantMessage): Promise<void>;
352
355
  handleOmfgCommand(complaint: string): Promise<void>;
353
356
  hasActiveOmfg(): boolean;
@@ -332,6 +332,7 @@ export class UiHelpers {
332
332
  () => this.ctx.ui.requestRender(),
333
333
  this.ctx.viewSession.extensionRunner?.getAssistantThinkingRenderers(),
334
334
  this.ctx.ui.imageBudget,
335
+ this.ctx.proseOnlyThinking,
335
336
  );
336
337
  this.ctx.chatContainer.addChild(assistantComponent);
337
338
  break;
@@ -24,7 +24,7 @@ Keep exploration lean:
24
24
  - Offer alternatives, not lectures.
25
25
  - NEVER restate information the agent already has, including errors they have seen.
26
26
  - Examples: type errors, LSP diagnostics, failed builds, failing tests, lint.
27
- - NEVER repeat advice you already gave.
27
+ - NEVER repeat advice you already gave, and NEVER send the same advice twice.
28
28
  - NEVER nitpick about things user stated they are okay with. You are the advocate for the user.
29
29
  </communication>
30
30
 
@@ -0,0 +1,3 @@
1
+ <system-reminder>
2
+ This is an ephemeral side-channel turn that reuses the current conversation's context. The tool catalog stays attached only to keep the prompt cache warm — tools are NOT available on this turn. Do NOT emit any tool call; reply with plain text only. Any tool call you produce is discarded without executing.
3
+ </system-reminder>
package/src/sdk.ts CHANGED
@@ -87,7 +87,7 @@ import {
87
87
  type MCPToolsLoadResult,
88
88
  parseMCPToolName,
89
89
  } from "./mcp";
90
- import { MCP_CONNECTING_EVENT_CHANNEL, type McpConnectingEvent } from "./mcp/startup-events";
90
+ import { MCP_CONNECTION_STATUS_EVENT_CHANNEL, type McpConnectionStatusEvent } from "./mcp/startup-events";
91
91
  import { createSessionMemoryRuntimeContext, resolveMemoryBackend } from "./memory-backend";
92
92
  import type { MnemopiSessionState } from "./mnemopi/state";
93
93
  import asyncResultTemplate from "./prompts/tools/async-result.md" with { type: "text" };
@@ -805,6 +805,7 @@ export interface BuildSystemPromptOptions {
805
805
  * as separate entries so providers can cache prompt prefixes without concatenating blocks.
806
806
  */
807
807
  export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}): Promise<BuildSystemPromptResult> {
808
+ const toolMap = options.tools ? new Map(options.tools.map(tool => [tool.name, tool])) : undefined;
808
809
  return await buildSystemPromptInternal({
809
810
  cwd: options.cwd,
810
811
  customPrompt: options.customPrompt,
@@ -812,6 +813,8 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
812
813
  contextFiles: options.contextFiles,
813
814
  appendSystemPrompt: options.appendPrompt,
814
815
  inlineToolDescriptors: options.inlineToolDescriptors,
816
+ toolNames: options.tools?.map(tool => tool.name),
817
+ tools: toolMap ? buildSystemPromptToolMetadata(toolMap) : undefined,
815
818
  });
816
819
  }
817
820
 
@@ -1617,12 +1620,13 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
1617
1620
  | ((liveSession: AgentSession, activation: DeferredMCPActivation) => void)
1618
1621
  | undefined;
1619
1622
  const startupQuiet = settings.get("startup.quiet");
1620
- const onMCPConnecting = (serverNames: string[]) => {
1621
- if (!options.hasUI || startupQuiet || serverNames.length === 0) return;
1622
- eventBus.emit(MCP_CONNECTING_EVENT_CHANNEL, { serverNames } satisfies McpConnectingEvent);
1623
+ const onMCPStatus = (event: McpConnectionStatusEvent) => {
1624
+ if (!options.hasUI || startupQuiet) return;
1625
+ if (event.type === "connecting" && event.serverNames.length === 0) return;
1626
+ eventBus.emit(MCP_CONNECTION_STATUS_EVENT_CHANNEL, event);
1623
1627
  };
1624
1628
  const mcpDiscoverOptions = {
1625
- onConnecting: onMCPConnecting,
1629
+ onStatus: onMCPStatus,
1626
1630
  enableProjectConfig: settings.get("mcp.enableProjectConfig") ?? true,
1627
1631
  // Always filter Exa - we have native integration
1628
1632
  filterExa: true,
@@ -2412,9 +2416,9 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
2412
2416
  : undefined;
2413
2417
  const transformProviderContext =
2414
2418
  obfuscator || snapcompactInline
2415
- ? (context: Context, transformModel: Model): Context => {
2419
+ ? async (context: Context, transformModel: Model): Promise<Context> => {
2416
2420
  let transformed = obfuscator ? obfuscateProviderContext(obfuscator, context) : context;
2417
- if (snapcompactInline) transformed = snapcompactInline.transform(transformed, transformModel);
2421
+ if (snapcompactInline) transformed = await snapcompactInline.transform(transformed, transformModel);
2418
2422
  return transformed;
2419
2423
  }
2420
2424
  : undefined;
@@ -2474,7 +2478,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
2474
2478
  presencePenalty: settings.get("presencePenalty") >= 0 ? settings.get("presencePenalty") : undefined,
2475
2479
  repetitionPenalty: settings.get("repetitionPenalty") >= 0 ? settings.get("repetitionPenalty") : undefined,
2476
2480
  serviceTier: initialServiceTier,
2477
- hideThinkingSummary: settings.get("hideThinkingBlock"),
2481
+ hideThinkingSummary: settings.get("omitThinking"),
2478
2482
  kimiApiFormat: settings.get("providers.kimiApiFormat") ?? "anthropic",
2479
2483
  preferWebsockets: preferOpenAICodexWebsockets,
2480
2484
  getToolContext: tc => toolContextStore.getContext(tc),