@linxiraos/pi-agent-core 1.1.20 → 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.
Files changed (41) hide show
  1. package/CHANGELOG.md +241 -2
  2. package/README.md +4 -3
  3. package/THIRD-PARTY-NOTICES.txt +3 -40
  4. package/dist/types/agent-loop.d.ts +14 -1
  5. package/dist/types/agent.d.ts +36 -0
  6. package/dist/types/compaction/anthropic.d.ts +22 -52
  7. package/dist/types/compaction/branch-summarization.d.ts +1 -1
  8. package/dist/types/compaction/compaction.d.ts +4 -2
  9. package/dist/types/compaction/messages.d.ts +9 -0
  10. package/dist/types/compaction/openai.d.ts +11 -0
  11. package/dist/types/compaction/pruning.d.ts +9 -0
  12. package/dist/types/compaction/transcript-tokens.d.ts +25 -2
  13. package/dist/types/index.d.ts +3 -0
  14. package/dist/types/live-steering.d.ts +34 -0
  15. package/dist/types/output-budget.d.ts +54 -0
  16. package/dist/types/sent-tool-definitions.d.ts +17 -0
  17. package/dist/types/speculative-execution.d.ts +2 -1
  18. package/dist/types/telemetry.d.ts +103 -60
  19. package/dist/types/tool-context.d.ts +32 -0
  20. package/dist/types/types.d.ts +105 -7
  21. package/package.json +10 -10
  22. package/src/agent-loop.ts +386 -90
  23. package/src/agent.ts +175 -26
  24. package/src/compaction/anthropic.ts +62 -105
  25. package/src/compaction/branch-summarization.ts +1 -1
  26. package/src/compaction/compaction-v2-streaming.ts +13 -4
  27. package/src/compaction/compaction.ts +63 -51
  28. package/src/compaction/messages.ts +12 -2
  29. package/src/compaction/openai.ts +37 -9
  30. package/src/compaction/prompts/anthropic-compaction-instructions.md +2 -6
  31. package/src/compaction/pruning.ts +1 -1
  32. package/src/compaction/transcript-tokens.ts +64 -3
  33. package/src/index.ts +6 -0
  34. package/src/live-steering.ts +89 -0
  35. package/src/output-budget.ts +142 -0
  36. package/src/sent-tool-definitions.ts +40 -0
  37. package/src/speculative-execution.ts +12 -0
  38. package/src/telemetry.ts +240 -117
  39. package/src/tokenizer.ts +45 -6
  40. package/src/tool-context.ts +55 -0
  41. package/src/types.ts +109 -7
@@ -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
@@ -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 `pi.gen_ai.*`
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 PiGenAIAttr {
82
- AgentStepNumber = "pi.gen_ai.agent.step.number",
83
- AgentStepCount = "pi.gen_ai.agent.step.count",
84
- RequestReasoningEffort = "pi.gen_ai.request.reasoning.effort",
85
- RequestToolChoice = "pi.gen_ai.request.tool.choice",
86
- RequestAvailableTools = "pi.gen_ai.request.available_tools",
87
- RequestMessages = "pi.gen_ai.request.messages",
88
- ResponseText = "pi.gen_ai.response.text",
89
- ResponseToolCalls = "pi.gen_ai.response.tool_calls",
90
- ResponseUpstreamProvider = "pi.gen_ai.response.upstream_provider",
91
- UsageTotalTokens = "pi.gen_ai.usage.total_tokens",
92
- UsageServerSideTools = "pi.gen_ai.usage.server_tool_requests",
93
- CostEstimatedUsd = "pi.gen_ai.cost.estimated_usd",
94
- CostInputUsd = "pi.gen_ai.cost.input_usd",
95
- CostOutputUsd = "pi.gen_ai.cost.output_usd",
96
- CostUnavailableReason = "pi.gen_ai.cost.unavailable_reason",
97
- ToolStatus = "pi.gen_ai.tool.status",
98
- ToolCallIntent = "pi.gen_ai.tool.call.intent",
99
- HandoffFromAgentName = "pi.gen_ai.handoff.from_agent.name",
100
- HandoffFromAgentId = "pi.gen_ai.handoff.from_agent.id",
101
- HandoffToAgentName = "pi.gen_ai.handoff.to_agent.name",
102
- HandoffToAgentId = "pi.gen_ai.handoff.to_agent.id",
103
- OneshotKind = "pi.gen_ai.oneshot.kind",
104
- GatewayName = "pi.gen_ai.gateway.name",
105
- GatewayEndpoint = "pi.gen_ai.gateway.endpoint",
106
- GatewayCallId = "pi.gen_ai.gateway.call_id",
107
- GatewayRoutedTo = "pi.gen_ai.gateway.routed_to",
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 = "pi.gen_ai.gateway.response_cache.status"
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 pi.gen_ai.cost.estimated_usd
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
- * pi.gen_ai.cost.unavailable_reason
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 `pi.gen_ai.gateway.*` attributes.
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 PiGenAIAttr.GatewayResponseCacheStatus}. Distinct from provider
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 `pi.gen_ai.oneshot.kind`. Values used by the agent:
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 = PiGenAIAttr.ToolStatus;
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 `pi.gen_ai.agent.*` attributes stamped on the `invoke_agent` span. */
574
- export declare const enum PiGenAIAggregateAttr {
575
- ChatsCount = "pi.gen_ai.agent.chats.count",
576
- ChatsTotalLatencyMs = "pi.gen_ai.agent.chats.total_latency_ms",
577
- ChatsStopReasonPrefix = "pi.gen_ai.agent.chats.stop_reason.",
578
- ToolsCount = "pi.gen_ai.agent.tools.count",
579
- ToolsOkCount = "pi.gen_ai.agent.tools.ok.count",
580
- ToolsErrorCount = "pi.gen_ai.agent.tools.error.count",
581
- ToolsSkippedCount = "pi.gen_ai.agent.tools.skipped.count",
582
- ToolsBlockedCount = "pi.gen_ai.agent.tools.blocked.count",
583
- ToolsTimeoutCount = "pi.gen_ai.agent.tools.timeout.count",
584
- ToolsAbortedCount = "pi.gen_ai.agent.tools.aborted.count",
585
- ToolsTotalLatencyMs = "pi.gen_ai.agent.tools.total_latency_ms",
586
- ToolsInvoked = "pi.gen_ai.agent.tools.invoked",
587
- ToolsAvailable = "pi.gen_ai.agent.tools.available",
588
- ToolsUnused = "pi.gen_ai.agent.tools.unused",
589
- UsageInputTokensTotal = "pi.gen_ai.agent.usage.input_tokens.total",
590
- UsageOutputTokensTotal = "pi.gen_ai.agent.usage.output_tokens.total",
591
- UsageCacheReadInputTokensTotal = "pi.gen_ai.agent.usage.cache_read.input_tokens.total",
592
- UsageCacheCreationInputTokensTotal = "pi.gen_ai.agent.usage.cache_creation.input_tokens.total",
593
- UsageReasoningOutputTokensTotal = "pi.gen_ai.agent.usage.reasoning.output_tokens.total",
594
- UsageTotalTokensTotal = "pi.gen_ai.agent.usage.total_tokens.total",
595
- CostEstimatedUsdTotal = "pi.gen_ai.agent.cost.estimated_usd.total",
596
- ErrorsCount = "pi.gen_ai.agent.errors.count"
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;