@linxiraos/pi-agent-core 1.1.27 → 1.2.0
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/CHANGELOG.md +6 -264
- package/README.md +1 -1
- package/dist/types/agent.d.ts +15 -2
- package/dist/types/compaction/branch-summarization.d.ts +1 -1
- package/dist/types/compaction/compaction.d.ts +10 -2
- package/dist/types/compaction/messages.d.ts +6 -0
- package/dist/types/compaction/pruning.d.ts +31 -7
- package/dist/types/compaction/tool-protection.d.ts +2 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/speculative-execution.d.ts +1 -1
- package/dist/types/telemetry.d.ts +60 -60
- package/dist/types/tool-arguments.d.ts +19 -0
- package/dist/types/types.d.ts +12 -1
- package/package.json +8 -8
- package/src/agent-loop.ts +15 -15
- package/src/agent.ts +49 -4
- package/src/append-only-context.ts +38 -29
- package/src/compaction/branch-summarization.ts +1 -1
- package/src/compaction/compaction-v2-streaming.ts +10 -1
- package/src/compaction/compaction.ts +36 -42
- package/src/compaction/message-cache.ts +1 -1
- package/src/compaction/messages.ts +25 -0
- package/src/compaction/openai.ts +2 -0
- package/src/compaction/pruning.ts +249 -77
- package/src/compaction/tool-protection.ts +8 -2
- package/src/index.ts +2 -0
- package/src/speculative-execution.ts +5 -9
- package/src/telemetry.ts +81 -80
- package/src/tool-arguments.ts +38 -0
- package/src/types.ts +12 -1
|
@@ -64,10 +64,9 @@ import type { CompactionEntry, SessionEntry } from "./entries";
|
|
|
64
64
|
import { NativeCompactionError } from "./errors";
|
|
65
65
|
import {
|
|
66
66
|
type ConvertToLlm,
|
|
67
|
-
createBranchSummaryMessage,
|
|
68
67
|
createCompactionSummaryMessage,
|
|
69
|
-
createCustomMessage,
|
|
70
68
|
defaultConvertToLlm,
|
|
69
|
+
getMessageFromEntry,
|
|
71
70
|
} from "./messages";
|
|
72
71
|
import {
|
|
73
72
|
assertRemoteCompactionInputFits,
|
|
@@ -142,34 +141,6 @@ function extractFileOperations(
|
|
|
142
141
|
return fileOps;
|
|
143
142
|
}
|
|
144
143
|
|
|
145
|
-
// ============================================================================
|
|
146
|
-
// Message Extraction
|
|
147
|
-
// ============================================================================
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Extract AgentMessage from an entry if it produces one.
|
|
151
|
-
* Returns undefined for entries that don't contribute to LLM context.
|
|
152
|
-
*/
|
|
153
|
-
function getMessageFromEntry(entry: SessionEntry): AgentMessage | undefined {
|
|
154
|
-
if (entry.type === "message") {
|
|
155
|
-
return entry.message;
|
|
156
|
-
}
|
|
157
|
-
if (entry.type === "custom_message") {
|
|
158
|
-
return createCustomMessage(
|
|
159
|
-
entry.customType,
|
|
160
|
-
entry.content,
|
|
161
|
-
entry.display,
|
|
162
|
-
entry.details,
|
|
163
|
-
entry.timestamp,
|
|
164
|
-
entry.attribution,
|
|
165
|
-
);
|
|
166
|
-
}
|
|
167
|
-
if (entry.type === "branch_summary") {
|
|
168
|
-
return createBranchSummaryMessage(entry.summary, entry.fromId, entry.timestamp);
|
|
169
|
-
}
|
|
170
|
-
return undefined;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
144
|
/** Result from compact() - SessionManager adds uuid/parentUuid when saving */
|
|
174
145
|
export interface CompactionResult<T = unknown> {
|
|
175
146
|
summary: string;
|
|
@@ -192,6 +163,14 @@ export interface CompactionSettings {
|
|
|
192
163
|
strategy?: "context-full" | "handoff" | "shake" | "snapcompact" | "off";
|
|
193
164
|
thresholdPercent?: number;
|
|
194
165
|
thresholdTokens?: number;
|
|
166
|
+
/**
|
|
167
|
+
* Stands in for the context window when the percentage or reserve-based
|
|
168
|
+
* threshold is computed (when `> 0` and smaller than the window); a positive
|
|
169
|
+
* `thresholdTokens` still wins and is checked against the real window (at
|
|
170
|
+
* or past it, the window less its reserve applies). Set by per-model
|
|
171
|
+
* compaction limits; request and overflow budgets keep the real window.
|
|
172
|
+
*/
|
|
173
|
+
baseWindowTokens?: number;
|
|
195
174
|
midTurnEnabled?: boolean;
|
|
196
175
|
/**
|
|
197
176
|
* Tokens reserved below the context window for the next prompt + response.
|
|
@@ -387,27 +366,42 @@ export function compactionContextTokens(providerContextTokens: number, storedCon
|
|
|
387
366
|
return Math.max(Math.max(0, providerContextTokens), Math.max(0, storedConversationEstimate));
|
|
388
367
|
}
|
|
389
368
|
|
|
369
|
+
/**
|
|
370
|
+
* The prompt budget below `contextWindow`: the window less
|
|
371
|
+
* {@link resolveBudgetReserveTokens}, capped at `contextWindow - 1` so it never
|
|
372
|
+
* reaches the whole window even when the reserve resolves to 0.
|
|
373
|
+
*/
|
|
374
|
+
function promptBudgetTokens(contextWindow: number, settings: CompactionSettings): number {
|
|
375
|
+
return Math.max(0, Math.min(contextWindow - 1, contextWindow - resolveBudgetReserveTokens(contextWindow, settings)));
|
|
376
|
+
}
|
|
377
|
+
|
|
390
378
|
export function resolveThresholdTokens(contextWindow: number, settings: CompactionSettings): number {
|
|
391
|
-
// Fixed token limit takes priority over percentage
|
|
379
|
+
// Fixed token limit takes priority over percentage, and is checked against
|
|
380
|
+
// the real window: `baseWindowTokens` only rescales the policies below.
|
|
392
381
|
const thresholdTokens = settings.thresholdTokens;
|
|
393
382
|
if (typeof thresholdTokens === "number" && Number.isFinite(thresholdTokens) && thresholdTokens > 0) {
|
|
394
|
-
//
|
|
383
|
+
// A trigger at or past the window can never fire before the request
|
|
384
|
+
// overflows (a provider may cap the window below the configured point);
|
|
385
|
+
// fall back to the window's prompt budget, as the reserve policy does.
|
|
386
|
+
// A trigger below the window is exact, as the `f` model entries promise,
|
|
387
|
+
// even one inside the reserve: the user chose that headroom. The step at
|
|
388
|
+
// the window is inherent; a monotone fallback would sit at window - 1.
|
|
389
|
+
if (thresholdTokens >= contextWindow) return promptBudgetTokens(contextWindow, settings);
|
|
395
390
|
return Math.min(contextWindow - 1, Math.max(1, thresholdTokens));
|
|
396
391
|
}
|
|
392
|
+
const baseWindowTokens = settings.baseWindowTokens;
|
|
393
|
+
if (typeof baseWindowTokens === "number" && Number.isFinite(baseWindowTokens) && baseWindowTokens > 0) {
|
|
394
|
+
contextWindow = Math.min(contextWindow, baseWindowTokens);
|
|
395
|
+
}
|
|
397
396
|
|
|
398
397
|
// Percentage-based threshold. The default absolute reserve can exceed bundled
|
|
399
398
|
// small-context windows, or nearly consume a 16k-class window; in those
|
|
400
399
|
// known-impossible default configurations, fall back to the proportional
|
|
401
400
|
// reserve so threshold/recovery-band checks stay usable. Explicit valid
|
|
402
|
-
// configured reserves still define the usable prompt budget.
|
|
403
|
-
// contextWindow - 1 (matching the fixed-token clamp above) so the threshold
|
|
404
|
-
// never reaches the whole window even when the reserve resolves to 0.
|
|
401
|
+
// configured reserves still define the usable prompt budget.
|
|
405
402
|
const thresholdPercent = settings.thresholdPercent;
|
|
406
403
|
if (typeof thresholdPercent !== "number" || !Number.isFinite(thresholdPercent) || thresholdPercent <= 0) {
|
|
407
|
-
return
|
|
408
|
-
0,
|
|
409
|
-
Math.min(contextWindow - 1, contextWindow - resolveBudgetReserveTokens(contextWindow, settings)),
|
|
410
|
-
);
|
|
404
|
+
return promptBudgetTokens(contextWindow, settings);
|
|
411
405
|
}
|
|
412
406
|
const clampedThresholdPercent = Math.min(99, Math.max(1, thresholdPercent));
|
|
413
407
|
return Math.floor(contextWindow * (clampedThresholdPercent / 100));
|
|
@@ -698,7 +692,7 @@ export interface SummaryOptions {
|
|
|
698
692
|
/**
|
|
699
693
|
* Optional telemetry handle. When provided, every LLM call emitted during
|
|
700
694
|
* compaction is wrapped in an OTEL chat span tagged with
|
|
701
|
-
* `
|
|
695
|
+
* `zeta.gen_ai.oneshot.kind` (`compaction_summary`, `compaction_short_summary`,
|
|
702
696
|
* or `compaction_turn_prefix`). `undefined` keeps the call paths zero-cost.
|
|
703
697
|
*/
|
|
704
698
|
telemetry?: AgentTelemetry;
|
|
@@ -1061,7 +1055,7 @@ export interface HandoffOptions {
|
|
|
1061
1055
|
metadata?: Record<string, unknown>;
|
|
1062
1056
|
/**
|
|
1063
1057
|
* Optional telemetry handle. When provided, the handoff LLM call is
|
|
1064
|
-
* wrapped in an OTEL chat span tagged with `
|
|
1058
|
+
* wrapped in an OTEL chat span tagged with `zeta.gen_ai.oneshot.kind = "handoff"`.
|
|
1065
1059
|
*/
|
|
1066
1060
|
telemetry?: AgentTelemetry;
|
|
1067
1061
|
/**
|
|
@@ -1874,7 +1868,7 @@ export async function compact(
|
|
|
1874
1868
|
),
|
|
1875
1869
|
{ signal },
|
|
1876
1870
|
);
|
|
1877
|
-
preserveData = withOpenAiRemoteCompactionPreserveData(
|
|
1871
|
+
preserveData = withOpenAiRemoteCompactionPreserveData(preserveData, remote);
|
|
1878
1872
|
usedRemoteCompaction = true;
|
|
1879
1873
|
} catch (err) {
|
|
1880
1874
|
// A user/session abort is a cancellation, not a remote failure —
|
|
@@ -48,7 +48,7 @@ export function registerMessageCacheInvalidator(invalidate: (message: AgentMessa
|
|
|
48
48
|
* the tag onto derived clones — harmless, because estimate memos key on message
|
|
49
49
|
* *identity* and a fresh clone starts with no memo entries anywhere.
|
|
50
50
|
*/
|
|
51
|
-
const kEstimateVersion = Symbol("
|
|
51
|
+
const kEstimateVersion = Symbol("zeta.messageEstimateVersion");
|
|
52
52
|
|
|
53
53
|
interface VersionedMessage {
|
|
54
54
|
[kEstimateVersion]?: number;
|
|
@@ -8,6 +8,7 @@ import type {
|
|
|
8
8
|
} from "@linxiraos/pi-ai";
|
|
9
9
|
import { prompt } from "@linxiraos/pi-utils";
|
|
10
10
|
import type { AgentMessage } from "../types";
|
|
11
|
+
import type { SessionEntry } from "./entries";
|
|
11
12
|
import branchSummaryContextPrompt from "./prompts/branch-summary-context.md" with { type: "text" };
|
|
12
13
|
import compactionSummaryContextPrompt from "./prompts/compaction-summary-context.md" with { type: "text" };
|
|
13
14
|
import handoffSummaryContextPrompt from "./prompts/handoff-summary-context.md" with { type: "text" };
|
|
@@ -291,3 +292,27 @@ export function convertMessageToLlm(message: AgentMessage): Message | undefined
|
|
|
291
292
|
export function defaultConvertToLlm(messages: AgentMessage[]): Message[] {
|
|
292
293
|
return messages.map(convertMessageToLlm).filter(message => message !== undefined);
|
|
293
294
|
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* The context message a session entry contributes, or `undefined` for entries
|
|
298
|
+
* that don't reach the LLM (compaction markers, labels, model changes, ...).
|
|
299
|
+
*/
|
|
300
|
+
export function getMessageFromEntry(entry: SessionEntry): AgentMessage | undefined {
|
|
301
|
+
if (entry.type === "message") {
|
|
302
|
+
return entry.message;
|
|
303
|
+
}
|
|
304
|
+
if (entry.type === "custom_message") {
|
|
305
|
+
return createCustomMessage(
|
|
306
|
+
entry.customType,
|
|
307
|
+
entry.content,
|
|
308
|
+
entry.display,
|
|
309
|
+
entry.details,
|
|
310
|
+
entry.timestamp,
|
|
311
|
+
entry.attribution,
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
if (entry.type === "branch_summary") {
|
|
315
|
+
return createBranchSummaryMessage(entry.summary, entry.fromId, entry.timestamp);
|
|
316
|
+
}
|
|
317
|
+
return undefined;
|
|
318
|
+
}
|
package/src/compaction/openai.ts
CHANGED
|
@@ -20,6 +20,7 @@ import { getCodexAttestationHeader } from "@linxiraos/pi-ai/providers/openai-cod
|
|
|
20
20
|
import { createOpenAICodexCompactionRequestContext } from "@linxiraos/pi-ai/providers/openai-codex-compaction";
|
|
21
21
|
import { applyCodexResponsesLiteShape } from "@linxiraos/pi-ai/providers/openai-codex/request-transformer";
|
|
22
22
|
import { createOpenAICodexCompatibilityMetadata } from "@linxiraos/pi-ai/providers/openai-codex-responses";
|
|
23
|
+
import { applySessionHeader } from "@linxiraos/pi-ai/providers/inference-headers";
|
|
23
24
|
import {
|
|
24
25
|
encodeResponsesToolResultOutput,
|
|
25
26
|
hoistInterleavedResponsesToolBatchMessages,
|
|
@@ -862,6 +863,7 @@ export async function requestOpenAiRemoteCompaction(
|
|
|
862
863
|
Authorization: `Bearer ${apiKey}`,
|
|
863
864
|
...model.headers,
|
|
864
865
|
};
|
|
866
|
+
applySessionHeader(headers, model.compat, opts?.sessionId);
|
|
865
867
|
|
|
866
868
|
// Codex endpoints require additional auth headers
|
|
867
869
|
if (isCodexResponses) {
|