pi-bar 0.3.46 → 0.3.47

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.
@@ -613,6 +613,7 @@ function tailText(text: string, maxChars: number): string {
613
613
  }
614
614
 
615
615
  function compactText(text: string): string {
616
+ if (typeof text !== "string") return "";
616
617
  return text.replace(ANSI_PATTERN, "").replace(/\s+/g, " ").trim();
617
618
  }
618
619
 
@@ -749,6 +750,16 @@ function extractTextContent(content: readonly unknown[] | undefined): string | u
749
750
  return compacted.length > 0 ? compacted : undefined;
750
751
  }
751
752
 
753
+ function customMessageText(message: unknown): string | undefined {
754
+ if (!message || typeof message !== "object") return undefined;
755
+ const record = message as Record<string, unknown>;
756
+ if (typeof record.content === "string") {
757
+ const compacted = compactText(record.content);
758
+ return compacted.length > 0 ? compacted : undefined;
759
+ }
760
+ return extractTextContent(record.content as readonly unknown[] | undefined);
761
+ }
762
+
752
763
  class ProgressFactCollector {
753
764
  private nextIndex = 1;
754
765
  private readonly activities: ProgressActivity[] = [];
@@ -766,6 +777,18 @@ class ProgressFactCollector {
766
777
  );
767
778
  }
768
779
 
780
+ recordCustomMessage(message: unknown): ProgressActivity | undefined {
781
+ const text = customMessageText(message);
782
+ if (!text) return undefined;
783
+ const record = (message ?? {}) as { customType?: unknown };
784
+ const customType = typeof record.customType === "string" ? record.customType : "custom";
785
+ return this.addActivity(
786
+ "user_message",
787
+ "immediate",
788
+ `custom (${customType}): ${truncateText(text, MAX_USER_TEXT_CHARS)}`,
789
+ );
790
+ }
791
+
769
792
  recordAssistantUpdate(message: unknown): ProgressActivity | undefined {
770
793
  if (!message || typeof message !== "object") return undefined;
771
794
  const record = message as Record<string, unknown>;
@@ -957,7 +980,7 @@ class FooterProgressEngine {
957
980
  this.startFreshRun();
958
981
  }
959
982
 
960
- recordUserMessage(ctx: ExtensionContext, prompt: string): void {
983
+ beginUserTurn(ctx: ExtensionContext): void {
961
984
  // A new user turn replaces the entire visible progress update context, so wipe both
962
985
  // the rendered text and the prior-turn checkpoint context that feeds the
963
986
  // model. Carrying old checkpoints biased the next turn's phrasing toward
@@ -967,9 +990,24 @@ class FooterProgressEngine {
967
990
  this.acceptedCheckpoints.splice(0);
968
991
  this.latestAcceptedActivityIndex = 0;
969
992
  this.requestRender();
993
+ }
994
+
995
+ recordUserMessage(ctx: ExtensionContext, prompt: string): void {
996
+ this.beginUserTurn(ctx);
970
997
  this.enqueue(ctx, this.facts.recordUserMessage(prompt));
971
998
  }
972
999
 
1000
+ // Custom messages (e.g. subagent results handed back to the main session via
1001
+ // sendMessage with triggerTurn) also start a run, so reset progress the same
1002
+ // way a user turn does. A text-less custom message (empty or image-only
1003
+ // content) still resets the turn — a blank footer is preferable to stale
1004
+ // progress from the previous run — it just records no activity.
1005
+ recordCustomMessage(ctx: ExtensionContext, message: unknown): void {
1006
+ this.beginUserTurn(ctx);
1007
+ const activity = this.facts.recordCustomMessage(message);
1008
+ if (activity) this.enqueue(ctx, activity);
1009
+ }
1010
+
973
1011
  recordAssistantUpdate(ctx: ExtensionContext, message: unknown): void {
974
1012
  const activity = this.facts.recordAssistantUpdate(message);
975
1013
  if (activity) this.enqueue(ctx, activity);
@@ -2465,7 +2503,23 @@ export default function (pi: ExtensionAPI) {
2465
2503
  pi.on("agent_end", async (_event, ctx) => { syncUsage(ctx); refresh(); });
2466
2504
  pi.on("session_compact", async (_event, ctx) => { syncUsage(ctx); refresh(); });
2467
2505
  pi.on("before_agent_start", async (event, ctx) => {
2468
- if (visibleSegments.includes("progress")) progress.recordUserMessage(ctx, event.prompt);
2506
+ if (!visibleSegments.includes("progress")) return;
2507
+ // Newer pi versions fire before_agent_start as a discriminated union: runs
2508
+ // started via sendMessage(..., { triggerTurn: true }) (e.g. subagent results
2509
+ // handed back to the main session) carry a custom `message` instead of a
2510
+ // `prompt`. Handle both shapes so custom-message runs do not crash.
2511
+ const record = event as unknown as { messageType?: unknown; message?: unknown; prompt?: string };
2512
+ if (record.messageType === "custom") {
2513
+ progress.recordCustomMessage(ctx, record.message);
2514
+ return;
2515
+ }
2516
+ if (typeof record.prompt === "string") {
2517
+ progress.recordUserMessage(ctx, record.prompt);
2518
+ return;
2519
+ }
2520
+ // Future event variants without a prompt still start a fresh run; they just
2521
+ // have no user text to record.
2522
+ progress.beginUserTurn(ctx);
2469
2523
  });
2470
2524
  pi.on("message_update", async (event, ctx) => {
2471
2525
  if (visibleSegments.includes("progress")) progress.recordAssistantUpdate(ctx, event.message);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-bar",
3
- "version": "0.3.46",
3
+ "version": "0.3.47",
4
4
  "description": "Pi footer/statusline showing active model, thinking level, context pressure, live progress updates, and extension statuses.",
5
5
  "type": "module",
6
6
  "keywords": [