omp-vcc 0.1.13 → 0.1.14

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.
@@ -0,0 +1,217 @@
1
+ // @ts-nocheck
2
+ import { estimateScriptAwareTokens } from "./token-estimate";
3
+
4
+ type UnknownRecord = Record<string, unknown>;
5
+
6
+ export interface RetainedToolOutputProjection {
7
+ version: 1;
8
+ retainedTokens: number;
9
+ omittedTokens: number;
10
+ pendingCount: number;
11
+ omissions: Array<{ entryId: string; marker: string }>;
12
+ }
13
+
14
+ export interface ToolOutputEntry {
15
+ id?: string;
16
+ type?: string;
17
+ message?: UnknownRecord;
18
+ }
19
+
20
+ export interface RetainedProjectionOptions {
21
+ /** Entry ids aligned with `messages`. Missing ids are allowed. */
22
+ entryIds?: Array<string | undefined>;
23
+ /** Serialized persisted source messages keyed by entry id. */
24
+ serializedByEntryId?: Record<string, string>;
25
+ /** Maps a persisted omission entry id to its tool call id. */
26
+ omissionToolCallIds?: Record<string, string>;
27
+ }
28
+
29
+ const isOutputMessage = (message: UnknownRecord | undefined): boolean => {
30
+ const role = message?.role;
31
+ return role === "toolResult" || (role === "bashExecution" && message?.excludeFromContext !== true);
32
+ };
33
+
34
+ const successfulAssistant = (message: UnknownRecord | undefined): boolean =>
35
+ message?.role === "assistant" && message.stopReason !== "error" && message.stopReason !== "aborted";
36
+
37
+ const outputText = (message: UnknownRecord): string => {
38
+ if (message.role === "bashExecution") return typeof message.output === "string" ? message.output : "";
39
+ const content = message.content;
40
+ if (typeof content === "string") return content;
41
+ if (!Array.isArray(content)) return "";
42
+ let text = "";
43
+ for (const part of content) {
44
+ if (part !== null && typeof part === "object" && "type" in part && part.type === "text" && "text" in part && typeof part.text === "string") {
45
+ text += part.text;
46
+ }
47
+ }
48
+ return text;
49
+ };
50
+
51
+ const outputTokens = (message: UnknownRecord): number => estimateScriptAwareTokens(outputText(message));
52
+
53
+ const uniqueIdCounts = (entries: ToolOutputEntry[]): Map<string, number> => {
54
+ const counts = new Map<string, number>();
55
+ for (const entry of entries) {
56
+ if (typeof entry.id !== "string" || entry.id.length === 0) continue;
57
+ counts.set(entry.id, (counts.get(entry.id) ?? 0) + 1);
58
+ }
59
+ return counts;
60
+ };
61
+
62
+ /**
63
+ * Select immutable, consumed tool output for the provider-visible retained-tail
64
+ * budget. Entries are message wrappers exactly as persisted by the host.
65
+ */
66
+ export const buildRetainedToolOutputProjection = (
67
+ entries: ToolOutputEntry[],
68
+ retainedToolOutputMaxTokens: number,
69
+ globalIndexById?: ReadonlyMap<string, number>,
70
+ ): RetainedToolOutputProjection => {
71
+ let lastAssistant = -1;
72
+ let pendingCount = 0;
73
+ for (let i = entries.length - 1; i >= 0; i--) {
74
+ const message = entries[i]?.message;
75
+ if (successfulAssistant(message)) {
76
+ lastAssistant = i;
77
+ break;
78
+ }
79
+ if (isOutputMessage(message)) pendingCount++;
80
+ }
81
+
82
+ const projection: RetainedToolOutputProjection = {
83
+ version: 1,
84
+ retainedTokens: 0,
85
+ omittedTokens: 0,
86
+ pendingCount,
87
+ omissions: [],
88
+ };
89
+ const limit = Number.isFinite(retainedToolOutputMaxTokens) && retainedToolOutputMaxTokens > 0
90
+ ? Math.floor(retainedToolOutputMaxTokens)
91
+ : 0;
92
+ if (limit === 0 || lastAssistant < 0) return projection;
93
+
94
+ const idCounts = uniqueIdCounts(entries);
95
+ let exhausted = false;
96
+ for (let i = lastAssistant - 1; i >= 0; i--) {
97
+ const entry = entries[i];
98
+ const message = entry?.message;
99
+ if (!isOutputMessage(message)) continue;
100
+ const text = outputText(message);
101
+ if (!text) continue;
102
+
103
+ // Missing/ambiguous ids cannot be replayed safely. They do not consume the
104
+ // allowance and cannot be persisted as omissions.
105
+ if (typeof entry?.id !== "string" || entry.id.length === 0 || idCounts.get(entry.id) !== 1) continue;
106
+
107
+ const tokens = outputTokens(message);
108
+ if (!exhausted && projection.retainedTokens + tokens <= limit) {
109
+ projection.retainedTokens += tokens;
110
+ continue;
111
+ }
112
+
113
+ exhausted = true;
114
+ projection.omittedTokens += tokens;
115
+ const globalIndex = globalIndexById?.get(entry.id);
116
+ projection.omissions.push({
117
+ entryId: entry.id,
118
+ marker: Number.isInteger(globalIndex)
119
+ ? `[Tool output text omitted from active context; recall #${globalIndex}.]`
120
+ : "[Tool output text omitted from active context; use recall.]",
121
+ });
122
+ }
123
+ projection.omissions.reverse();
124
+ return projection;
125
+ };
126
+
127
+ const directMessageId = (message: UnknownRecord): string | undefined => {
128
+ if (typeof message.entryId === "string") return message.entryId;
129
+ if (typeof message._entryId === "string") return message._entryId;
130
+ return undefined;
131
+ };
132
+
133
+ const replaceText = (message: UnknownRecord, marker: string): UnknownRecord => {
134
+ if (message.role === "bashExecution") {
135
+ if (typeof message.output !== "string") return message;
136
+ return { ...message, output: marker };
137
+ }
138
+ if (typeof message.content === "string") return { ...message, content: marker };
139
+ if (!Array.isArray(message.content)) return message;
140
+ let changed = false;
141
+ const content = message.content.map((part: unknown) => {
142
+ if (part !== null && typeof part === "object" && "type" in part && part.type === "text" && "text" in part && typeof part.text === "string") {
143
+ changed = true;
144
+ return { ...part, text: marker };
145
+ }
146
+ return part;
147
+ });
148
+ return changed ? { ...message, content } : message;
149
+ };
150
+
151
+ const findProjectionTarget = (
152
+ omission: { entryId: string },
153
+ messages: UnknownRecord[],
154
+ metadata: RetainedProjectionOptions,
155
+ ): number => {
156
+ const direct: number[] = [];
157
+ for (let i = 0; i < messages.length; i++) {
158
+ const id = metadata.entryIds?.[i] ?? directMessageId(messages[i]);
159
+ if (id === omission.entryId) direct.push(i);
160
+ }
161
+ if (direct.length === 1) return direct[0];
162
+ if (direct.length > 1) return -1;
163
+
164
+ const toolCallId = metadata.omissionToolCallIds?.[omission.entryId];
165
+ if (typeof toolCallId === "string" && toolCallId.length > 0) {
166
+ const byCall: number[] = [];
167
+ for (let i = 0; i < messages.length; i++) {
168
+ if (messages[i]?.toolCallId === toolCallId) byCall.push(i);
169
+ }
170
+ if (byCall.length === 1) return byCall[0];
171
+ if (byCall.length > 1) return -1;
172
+ }
173
+
174
+ const serialized = metadata.serializedByEntryId?.[omission.entryId];
175
+ if (typeof serialized !== "string") return -1;
176
+ const bySerialized: number[] = [];
177
+ for (let i = 0; i < messages.length; i++) {
178
+ let current: string | undefined;
179
+ try { current = JSON.stringify(messages[i]); } catch { current = undefined; }
180
+ if (current === serialized) bySerialized.push(i);
181
+ }
182
+ return bySerialized.length === 1 ? bySerialized[0] : -1;
183
+ };
184
+
185
+ /** Replay a persisted projection without mutating any host/session object. */
186
+ export const applyRetainedToolOutputProjection = (
187
+ messages: UnknownRecord[],
188
+ projection: RetainedToolOutputProjection,
189
+ options: RetainedProjectionOptions = {},
190
+ ): UnknownRecord[] => {
191
+ if (!Array.isArray(messages) || !projection || projection.version !== 1 || !Array.isArray(projection.omissions)) return messages;
192
+ const next = messages.slice();
193
+ for (const omission of projection.omissions) {
194
+ if (typeof omission?.entryId !== "string" || typeof omission?.marker !== "string") return messages;
195
+ const target = findProjectionTarget(omission, messages, options);
196
+ if (target < 0 || !isOutputMessage(messages[target])) return messages;
197
+ next[target] = replaceText(messages[target], omission.marker);
198
+ }
199
+ return next;
200
+ };
201
+
202
+ export interface ApplyToolOutputBudgetResult {
203
+ messages: UnknownRecord[];
204
+ projection: RetainedToolOutputProjection;
205
+ }
206
+
207
+ /** Convenience build+replay for a context payload aligned with persisted entries. */
208
+ export const applyToolOutputBudget = (
209
+ messages: UnknownRecord[],
210
+ entries: ToolOutputEntry[],
211
+ retainedToolOutputMaxTokens: number,
212
+ globalIndexById?: ReadonlyMap<string, number>,
213
+ options: RetainedProjectionOptions = {},
214
+ ): ApplyToolOutputBudgetResult => {
215
+ const projection = buildRetainedToolOutputProjection(entries, retainedToolOutputMaxTokens, globalIndexById);
216
+ return { messages: applyRetainedToolOutputProjection(messages, projection, options), projection };
217
+ };
@@ -1,12 +1,47 @@
1
1
  // @ts-nocheck
