pi-midcompact 0.2.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.
- package/README.md +155 -0
- package/figures/agent-planning.svg +118 -0
- package/figures/context-projection.svg +64 -0
- package/figures/transaction-lifecycle.svg +117 -0
- package/package.json +57 -0
- package/skills/midcompact/SKILL.md +57 -0
- package/src/atoms.ts +152 -0
- package/src/index.ts +436 -0
- package/src/messages.ts +116 -0
- package/src/plan.ts +93 -0
- package/src/projection.ts +61 -0
- package/src/renderers.ts +67 -0
- package/src/review-ui.ts +198 -0
- package/src/state.ts +64 -0
- package/src/telemetry.ts +81 -0
- package/src/types.ts +152 -0
package/src/telemetry.ts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { ContextUsageSnapshot, DraftPlan, DraftTelemetry, TransactionState } from "./types.js";
|
|
2
|
+
|
|
3
|
+
interface ContextUsageLike {
|
|
4
|
+
tokens: number | null;
|
|
5
|
+
contextWindow: number;
|
|
6
|
+
percent: number | null;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function snapshotContextUsage(usage: ContextUsageLike | undefined): ContextUsageSnapshot | undefined {
|
|
10
|
+
if (!usage || !Number.isFinite(usage.contextWindow) || usage.contextWindow <= 0) return undefined;
|
|
11
|
+
return {
|
|
12
|
+
tokens: typeof usage.tokens === "number" && Number.isFinite(usage.tokens) ? Math.max(0, Math.round(usage.tokens)) : null,
|
|
13
|
+
contextWindow: Math.max(1, Math.round(usage.contextWindow)),
|
|
14
|
+
percent: typeof usage.percent === "number" && Number.isFinite(usage.percent) ? usage.percent : null,
|
|
15
|
+
capturedAt: new Date().toISOString(),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function draftTelemetry(transaction: TransactionState | undefined, draft: DraftPlan | undefined): DraftTelemetry {
|
|
20
|
+
const ranges = draft?.ranges ?? [];
|
|
21
|
+
const selectedOriginalApproxTokens = ranges.reduce((sum, range) => sum + range.originalApproxTokens, 0);
|
|
22
|
+
const selectedCompressedApproxTokens = ranges.reduce((sum, range) => sum + range.compressedApproxTokens, 0);
|
|
23
|
+
const estimatedSavedTokens = Math.max(0, selectedOriginalApproxTokens - selectedCompressedApproxTokens);
|
|
24
|
+
const anchorTokens = transaction?.anchorUsage?.tokens ?? null;
|
|
25
|
+
const contextWindow = transaction?.anchorUsage?.contextWindow ?? null;
|
|
26
|
+
const anchorPercent = transaction?.anchorUsage?.percent ?? null;
|
|
27
|
+
const projectedTokens = anchorTokens === null ? null : Math.max(0, anchorTokens - estimatedSavedTokens);
|
|
28
|
+
const projectedPercent = projectedTokens === null || contextWindow === null || contextWindow <= 0
|
|
29
|
+
? null
|
|
30
|
+
: (projectedTokens / contextWindow) * 100;
|
|
31
|
+
return {
|
|
32
|
+
anchorTokens,
|
|
33
|
+
contextWindow,
|
|
34
|
+
anchorPercent,
|
|
35
|
+
selectedOriginalApproxTokens,
|
|
36
|
+
selectedCompressedApproxTokens,
|
|
37
|
+
estimatedSavedTokens,
|
|
38
|
+
projectedTokens,
|
|
39
|
+
projectedPercent,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function formatTokenCount(tokens: number | null): string {
|
|
44
|
+
if (tokens === null || !Number.isFinite(tokens)) return "?";
|
|
45
|
+
const abs = Math.abs(tokens);
|
|
46
|
+
if (abs >= 1_000_000) return `${trim(tokens / 1_000_000)}M`;
|
|
47
|
+
if (abs >= 1_000) return `${trim(tokens / 1_000)}k`;
|
|
48
|
+
return String(Math.round(tokens));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function formatPercent(percent: number | null, approximate = false): string {
|
|
52
|
+
if (percent === null || !Number.isFinite(percent)) return "?";
|
|
53
|
+
return `${approximate ? "~" : ""}${trim(percent)}%`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function formatTelemetry(telemetry: DraftTelemetry): string {
|
|
57
|
+
const lines = ["Context awareness (informational, not a target):"];
|
|
58
|
+
if (telemetry.contextWindow !== null) {
|
|
59
|
+
lines.push(
|
|
60
|
+
`anchor: ${formatTokenCount(telemetry.anchorTokens)} / ${formatTokenCount(telemetry.contextWindow)} (${formatPercent(telemetry.anchorPercent)})`,
|
|
61
|
+
);
|
|
62
|
+
} else {
|
|
63
|
+
lines.push("anchor: Pi context usage unavailable");
|
|
64
|
+
}
|
|
65
|
+
lines.push(
|
|
66
|
+
`draft selection: ~${formatTokenCount(telemetry.selectedOriginalApproxTokens)} → ~${formatTokenCount(telemetry.selectedCompressedApproxTokens)} (save ~${formatTokenCount(telemetry.estimatedSavedTokens)})`,
|
|
67
|
+
);
|
|
68
|
+
if (telemetry.contextWindow !== null && telemetry.projectedTokens !== null) {
|
|
69
|
+
lines.push(
|
|
70
|
+
`projected if committed now: ~${formatTokenCount(telemetry.projectedTokens)} / ${formatTokenCount(telemetry.contextWindow)} (${formatPercent(telemetry.projectedPercent, true)})`,
|
|
71
|
+
);
|
|
72
|
+
} else {
|
|
73
|
+
lines.push("projected total: unavailable until Pi reports anchor usage");
|
|
74
|
+
}
|
|
75
|
+
return lines.join("\n");
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function trim(value: number): string {
|
|
79
|
+
const rounded = Math.round(value * 10) / 10;
|
|
80
|
+
return Number.isInteger(rounded) ? String(rounded) : rounded.toFixed(1);
|
|
81
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
export interface MessageLike {
|
|
2
|
+
role: string;
|
|
3
|
+
content?: unknown;
|
|
4
|
+
timestamp?: number;
|
|
5
|
+
toolCallId?: string;
|
|
6
|
+
toolName?: string;
|
|
7
|
+
customType?: string;
|
|
8
|
+
details?: unknown;
|
|
9
|
+
command?: string;
|
|
10
|
+
output?: string;
|
|
11
|
+
summary?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface SessionEntryLike {
|
|
15
|
+
id: string;
|
|
16
|
+
parentId?: string | null;
|
|
17
|
+
type: string;
|
|
18
|
+
customType?: string;
|
|
19
|
+
data?: unknown;
|
|
20
|
+
message?: MessageLike;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface MessageRef {
|
|
24
|
+
message: MessageLike;
|
|
25
|
+
key: string;
|
|
26
|
+
entryId?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export type AtomKind =
|
|
30
|
+
| "user"
|
|
31
|
+
| "assistant"
|
|
32
|
+
| "tool_exchange"
|
|
33
|
+
| "bash"
|
|
34
|
+
| "compressed"
|
|
35
|
+
| "custom"
|
|
36
|
+
| "orphan_tool_result"
|
|
37
|
+
| "other";
|
|
38
|
+
|
|
39
|
+
export interface Atom {
|
|
40
|
+
ref: string;
|
|
41
|
+
index: number;
|
|
42
|
+
kind: AtomKind;
|
|
43
|
+
messages: MessageRef[];
|
|
44
|
+
entryIds: string[];
|
|
45
|
+
messageKeys: string[];
|
|
46
|
+
preview: string;
|
|
47
|
+
fullText: string;
|
|
48
|
+
approxTokens: number;
|
|
49
|
+
compressible: boolean;
|
|
50
|
+
protocolClosed: boolean;
|
|
51
|
+
toolNames: string[];
|
|
52
|
+
roles: string[];
|
|
53
|
+
compressedBlockId?: string;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface ContextUsageSnapshot {
|
|
57
|
+
tokens: number | null;
|
|
58
|
+
contextWindow: number;
|
|
59
|
+
percent: number | null;
|
|
60
|
+
capturedAt: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface CompressionBlock {
|
|
64
|
+
id: string;
|
|
65
|
+
topic?: string;
|
|
66
|
+
summary: string;
|
|
67
|
+
entryIds: string[];
|
|
68
|
+
messageKeys: string[];
|
|
69
|
+
createdAt: string;
|
|
70
|
+
originalApproxTokens: number;
|
|
71
|
+
compressedApproxTokens: number;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface CommitStats {
|
|
75
|
+
transactionId: string;
|
|
76
|
+
committedAt: string;
|
|
77
|
+
addedBlockIds: string[];
|
|
78
|
+
addedRangeCount: number;
|
|
79
|
+
selectedOriginalApproxTokens: number;
|
|
80
|
+
selectedCompressedApproxTokens: number;
|
|
81
|
+
estimatedSavedTokens: number;
|
|
82
|
+
anchorUsage?: ContextUsageSnapshot;
|
|
83
|
+
projectedTokens: number | null;
|
|
84
|
+
projectedPercent: number | null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface CompressionState {
|
|
88
|
+
/** State format stays v1 for backward compatibility with v0.1.x sessions. */
|
|
89
|
+
version: 1;
|
|
90
|
+
createdAt: string;
|
|
91
|
+
blocks: CompressionBlock[];
|
|
92
|
+
lastCommit?: CommitStats;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export interface DraftRange {
|
|
96
|
+
id: string;
|
|
97
|
+
startRef: string;
|
|
98
|
+
endRef: string;
|
|
99
|
+
startIndex: number;
|
|
100
|
+
endIndex: number;
|
|
101
|
+
topic?: string;
|
|
102
|
+
summary: string;
|
|
103
|
+
entryIds: string[];
|
|
104
|
+
messageKeys: string[];
|
|
105
|
+
originalApproxTokens: number;
|
|
106
|
+
compressedApproxTokens: number;
|
|
107
|
+
startPreview: string;
|
|
108
|
+
endPreview: string;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export interface DraftPlan {
|
|
112
|
+
version: 1;
|
|
113
|
+
transactionId: string;
|
|
114
|
+
revision: number;
|
|
115
|
+
ranges: DraftRange[];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export interface TransactionState {
|
|
119
|
+
version: 1;
|
|
120
|
+
id: string;
|
|
121
|
+
anchorEntryId: string;
|
|
122
|
+
startedAt: string;
|
|
123
|
+
/** Frozen awareness captured when /midcompact starts; informational, never a target. */
|
|
124
|
+
anchorUsage?: ContextUsageSnapshot;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export interface DraftTelemetry {
|
|
128
|
+
anchorTokens: number | null;
|
|
129
|
+
contextWindow: number | null;
|
|
130
|
+
anchorPercent: number | null;
|
|
131
|
+
selectedOriginalApproxTokens: number;
|
|
132
|
+
selectedCompressedApproxTokens: number;
|
|
133
|
+
estimatedSavedTokens: number;
|
|
134
|
+
projectedTokens: number | null;
|
|
135
|
+
projectedPercent: number | null;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export interface LocateQuery {
|
|
139
|
+
ref?: string;
|
|
140
|
+
pattern?: string;
|
|
141
|
+
source?: "any" | "user" | "assistant" | "tool_call" | "tool_result";
|
|
142
|
+
toolName?: string;
|
|
143
|
+
direction?: "oldest" | "newest";
|
|
144
|
+
limit?: number;
|
|
145
|
+
detail?: "brief" | "full";
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export type ReviewAction =
|
|
149
|
+
| { action: "close" }
|
|
150
|
+
| { action: "edit-summary"; draftId: string }
|
|
151
|
+
| { action: "edit-topic"; draftId: string }
|
|
152
|
+
| { action: "remove"; draftId: string };
|