@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.
- package/CHANGELOG.md +241 -2
- 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
package/src/tokenizer.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { Model } from "@linxiraos/pi-ai";
|
|
2
2
|
import type { ModelTokenizer } from "@linxiraos/pi-catalog/types";
|
|
3
3
|
import * as natives from "@linxiraos/pi-natives";
|
|
4
|
+
import { materializeString, stringifyJson } from "@linxiraos/pi-utils";
|
|
5
|
+
import { LRUCache } from "@linxiraos/pi-utils/lru";
|
|
4
6
|
import * as snapcompact from "@linxiraos/pi-snapcompact";
|
|
5
|
-
import { stringifyJson } from "@linxiraos/pi-utils";
|
|
6
7
|
import { isEstimateCacheable, messageEstimateVersion } from "./compaction/message-cache";
|
|
7
8
|
import type { AgentMessage } from "./types";
|
|
8
9
|
|
|
@@ -67,13 +68,43 @@ interface NativeTokenCount {
|
|
|
67
68
|
exact: boolean;
|
|
68
69
|
}
|
|
69
70
|
|
|
71
|
+
// Growing streamed text and large tool results must not evict the reusable
|
|
72
|
+
// short fragments. Account for UTF-16 key storage plus a per-entry allowance.
|
|
73
|
+
const NATIVE_CACHE_MAX_LENGTH = 16 * 1024;
|
|
74
|
+
|
|
75
|
+
function countNativeFragment(
|
|
76
|
+
text: string,
|
|
77
|
+
encoding: natives.Encoding | null | undefined,
|
|
78
|
+
counts: LRUCache<string, number>,
|
|
79
|
+
): number {
|
|
80
|
+
if (text.length > NATIVE_CACHE_MAX_LENGTH) return natives.countTokens(text, encoding);
|
|
81
|
+
const cached = counts.get(text);
|
|
82
|
+
if (cached !== undefined) return cached;
|
|
83
|
+
const tokens = natives.countTokens(text, encoding);
|
|
84
|
+
// Detach sliced strings so a small key cannot retain a much larger source.
|
|
85
|
+
counts.set(materializeString(text), tokens);
|
|
86
|
+
return tokens;
|
|
87
|
+
}
|
|
88
|
+
|
|
70
89
|
function countTokensNat(
|
|
71
90
|
text: string | string[],
|
|
72
91
|
encoding: natives.Encoding | null | undefined,
|
|
73
92
|
mode: TokenCountMode,
|
|
93
|
+
counts: LRUCache<string, number>,
|
|
74
94
|
): NativeTokenCount {
|
|
75
95
|
try {
|
|
76
|
-
|
|
96
|
+
let tokens: number;
|
|
97
|
+
if (typeof text === "string") {
|
|
98
|
+
tokens = countNativeFragment(text, encoding, counts);
|
|
99
|
+
} else if (text.length > 0 && text.length < 16) {
|
|
100
|
+
// The native API sums independent fragments, not their concatenation.
|
|
101
|
+
// Keep its parallel batch path for arrays of 16 or more fragments.
|
|
102
|
+
tokens = 0;
|
|
103
|
+
for (const fragment of text) tokens += countNativeFragment(fragment, encoding, counts);
|
|
104
|
+
} else {
|
|
105
|
+
tokens = natives.countTokens(text, encoding);
|
|
106
|
+
}
|
|
107
|
+
return { tokens, exact: true };
|
|
77
108
|
} catch (error) {
|
|
78
109
|
if (
|
|
79
110
|
!(error instanceof Error) ||
|
|
@@ -131,6 +162,13 @@ interface MessageEstimate {
|
|
|
131
162
|
export class Tokenizer {
|
|
132
163
|
readonly #encoding: natives.Encoding | null;
|
|
133
164
|
|
|
165
|
+
/** Exact counts only; byte fallbacks remain mode-dependent and uncached. */
|
|
166
|
+
readonly #nativeCounts = new LRUCache<string, number>({
|
|
167
|
+
max: 256,
|
|
168
|
+
maxSize: 512 * 1024,
|
|
169
|
+
sizeCalculation: (_tokens, text) => text.length * 2 + 64,
|
|
170
|
+
});
|
|
171
|
+
|
|
134
172
|
/**
|
|
135
173
|
* Per-message estimate memo. Keyed by message identity, deliberately not a
|
|
136
174
|
* symbol-tagged property: callers spread messages to derive throwaway
|
|
@@ -150,9 +188,10 @@ export class Tokenizer {
|
|
|
150
188
|
}
|
|
151
189
|
|
|
152
190
|
countTokens(text: string | string[], mode: TokenCountMode = "approximate"): number {
|
|
153
|
-
if (mode === "strict") return countTokensNat(text, this.#encoding, mode).tokens;
|
|
154
|
-
if (!testEnv && this.#encoding !== null)
|
|
155
|
-
|
|
191
|
+
if (mode === "strict") return countTokensNat(text, this.#encoding, mode, this.#nativeCounts).tokens;
|
|
192
|
+
if (!testEnv && this.#encoding !== null)
|
|
193
|
+
return countTokensNat(text, this.#encoding, mode, this.#nativeCounts).tokens;
|
|
194
|
+
if (accurate) return countTokensNat(text, undefined, mode, this.#nativeCounts).tokens;
|
|
156
195
|
return sumFragments(text, mode === "upperbound" ? byteLength : byteEstimate);
|
|
157
196
|
}
|
|
158
197
|
|
|
@@ -170,7 +209,7 @@ export class Tokenizer {
|
|
|
170
209
|
checkTokenBudget(text: string | string[], budget: number): TokenBudgetCheck {
|
|
171
210
|
const bound = sumFragments(text, byteLength);
|
|
172
211
|
if (bound <= budget) return { fits: true, tokens: bound, exact: false };
|
|
173
|
-
const result = countTokensNat(text, this.#encoding, "strict");
|
|
212
|
+
const result = countTokensNat(text, this.#encoding, "strict", this.#nativeCounts);
|
|
174
213
|
return { fits: result.tokens <= budget, tokens: result.tokens, exact: result.exact };
|
|
175
214
|
}
|
|
176
215
|
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { ToolResultMessage } from "@linxiraos/pi-ai";
|
|
2
|
+
import type { AgentMessage } from "./types";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Symbol-keyed carrier for passive context reported by a tool executed outside
|
|
6
|
+
* the agent loop (Cursor exec-channel dispatch). The executor attaches the
|
|
7
|
+
* joined context to the {@link ToolResultMessage} it returns; `Agent` reads it
|
|
8
|
+
* when the provider hands the result back and injects it after the buffered
|
|
9
|
+
* results. Symbol keys never serialize, so the context cannot leak into the
|
|
10
|
+
* persisted tool result.
|
|
11
|
+
*/
|
|
12
|
+
export const TOOL_RESULT_ADDITIONAL_CONTEXT = Symbol("tool-result-additional-context");
|
|
13
|
+
|
|
14
|
+
/** A tool result optionally carrying {@link TOOL_RESULT_ADDITIONAL_CONTEXT}. */
|
|
15
|
+
export type ToolResultWithAdditionalContext = ToolResultMessage & { [TOOL_RESULT_ADDITIONAL_CONTEXT]?: string };
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* True for a passive-context value worth delivering: a string with at least
|
|
19
|
+
* one non-whitespace character. Shared by every producer and aggregation site
|
|
20
|
+
* so blank values never produce a developer message.
|
|
21
|
+
*/
|
|
22
|
+
export function isNonBlankContext(value: unknown): value is string {
|
|
23
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Join passive context values in order, dropping blanks and repeats of an
|
|
28
|
+
* earlier value (compared without surrounding whitespace; the first original
|
|
29
|
+
* is kept). Returns undefined when nothing remains.
|
|
30
|
+
*/
|
|
31
|
+
export function joinAdditionalContext(values: Iterable<string | undefined>): string | undefined {
|
|
32
|
+
const seen = new Set<string>();
|
|
33
|
+
const kept: string[] = [];
|
|
34
|
+
for (const value of values) {
|
|
35
|
+
if (!isNonBlankContext(value)) continue;
|
|
36
|
+
const key = value.trim();
|
|
37
|
+
if (seen.has(key)) continue;
|
|
38
|
+
seen.add(key);
|
|
39
|
+
kept.push(value);
|
|
40
|
+
}
|
|
41
|
+
return kept.length > 0 ? kept.join("\n\n") : undefined;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Build the developer message that carries passive tool context to the next
|
|
46
|
+
* provider request. Emitted after the tool results it belongs to.
|
|
47
|
+
*/
|
|
48
|
+
export function createAdditionalContextMessage(text: string): AgentMessage {
|
|
49
|
+
return {
|
|
50
|
+
role: "developer",
|
|
51
|
+
content: [{ type: "text", text }],
|
|
52
|
+
attribution: "agent",
|
|
53
|
+
timestamp: Date.now(),
|
|
54
|
+
};
|
|
55
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -24,6 +24,7 @@ import type { Dialect } from "@linxiraos/pi-ai/dialect";
|
|
|
24
24
|
import type { HarmonyAuditEvent } from "@linxiraos/pi-ai/utils/harmony-leak";
|
|
25
25
|
import type { AppendOnlyContextManager } from "./append-only-context";
|
|
26
26
|
import type { AgentRunCoverage, AgentRunSummary } from "./run-collector";
|
|
27
|
+
import type { SentToolDefinitions } from "./sent-tool-definitions";
|
|
27
28
|
import type { AgentTelemetryConfig } from "./telemetry";
|
|
28
29
|
|
|
29
30
|
/** Stream function - can return sync or Promise for async config lookup */
|
|
@@ -68,6 +69,12 @@ export interface AgentTurnEndContext {
|
|
|
68
69
|
message: AgentMessage;
|
|
69
70
|
/** Tool results produced by this turn, already paired with `message` in the live context. */
|
|
70
71
|
toolResults: ToolResultMessage[];
|
|
72
|
+
/**
|
|
73
|
+
* Passive model-visible messages appended after the tool results at this
|
|
74
|
+
* boundary. The agent loop always sends an array (possibly empty);
|
|
75
|
+
* absent is equivalent to empty for hosts that construct the context.
|
|
76
|
+
*/
|
|
77
|
+
additionalMessages?: AgentMessage[];
|
|
71
78
|
/** True when the current tool-loop batch is continuing without yielding to post-turn steering. */
|
|
72
79
|
willContinue: boolean;
|
|
73
80
|
}
|
|
@@ -163,8 +170,10 @@ export interface AgentLoopConfig extends SimpleStreamOptions {
|
|
|
163
170
|
|
|
164
171
|
/**
|
|
165
172
|
* When to interrupt tool execution for steering messages.
|
|
166
|
-
* - "immediate" =
|
|
167
|
-
*
|
|
173
|
+
* - "immediate" = cut interruptible waits short and raise the cooperative
|
|
174
|
+
* `steeringSignal` for other running tools (default)
|
|
175
|
+
* - "wait" = let non-interruptible tools finish undisturbed; interruptible
|
|
176
|
+
* waits are still cut short, since they have no work to complete
|
|
168
177
|
*/
|
|
169
178
|
interruptMode?: "immediate" | "wait";
|
|
170
179
|
|
|
@@ -238,6 +247,9 @@ export interface AgentLoopConfig extends SimpleStreamOptions {
|
|
|
238
247
|
*/
|
|
239
248
|
transformProviderContext?: (context: Context, model: Model) => Context | Promise<Context>;
|
|
240
249
|
|
|
250
|
+
/** Remembers sent tool definitions to fill {@link Context.inactiveTools}. */
|
|
251
|
+
sentToolDefinitions?: SentToolDefinitions;
|
|
252
|
+
|
|
241
253
|
/**
|
|
242
254
|
* Resolves the API key or resolver for the current model before each LLM call.
|
|
243
255
|
*
|
|
@@ -259,8 +271,9 @@ export interface AgentLoopConfig extends SimpleStreamOptions {
|
|
|
259
271
|
/**
|
|
260
272
|
* Peeks whether steering messages are queued, without consuming them.
|
|
261
273
|
*
|
|
262
|
-
* Polled while a tool batch runs (
|
|
263
|
-
* whether to abort in-flight and skip
|
|
274
|
+
* Polled while a tool batch runs (in "wait" mode, only when the batch holds an
|
|
275
|
+
* interruptible tool) to decide whether to abort in-flight and skip
|
|
276
|
+
* not-yet-started *interruptible* waits;
|
|
264
277
|
* every other already-emitted call still executes and the message injects
|
|
265
278
|
* at the batch boundary. The queue keeps
|
|
266
279
|
* owning its messages until the loop reaches the next injection boundary and
|
|
@@ -285,14 +298,36 @@ export interface AgentLoopConfig extends SimpleStreamOptions {
|
|
|
285
298
|
*/
|
|
286
299
|
waitForSteeringMessages?: (signal?: AbortSignal) => Promise<void>;
|
|
287
300
|
|
|
301
|
+
/**
|
|
302
|
+
* Called when live steering dequeues messages via {@link getSteeringMessages}
|
|
303
|
+
* for the response being streamed. The loop records them in the transcript
|
|
304
|
+
* after that response (or its tool batch); an abort before then leaves them
|
|
305
|
+
* unrecorded for the host to requeue.
|
|
306
|
+
*/
|
|
307
|
+
onLiveSteeringTaken?: (messages: AgentMessage[]) => void;
|
|
308
|
+
|
|
288
309
|
/**
|
|
289
310
|
* Peeks whether IRC messages should interrupt an interruptible waiting tool.
|
|
290
311
|
*
|
|
291
312
|
* Uses the same delivery rules as steering: the poll is non-consuming, only
|
|
292
|
-
* runs for interruptible tools, and
|
|
313
|
+
* runs for interruptible tools, and cuts them short even when interruptMode
|
|
314
|
+
* is "wait".
|
|
293
315
|
* The host owns message injection at the next boundary.
|
|
294
316
|
*/
|
|
295
317
|
hasIrcInterrupts?: () => boolean | Promise<boolean>;
|
|
318
|
+
/**
|
|
319
|
+
* Peeks whether a background completion (finished job, exited supervised
|
|
320
|
+
* process) is queued for aside injection at the next boundary.
|
|
321
|
+
*
|
|
322
|
+
* Same rules as {@link hasIrcInterrupts}: non-consuming, only cuts
|
|
323
|
+
* *interruptible* waits short, in either interruptMode. Without
|
|
324
|
+
* it a completion notice sits behind an hour-long `wait` that the agent
|
|
325
|
+
* would have abandoned had it seen the notice. Unlike a peer IRC it never
|
|
326
|
+
* raises {@link ToolCallContext.steeringSignal}: a queued completion must
|
|
327
|
+
* not push ordinary foreground work (auto-background bash/eval) into the
|
|
328
|
+
* background.
|
|
329
|
+
*/
|
|
330
|
+
hasBackgroundCompletions?: () => boolean | Promise<boolean>;
|
|
296
331
|
|
|
297
332
|
/**
|
|
298
333
|
* Returns follow-up messages to process after the agent would otherwise stop.
|
|
@@ -323,7 +358,11 @@ export interface AgentLoopConfig extends SimpleStreamOptions {
|
|
|
323
358
|
|
|
324
359
|
/**
|
|
325
360
|
* Provides tool execution context, resolved per tool call.
|
|
326
|
-
* Use for late-bound UI or session state access.
|
|
361
|
+
* Use for late-bound UI or session state access. The loop passes the tool
|
|
362
|
+
* call's {@link ToolCallContext}; hosts that support passive tool context
|
|
363
|
+
* surface its `addAdditionalContext` sink as
|
|
364
|
+
* {@link AgentToolContext.addAdditionalContext}. The returned object is
|
|
365
|
+
* handed to the tool as-is.
|
|
327
366
|
*/
|
|
328
367
|
getToolContext?: (toolCall?: ToolCallContext) => AgentToolContext | undefined;
|
|
329
368
|
|
|
@@ -401,6 +440,12 @@ export interface AgentLoopConfig extends SimpleStreamOptions {
|
|
|
401
440
|
* model's text output back into canonical `toolCall` blocks.
|
|
402
441
|
*/
|
|
403
442
|
dialect?: Dialect;
|
|
443
|
+
/**
|
|
444
|
+
* Per-call owned-dialect resolver, read once per LLM call with the model
|
|
445
|
+
* being requested. Authoritative when set: its return value (including
|
|
446
|
+
* `undefined` = native tool calling) replaces the static {@link dialect}.
|
|
447
|
+
*/
|
|
448
|
+
getDialect?: (model: Model) => Dialect | undefined;
|
|
404
449
|
/**
|
|
405
450
|
* When owned (in-band) tool calling is active and the model starts
|
|
406
451
|
* fabricating a tool result inside its own turn, control how the loop reacts:
|
|
@@ -550,6 +595,14 @@ export interface AgentLoopConfig extends SimpleStreamOptions {
|
|
|
550
595
|
*/
|
|
551
596
|
transformAssistantMessage?: (message: AssistantMessage, signal?: AbortSignal) => Promise<void> | void;
|
|
552
597
|
|
|
598
|
+
/**
|
|
599
|
+
* Declares that {@link transformAssistantMessage} never rewrites or removes a
|
|
600
|
+
* tool call the model streamed (it may edit text or append new calls). Stream
|
|
601
|
+
* speculation sessions and direct speculative candidates plan from streamed
|
|
602
|
+
* calls, so they stay disabled under a transform unless this is set.
|
|
603
|
+
*/
|
|
604
|
+
transformAssistantMessagePreservesToolCalls?: boolean;
|
|
605
|
+
|
|
553
606
|
/**
|
|
554
607
|
* Called after a tool finishes executing, before `tool_execution_end` and the
|
|
555
608
|
* tool-result message are emitted.
|
|
@@ -594,6 +647,13 @@ export interface ToolCallContext {
|
|
|
594
647
|
* always safe (the message injects at the next batch boundary).
|
|
595
648
|
*/
|
|
596
649
|
steeringSignal?: AbortSignal;
|
|
650
|
+
/**
|
|
651
|
+
* Loop-owned sink for passive context reported while this call executes.
|
|
652
|
+
* Values join the call's context at the batch boundary and are injected
|
|
653
|
+
* after the batch's tool results, in assistant tool-call order, before the
|
|
654
|
+
* next provider request. Blank values are ignored.
|
|
655
|
+
*/
|
|
656
|
+
addAdditionalContext?: (context: string) => void;
|
|
597
657
|
}
|
|
598
658
|
|
|
599
659
|
/** A single tool-call content block emitted by an assistant message. */
|
|
@@ -688,9 +748,24 @@ export interface SpeculativeExecutionHost {
|
|
|
688
748
|
commitDefault: () => Promise<AgentToolResult<unknown>>,
|
|
689
749
|
): Promise<SpeculativeCommitDecision>;
|
|
690
750
|
discard?(context: SpeculativeDiscardContext): void | Promise<void>;
|
|
751
|
+
/**
|
|
752
|
+
* Authorize a stream session to start effectful work (e.g. subagents) from
|
|
753
|
+
* partially streamed arguments. The session owns that work and must abort it
|
|
754
|
+
* when the finalized call is invalid, blocked, or changed. Hosts without this
|
|
755
|
+
* hook deny every launch.
|
|
756
|
+
*/
|
|
757
|
+
authorizeLaunch?(context: SpeculativeLaunchContext): SpeculativeAuthorization | Promise<SpeculativeAuthorization>;
|
|
691
758
|
close?(reason: string): void | Promise<void>;
|
|
692
759
|
}
|
|
693
760
|
|
|
761
|
+
/** Effectful work a tool-owned stream session asks to start before its outer call dispatches. */
|
|
762
|
+
export interface SpeculativeLaunchContext {
|
|
763
|
+
tool: SpeculativeToolReference;
|
|
764
|
+
toolCall: AgentToolCall;
|
|
765
|
+
/** Arguments the launch was planned from: the streamed prefix of the outer call. */
|
|
766
|
+
args: Readonly<Record<string, unknown>>;
|
|
767
|
+
}
|
|
768
|
+
|
|
694
769
|
export interface ToolSpeculationStreamContext {
|
|
695
770
|
readonly coordinator: SpeculativeOperationSink;
|
|
696
771
|
readonly parentToolCallId: string;
|
|
@@ -737,6 +812,8 @@ export interface ToolSpeculationStreamSession {
|
|
|
737
812
|
export interface SpeculativeOperationSink {
|
|
738
813
|
readonly maxInFlight: number;
|
|
739
814
|
admit(definition: SpeculativeChildDefinition): Promise<SpeculativeChildHandle | undefined>;
|
|
815
|
+
/** Host-gated permission for effectful stream work; see {@link SpeculativeExecutionHost.authorizeLaunch}. */
|
|
816
|
+
authorizeLaunch?(context: SpeculativeLaunchContext): Promise<SpeculativeAuthorization>;
|
|
740
817
|
discardChildren?(parentToolCallId: string, reason: string): void | Promise<void>;
|
|
741
818
|
close(reason: string): void | Promise<void>;
|
|
742
819
|
}
|
|
@@ -796,11 +873,20 @@ export interface SpeculativeToolExecutionConfig {
|
|
|
796
873
|
* written back to the tool-call block on the assistant message, and seen by
|
|
797
874
|
* history, scheduling, execution events, and `tool.execute` alike. It is
|
|
798
875
|
* ignored when `block` is true.
|
|
876
|
+
*
|
|
877
|
+
* Set `additionalContext` to attach passive model-visible context to this call.
|
|
878
|
+
* Non-empty values from a tool batch are injected in assistant tool-call order,
|
|
879
|
+
* a value identical to an earlier one in the batch only once,
|
|
880
|
+
* after every result settles and before the next provider request. It is
|
|
881
|
+
* dropped when the call is blocked or skipped, or when its final result is an
|
|
882
|
+
* error (including an approval denial raised by the tool's own gate). Within a
|
|
883
|
+
* call it follows any context the tool reported during execution.
|
|
799
884
|
*/
|
|
800
885
|
export interface BeforeToolCallResult {
|
|
801
886
|
block?: boolean;
|
|
802
887
|
reason?: string;
|
|
803
888
|
args?: Record<string, unknown>;
|
|
889
|
+
additionalContext?: string;
|
|
804
890
|
}
|
|
805
891
|
|
|
806
892
|
/**
|
|
@@ -968,6 +1054,16 @@ export type ToolApproval = ToolApprovalDecision | ((args: unknown) => ToolApprov
|
|
|
968
1054
|
* Apps can extend via declaration merging.
|
|
969
1055
|
*/
|
|
970
1056
|
export interface AgentToolContext {
|
|
1057
|
+
/**
|
|
1058
|
+
* Attach trusted, agent-authored instructions to the next provider request.
|
|
1059
|
+
* The host emits them after tool results with developer/system priority where
|
|
1060
|
+
* the selected transport supports it. Do not use this channel for raw tool
|
|
1061
|
+
* output, retrieved documents, web content, or other untrusted data; return
|
|
1062
|
+
* those through the ordinary tool result instead. Hosts populate it from
|
|
1063
|
+
* {@link ToolCallContext.addAdditionalContext} (or their own collector for
|
|
1064
|
+
* calls dispatched outside the loop); absent when the host has no sink.
|
|
1065
|
+
*/
|
|
1066
|
+
addAdditionalContext?(context: string): void;
|
|
971
1067
|
/** Present only while the matching outer tool owns its finalized stream session. */
|
|
972
1068
|
[SPECULATIVE_STREAM_SESSION]?: ToolSpeculationStreamSession;
|
|
973
1069
|
}
|
|
@@ -1021,6 +1117,12 @@ export interface AgentTool<
|
|
|
1021
1117
|
loadMode?: ToolLoadMode;
|
|
1022
1118
|
/** Short one-line summary used for tool discovery indexes. */
|
|
1023
1119
|
summary?: string;
|
|
1120
|
+
/**
|
|
1121
|
+
* On-demand documentation topics (`topic → markdown`), readable as
|
|
1122
|
+
* `xd://<tool>/<topic>`. Lets a tool keep large sub-surfaces out of its
|
|
1123
|
+
* description and advertise only a one-line pointer per topic.
|
|
1124
|
+
*/
|
|
1125
|
+
docTopics?(): Readonly<Record<string, string>>;
|
|
1024
1126
|
/**
|
|
1025
1127
|
* Concurrency mode for tool scheduling when multiple calls are in one turn.
|
|
1026
1128
|
* - "shared": can run alongside other shared tools (default)
|
|
@@ -1045,7 +1147,7 @@ export interface AgentTool<
|
|
|
1045
1147
|
* cleanly (e.g. `job` poll), so the abort surfaces the tool's current
|
|
1046
1148
|
* snapshot rather than corrupting a side effect. Every other call runs to
|
|
1047
1149
|
* completion even when steering is queued; the message lands at the next
|
|
1048
|
-
* batch boundary. Honored
|
|
1150
|
+
* batch boundary. Honored in both `interruptMode`s.
|
|
1049
1151
|
*/
|
|
1050
1152
|
interruptible?: boolean | ((args: Partial<Static<TParameters>>) => boolean);
|
|
1051
1153
|
/**
|