pi-bar 0.3.25 → 0.3.26
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.
- package/README.md +3 -3
- package/extensions/status-footer.ts +19 -352
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
claude-opus-4.7 ❯ think:med ❯ 2.6% / 1.0M ❯ Inspecting package structure ❯ plan:active
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
-

|
|
13
|
+

|
|
14
|
+

|
|
15
15
|
|
|
16
16
|
## Why use it?
|
|
17
17
|
|
|
@@ -43,11 +43,7 @@ type SerializedStatusFilter =
|
|
|
43
43
|
type TldrActivityType =
|
|
44
44
|
| "user_message"
|
|
45
45
|
| "assistant_update"
|
|
46
|
-
| "tool_input_start"
|
|
47
|
-
| "tool_input_update"
|
|
48
|
-
| "tool_input_end"
|
|
49
46
|
| "tool_call"
|
|
50
|
-
| "tool_execution_update"
|
|
51
47
|
| "tool_result"
|
|
52
48
|
| "assistant_final"
|
|
53
49
|
| "assistant_failure";
|
|
@@ -58,36 +54,17 @@ type TldrActivity = {
|
|
|
58
54
|
displayPriority: TldrDisplayPriority;
|
|
59
55
|
text: string;
|
|
60
56
|
toolCallId?: string;
|
|
61
|
-
// Identifies a continuous streaming run (tool input arg streaming, or tool
|
|
62
|
-
// execution output streaming). Activities sharing a group represent updates
|
|
63
|
-
// to the same task; staleness handling treats them as one stream.
|
|
64
|
-
progressGroup?: string;
|
|
65
57
|
};
|
|
66
58
|
type TldrCheckpoint = {
|
|
67
59
|
activityIndex: number;
|
|
68
|
-
activityType: TldrActivityType;
|
|
69
60
|
displayPriority: TldrDisplayPriority;
|
|
70
61
|
text: string;
|
|
71
|
-
progressGroup?: string;
|
|
72
62
|
};
|
|
73
63
|
type TldrCheckpointJob = {
|
|
74
64
|
activityIndex: number;
|
|
75
|
-
activityType: TldrActivityType;
|
|
76
65
|
displayPriority: TldrDisplayPriority;
|
|
77
66
|
runId: number;
|
|
78
|
-
progressGroup?: string;
|
|
79
67
|
};
|
|
80
|
-
|
|
81
|
-
// Activities that represent ongoing streaming work. These are treated
|
|
82
|
-
// specially: their checkpoints can render even when newer same-stream activity
|
|
83
|
-
// exists, and the display refresh slows to a 10s cadence for repeat renders so
|
|
84
|
-
// the footer does not become a model-call ticker.
|
|
85
|
-
function isToolProgressActivity(activityType: TldrActivityType): boolean {
|
|
86
|
-
return (
|
|
87
|
-
activityType === "tool_input_update" ||
|
|
88
|
-
activityType === "tool_execution_update"
|
|
89
|
-
);
|
|
90
|
-
}
|
|
91
68
|
type TldrModelPreference = { provider: string; id: string };
|
|
92
69
|
type FastModelAuth = {
|
|
93
70
|
model: Parameters<typeof complete>[0];
|
|
@@ -111,12 +88,6 @@ const TLDR_MAX_TOKENS = 120;
|
|
|
111
88
|
const TLDR_REQUEST_TIMEOUT_MS = 2_000;
|
|
112
89
|
const TLDR_DISPLAY_UPDATE_INTERVAL_MS = 1_200;
|
|
113
90
|
const TLDR_TARGET_SUMMARY_CHARS = 60;
|
|
114
|
-
// While rendering successive progress checkpoints from the same stream (e.g.
|
|
115
|
-
// long bash output), slow the display refresh dramatically. The TLDR text
|
|
116
|
-
// barely changes between adjacent chunks; refreshing every 1.2s would burn API
|
|
117
|
-
// calls and footer renders for noise.
|
|
118
|
-
const TOOL_PROGRESS_DISPLAY_UPDATE_INTERVAL_MS = 10_000;
|
|
119
|
-
const MAX_TOOL_PROGRESS_PARTIAL_CHARS = 280;
|
|
120
91
|
// Burst debouncing on the generation side: coalesce rapid-fire activities into
|
|
121
92
|
// one model call. Quiet window catches short bursts; max wait surfaces progress
|
|
122
93
|
// during continuous activity.
|
|
@@ -477,58 +448,13 @@ function extractTextContent(content: readonly unknown[] | undefined): string | u
|
|
|
477
448
|
return compacted.length > 0 ? compacted : undefined;
|
|
478
449
|
}
|
|
479
450
|
|
|
480
|
-
type ToolInputAssistantEvent =
|
|
481
|
-
| { type: "toolcall_start"; contentIndex: number; partial: unknown }
|
|
482
|
-
| {
|
|
483
|
-
type: "toolcall_delta";
|
|
484
|
-
contentIndex: number;
|
|
485
|
-
delta: string;
|
|
486
|
-
partial: unknown;
|
|
487
|
-
}
|
|
488
|
-
| {
|
|
489
|
-
type: "toolcall_end";
|
|
490
|
-
contentIndex: number;
|
|
491
|
-
toolCall: { id: string; name: string; arguments?: unknown };
|
|
492
|
-
partial: unknown;
|
|
493
|
-
};
|
|
494
|
-
|
|
495
|
-
function toolCallAtContentIndex(
|
|
496
|
-
partial: unknown,
|
|
497
|
-
contentIndex: number,
|
|
498
|
-
): { id: string; name: string; arguments?: unknown } | undefined {
|
|
499
|
-
if (!partial || typeof partial !== "object") return undefined;
|
|
500
|
-
const content = (partial as { content?: unknown }).content;
|
|
501
|
-
if (!Array.isArray(content)) return undefined;
|
|
502
|
-
const block = content[contentIndex];
|
|
503
|
-
if (!block || typeof block !== "object") return undefined;
|
|
504
|
-
const record = block as Record<string, unknown>;
|
|
505
|
-
if (record.type !== "toolCall") return undefined;
|
|
506
|
-
if (typeof record.id !== "string" || typeof record.name !== "string") {
|
|
507
|
-
return undefined;
|
|
508
|
-
}
|
|
509
|
-
return {
|
|
510
|
-
id: record.id,
|
|
511
|
-
name: record.name,
|
|
512
|
-
arguments: record.arguments,
|
|
513
|
-
};
|
|
514
|
-
}
|
|
515
|
-
|
|
516
451
|
class TldrFactCollector {
|
|
517
452
|
private nextIndex = 1;
|
|
518
453
|
private readonly activities: TldrActivity[] = [];
|
|
519
|
-
// Streaming tool inputs (`toolcall_start/delta/end` from message_update)
|
|
520
|
-
// arrive before the final tool id is available; content index is the best
|
|
521
|
-
// stable anchor until the id appears.
|
|
522
|
-
private readonly activeToolInputAnchors = new Map<string, string>();
|
|
523
454
|
|
|
524
455
|
resetConversation(): void {
|
|
525
456
|
this.nextIndex = 1;
|
|
526
457
|
this.activities.splice(0);
|
|
527
|
-
this.activeToolInputAnchors.clear();
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
latestActivity(): TldrActivity | undefined {
|
|
531
|
-
return this.activities[this.activities.length - 1];
|
|
532
458
|
}
|
|
533
459
|
|
|
534
460
|
recordUserMessage(prompt: string): TldrActivity {
|
|
@@ -560,79 +486,6 @@ class TldrFactCollector {
|
|
|
560
486
|
);
|
|
561
487
|
}
|
|
562
488
|
|
|
563
|
-
// Stage 1: tool input streaming. The model is writing tool name + args; pi
|
|
564
|
-
// surfaces this via `assistantMessageEvent` on `message_update` with
|
|
565
|
-
// toolcall_start / toolcall_delta / toolcall_end. Useful when args are long
|
|
566
|
-
// (big edits, structured plans) and the user wants visible progress before
|
|
567
|
-
// the actual tool execution starts.
|
|
568
|
-
recordToolInputActivity(
|
|
569
|
-
event: ToolInputAssistantEvent | undefined,
|
|
570
|
-
): TldrActivity | undefined {
|
|
571
|
-
if (!event) return undefined;
|
|
572
|
-
|
|
573
|
-
switch (event.type) {
|
|
574
|
-
case "toolcall_start": {
|
|
575
|
-
const toolCall = toolCallAtContentIndex(event.partial, event.contentIndex);
|
|
576
|
-
const toolName = toolCall?.name ?? "unknown";
|
|
577
|
-
const anchorKey = toolCall?.id ?? `content:${event.contentIndex}`;
|
|
578
|
-
const anchorText = `tool input: ${toolName}`;
|
|
579
|
-
this.activeToolInputAnchors.set(anchorKey, anchorText);
|
|
580
|
-
return this.addActivity(
|
|
581
|
-
"tool_input_start",
|
|
582
|
-
"normal",
|
|
583
|
-
anchorText,
|
|
584
|
-
toolCall?.id,
|
|
585
|
-
);
|
|
586
|
-
}
|
|
587
|
-
case "toolcall_delta": {
|
|
588
|
-
const toolCall = toolCallAtContentIndex(event.partial, event.contentIndex);
|
|
589
|
-
const anchorKey = toolCall?.id ?? `content:${event.contentIndex}`;
|
|
590
|
-
const anchorText =
|
|
591
|
-
this.activeToolInputAnchors.get(anchorKey) ??
|
|
592
|
-
`tool input: ${toolCall?.name ?? "unknown"}`;
|
|
593
|
-
|
|
594
|
-
// Repeated delta updates for the same call collapse into the latest
|
|
595
|
-
// snapshot. Otherwise long arg streams would push out useful facts.
|
|
596
|
-
const last = this.activities[this.activities.length - 1];
|
|
597
|
-
if (
|
|
598
|
-
last?.activityType === "tool_input_update" &&
|
|
599
|
-
last.progressGroup === `tool-input:${anchorKey}`
|
|
600
|
-
) {
|
|
601
|
-
this.activities.pop();
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
const argSnapshot = compactValue(toolCall?.arguments, 200);
|
|
605
|
-
const chunk = event.delta
|
|
606
|
-
? `latest: ${truncateText(compactText(event.delta), 120)}`
|
|
607
|
-
: undefined;
|
|
608
|
-
const update = [argSnapshot, chunk].filter(Boolean).join("; ");
|
|
609
|
-
|
|
610
|
-
return this.addActivity(
|
|
611
|
-
"tool_input_update",
|
|
612
|
-
"normal",
|
|
613
|
-
`${anchorText}; ${update || "streaming"}`,
|
|
614
|
-
toolCall?.id,
|
|
615
|
-
`tool-input:${anchorKey}`,
|
|
616
|
-
);
|
|
617
|
-
}
|
|
618
|
-
case "toolcall_end": {
|
|
619
|
-
const anchorKey = event.toolCall.id;
|
|
620
|
-
const anchorText =
|
|
621
|
-
this.activeToolInputAnchors.get(anchorKey) ??
|
|
622
|
-
`tool input: ${event.toolCall.name}`;
|
|
623
|
-
this.activeToolInputAnchors.delete(anchorKey);
|
|
624
|
-
|
|
625
|
-
const args = compactValue(event.toolCall.arguments, 240);
|
|
626
|
-
return this.addActivity(
|
|
627
|
-
"tool_input_end",
|
|
628
|
-
"normal",
|
|
629
|
-
args ? `${anchorText} completed; ${args}` : `${anchorText} completed`,
|
|
630
|
-
event.toolCall.id,
|
|
631
|
-
);
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
|
|
636
489
|
recordToolCall(event: {
|
|
637
490
|
toolName: string;
|
|
638
491
|
input?: Record<string, unknown>;
|
|
@@ -646,45 +499,6 @@ class TldrFactCollector {
|
|
|
646
499
|
);
|
|
647
500
|
}
|
|
648
501
|
|
|
649
|
-
// Stage 2: tool execution streaming. The host is running the tool and
|
|
650
|
-
// emitting partial output. Tagged with progressGroup so adjacent chunks of
|
|
651
|
-
// the same execution are treated as one continuous stream.
|
|
652
|
-
recordToolExecutionUpdate(event: {
|
|
653
|
-
toolCallId: string;
|
|
654
|
-
toolName: string;
|
|
655
|
-
partialResult: unknown;
|
|
656
|
-
}): TldrActivity {
|
|
657
|
-
const partialText =
|
|
658
|
-
extractTextContent(
|
|
659
|
-
Array.isArray((event.partialResult as { content?: unknown })?.content)
|
|
660
|
-
? ((event.partialResult as { content: unknown[] }).content as unknown[])
|
|
661
|
-
: undefined,
|
|
662
|
-
) ??
|
|
663
|
-
compactValue(event.partialResult, MAX_TOOL_PROGRESS_PARTIAL_CHARS) ??
|
|
664
|
-
"streaming";
|
|
665
|
-
|
|
666
|
-
const progressGroup = `tool-execution:${event.toolCallId}`;
|
|
667
|
-
|
|
668
|
-
// Adjacent execution-update activities for the same stream collapse into
|
|
669
|
-
// the latest. The retained activity list stays bounded and the model sees
|
|
670
|
-
// one current snapshot of in-progress output.
|
|
671
|
-
const last = this.activities[this.activities.length - 1];
|
|
672
|
-
if (
|
|
673
|
-
last?.activityType === "tool_execution_update" &&
|
|
674
|
-
last.progressGroup === progressGroup
|
|
675
|
-
) {
|
|
676
|
-
this.activities.pop();
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
return this.addActivity(
|
|
680
|
-
"tool_execution_update",
|
|
681
|
-
"normal",
|
|
682
|
-
`running ${event.toolName}; ${tailText(partialText, MAX_TOOL_PROGRESS_PARTIAL_CHARS)}`,
|
|
683
|
-
event.toolCallId,
|
|
684
|
-
progressGroup,
|
|
685
|
-
);
|
|
686
|
-
}
|
|
687
|
-
|
|
688
502
|
recordToolResult(event: {
|
|
689
503
|
toolName: string;
|
|
690
504
|
input?: Record<string, unknown>;
|
|
@@ -703,24 +517,15 @@ class TldrFactCollector {
|
|
|
703
517
|
const status = isError ? "error" : "ok";
|
|
704
518
|
const tool = compactToolInput(event.toolName, event.input);
|
|
705
519
|
|
|
706
|
-
//
|
|
707
|
-
//
|
|
708
|
-
// tool_execution_update progress. The model only needs the final outcome.
|
|
520
|
+
// Result fact subsumes the prior tool_call fact for the same call. Drop the
|
|
521
|
+
// matching tool_call so the model sees one combined fact instead of two.
|
|
709
522
|
if (event.toolCallId) {
|
|
710
|
-
const
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
activity.activityType === "tool_input_start" ||
|
|
717
|
-
activity.activityType === "tool_input_update" ||
|
|
718
|
-
activity.activityType === "tool_input_end" ||
|
|
719
|
-
activity.activityType === "tool_execution_update"
|
|
720
|
-
) {
|
|
721
|
-
this.activities.splice(i, 1);
|
|
722
|
-
}
|
|
723
|
-
}
|
|
523
|
+
const idx = this.activities.findIndex(
|
|
524
|
+
(activity) =>
|
|
525
|
+
activity.activityType === "tool_call" &&
|
|
526
|
+
activity.toolCallId === event.toolCallId,
|
|
527
|
+
);
|
|
528
|
+
if (idx !== -1) this.activities.splice(idx, 1);
|
|
724
529
|
}
|
|
725
530
|
|
|
726
531
|
return this.addActivity(
|
|
@@ -788,15 +593,13 @@ class TldrFactCollector {
|
|
|
788
593
|
displayPriority: TldrDisplayPriority,
|
|
789
594
|
text: string,
|
|
790
595
|
toolCallId?: string,
|
|
791
|
-
progressGroup?: string,
|
|
792
596
|
): TldrActivity {
|
|
793
|
-
const activity
|
|
597
|
+
const activity = {
|
|
794
598
|
index: this.nextIndex,
|
|
795
599
|
activityType,
|
|
796
600
|
displayPriority,
|
|
797
601
|
text: truncateText(text, MAX_ACTIVITY_TEXT_CHARS),
|
|
798
602
|
toolCallId,
|
|
799
|
-
progressGroup,
|
|
800
603
|
};
|
|
801
604
|
this.nextIndex++;
|
|
802
605
|
this.activities.push(activity);
|
|
@@ -828,9 +631,6 @@ class FooterTldrEngine {
|
|
|
828
631
|
private pendingNormalCheckpoint?: TldrCheckpointJob;
|
|
829
632
|
private normalCheckpointTimer?: ReturnType<typeof setTimeout>;
|
|
830
633
|
private normalCheckpointBurstStartedAt?: number;
|
|
831
|
-
// Last progress stream that produced a rendered checkpoint. Used to slow the
|
|
832
|
-
// display refresh for repeat renders from the same stream.
|
|
833
|
-
private lastRenderedProgressGroup?: string;
|
|
834
634
|
|
|
835
635
|
constructor(private readonly requestRender: () => void) {}
|
|
836
636
|
|
|
@@ -855,22 +655,7 @@ class FooterTldrEngine {
|
|
|
855
655
|
this.enqueue(ctx, this.facts.recordUserMessage(prompt));
|
|
856
656
|
}
|
|
857
657
|
|
|
858
|
-
recordAssistantUpdate(
|
|
859
|
-
ctx: ExtensionContext,
|
|
860
|
-
message: unknown,
|
|
861
|
-
assistantMessageEvent?: unknown,
|
|
862
|
-
): void {
|
|
863
|
-
// Tool input streaming hides inside `message_update`. Drain that signal
|
|
864
|
-
// first; only if no tool-input fact was produced does this look like a
|
|
865
|
-
// plain assistant text update worth recording.
|
|
866
|
-
const toolInputActivity = this.facts.recordToolInputActivity(
|
|
867
|
-
normalizeToolInputEvent(assistantMessageEvent),
|
|
868
|
-
);
|
|
869
|
-
if (toolInputActivity) {
|
|
870
|
-
this.enqueue(ctx, toolInputActivity);
|
|
871
|
-
return;
|
|
872
|
-
}
|
|
873
|
-
|
|
658
|
+
recordAssistantUpdate(ctx: ExtensionContext, message: unknown): void {
|
|
874
659
|
const activity = this.facts.recordAssistantUpdate(message);
|
|
875
660
|
if (activity) this.enqueue(ctx, activity);
|
|
876
661
|
}
|
|
@@ -882,17 +667,6 @@ class FooterTldrEngine {
|
|
|
882
667
|
this.enqueue(ctx, this.facts.recordToolCall(event));
|
|
883
668
|
}
|
|
884
669
|
|
|
885
|
-
recordToolExecutionUpdate(
|
|
886
|
-
ctx: ExtensionContext,
|
|
887
|
-
event: {
|
|
888
|
-
toolCallId: string;
|
|
889
|
-
toolName: string;
|
|
890
|
-
partialResult: unknown;
|
|
891
|
-
},
|
|
892
|
-
): void {
|
|
893
|
-
this.enqueue(ctx, this.facts.recordToolExecutionUpdate(event));
|
|
894
|
-
}
|
|
895
|
-
|
|
896
670
|
recordToolResult(
|
|
897
671
|
ctx: ExtensionContext,
|
|
898
672
|
event: {
|
|
@@ -927,7 +701,6 @@ class FooterTldrEngine {
|
|
|
927
701
|
this.lastDisplayAt = Number.NEGATIVE_INFINITY;
|
|
928
702
|
this.acceptedCheckpoints.splice(0);
|
|
929
703
|
this.pendingDisplayCheckpoint = undefined;
|
|
930
|
-
this.lastRenderedProgressGroup = undefined;
|
|
931
704
|
this.currentText = null;
|
|
932
705
|
this.requestRender();
|
|
933
706
|
}
|
|
@@ -996,12 +769,10 @@ class FooterTldrEngine {
|
|
|
996
769
|
|
|
997
770
|
private enqueue(ctx: ExtensionContext, activity: TldrActivity): void {
|
|
998
771
|
if (!ctx.hasUI) return;
|
|
999
|
-
const job
|
|
772
|
+
const job = {
|
|
1000
773
|
activityIndex: activity.index,
|
|
1001
|
-
activityType: activity.activityType,
|
|
1002
774
|
displayPriority: activity.displayPriority,
|
|
1003
775
|
runId: this.runId,
|
|
1004
|
-
progressGroup: activity.progressGroup,
|
|
1005
776
|
};
|
|
1006
777
|
|
|
1007
778
|
if (job.displayPriority === "immediate") {
|
|
@@ -1035,13 +806,9 @@ class FooterTldrEngine {
|
|
|
1035
806
|
this.scheduleNormalCheckpoint(ctx, job);
|
|
1036
807
|
}
|
|
1037
808
|
|
|
1038
|
-
private
|
|
809
|
+
private clearPendingDisplay(): void {
|
|
1039
810
|
if (this.displayTimer) clearTimeout(this.displayTimer);
|
|
1040
811
|
this.displayTimer = undefined;
|
|
1041
|
-
}
|
|
1042
|
-
|
|
1043
|
-
private clearPendingDisplay(): void {
|
|
1044
|
-
this.clearDisplayTimer();
|
|
1045
812
|
this.pendingDisplayCheckpoint = undefined;
|
|
1046
813
|
}
|
|
1047
814
|
|
|
@@ -1099,12 +866,10 @@ class FooterTldrEngine {
|
|
|
1099
866
|
const rawText = extractTextContent(response.content) ?? "";
|
|
1100
867
|
const text = sanitizeTldrText(rawText);
|
|
1101
868
|
if (!text) return;
|
|
1102
|
-
const checkpoint
|
|
869
|
+
const checkpoint = {
|
|
1103
870
|
activityIndex: job.activityIndex,
|
|
1104
|
-
activityType: job.activityType,
|
|
1105
871
|
displayPriority: job.displayPriority,
|
|
1106
872
|
text,
|
|
1107
|
-
progressGroup: job.progressGroup,
|
|
1108
873
|
};
|
|
1109
874
|
this.acceptCheckpoint(checkpoint);
|
|
1110
875
|
this.considerDisplayingCheckpoint(ctx, checkpoint);
|
|
@@ -1136,7 +901,7 @@ class FooterTldrEngine {
|
|
|
1136
901
|
checkpoint: TldrCheckpoint,
|
|
1137
902
|
): void {
|
|
1138
903
|
if (checkpoint.activityIndex <= this.lastRenderedActivityIndex) return;
|
|
1139
|
-
if (this.
|
|
904
|
+
if (checkpoint.activityIndex !== this.facts.latestActivityIndex()) return;
|
|
1140
905
|
|
|
1141
906
|
if (checkpoint.displayPriority !== "normal" || !this.lastRenderedText) {
|
|
1142
907
|
this.clearPendingDisplay();
|
|
@@ -1144,66 +909,22 @@ class FooterTldrEngine {
|
|
|
1144
909
|
return;
|
|
1145
910
|
}
|
|
1146
911
|
|
|
1147
|
-
const displayIntervalMs = this.displayIntervalFor(checkpoint);
|
|
1148
912
|
const elapsedMs = performance.now() - this.lastDisplayAt;
|
|
1149
|
-
if (elapsedMs >=
|
|
913
|
+
if (elapsedMs >= TLDR_DISPLAY_UPDATE_INTERVAL_MS) {
|
|
1150
914
|
this.clearPendingDisplay();
|
|
1151
915
|
this.renderCheckpoint(checkpoint);
|
|
1152
916
|
return;
|
|
1153
917
|
}
|
|
1154
918
|
|
|
1155
919
|
this.pendingDisplayCheckpoint = checkpoint;
|
|
1156
|
-
|
|
1157
|
-
// 10s progress-stream debounce should not block a subsequent ordinary
|
|
1158
|
-
// update that wants to render at the normal 1.2s cadence.
|
|
1159
|
-
this.clearDisplayTimer();
|
|
920
|
+
if (this.displayTimer) return;
|
|
1160
921
|
this.displayTimer = setTimeout(() => {
|
|
1161
922
|
this.displayTimer = undefined;
|
|
1162
923
|
const pending = this.pendingDisplayCheckpoint;
|
|
1163
924
|
this.pendingDisplayCheckpoint = undefined;
|
|
1164
|
-
if (!pending) return;
|
|
1165
|
-
if (this.shouldDropStaleCheckpoint(pending)) return;
|
|
925
|
+
if (!pending || pending.activityIndex !== this.facts.latestActivityIndex()) return;
|
|
1166
926
|
this.renderCheckpoint(pending);
|
|
1167
|
-
},
|
|
1168
|
-
}
|
|
1169
|
-
|
|
1170
|
-
// A model call for chunk N may finish after chunk N+1 has been recorded.
|
|
1171
|
-
// Most stale checkpoints are dropped, but progress streams are an exception:
|
|
1172
|
-
// while the same stream is still active, a slightly-behind TLDR is still
|
|
1173
|
-
// useful, and dropping it would leave the footer stuck on older text.
|
|
1174
|
-
private shouldDropStaleCheckpoint(checkpoint: TldrCheckpoint): boolean {
|
|
1175
|
-
if (checkpoint.activityIndex === this.facts.latestActivityIndex()) {
|
|
1176
|
-
return false;
|
|
1177
|
-
}
|
|
1178
|
-
|
|
1179
|
-
// Boundary checkpoints (user_message, assistant_final, etc.) must stay
|
|
1180
|
-
// latest-only. A stale prompt-start or final TLDR would describe the wrong
|
|
1181
|
-
// turn entirely.
|
|
1182
|
-
if (checkpoint.displayPriority !== "normal") return true;
|
|
1183
|
-
|
|
1184
|
-
if (!isToolProgressActivity(checkpoint.activityType)) return true;
|
|
1185
|
-
if (!checkpoint.progressGroup) return true;
|
|
1186
|
-
|
|
1187
|
-
const latestActivity = this.facts.latestActivity();
|
|
1188
|
-
return !(
|
|
1189
|
-
latestActivity &&
|
|
1190
|
-
latestActivity.index > checkpoint.activityIndex &&
|
|
1191
|
-
latestActivity.activityType === checkpoint.activityType &&
|
|
1192
|
-
latestActivity.progressGroup === checkpoint.progressGroup
|
|
1193
|
-
);
|
|
1194
|
-
}
|
|
1195
|
-
|
|
1196
|
-
private displayIntervalFor(checkpoint: TldrCheckpoint): number {
|
|
1197
|
-
if (!isToolProgressActivity(checkpoint.activityType)) {
|
|
1198
|
-
return TLDR_DISPLAY_UPDATE_INTERVAL_MS;
|
|
1199
|
-
}
|
|
1200
|
-
// First render from a new progress group uses the normal cadence so the
|
|
1201
|
-
// user sees the stream's first TLDR quickly. Subsequent renders for the
|
|
1202
|
-
// same group slow down: the model output barely changes between chunks.
|
|
1203
|
-
return checkpoint.progressGroup &&
|
|
1204
|
-
checkpoint.progressGroup === this.lastRenderedProgressGroup
|
|
1205
|
-
? TOOL_PROGRESS_DISPLAY_UPDATE_INTERVAL_MS
|
|
1206
|
-
: TLDR_DISPLAY_UPDATE_INTERVAL_MS;
|
|
927
|
+
}, TLDR_DISPLAY_UPDATE_INTERVAL_MS - elapsedMs);
|
|
1207
928
|
}
|
|
1208
929
|
|
|
1209
930
|
private renderCheckpoint(checkpoint: TldrCheckpoint): void {
|
|
@@ -1212,9 +933,6 @@ class FooterTldrEngine {
|
|
|
1212
933
|
this.lastRenderedActivityIndex = checkpoint.activityIndex;
|
|
1213
934
|
this.lastRenderedText = checkpoint.text;
|
|
1214
935
|
this.lastDisplayAt = performance.now();
|
|
1215
|
-
this.lastRenderedProgressGroup = isToolProgressActivity(checkpoint.activityType)
|
|
1216
|
-
? checkpoint.progressGroup
|
|
1217
|
-
: undefined;
|
|
1218
936
|
this.currentText = checkpoint.text;
|
|
1219
937
|
this.requestRender();
|
|
1220
938
|
}
|
|
@@ -1283,50 +1001,6 @@ function formatRawActivity(activity: TldrActivity): string {
|
|
|
1283
1001
|
return `- ${activity.text}`;
|
|
1284
1002
|
}
|
|
1285
1003
|
|
|
1286
|
-
// Narrows the unknown `assistantMessageEvent` from `message_update` to the
|
|
1287
|
-
// three tool-input streaming variants. Other event shapes (text_start, etc.)
|
|
1288
|
-
// produce undefined, signalling "no tool input fact to record".
|
|
1289
|
-
function normalizeToolInputEvent(value: unknown): ToolInputAssistantEvent | undefined {
|
|
1290
|
-
if (!value || typeof value !== "object") return undefined;
|
|
1291
|
-
const record = value as Record<string, unknown>;
|
|
1292
|
-
const type = record.type;
|
|
1293
|
-
if (type !== "toolcall_start" && type !== "toolcall_delta" && type !== "toolcall_end") {
|
|
1294
|
-
return undefined;
|
|
1295
|
-
}
|
|
1296
|
-
if (typeof record.contentIndex !== "number") return undefined;
|
|
1297
|
-
|
|
1298
|
-
if (type === "toolcall_delta") {
|
|
1299
|
-
return {
|
|
1300
|
-
type,
|
|
1301
|
-
contentIndex: record.contentIndex,
|
|
1302
|
-
delta: typeof record.delta === "string" ? record.delta : "",
|
|
1303
|
-
partial: record.partial,
|
|
1304
|
-
};
|
|
1305
|
-
}
|
|
1306
|
-
if (type === "toolcall_end") {
|
|
1307
|
-
const toolCall = record.toolCall;
|
|
1308
|
-
if (
|
|
1309
|
-
!toolCall ||
|
|
1310
|
-
typeof toolCall !== "object" ||
|
|
1311
|
-
typeof (toolCall as Record<string, unknown>).id !== "string" ||
|
|
1312
|
-
typeof (toolCall as Record<string, unknown>).name !== "string"
|
|
1313
|
-
) {
|
|
1314
|
-
return undefined;
|
|
1315
|
-
}
|
|
1316
|
-
return {
|
|
1317
|
-
type,
|
|
1318
|
-
contentIndex: record.contentIndex,
|
|
1319
|
-
toolCall: toolCall as { id: string; name: string; arguments?: unknown },
|
|
1320
|
-
partial: record.partial,
|
|
1321
|
-
};
|
|
1322
|
-
}
|
|
1323
|
-
return {
|
|
1324
|
-
type,
|
|
1325
|
-
contentIndex: record.contentIndex,
|
|
1326
|
-
partial: record.partial,
|
|
1327
|
-
};
|
|
1328
|
-
}
|
|
1329
|
-
|
|
1330
1004
|
// Strips ANSI/CSI/OSC/DCS/SOS/PM/APC escape sequences from model output before
|
|
1331
1005
|
// it lands in the terminal. The TLDR text is model-controlled and renders into
|
|
1332
1006
|
// the footer; the system prompt asking for plain text is not a security
|
|
@@ -1744,18 +1418,11 @@ export default function (pi: ExtensionAPI) {
|
|
|
1744
1418
|
tldr.recordUserMessage(ctx, event.prompt);
|
|
1745
1419
|
});
|
|
1746
1420
|
pi.on("message_update", async (event, ctx) => {
|
|
1747
|
-
tldr.recordAssistantUpdate(
|
|
1748
|
-
ctx,
|
|
1749
|
-
event.message,
|
|
1750
|
-
(event as { assistantMessageEvent?: unknown }).assistantMessageEvent,
|
|
1751
|
-
);
|
|
1421
|
+
tldr.recordAssistantUpdate(ctx, event.message);
|
|
1752
1422
|
});
|
|
1753
1423
|
pi.on("tool_call", async (event, ctx) => {
|
|
1754
1424
|
tldr.recordToolCall(ctx, event);
|
|
1755
1425
|
});
|
|
1756
|
-
pi.on("tool_execution_update", async (event, ctx) => {
|
|
1757
|
-
tldr.recordToolExecutionUpdate(ctx, event);
|
|
1758
|
-
});
|
|
1759
1426
|
pi.on("tool_result", async (event, ctx) => {
|
|
1760
1427
|
tldr.recordToolResult(ctx, event);
|
|
1761
1428
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-bar",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.26",
|
|
4
4
|
"description": "Pi footer/statusline showing active model, thinking level, context pressure, live TLDR, and extension statuses.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -51,6 +51,6 @@
|
|
|
51
51
|
"extensions": [
|
|
52
52
|
"./extensions/status-footer.ts"
|
|
53
53
|
],
|
|
54
|
-
"image": "https://cdn.jsdelivr.net/npm/pi-bar@0.3.
|
|
54
|
+
"image": "https://cdn.jsdelivr.net/npm/pi-bar@0.3.26/assets/screenshot-yellow.png"
|
|
55
55
|
}
|
|
56
56
|
}
|