@linxiraos/pi-agent-core 1.1.19 → 1.1.22
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 +253 -10
- package/README.md +4 -3
- package/THIRD-PARTY-NOTICES.txt +3 -40
- package/dist/types/agent-loop.d.ts +14 -1
- package/dist/types/agent.d.ts +36 -0
- package/dist/types/compaction/anthropic.d.ts +22 -52
- package/dist/types/compaction/branch-summarization.d.ts +1 -1
- package/dist/types/compaction/compaction.d.ts +4 -2
- package/dist/types/compaction/messages.d.ts +9 -0
- package/dist/types/compaction/openai.d.ts +11 -0
- package/dist/types/compaction/pruning.d.ts +9 -0
- package/dist/types/compaction/transcript-tokens.d.ts +25 -2
- package/dist/types/index.d.ts +3 -0
- package/dist/types/live-steering.d.ts +34 -0
- package/dist/types/output-budget.d.ts +54 -0
- package/dist/types/sent-tool-definitions.d.ts +17 -0
- package/dist/types/speculative-execution.d.ts +2 -1
- package/dist/types/telemetry.d.ts +103 -60
- package/dist/types/tool-context.d.ts +32 -0
- package/dist/types/types.d.ts +105 -7
- package/package.json +10 -10
- package/src/agent-loop.ts +386 -90
- package/src/agent.ts +175 -26
- package/src/compaction/anthropic.ts +62 -105
- package/src/compaction/branch-summarization.ts +1 -1
- package/src/compaction/compaction-v2-streaming.ts +13 -4
- package/src/compaction/compaction.ts +63 -51
- package/src/compaction/messages.ts +12 -2
- package/src/compaction/openai.ts +37 -9
- package/src/compaction/prompts/anthropic-compaction-instructions.md +2 -6
- package/src/compaction/pruning.ts +1 -1
- package/src/compaction/transcript-tokens.ts +64 -3
- package/src/index.ts +6 -0
- package/src/live-steering.ts +89 -0
- package/src/output-budget.ts +142 -0
- package/src/sent-tool-definitions.ts +40 -0
- package/src/speculative-execution.ts +12 -0
- package/src/telemetry.ts +240 -117
- package/src/tokenizer.ts +45 -6
- package/src/tool-context.ts +55 -0
- package/src/types.ts +109 -7
|
@@ -45,6 +45,13 @@ export interface CompactionSummaryMessage {
|
|
|
45
45
|
images?: ImageContent[];
|
|
46
46
|
/** Post-pass dead-end warning attached to this compaction (progress guard). */
|
|
47
47
|
warning?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Thinking-binding rewrite marker when it must differ from `timestamp`: a
|
|
50
|
+
* natively replayed summary predates it before the retained tail so that
|
|
51
|
+
* tail's bound thinking stays valid. `timestamp` remains the commit time,
|
|
52
|
+
* which is what invalidates the tail's pre-compaction usage reports.
|
|
53
|
+
*/
|
|
54
|
+
historyRewriteAt?: number;
|
|
48
55
|
timestamp: number;
|
|
49
56
|
}
|
|
50
57
|
export type CoreCompactionMessage = CustomMessage | HookMessage | BranchSummaryMessage | CompactionSummaryMessage;
|
|
@@ -79,6 +86,8 @@ export interface CompactionSummaryMessageOptions {
|
|
|
79
86
|
method?: string;
|
|
80
87
|
/** Estimated context tokens after the rewrite, for display alongside `tokensBefore`. */
|
|
81
88
|
tokensAfter?: number;
|
|
89
|
+
/** See {@link CompactionSummaryMessage.historyRewriteAt}. */
|
|
90
|
+
historyRewriteAt?: number;
|
|
82
91
|
}
|
|
83
92
|
export declare function createCompactionSummaryMessage(summary: string, tokensBefore: number, timestamp: string, options?: CompactionSummaryMessageOptions): CompactionSummaryMessage;
|
|
84
93
|
export declare function createCustomMessage(customType: string, content: string | (TextContent | ImageContent)[], display: boolean, details: unknown | undefined, timestamp: string, attribution?: MessageAttribution): CustomMessage;
|
|
@@ -33,6 +33,8 @@ export interface TrimRemoteCompactionInputResult {
|
|
|
33
33
|
rewrittenOutputs: number;
|
|
34
34
|
estimatedTokensBefore: number;
|
|
35
35
|
estimatedTokensAfter: number;
|
|
36
|
+
/** Whether `input` fits the model window; false means it must not be sent. */
|
|
37
|
+
fits: boolean;
|
|
36
38
|
}
|
|
37
39
|
/**
|
|
38
40
|
* Preserve the full native transcript unless trailing tool outputs alone push a
|
|
@@ -41,6 +43,15 @@ export interface TrimRemoteCompactionInputResult {
|
|
|
41
43
|
* matching Codex's recovery path for oversized tool turns.
|
|
42
44
|
*/
|
|
43
45
|
export declare function trimRemoteCompactionInputToContextWindow(input: Array<Record<string, unknown>>, tokenizer: Tokenizer, contextWindow: number | null | undefined, instructions: string, tools?: unknown[]): TrimRemoteCompactionInputResult;
|
|
46
|
+
/**
|
|
47
|
+
* Refuse a native compaction request whose prepared input cannot fit the model
|
|
48
|
+
* window, before any network I/O. Re-expanded history behind an unreadable
|
|
49
|
+
* native boundary can exceed the window even when live context does not.
|
|
50
|
+
*
|
|
51
|
+
* @throws Error flagged `ContextOverflow` when `trimmed.fits` is false, so
|
|
52
|
+
* compaction callers skip retries and advance to the next method.
|
|
53
|
+
*/
|
|
54
|
+
export declare function assertRemoteCompactionInputFits(trimmed: TrimRemoteCompactionInputResult, model: Model): void;
|
|
44
55
|
export type OpenAiRemoteCompactionItem = {
|
|
45
56
|
type: "compaction" | "compaction_summary";
|
|
46
57
|
encrypted_content?: string;
|
|
@@ -82,6 +82,15 @@ export interface SupersedePruneConfig {
|
|
|
82
82
|
/** Tool-result protection matchers (same contract as {@link PruneConfig.protectedTools}). */
|
|
83
83
|
protectedTools: ProtectedToolMatcher[];
|
|
84
84
|
}
|
|
85
|
+
/**
|
|
86
|
+
* Generic age-based pruning floor. Below this, blanking a result to
|
|
87
|
+
* `[Output truncated - N tokens]` recovers nothing — the placeholder itself
|
|
88
|
+
* costs ~8 tokens, so a sub-floor result grows the context (and churns the
|
|
89
|
+
* prompt cache) instead of shrinking it. Superseded/useless results keep their
|
|
90
|
+
* own rules: useless already drops no-savings candidates, superseded prunes for
|
|
91
|
+
* correctness regardless of size.
|
|
92
|
+
*/
|
|
93
|
+
export declare const MIN_PRUNE_TOKENS = 50;
|
|
85
94
|
/**
|
|
86
95
|
* Prune superseded tool results (e.g. stale `read` outputs replaced by a newer
|
|
87
96
|
* read of the same file) and, when `pruneUseless` is set, results their tool
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* - Not `aborted` / `error`: those turns report partial or zero usage.
|
|
18
18
|
* - `hasContextTokenUsage(usage)`: the report must carry usable context numbers.
|
|
19
19
|
*/
|
|
20
|
-
import type { AssistantMessage } from "@linxiraos/pi-ai";
|
|
20
|
+
import type { AssistantMessage, Message } from "@linxiraos/pi-ai";
|
|
21
21
|
import type { Tokenizer } from "../tokenizer.js";
|
|
22
22
|
import type { AgentMessage } from "../types.js";
|
|
23
23
|
/** A provider usage report that accounts for a prefix of the transcript. */
|
|
@@ -37,6 +37,14 @@ export interface TranscriptUsageAnchor {
|
|
|
37
37
|
* the session-entry walkers.
|
|
38
38
|
*/
|
|
39
39
|
export declare function isTranscriptUsageAnchor(message: AgentMessage): message is AssistantMessage;
|
|
40
|
+
/** Options for {@link findTranscriptUsageAnchor}. */
|
|
41
|
+
export interface TranscriptUsageAnchorOptions {
|
|
42
|
+
/**
|
|
43
|
+
* Also treat usage reported at or before the newest `prunedAt` as stale,
|
|
44
|
+
* the same rule {@link findRequestUsageAnchor} applies to request contexts.
|
|
45
|
+
*/
|
|
46
|
+
skipPrunedAnchors?: boolean;
|
|
47
|
+
}
|
|
40
48
|
/**
|
|
41
49
|
* Newest assistant turn in `messages[fromIndex..]` whose usage can anchor the
|
|
42
50
|
* transcript, or `undefined` when none qualifies (fresh context, or every
|
|
@@ -45,7 +53,20 @@ export declare function isTranscriptUsageAnchor(message: AgentMessage): message
|
|
|
45
53
|
* `fromIndex` excludes turns whose usage is stale — anything a compaction
|
|
46
54
|
* summarized away describes a prompt that is no longer sent.
|
|
47
55
|
*/
|
|
48
|
-
export declare function findTranscriptUsageAnchor(messages: readonly AgentMessage[], fromIndex?: number): TranscriptUsageAnchor | undefined;
|
|
56
|
+
export declare function findTranscriptUsageAnchor(messages: readonly AgentMessage[], fromIndex?: number, options?: TranscriptUsageAnchorOptions): TranscriptUsageAnchor | undefined;
|
|
57
|
+
/**
|
|
58
|
+
* Newest assistant turn in a provider request's `messages` whose usage still
|
|
59
|
+
* describes the prefix it sits on, or `undefined` when none does.
|
|
60
|
+
*
|
|
61
|
+
* Request contexts carry no compaction index, so staleness is read from the
|
|
62
|
+
* rewrite markers themselves: a compaction/branch summary or pruned tool result
|
|
63
|
+
* (`prunedAt`) replaced text that every report made at or before the rewrite
|
|
64
|
+
* already counted. A summary's rewrite time is its `timestamp` (commit time);
|
|
65
|
+
* its `historyRewriteAt` may be predated before a natively replayed retained
|
|
66
|
+
* tail so that tail's bound thinking survives, but the tail's usage still
|
|
67
|
+
* counted the summarized prefix.
|
|
68
|
+
*/
|
|
69
|
+
export declare function findRequestUsageAnchor(messages: readonly Message[]): TranscriptUsageAnchor | undefined;
|
|
49
70
|
/** Options for {@link estimateTranscriptTokens}. */
|
|
50
71
|
export interface TranscriptTokenOptions {
|
|
51
72
|
/**
|
|
@@ -54,6 +75,8 @@ export interface TranscriptTokenOptions {
|
|
|
54
75
|
* accounting is governed separately by {@link countFromIndex}.
|
|
55
76
|
*/
|
|
56
77
|
anchorFromIndex?: number;
|
|
78
|
+
/** Forwarded to {@link findTranscriptUsageAnchor}. */
|
|
79
|
+
skipPrunedAnchors?: boolean;
|
|
57
80
|
/**
|
|
58
81
|
* First message whose content is counted locally when no anchor is found.
|
|
59
82
|
* Defaults to 0 (count the whole transcript), which is what a floor
|
package/dist/types/index.d.ts
CHANGED
|
@@ -2,13 +2,16 @@ export * from "./agent.js";
|
|
|
2
2
|
export * from "./agent-loop.js";
|
|
3
3
|
export * from "./append-only-context.js";
|
|
4
4
|
export * from "./compaction.js";
|
|
5
|
+
export * from "./output-budget.js";
|
|
5
6
|
export * from "./pause.js";
|
|
6
7
|
export * from "./proxy.js";
|
|
7
8
|
export * from "./replay-policy.js";
|
|
8
9
|
export * from "./run-collector.js";
|
|
10
|
+
export * from "./sent-tool-definitions.js";
|
|
9
11
|
export * from "./speculative-execution.js";
|
|
10
12
|
export * from "./telemetry.js";
|
|
11
13
|
export * from "./thinking.js";
|
|
14
|
+
export * from "./tool-context.js";
|
|
12
15
|
export * from "./tokenizer.js";
|
|
13
16
|
export * from "./types.js";
|
|
14
17
|
export * from "./utils/yield.js";
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent side of provider live steering ({@link LiveSteering}).
|
|
3
|
+
*
|
|
4
|
+
* A provider that can put user input into the response it is streaming (OpenAI
|
|
5
|
+
* Responses `response.steer`) pulls queued steering through a
|
|
6
|
+
* {@link LiveSteeringChannel}. The loop records what the provider accepted right
|
|
7
|
+
* after that response, so the transcript matches what the model saw; anything
|
|
8
|
+
* it declined is injected at the next boundary like ordinary steering.
|
|
9
|
+
*/
|
|
10
|
+
import type { LiveSteerClaim, LiveSteering, UserMessage } from "@linxiraos/pi-ai";
|
|
11
|
+
import type { AgentMessage } from "./types.js";
|
|
12
|
+
/** Steering-queue access for one provider call, supplied by the agent loop. */
|
|
13
|
+
export interface LiveSteeringQueue {
|
|
14
|
+
/** Resolves once steering is queued or `signal` aborts; never consumes. */
|
|
15
|
+
wait(signal: AbortSignal): Promise<void>;
|
|
16
|
+
/** Dequeues the next steering batch. */
|
|
17
|
+
take(signal: AbortSignal): Promise<AgentMessage[]>;
|
|
18
|
+
/**
|
|
19
|
+
* Provider view of `messages` appended to the in-flight call's context, or
|
|
20
|
+
* `undefined` when that view is not purely user messages.
|
|
21
|
+
*/
|
|
22
|
+
toProvider(messages: AgentMessage[], signal: AbortSignal): Promise<UserMessage[] | undefined>;
|
|
23
|
+
}
|
|
24
|
+
/** One provider call's {@link LiveSteering} source. */
|
|
25
|
+
export declare class LiveSteeringChannel implements LiveSteering {
|
|
26
|
+
#private;
|
|
27
|
+
/** Steering the provider delivered into the in-flight response, in queue order. */
|
|
28
|
+
readonly accepted: AgentMessage[];
|
|
29
|
+
/** Steering taken from the queue but not delivered; always queued after {@link accepted}. */
|
|
30
|
+
readonly deferred: AgentMessage[];
|
|
31
|
+
constructor(queue: LiveSteeringQueue);
|
|
32
|
+
wait(signal: AbortSignal): Promise<void>;
|
|
33
|
+
claim(signal: AbortSignal): Promise<LiveSteerClaim | undefined>;
|
|
34
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { Context, Model } from "@linxiraos/pi-ai";
|
|
2
|
+
import type { Tokenizer } from "./tokenizer.js";
|
|
3
|
+
/** Smallest output cap {@link fitOutputTokensToContextWindow} will request. */
|
|
4
|
+
export declare const MIN_FITTED_OUTPUT_TOKENS = 1024;
|
|
5
|
+
/**
|
|
6
|
+
* Absolute headway subtracted from the remaining room (unconditionally, on anchored and
|
|
7
|
+
* fully-local counts alike). Proportional padding covers
|
|
8
|
+
* tokenizer drift that scales with the prompt, but a host can still count a few tokens more
|
|
9
|
+
* than any local estimate can see (chat-template framing, reasoning wrappers). Measured
|
|
10
|
+
* against a 262,144-token host: fitted caps landed +11 to +42 tokens over and were rejected
|
|
11
|
+
* with 400s. 64 tokens covers the observed drift with margin to spare; the
|
|
12
|
+
* {@link MIN_FITTED_OUTPUT_TOKENS} floor still applies.
|
|
13
|
+
*/
|
|
14
|
+
export declare const OUTPUT_FIT_HEADWAY_TOKENS = 64;
|
|
15
|
+
/**
|
|
16
|
+
* Output cap for a request, so prompt plus output stays inside the model's
|
|
17
|
+
* context window.
|
|
18
|
+
*
|
|
19
|
+
* Chat Completions-style providers (DeepSeek, OpenAI, vLLM, ...) reject a
|
|
20
|
+
* request whose prompt tokens plus `max_tokens` exceed the window. Every
|
|
21
|
+
* request asks for `model.maxTokens` of output by default, so without this a
|
|
22
|
+
* large model output cap (DeepSeek V4: ~384k of a ~1M window) makes every
|
|
23
|
+
* request fail once the prompt passes window minus output cap, long before
|
|
24
|
+
* compaction triggers, and side turns (`/btw`, recaps) have no overflow
|
|
25
|
+
* recovery at all.
|
|
26
|
+
*
|
|
27
|
+
* The prompt size is the provider's own report from the newest trustworthy
|
|
28
|
+
* assistant turn (see {@link findRequestUsageAnchor}) plus a local count of
|
|
29
|
+
* only the messages appended after it; the whole context is counted locally
|
|
30
|
+
* only when no turn can anchor (fresh or freshly rewritten context).
|
|
31
|
+
*
|
|
32
|
+
* Returns `maxTokens` unchanged when the requested cap already fits, the
|
|
33
|
+
* model declares no window, the host ends generation at the window itself
|
|
34
|
+
* instead of rejecting the request (`stops-output-at-context-window`, e.g.
|
|
35
|
+
* Claude 4.5+ on the Claude API), or nothing would be requested (including an
|
|
36
|
+
* OpenRouter-hosted model with no caller cap: the transport omits the catalog
|
|
37
|
+
* default there so each upstream self-caps, and a fitted value would turn into
|
|
38
|
+
* an explicit cap that filters upstreams). Otherwise returns
|
|
39
|
+
* the remaining room minus {@link OUTPUT_FIT_HEADWAY_TOKENS} (never below
|
|
40
|
+
* {@link MIN_FITTED_OUTPUT_TOKENS}); a
|
|
41
|
+
* prompt that fills the whole window still overflows and is left to the
|
|
42
|
+
* caller's compaction. Near a full window the floor means a turn can stop on
|
|
43
|
+
* `length` instead of failing with a 400.
|
|
44
|
+
*
|
|
45
|
+
* Lives here, not next to the default in pi-ai's `mapOptionsForApi`, because
|
|
46
|
+
* pi-ai has no tokenizer; callers apply it in their `streamFn` (coding-agent
|
|
47
|
+
* does so in its shared settings-aware wrapper).
|
|
48
|
+
*
|
|
49
|
+
* Not fixed: Anthropic budget-thinking transports raise `max_tokens` back to
|
|
50
|
+
* at least the thinking budget plus a fallback buffer downstream
|
|
51
|
+
* (`ensureMaxTokensForThinking`), so a fitted cap below that is overridden
|
|
52
|
+
* and the request can still exceed the window as before.
|
|
53
|
+
*/
|
|
54
|
+
export declare function fitOutputTokensToContextWindow(model: Model, context: Context, maxTokens: number | undefined, tokenizer: Tokenizer): number | undefined;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Message, Tool } from "@linxiraos/pi-ai";
|
|
2
|
+
/**
|
|
3
|
+
* Last wire definition this Agent sent for each tool name, so a provider that keeps
|
|
4
|
+
* withdrawn tools declared (Anthropic `tool_removal`) can re-declare them byte-identically.
|
|
5
|
+
* Used by prepareProviderCall and Agent.buildSideRequestContext.
|
|
6
|
+
*/
|
|
7
|
+
export declare class SentToolDefinitions {
|
|
8
|
+
#private;
|
|
9
|
+
/** Remember the definitions a request is about to send. */
|
|
10
|
+
record(tools: readonly Tool[]): void;
|
|
11
|
+
/**
|
|
12
|
+
* Definitions for names the latest `requestControls.tools.declared` in `messages` holds
|
|
13
|
+
* that are not in `active`; undefined when none. Names never sent by this Agent are
|
|
14
|
+
* skipped: the provider drops them from the declaration.
|
|
15
|
+
*/
|
|
16
|
+
inactiveFor(messages: readonly Message[], active: readonly Tool[]): Tool[] | undefined;
|
|
17
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type AssistantMessage } from "@linxiraos/pi-ai";
|
|
2
|
-
import type { AgentContext, AgentLoopConfig, AgentTool, AgentToolCall, AgentToolResult, SpeculativeChildDefinition, SpeculativeChildHandle, SpeculativeToolExecutionConfig, ToolSpeculationEffect, ToolSpeculationStreamSession } from "./types.js";
|
|
2
|
+
import type { AgentContext, AgentLoopConfig, AgentTool, AgentToolCall, AgentToolResult, SpeculativeAuthorization, SpeculativeChildDefinition, SpeculativeChildHandle, SpeculativeLaunchContext, SpeculativeToolExecutionConfig, ToolSpeculationEffect, ToolSpeculationStreamSession } from "./types.js";
|
|
3
3
|
export type SpeculativeRawOutcome = {
|
|
4
4
|
result: AgentToolResult<unknown>;
|
|
5
5
|
isError: boolean;
|
|
@@ -55,6 +55,7 @@ export declare class SpeculativeOperationCoordinator {
|
|
|
55
55
|
*/
|
|
56
56
|
directExecutionArgsFor(toolCallId: string, rawArgs: Readonly<Record<string, unknown>>): Record<string, unknown> | undefined;
|
|
57
57
|
reconcileFinalCalls(calls: ReadonlyMap<string, AgentToolCall>): Promise<void>;
|
|
58
|
+
authorizeLaunch(context: SpeculativeLaunchContext): Promise<SpeculativeAuthorization>;
|
|
58
59
|
discardChildren(parentToolCallId: string, reason: string): Promise<void>;
|
|
59
60
|
claim(tool: AgentTool | undefined, toolCall: AgentToolCall, args: Record<string, unknown>): Promise<SpeculativeRawOutcome | undefined>;
|
|
60
61
|
admitFinalized(context: AgentContext, toolCall: AgentToolCall, loopConfig: AgentLoopConfig, signal: AbortSignal | undefined): void;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* OpenTelemetry instrumentation for the agent loop.
|
|
3
3
|
*
|
|
4
4
|
* Implements the OpenTelemetry GenAI semantic conventions
|
|
5
|
-
* (https://opentelemetry.io/docs/specs/semconv/gen-ai/) plus `
|
|
5
|
+
* (https://opentelemetry.io/docs/specs/semconv/gen-ai/) plus `omp.gen_ai.*`
|
|
6
6
|
* extension attributes for run summaries, dashboard summaries, and cost hints
|
|
7
7
|
* that are useful to downstream observability UIs.
|
|
8
8
|
*
|
|
@@ -78,35 +78,41 @@ export declare const enum OpenAIAttr {
|
|
|
78
78
|
ResponseServiceTier = "openai.response.service_tier"
|
|
79
79
|
}
|
|
80
80
|
/** Project extension attributes. Kept out of the reserved `gen_ai.*` namespace. */
|
|
81
|
-
export declare const enum
|
|
82
|
-
AgentStepNumber = "
|
|
83
|
-
AgentStepCount = "
|
|
84
|
-
RequestReasoningEffort = "
|
|
85
|
-
RequestToolChoice = "
|
|
86
|
-
RequestAvailableTools = "
|
|
87
|
-
RequestMessages = "
|
|
88
|
-
ResponseText = "
|
|
89
|
-
ResponseToolCalls = "
|
|
90
|
-
ResponseUpstreamProvider = "
|
|
91
|
-
UsageTotalTokens = "
|
|
92
|
-
UsageServerSideTools = "
|
|
93
|
-
CostEstimatedUsd = "
|
|
94
|
-
CostInputUsd = "
|
|
95
|
-
CostOutputUsd = "
|
|
96
|
-
CostUnavailableReason = "
|
|
97
|
-
ToolStatus = "
|
|
98
|
-
ToolCallIntent = "
|
|
99
|
-
HandoffFromAgentName = "
|
|
100
|
-
HandoffFromAgentId = "
|
|
101
|
-
HandoffToAgentName = "
|
|
102
|
-
HandoffToAgentId = "
|
|
103
|
-
OneshotKind = "
|
|
104
|
-
GatewayName = "
|
|
105
|
-
GatewayEndpoint = "
|
|
106
|
-
GatewayCallId = "
|
|
107
|
-
GatewayRoutedTo = "
|
|
81
|
+
export declare const enum OmpGenAIAttr {
|
|
82
|
+
AgentStepNumber = "omp.gen_ai.agent.step.number",
|
|
83
|
+
AgentStepCount = "omp.gen_ai.agent.step.count",
|
|
84
|
+
RequestReasoningEffort = "omp.gen_ai.request.reasoning.effort",
|
|
85
|
+
RequestToolChoice = "omp.gen_ai.request.tool.choice",
|
|
86
|
+
RequestAvailableTools = "omp.gen_ai.request.available_tools",
|
|
87
|
+
RequestMessages = "omp.gen_ai.request.messages",
|
|
88
|
+
ResponseText = "omp.gen_ai.response.text",
|
|
89
|
+
ResponseToolCalls = "omp.gen_ai.response.tool_calls",
|
|
90
|
+
ResponseUpstreamProvider = "omp.gen_ai.response.upstream_provider",
|
|
91
|
+
UsageTotalTokens = "omp.gen_ai.usage.total_tokens",
|
|
92
|
+
UsageServerSideTools = "omp.gen_ai.usage.server_tool_requests",
|
|
93
|
+
CostEstimatedUsd = "omp.gen_ai.cost.estimated_usd",
|
|
94
|
+
CostInputUsd = "omp.gen_ai.cost.input_usd",
|
|
95
|
+
CostOutputUsd = "omp.gen_ai.cost.output_usd",
|
|
96
|
+
CostUnavailableReason = "omp.gen_ai.cost.unavailable_reason",
|
|
97
|
+
ToolStatus = "omp.gen_ai.tool.status",
|
|
98
|
+
ToolCallIntent = "omp.gen_ai.tool.call.intent",
|
|
99
|
+
HandoffFromAgentName = "omp.gen_ai.handoff.from_agent.name",
|
|
100
|
+
HandoffFromAgentId = "omp.gen_ai.handoff.from_agent.id",
|
|
101
|
+
HandoffToAgentName = "omp.gen_ai.handoff.to_agent.name",
|
|
102
|
+
HandoffToAgentId = "omp.gen_ai.handoff.to_agent.id",
|
|
103
|
+
OneshotKind = "omp.gen_ai.oneshot.kind",
|
|
104
|
+
GatewayName = "omp.gen_ai.gateway.name",
|
|
105
|
+
GatewayEndpoint = "omp.gen_ai.gateway.endpoint",
|
|
106
|
+
GatewayCallId = "omp.gen_ai.gateway.call_id",
|
|
107
|
+
GatewayRoutedTo = "omp.gen_ai.gateway.routed_to",
|
|
108
108
|
/** Cloudflare AI Gateway response-cache status (`cf-aig-cache-status`), never prompt-cache. */
|
|
109
|
-
GatewayResponseCacheStatus = "
|
|
109
|
+
GatewayResponseCacheStatus = "omp.gen_ai.gateway.response_cache.status",
|
|
110
|
+
/** Caller-level reason a judgment ran (`find`, `ttsr`, `judge_batch`, …). */
|
|
111
|
+
JudgmentPurpose = "omp.gen_ai.judgment.purpose",
|
|
112
|
+
/** Questions the caller asked in one judgment request. */
|
|
113
|
+
JudgmentQuestions = "omp.gen_ai.judgment.questions",
|
|
114
|
+
/** Questions answered from the local judgment cache instead of the provider. */
|
|
115
|
+
JudgmentCachedQuestions = "omp.gen_ai.judgment.cached_questions"
|
|
110
116
|
}
|
|
111
117
|
/** GenAI operation names — values for {@link GenAIAttr.OperationName}. */
|
|
112
118
|
export declare const GenAIOperation: {
|
|
@@ -114,6 +120,8 @@ export declare const GenAIOperation: {
|
|
|
114
120
|
readonly ExecuteTool: "execute_tool";
|
|
115
121
|
readonly InvokeAgent: "invoke_agent";
|
|
116
122
|
readonly Handoff: "handoff";
|
|
123
|
+
/** Typed judgment over a state (System One or a prompted judge model); a project extension value. */
|
|
124
|
+
readonly Judgment: "judgment";
|
|
117
125
|
readonly GenerateContent: "generate_content";
|
|
118
126
|
readonly TextCompletion: "text_completion";
|
|
119
127
|
readonly CreateAgent: "create_agent";
|
|
@@ -121,7 +129,7 @@ export declare const GenAIOperation: {
|
|
|
121
129
|
};
|
|
122
130
|
export type GenAIOperationName = (typeof GenAIOperation)[keyof typeof GenAIOperation];
|
|
123
131
|
/** Identifies which agent span a callback is reporting on. */
|
|
124
|
-
export type TelemetrySpanKind = "invoke_agent" | "chat" | "execute_tool" | "handoff";
|
|
132
|
+
export type TelemetrySpanKind = "invoke_agent" | "chat" | "execute_tool" | "handoff" | "judgment";
|
|
125
133
|
/**
|
|
126
134
|
* Aggregated usage + cost surface passed to {@link AgentTelemetryConfig.costEstimator}.
|
|
127
135
|
* Mirrors the bucketed shape we already emit as span attributes so the
|
|
@@ -144,9 +152,9 @@ export interface CostEstimatorContext {
|
|
|
144
152
|
}
|
|
145
153
|
/**
|
|
146
154
|
* Cost estimator result.
|
|
147
|
-
* { usd: number } — cost is known; emitted as
|
|
155
|
+
* { usd: number } — cost is known; emitted as omp.gen_ai.cost.estimated_usd
|
|
148
156
|
* { unavailable: string } — cost is intentionally unknown; emitted as
|
|
149
|
-
*
|
|
157
|
+
* omp.gen_ai.cost.unavailable_reason
|
|
150
158
|
* undefined — no opinion; nothing emitted
|
|
151
159
|
*/
|
|
152
160
|
export type CostEstimate = {
|
|
@@ -177,6 +185,8 @@ export interface CostDelta {
|
|
|
177
185
|
*/
|
|
178
186
|
export interface ChatUsageEvent {
|
|
179
187
|
readonly span: Span;
|
|
188
|
+
/** Operation that produced the usage: `chat` for model turns, `judgment` for typed judgments. */
|
|
189
|
+
readonly operation: GenAIOperationName;
|
|
180
190
|
readonly agent: AgentIdentity | undefined;
|
|
181
191
|
readonly conversationId: string | undefined;
|
|
182
192
|
readonly stepNumber: number | undefined;
|
|
@@ -195,7 +205,7 @@ export interface ChatUsageEvent {
|
|
|
195
205
|
*
|
|
196
206
|
* Use this to reconcile gateway-issued ids (e.g. `x-litellm-call-id`) with
|
|
197
207
|
* downstream billing / spend dashboards. Known gateway patterns are also
|
|
198
|
-
* auto-stamped on the chat span as `
|
|
208
|
+
* auto-stamped on the chat span as `omp.gen_ai.gateway.*` attributes.
|
|
199
209
|
*/
|
|
200
210
|
readonly headers: Readonly<Record<string, string>> | undefined;
|
|
201
211
|
}
|
|
@@ -417,7 +427,7 @@ export interface GatewayHeaderDetection {
|
|
|
417
427
|
export declare function detectGatewayFromHeaders(headers: Readonly<Record<string, string>> | undefined): GatewayHeaderDetection | undefined;
|
|
418
428
|
/**
|
|
419
429
|
* Bounded Cloudflare AI Gateway response-cache statuses emitted on
|
|
420
|
-
* {@link
|
|
430
|
+
* {@link OmpGenAIAttr.GatewayResponseCacheStatus}. Distinct from provider
|
|
421
431
|
* prompt-cache token counters (`gen_ai.usage.cache_*`).
|
|
422
432
|
*
|
|
423
433
|
* Cloudflare documents `HIT` / `MISS` on `cf-aig-cache-status`; `bypass` covers
|
|
@@ -453,6 +463,39 @@ export interface ManualChatTelemetryOptions {
|
|
|
453
463
|
readonly endSpan?: boolean;
|
|
454
464
|
}
|
|
455
465
|
export declare function recordManualChatTelemetry(telemetry: AgentTelemetry | undefined, options: ManualChatTelemetryOptions): Promise<Span | undefined>;
|
|
466
|
+
/**
|
|
467
|
+
* One judgment attempt reported to {@link recordJudgmentTelemetry}: a billed
|
|
468
|
+
* System One request, one prompted-model attempt, or a request answered
|
|
469
|
+
* entirely from the local judgment cache.
|
|
470
|
+
*/
|
|
471
|
+
export interface JudgmentTelemetryOptions {
|
|
472
|
+
readonly provider: string;
|
|
473
|
+
/** Requested model id. */
|
|
474
|
+
readonly model: string;
|
|
475
|
+
/** Model that answered, when the provider resolves an alias (`jev-latest` → `jev-1.13.0`). */
|
|
476
|
+
readonly responseModel?: string;
|
|
477
|
+
/** Caller-level reason the judgment ran; stamped as {@link OmpGenAIAttr.JudgmentPurpose}. */
|
|
478
|
+
readonly purpose: string;
|
|
479
|
+
/** Priced usage of this attempt — the same amount the session ledger bills. */
|
|
480
|
+
readonly usage: Usage;
|
|
481
|
+
readonly stopReason: StopReason;
|
|
482
|
+
readonly errorMessage?: string;
|
|
483
|
+
/** Epoch ms the attempt started. */
|
|
484
|
+
readonly startTime: number;
|
|
485
|
+
/** Questions in the request; omitted for prompted backends, which report per completion attempt. */
|
|
486
|
+
readonly questions?: number;
|
|
487
|
+
/** Questions answered from the local cache rather than the provider. */
|
|
488
|
+
readonly cachedQuestions?: number;
|
|
489
|
+
}
|
|
490
|
+
/**
|
|
491
|
+
* Emit one `judgment` span per judgment attempt and forward any billed usage
|
|
492
|
+
* to `onCostDelta` / `onChatUsage` with `operation: "judgment"`. Cost is the
|
|
493
|
+
* attempt's own priced usage rather than {@link AgentTelemetryConfig.costEstimator},
|
|
494
|
+
* so spans, metrics, and the session ledger agree on every judgment dollar.
|
|
495
|
+
* Attempts that billed nothing (cache hits, pre-response failures) keep their
|
|
496
|
+
* span but fire no usage hooks. No-op when telemetry is disabled.
|
|
497
|
+
*/
|
|
498
|
+
export declare function recordJudgmentTelemetry(telemetry: AgentTelemetry | undefined, options: JudgmentTelemetryOptions): Promise<void>;
|
|
456
499
|
/**
|
|
457
500
|
* Options accepted by {@link instrumentedCompleteSimple}. Mirrors the
|
|
458
501
|
* `streamAssistantResponse` chat-span lifecycle for oneshot LLM calls
|
|
@@ -465,7 +508,7 @@ export interface InstrumentedChatSpanOptions {
|
|
|
465
508
|
/** Step index recorded on the span; defaults to `-1` for non-loop calls. */
|
|
466
509
|
readonly stepNumber?: number;
|
|
467
510
|
/**
|
|
468
|
-
* Tag stamped onto `
|
|
511
|
+
* Tag stamped onto `omp.gen_ai.oneshot.kind`. Values used by the agent:
|
|
469
512
|
* `compaction_summary`, `compaction_short_summary`, `compaction_turn_prefix`,
|
|
470
513
|
* `handoff`, `branch_summary`, `image_question`. Free-form to allow callers
|
|
471
514
|
* outside this package to add new kinds without bumping the helper.
|
|
@@ -535,7 +578,7 @@ export declare function finishExecuteToolSpan(telemetry: AgentTelemetry | undefi
|
|
|
535
578
|
readonly toolName: string;
|
|
536
579
|
}): void;
|
|
537
580
|
/** Span attribute carrying the terminal {@link ToolStatus}. */
|
|
538
|
-
export declare const EXECUTE_TOOL_STATUS_ATTR =
|
|
581
|
+
export declare const EXECUTE_TOOL_STATUS_ATTR = OmpGenAIAttr.ToolStatus;
|
|
539
582
|
/**
|
|
540
583
|
* Record a tool that bypassed the span lifecycle entirely (pre-run
|
|
541
584
|
* interrupt, post-execution tail sweep for calls that never produced a
|
|
@@ -570,30 +613,30 @@ export declare function finishInvokeAgentSpan(telemetry: AgentTelemetry | undefi
|
|
|
570
613
|
* calling this helper.
|
|
571
614
|
*/
|
|
572
615
|
export declare function fireOnRunEnd(telemetry: AgentTelemetry, summary: AgentRunSummary, coverage: AgentRunCoverage): void;
|
|
573
|
-
/** Aggregate `
|
|
574
|
-
export declare const enum
|
|
575
|
-
ChatsCount = "
|
|
576
|
-
ChatsTotalLatencyMs = "
|
|
577
|
-
ChatsStopReasonPrefix = "
|
|
578
|
-
ToolsCount = "
|
|
579
|
-
ToolsOkCount = "
|
|
580
|
-
ToolsErrorCount = "
|
|
581
|
-
ToolsSkippedCount = "
|
|
582
|
-
ToolsBlockedCount = "
|
|
583
|
-
ToolsTimeoutCount = "
|
|
584
|
-
ToolsAbortedCount = "
|
|
585
|
-
ToolsTotalLatencyMs = "
|
|
586
|
-
ToolsInvoked = "
|
|
587
|
-
ToolsAvailable = "
|
|
588
|
-
ToolsUnused = "
|
|
589
|
-
UsageInputTokensTotal = "
|
|
590
|
-
UsageOutputTokensTotal = "
|
|
591
|
-
UsageCacheReadInputTokensTotal = "
|
|
592
|
-
UsageCacheCreationInputTokensTotal = "
|
|
593
|
-
UsageReasoningOutputTokensTotal = "
|
|
594
|
-
UsageTotalTokensTotal = "
|
|
595
|
-
CostEstimatedUsdTotal = "
|
|
596
|
-
ErrorsCount = "
|
|
616
|
+
/** Aggregate `omp.gen_ai.agent.*` attributes stamped on the `invoke_agent` span. */
|
|
617
|
+
export declare const enum OmpGenAIAggregateAttr {
|
|
618
|
+
ChatsCount = "omp.gen_ai.agent.chats.count",
|
|
619
|
+
ChatsTotalLatencyMs = "omp.gen_ai.agent.chats.total_latency_ms",
|
|
620
|
+
ChatsStopReasonPrefix = "omp.gen_ai.agent.chats.stop_reason.",
|
|
621
|
+
ToolsCount = "omp.gen_ai.agent.tools.count",
|
|
622
|
+
ToolsOkCount = "omp.gen_ai.agent.tools.ok.count",
|
|
623
|
+
ToolsErrorCount = "omp.gen_ai.agent.tools.error.count",
|
|
624
|
+
ToolsSkippedCount = "omp.gen_ai.agent.tools.skipped.count",
|
|
625
|
+
ToolsBlockedCount = "omp.gen_ai.agent.tools.blocked.count",
|
|
626
|
+
ToolsTimeoutCount = "omp.gen_ai.agent.tools.timeout.count",
|
|
627
|
+
ToolsAbortedCount = "omp.gen_ai.agent.tools.aborted.count",
|
|
628
|
+
ToolsTotalLatencyMs = "omp.gen_ai.agent.tools.total_latency_ms",
|
|
629
|
+
ToolsInvoked = "omp.gen_ai.agent.tools.invoked",
|
|
630
|
+
ToolsAvailable = "omp.gen_ai.agent.tools.available",
|
|
631
|
+
ToolsUnused = "omp.gen_ai.agent.tools.unused",
|
|
632
|
+
UsageInputTokensTotal = "omp.gen_ai.agent.usage.input_tokens.total",
|
|
633
|
+
UsageOutputTokensTotal = "omp.gen_ai.agent.usage.output_tokens.total",
|
|
634
|
+
UsageCacheReadInputTokensTotal = "omp.gen_ai.agent.usage.cache_read.input_tokens.total",
|
|
635
|
+
UsageCacheCreationInputTokensTotal = "omp.gen_ai.agent.usage.cache_creation.input_tokens.total",
|
|
636
|
+
UsageReasoningOutputTokensTotal = "omp.gen_ai.agent.usage.reasoning.output_tokens.total",
|
|
637
|
+
UsageTotalTokensTotal = "omp.gen_ai.agent.usage.total_tokens.total",
|
|
638
|
+
CostEstimatedUsdTotal = "omp.gen_ai.agent.cost.estimated_usd.total",
|
|
639
|
+
ErrorsCount = "omp.gen_ai.agent.errors.count"
|
|
597
640
|
}
|
|
598
641
|
/**
|
|
599
642
|
* Run `fn` with `span` activated on the OTEL context. Spans created
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ToolResultMessage } from "@linxiraos/pi-ai";
|
|
2
|
+
import type { AgentMessage } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Symbol-keyed carrier for passive context reported by a tool executed outside
|
|
5
|
+
* the agent loop (Cursor exec-channel dispatch). The executor attaches the
|
|
6
|
+
* joined context to the {@link ToolResultMessage} it returns; `Agent` reads it
|
|
7
|
+
* when the provider hands the result back and injects it after the buffered
|
|
8
|
+
* results. Symbol keys never serialize, so the context cannot leak into the
|
|
9
|
+
* persisted tool result.
|
|
10
|
+
*/
|
|
11
|
+
export declare const TOOL_RESULT_ADDITIONAL_CONTEXT: unique symbol;
|
|
12
|
+
/** A tool result optionally carrying {@link TOOL_RESULT_ADDITIONAL_CONTEXT}. */
|
|
13
|
+
export type ToolResultWithAdditionalContext = ToolResultMessage & {
|
|
14
|
+
[TOOL_RESULT_ADDITIONAL_CONTEXT]?: string;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* True for a passive-context value worth delivering: a string with at least
|
|
18
|
+
* one non-whitespace character. Shared by every producer and aggregation site
|
|
19
|
+
* so blank values never produce a developer message.
|
|
20
|
+
*/
|
|
21
|
+
export declare function isNonBlankContext(value: unknown): value is string;
|
|
22
|
+
/**
|
|
23
|
+
* Join passive context values in order, dropping blanks and repeats of an
|
|
24
|
+
* earlier value (compared without surrounding whitespace; the first original
|
|
25
|
+
* is kept). Returns undefined when nothing remains.
|
|
26
|
+
*/
|
|
27
|
+
export declare function joinAdditionalContext(values: Iterable<string | undefined>): string | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* Build the developer message that carries passive tool context to the next
|
|
30
|
+
* provider request. Emitted after the tool results it belongs to.
|
|
31
|
+
*/
|
|
32
|
+
export declare function createAdditionalContextMessage(text: string): AgentMessage;
|