2
2
  import type { CompactionReason } from "./types";
3
3
 
4
+ import type { RetainedToolOutputProjection } from "./core/tool-output-budget";
5
+
6
+ export interface PiVccAppendCoverage {
7
+ firstCoveredEntryId: string;
8
+ lastCoveredEntryId: string;
9
+ firstKeptEntryId: string;
10
+ sourceMessageCount: number;
11
+ includesLegacySummary?: boolean;
12
+ rebasedFromCompactionId?: string;
13
+ }
14
+
15
+ export interface PiVccAppendSegment {
16
+ sequence: number;
17
+ summary: string;
18
+ coverage: PiVccAppendCoverage;
19
+ tokensBefore: number;
20
+ }
21
+
22
+ export interface PiVccAppendDetails {
23
+ compactor: "omp-vcc";
24
+ version: 3;
25
+ summaryMode: "append";
26
+ chainStart: boolean;
27
+ segment: PiVccAppendSegment;
28
+ trailingSummary: string;
29
+ sections: string[];
30
+ sourceMessageCount: number;
31
+ previousSummaryUsed: boolean;
32
+ retainedToolOutputProjection?: RetainedToolOutputProjection;
33
+ reason?: CompactionReason;
34
+ willRetry?: boolean;
35
+ savings?: PiVccCompactionDetails["savings"];
36
+ }
37
+
4
38
  export interface PiVccCompactionDetails {
5
39
  compactor: "pi-vcc" | "omp-vcc";
6
40
  version: number;
7
41
  sections: string[];
8
42
  sourceMessageCount: number;
9
43
  previousSummaryUsed: boolean;
44
+ retainedToolOutputProjection?: RetainedToolOutputProjection;
10
45
  reason?: CompactionReason;
11
46
  willRetry?: boolean;
12
47
  savings?: {