@oh-my-pi/pi-agent-core 18.2.11 → 18.3.1
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 +24 -0
- package/README.md +3 -2
- package/THIRD-PARTY-NOTICES.txt +3 -3
- package/dist/types/agent-loop.d.ts +6 -0
- package/dist/types/agent.d.ts +14 -0
- package/dist/types/compaction/anthropic.d.ts +22 -52
- package/dist/types/compaction/compaction.d.ts +2 -0
- package/dist/types/compaction/messages.d.ts +9 -0
- package/dist/types/compaction/transcript-tokens.d.ts +14 -1
- package/dist/types/index.d.ts +3 -0
- package/dist/types/live-steering.d.ts +34 -0
- package/dist/types/output-budget.d.ts +43 -0
- package/dist/types/sent-tool-definitions.d.ts +17 -0
- package/dist/types/tool-context.d.ts +31 -0
- package/dist/types/types.d.ts +63 -9
- package/package.json +12 -9
- package/src/agent-loop.ts +219 -44
- package/src/agent.ts +87 -4
- package/src/compaction/anthropic.ts +60 -103
- package/src/compaction/compaction.ts +58 -48
- package/src/compaction/messages.ts +12 -2
- package/src/compaction/prompts/anthropic-compaction-instructions.md +2 -6
- package/src/compaction/transcript-tokens.ts +31 -1
- package/src/index.ts +6 -0
- package/src/live-steering.ts +89 -0
- package/src/output-budget.ts +130 -0
- package/src/sent-tool-definitions.ts +40 -0
- package/src/tool-context.ts +49 -0
- package/src/types.ts +64 -9
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,30 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [18.3.1] - 2026-09-25
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added live steering support for Codex WebSocket transports, allowing users to provide input while a response is in progress.
|
|
10
|
+
- Added passive tool-call context support, allowing hooks and tools to supply additional context for subsequent model processing.
|
|
11
|
+
- Improved context-window handling by automatically adjusting output-token limits and supporting models that truncate output at the context-window limit.
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- Improved prompt token counting for requests with anchored prefixes by using provider-reported usage and limiting local estimation to new message content.
|
|
16
|
+
|
|
17
|
+
## [18.3.0] - 2026-09-24
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- Added support for documenting agent tools on demand through the new `AgentTool.docTopics` method.
|
|
22
|
+
- Added `TOOL_INTERRUPT_ABORT_REASON` so interruptible tools can distinguish queued steering, peer messages, or background completions from a full run abort.
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- Improved interrupt handling so tools respect wait mode and can be interrupted when appropriate.
|
|
27
|
+
- Updated Anthropic compaction compatibility with signature verification.
|
|
28
|
+
|
|
5
29
|
## [18.2.11] - 2026-09-23
|
|
6
30
|
|
|
7
31
|
### Fixed
|
package/README.md
CHANGED
|
@@ -154,8 +154,9 @@ const agent = new Agent({
|
|
|
154
154
|
// Dynamic model-scoped API key resolution (for expiring OAuth tokens)
|
|
155
155
|
getApiKey: async (model) => tokenForModel(model),
|
|
156
156
|
|
|
157
|
-
// Tool execution context (late-bound UI/session access)
|
|
158
|
-
|
|
157
|
+
// Tool execution context (late-bound UI/session access). Surface the loop's
|
|
158
|
+
// passive-context sink so tools can call ctx.addAdditionalContext(...).
|
|
159
|
+
getToolContext: toolCall => ({ addAdditionalContext: toolCall?.addAdditionalContext /* app-defined */ }),
|
|
159
160
|
});
|
|
160
161
|
```
|
|
161
162
|
|
package/THIRD-PARTY-NOTICES.txt
CHANGED
|
@@ -78,8 +78,8 @@ below.
|
|
|
78
78
|
uutils coreutils (https://github.com/uutils/coreutils), MIT
|
|
79
79
|
--------------------------------------------------------------------------------
|
|
80
80
|
Covers: base32, base64, basename, cat, cksum (shared checksum machinery),
|
|
81
|
-
b2sum, md5sum, sha1sum, sha224sum, sha256sum, sha384sum, sha512sum, comm,
|
|
82
|
-
date, dirname, head, hostname, ln, ls, mkdir, mktemp, mv, nproc, paste,
|
|
81
|
+
b2sum, md5sum, sha1sum, sha224sum, sha256sum, sha384sum, sha512sum, comm, cp,
|
|
82
|
+
cut, date, dirname, head, hostname, ln, ls, mkdir, mktemp, mv, nproc, paste,
|
|
83
83
|
printenv, readlink, realpath, rm, seq, sort, stat, tac, tail, tee, touch, tr,
|
|
84
84
|
truncate, uname, uniq, wc, whoami, yes.
|
|
85
85
|
|
|
@@ -293,7 +293,7 @@ MIT License
|
|
|
293
293
|
Copyright (c) 2026 Sander Land
|
|
294
294
|
(measured tokenizer vocabulary data, reconstruction model, and reference
|
|
295
295
|
implementation: https://github.com/sanderland/ctok)
|
|
296
|
-
Copyright (c) 2026 Can Bölük and the
|
|
296
|
+
Copyright (c) 2026 Can Bölük and the omp contributors
|
|
297
297
|
Copyright (c) 2026 Stencil Labs, Inc.
|
|
298
298
|
(Rust implementation and the compact binary vocabulary encoding in
|
|
299
299
|
crates/pi-natives/src/utok/claude)
|
|
@@ -32,6 +32,12 @@ export declare function createToolScopedAbortReason(message: string, toolCallMes
|
|
|
32
32
|
* boundary; this reason stops after persisting the completed tool batch.
|
|
33
33
|
*/
|
|
34
34
|
export declare const TERMINAL_TOOL_RESULT_ABORT_REASON: unique symbol;
|
|
35
|
+
/**
|
|
36
|
+
* Abort reason carried by an interruptible tool's signal when queued steering,
|
|
37
|
+
* a peer IRC, or a background completion cut it short. Lets a wait tell the
|
|
38
|
+
* designed wake path apart from an external/user abort of the run.
|
|
39
|
+
*/
|
|
40
|
+
export declare const TOOL_INTERRUPT_ABORT_REASON: unique symbol;
|
|
35
41
|
export declare function resolveOwnedDialectFromEnv(value: string | undefined): Dialect | undefined;
|
|
36
42
|
/**
|
|
37
43
|
* Start an agent loop with a new prompt message.
|
package/dist/types/agent.d.ts
CHANGED
|
@@ -159,6 +159,11 @@ export interface AgentOptions {
|
|
|
159
159
|
pruneToolDescriptions?: boolean;
|
|
160
160
|
/** Owned tool-calling dialect. Undefined keeps provider-native tool calling. */
|
|
161
161
|
dialect?: Dialect;
|
|
162
|
+
/**
|
|
163
|
+
* Per-request owned-dialect resolver, consulted with the model being requested.
|
|
164
|
+
* Authoritative when set (like {@link serviceTierResolver}): replaces {@link dialect}.
|
|
165
|
+
*/
|
|
166
|
+
dialectResolver?: (model: Model) => Dialect | undefined;
|
|
162
167
|
/**
|
|
163
168
|
* When owned tool calling is active and the model fabricates a tool result
|
|
164
169
|
* mid-turn: `true` (default) aborts the provider request immediately; `false`
|
|
@@ -360,6 +365,15 @@ export declare class Agent {
|
|
|
360
365
|
set serviceTierResolver(value: ((model: Model) => ServiceTier | undefined) | undefined);
|
|
361
366
|
get hideThinkingSummary(): boolean | undefined;
|
|
362
367
|
set hideThinkingSummary(value: boolean | undefined);
|
|
368
|
+
/** Strip tool descriptions from provider-bound specs; read per request. */
|
|
369
|
+
get pruneToolDescriptions(): boolean;
|
|
370
|
+
set pruneToolDescriptions(value: boolean);
|
|
371
|
+
/** Inject/strip the intent field on tool calls; applies from the next prompt run. */
|
|
372
|
+
get intentTracing(): boolean;
|
|
373
|
+
set intentTracing(value: boolean);
|
|
374
|
+
/** Abort the provider request on a fabricated tool result; applies from the next prompt run. */
|
|
375
|
+
get abortOnFabricatedToolResult(): boolean | undefined;
|
|
376
|
+
set abortOnFabricatedToolResult(value: boolean | undefined);
|
|
363
377
|
/**
|
|
364
378
|
* Get the current max retry delay in milliseconds.
|
|
365
379
|
*/
|
|
@@ -1,33 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Anthropic
|
|
2
|
+
* Anthropic on-demand compaction (`compact-2026-09-04` beta).
|
|
3
3
|
*
|
|
4
|
-
* The
|
|
5
|
-
* prompt, tools
|
|
6
|
-
*
|
|
7
|
-
* cached prefix and stops; the summary arrives as a `compaction` block that
|
|
8
|
-
* the provider surfaces as an `anthropicCompaction` payload. The summary is
|
|
9
|
-
* plain text, so it doubles as the compaction entry's readable summary for
|
|
10
|
-
* every other provider, while the Anthropic provider replays it as a native
|
|
11
|
-
* block (the API drops everything that precedes it). The retained tail after
|
|
12
|
-
* the cut point is replayed from session entries exactly like a local summary.
|
|
4
|
+
* The request sends only the prefix to summarize, with the live conversation's
|
|
5
|
+
* system prompt, tools and thinking settings. The returned signed block
|
|
6
|
+
* replaces that prefix; the retained tail is replayed from session entries.
|
|
13
7
|
*/
|
|
14
8
|
import type { AnthropicCompactionPayload, ApiKey, Effort, Message, Model, SimpleStreamOptions, Tool, Usage } from "@oh-my-pi/pi-ai";
|
|
15
9
|
import { type InstrumentedChatSpanOptions } from "../telemetry.js";
|
|
10
|
+
import type { AgentMessage } from "../types.js";
|
|
16
11
|
export declare const ANTHROPIC_COMPACTION_PRESERVE_KEY = "anthropicCompaction";
|
|
17
|
-
/** The API rejects a `compact_20260112` trigger below this many input tokens. */
|
|
18
|
-
export declare const ANTHROPIC_COMPACTION_MIN_TRIGGER_TOKENS = 50000;
|
|
19
|
-
/**
|
|
20
|
-
* Smallest context the native lane accepts. The trigger sits at the API
|
|
21
|
-
* floor, so a prompt that lands below it is answered instead of compacted;
|
|
22
|
-
* the margin over the floor absorbs the difference between the last reported
|
|
23
|
-
* context size and the compaction request's own input.
|
|
24
|
-
*/
|
|
25
|
-
export declare const ANTHROPIC_COMPACTION_MIN_CONTEXT_TOKENS = 55000;
|
|
26
12
|
/** Summary persisted under {@link ANTHROPIC_COMPACTION_PRESERVE_KEY}. */
|
|
27
13
|
export interface AnthropicCompactionPreserveData {
|
|
28
14
|
provider: string;
|
|
29
15
|
content: string;
|
|
30
|
-
/**
|
|
16
|
+
/** Signature attached to an on-demand block; replayed verbatim. */
|
|
17
|
+
signature?: string;
|
|
18
|
+
/** Legacy threshold block state; replay-only. */
|
|
31
19
|
encryptedContent?: string;
|
|
32
20
|
/** Harness file metadata (`<files>` section) replayed after the native block. */
|
|
33
21
|
filesText?: string;
|
|
@@ -51,33 +39,17 @@ export declare function withAnthropicCompactionPreserveData(preserveData: Record
|
|
|
51
39
|
/** Replay payload for a compaction summary the active model produced natively. */
|
|
52
40
|
export declare function getAnthropicCompactionPayload(preserveData: Record<string, unknown> | undefined): AnthropicCompactionPayload | undefined;
|
|
53
41
|
/**
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* the whole conversation so the prompt cache the live turn wrote is read, but
|
|
58
|
-
* the summary must cover only the history before that tail — the local
|
|
59
|
-
* summarizer never sees the tail, and the rebuilt context replays it after the
|
|
60
|
-
* summary. Counting mirrors the provider's message conversion (consecutive
|
|
61
|
-
* tool results collapse into one user message; developer messages are user
|
|
62
|
-
* messages). Structured for the prompt template, which renders the
|
|
63
|
-
* singular/plural wording; the description quotes no content: quoting the
|
|
64
|
-
* tail would hand the summarizer the very facts it must leave to the tail.
|
|
42
|
+
* Move the existing keep-tail boundary forward until the summary/tail boundary
|
|
43
|
+
* alternates wire roles and no tool call is separated from its result. If no
|
|
44
|
+
* boundary is safe, the request summarizes the entire snapshot (empty tail).
|
|
65
45
|
*/
|
|
66
|
-
export
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
|
|
73
|
-
* API default entirely. The template lays out the retained-tail boundary
|
|
74
|
-
* first, so the summary covers only the history the rebuilt context drops,
|
|
75
|
-
* then the caller's extra context, the same structure prompt as the local
|
|
76
|
-
* summarizer, the caller's focus, and the tool-abstention clause the API
|
|
77
|
-
* recommends when tools are defined (a summarization pass that calls a tool
|
|
78
|
-
* yields no summary).
|
|
79
|
-
*/
|
|
80
|
-
export declare function buildAnthropicCompactionInstructions(basePrompt: string, customInstructions: string | undefined, extraContext: string | undefined, retainedTail: RetainedTailScope | undefined): string;
|
|
46
|
+
export declare function findAnthropicCompactionCut(messages: readonly (AgentMessage | {
|
|
47
|
+
role: "system";
|
|
48
|
+
content: string;
|
|
49
|
+
timestamp: number;
|
|
50
|
+
})[], initialCut: number): number;
|
|
51
|
+
/** Instructions replace the API default; the request contains only summarized messages. */
|
|
52
|
+
export declare function buildAnthropicCompactionInstructions(basePrompt: string, customInstructions: string | undefined, extraContext: string | undefined): string;
|
|
81
53
|
export interface AnthropicNativeCompactionRequest {
|
|
82
54
|
systemPrompt: string[];
|
|
83
55
|
messages: Message[];
|
|
@@ -88,21 +60,19 @@ export interface AnthropicNativeCompactionRequest {
|
|
|
88
60
|
}
|
|
89
61
|
export interface AnthropicNativeCompactionResponse {
|
|
90
62
|
content: string;
|
|
91
|
-
|
|
63
|
+
signature: string;
|
|
92
64
|
usage: Usage;
|
|
93
65
|
model: string;
|
|
94
66
|
}
|
|
95
67
|
export interface AnthropicNativeCompactionOptions extends Pick<SimpleStreamOptions, "initiatorOverride" | "metadata" | "fetch" | "sessionId" | "promptCacheKey" | "providerSessionState" | "maxInFlightRequests">, Pick<InstrumentedChatSpanOptions, "completeImpl" | "telemetry" | "retry"> {
|
|
96
68
|
}
|
|
97
69
|
/**
|
|
98
|
-
* Run one compaction request and return the summary the API wrote
|
|
99
|
-
* opaque `encrypted_content` the API attached for the replay. `completeSimple`
|
|
70
|
+
* Run one compaction request and return the summary and signature the API wrote. `completeSimple`
|
|
100
71
|
* resolves terminal failures as messages, so their classification is restored
|
|
101
72
|
* here: an aborted response is an `AbortError` (a cancellation, never a native
|
|
102
73
|
* failure) and an error response keeps its HTTP status, so auth and timeout
|
|
103
74
|
* handling downstream classify it the same way as the OpenAI lanes. A response
|
|
104
|
-
* without a summary is a native failure
|
|
105
|
-
*
|
|
106
|
-
* the model called a tool during summarization.
|
|
75
|
+
* without a summary is a native failure, including tool use, refusals and
|
|
76
|
+
* output limits; the configured method order can then choose a fallback.
|
|
107
77
|
*/
|
|
108
78
|
export declare function requestAnthropicNativeCompaction(model: Model<"anthropic-messages">, apiKey: ApiKey, request: AnthropicNativeCompactionRequest, signal: AbortSignal | undefined, options: AnthropicNativeCompactionOptions): Promise<AnthropicNativeCompactionResponse>;
|
|
@@ -297,6 +297,8 @@ export interface CompactionPreparation {
|
|
|
297
297
|
turnPrefixMessages: AgentMessage[];
|
|
298
298
|
/** Messages kept in full after compaction (recent history) */
|
|
299
299
|
recentMessages: AgentMessage[];
|
|
300
|
+
/** Entry IDs parallel to recentMessages, for an Anthropic-safe keep-tail boundary. */
|
|
301
|
+
recentEntryIds?: string[];
|
|
300
302
|
/** Whether this is a split turn (cut point in middle of turn) */
|
|
301
303
|
isSplitTurn: boolean;
|
|
302
304
|
tokensBefore: number;
|
|
@@ -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;
|
|
@@ -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 "@oh-my-pi/pi-ai";
|
|
20
|
+
import type { AssistantMessage, Message } from "@oh-my-pi/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. */
|
|
@@ -46,6 +46,19 @@ export declare function isTranscriptUsageAnchor(message: AgentMessage): message
|
|
|
46
46
|
* summarized away describes a prompt that is no longer sent.
|
|
47
47
|
*/
|
|
48
48
|
export declare function findTranscriptUsageAnchor(messages: readonly AgentMessage[], fromIndex?: number): TranscriptUsageAnchor | undefined;
|
|
49
|
+
/**
|
|
50
|
+
* Newest assistant turn in a provider request's `messages` whose usage still
|
|
51
|
+
* describes the prefix it sits on, or `undefined` when none does.
|
|
52
|
+
*
|
|
53
|
+
* Request contexts carry no compaction index, so staleness is read from the
|
|
54
|
+
* rewrite markers themselves: a compaction/branch summary or pruned tool result
|
|
55
|
+
* (`prunedAt`) replaced text that every report made at or before the rewrite
|
|
56
|
+
* already counted. A summary's rewrite time is its `timestamp` (commit time);
|
|
57
|
+
* its `historyRewriteAt` may be predated before a natively replayed retained
|
|
58
|
+
* tail so that tail's bound thinking survives, but the tail's usage still
|
|
59
|
+
* counted the summarized prefix.
|
|
60
|
+
*/
|
|
61
|
+
export declare function findRequestUsageAnchor(messages: readonly Message[]): TranscriptUsageAnchor | undefined;
|
|
49
62
|
/** Options for {@link estimateTranscriptTokens}. */
|
|
50
63
|
export interface TranscriptTokenOptions {
|
|
51
64
|
/**
|
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 "@oh-my-pi/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,43 @@
|
|
|
1
|
+
import type { Context, Model } from "@oh-my-pi/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
|
+
* Output cap for a request, so prompt plus output stays inside the model's
|
|
7
|
+
* context window.
|
|
8
|
+
*
|
|
9
|
+
* Chat Completions-style providers (DeepSeek, OpenAI, vLLM, ...) reject a
|
|
10
|
+
* request whose prompt tokens plus `max_tokens` exceed the window. Every
|
|
11
|
+
* request asks for `model.maxTokens` of output by default, so without this a
|
|
12
|
+
* large model output cap (DeepSeek V4: ~384k of a ~1M window) makes every
|
|
13
|
+
* request fail once the prompt passes window minus output cap, long before
|
|
14
|
+
* compaction triggers, and side turns (`/btw`, recaps) have no overflow
|
|
15
|
+
* recovery at all.
|
|
16
|
+
*
|
|
17
|
+
* The prompt size is the provider's own report from the newest trustworthy
|
|
18
|
+
* assistant turn (see {@link findRequestUsageAnchor}) plus a local count of
|
|
19
|
+
* only the messages appended after it; the whole context is counted locally
|
|
20
|
+
* only when no turn can anchor (fresh or freshly rewritten context).
|
|
21
|
+
*
|
|
22
|
+
* Returns `maxTokens` unchanged when the requested cap already fits, the
|
|
23
|
+
* model declares no window, the host ends generation at the window itself
|
|
24
|
+
* instead of rejecting the request (`stops-output-at-context-window`, e.g.
|
|
25
|
+
* Claude 4.5+ on the Claude API), or nothing would be requested (including an
|
|
26
|
+
* OpenRouter-hosted model with no caller cap: the transport omits the catalog
|
|
27
|
+
* default there so each upstream self-caps, and a fitted value would turn into
|
|
28
|
+
* an explicit cap that filters upstreams). Otherwise returns
|
|
29
|
+
* the remaining room (never below {@link MIN_FITTED_OUTPUT_TOKENS}); a
|
|
30
|
+
* prompt that fills the whole window still overflows and is left to the
|
|
31
|
+
* caller's compaction. Near a full window the floor means a turn can stop on
|
|
32
|
+
* `length` instead of failing with a 400.
|
|
33
|
+
*
|
|
34
|
+
* Lives here, not next to the default in pi-ai's `mapOptionsForApi`, because
|
|
35
|
+
* pi-ai has no tokenizer; callers apply it in their `streamFn` (coding-agent
|
|
36
|
+
* does so in its shared settings-aware wrapper).
|
|
37
|
+
*
|
|
38
|
+
* Not fixed: Anthropic budget-thinking transports raise `max_tokens` back to
|
|
39
|
+
* at least the thinking budget plus a fallback buffer downstream
|
|
40
|
+
* (`ensureMaxTokensForThinking`), so a fitted cap below that is overridden
|
|
41
|
+
* and the request can still exceed the window as before.
|
|
42
|
+
*/
|
|
43
|
+
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 "@oh-my-pi/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
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ToolResultMessage } from "@oh-my-pi/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. Returns undefined
|
|
24
|
+
* when nothing remains.
|
|
25
|
+
*/
|
|
26
|
+
export declare function joinAdditionalContext(values: Iterable<string | undefined>): string | undefined;
|
|
27
|
+
/**
|
|
28
|
+
* Build the developer message that carries passive tool context to the next
|
|
29
|
+
* provider request. Emitted after the tool results it belongs to.
|
|
30
|
+
*/
|
|
31
|
+
export declare function createAdditionalContextMessage(text: string): AgentMessage;
|
package/dist/types/types.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type { Dialect } from "@oh-my-pi/pi-ai/dialect";
|
|
|
3
3
|
import type { HarmonyAuditEvent } from "@oh-my-pi/pi-ai/utils/harmony-leak";
|
|
4
4
|
import type { AppendOnlyContextManager } from "./append-only-context.js";
|
|
5
5
|
import type { AgentRunCoverage, AgentRunSummary } from "./run-collector.js";
|
|
6
|
+
import type { SentToolDefinitions } from "./sent-tool-definitions.js";
|
|
6
7
|
import type { AgentTelemetryConfig } from "./telemetry.js";
|
|
7
8
|
/** Stream function - can return sync or Promise for async config lookup */
|
|
8
9
|
export type StreamFn = (...args: Parameters<typeof streamSimple>) => AssistantMessageEventStream | Promise<AssistantMessageEventStream>;
|
|
@@ -35,6 +36,12 @@ export interface AgentTurnEndContext {
|
|
|
35
36
|
message: AgentMessage;
|
|
36
37
|
/** Tool results produced by this turn, already paired with `message` in the live context. */
|
|
37
38
|
toolResults: ToolResultMessage[];
|
|
39
|
+
/**
|
|
40
|
+
* Passive model-visible messages appended after the tool results at this
|
|
41
|
+
* boundary. The agent loop always sends an array (possibly empty);
|
|
42
|
+
* absent is equivalent to empty for hosts that construct the context.
|
|
43
|
+
*/
|
|
44
|
+
additionalMessages?: AgentMessage[];
|
|
38
45
|
/** True when the current tool-loop batch is continuing without yielding to post-turn steering. */
|
|
39
46
|
willContinue: boolean;
|
|
40
47
|
}
|
|
@@ -117,8 +124,10 @@ export interface AgentLoopConfig extends SimpleStreamOptions {
|
|
|
117
124
|
model: Model;
|
|
118
125
|
/**
|
|
119
126
|
* When to interrupt tool execution for steering messages.
|
|
120
|
-
* - "immediate" =
|
|
121
|
-
*
|
|
127
|
+
* - "immediate" = cut interruptible waits short and raise the cooperative
|
|
128
|
+
* `steeringSignal` for other running tools (default)
|
|
129
|
+
* - "wait" = let non-interruptible tools finish undisturbed; interruptible
|
|
130
|
+
* waits are still cut short, since they have no work to complete
|
|
122
131
|
*/
|
|
123
132
|
interruptMode?: "immediate" | "wait";
|
|
124
133
|
/**
|
|
@@ -185,6 +194,8 @@ export interface AgentLoopConfig extends SimpleStreamOptions {
|
|
|
185
194
|
* and provider send.
|
|
186
195
|
*/
|
|
187
196
|
transformProviderContext?: (context: Context, model: Model) => Context | Promise<Context>;
|
|
197
|
+
/** Remembers sent tool definitions to fill {@link Context.inactiveTools}. */
|
|
198
|
+
sentToolDefinitions?: SentToolDefinitions;
|
|
188
199
|
/**
|
|
189
200
|
* Resolves the API key or resolver for the current model before each LLM call.
|
|
190
201
|
*
|
|
@@ -204,8 +215,9 @@ export interface AgentLoopConfig extends SimpleStreamOptions {
|
|
|
204
215
|
/**
|
|
205
216
|
* Peeks whether steering messages are queued, without consuming them.
|
|
206
217
|
*
|
|
207
|
-
* Polled while a tool batch runs (
|
|
208
|
-
* whether to abort in-flight and skip
|
|
218
|
+
* Polled while a tool batch runs (in "wait" mode, only when the batch holds an
|
|
219
|
+
* interruptible tool) to decide whether to abort in-flight and skip
|
|
220
|
+
* not-yet-started *interruptible* waits;
|
|
209
221
|
* every other already-emitted call still executes and the message injects
|
|
210
222
|
* at the batch boundary. The queue keeps
|
|
211
223
|
* owning its messages until the loop reaches the next injection boundary and
|
|
@@ -232,7 +244,8 @@ export interface AgentLoopConfig extends SimpleStreamOptions {
|
|
|
232
244
|
* Peeks whether IRC messages should interrupt an interruptible waiting tool.
|
|
233
245
|
*
|
|
234
246
|
* Uses the same delivery rules as steering: the poll is non-consuming, only
|
|
235
|
-
* runs for interruptible tools, and
|
|
247
|
+
* runs for interruptible tools, and cuts them short even when interruptMode
|
|
248
|
+
* is "wait".
|
|
236
249
|
* The host owns message injection at the next boundary.
|
|
237
250
|
*/
|
|
238
251
|
hasIrcInterrupts?: () => boolean | Promise<boolean>;
|
|
@@ -241,8 +254,8 @@ export interface AgentLoopConfig extends SimpleStreamOptions {
|
|
|
241
254
|
* process) is queued for aside injection at the next boundary.
|
|
242
255
|
*
|
|
243
256
|
* Same rules as {@link hasIrcInterrupts}: non-consuming, only cuts
|
|
244
|
-
* *interruptible* waits short,
|
|
245
|
-
* it a completion notice sits behind an hour-long `
|
|
257
|
+
* *interruptible* waits short, in either interruptMode. Without
|
|
258
|
+
* it a completion notice sits behind an hour-long `wait` that the agent
|
|
246
259
|
* would have abandoned had it seen the notice. Unlike a peer IRC it never
|
|
247
260
|
* raises {@link ToolCallContext.steeringSignal}: a queued completion must
|
|
248
261
|
* not push ordinary foreground work (auto-background bash/eval) into the
|
|
@@ -277,7 +290,11 @@ export interface AgentLoopConfig extends SimpleStreamOptions {
|
|
|
277
290
|
onBeforeYield?: () => Promise<void> | void;
|
|
278
291
|
/**
|
|
279
292
|
* Provides tool execution context, resolved per tool call.
|
|
280
|
-
* Use for late-bound UI or session state access.
|
|
293
|
+
* Use for late-bound UI or session state access. The loop passes the tool
|
|
294
|
+
* call's {@link ToolCallContext}; hosts that support passive tool context
|
|
295
|
+
* surface its `addAdditionalContext` sink as
|
|
296
|
+
* {@link AgentToolContext.addAdditionalContext}. The returned object is
|
|
297
|
+
* handed to the tool as-is.
|
|
281
298
|
*/
|
|
282
299
|
getToolContext?: (toolCall?: ToolCallContext) => AgentToolContext | undefined;
|
|
283
300
|
/**
|
|
@@ -351,6 +368,12 @@ export interface AgentLoopConfig extends SimpleStreamOptions {
|
|
|
351
368
|
* model's text output back into canonical `toolCall` blocks.
|
|
352
369
|
*/
|
|
353
370
|
dialect?: Dialect;
|
|
371
|
+
/**
|
|
372
|
+
* Per-call owned-dialect resolver, read once per LLM call with the model
|
|
373
|
+
* being requested. Authoritative when set: its return value (including
|
|
374
|
+
* `undefined` = native tool calling) replaces the static {@link dialect}.
|
|
375
|
+
*/
|
|
376
|
+
getDialect?: (model: Model) => Dialect | undefined;
|
|
354
377
|
/**
|
|
355
378
|
* When owned (in-band) tool calling is active and the model starts
|
|
356
379
|
* fabricating a tool result inside its own turn, control how the loop reacts:
|
|
@@ -528,6 +551,13 @@ export interface ToolCallContext {
|
|
|
528
551
|
* always safe (the message injects at the next batch boundary).
|
|
529
552
|
*/
|
|
530
553
|
steeringSignal?: AbortSignal;
|
|
554
|
+
/**
|
|
555
|
+
* Loop-owned sink for passive context reported while this call executes.
|
|
556
|
+
* Values join the call's context at the batch boundary and are injected
|
|
557
|
+
* after the batch's tool results, in assistant tool-call order, before the
|
|
558
|
+
* next provider request. Blank values are ignored.
|
|
559
|
+
*/
|
|
560
|
+
addAdditionalContext?: (context: string) => void;
|
|
531
561
|
}
|
|
532
562
|
/** A single tool-call content block emitted by an assistant message. */
|
|
533
563
|
export type AgentToolCall = Extract<AssistantMessage["content"][number], {
|
|
@@ -716,11 +746,19 @@ export interface SpeculativeToolExecutionConfig {
|
|
|
716
746
|
* written back to the tool-call block on the assistant message, and seen by
|
|
717
747
|
* history, scheduling, execution events, and `tool.execute` alike. It is
|
|
718
748
|
* ignored when `block` is true.
|
|
749
|
+
*
|
|
750
|
+
* Set `additionalContext` to attach passive model-visible context to this call.
|
|
751
|
+
* Non-empty values from a tool batch are injected in assistant tool-call order
|
|
752
|
+
* after every result settles and before the next provider request. It is
|
|
753
|
+
* dropped when the call is blocked or skipped, or when its final result is an
|
|
754
|
+
* error (including an approval denial raised by the tool's own gate). Within a
|
|
755
|
+
* call it follows any context the tool reported during execution.
|
|
719
756
|
*/
|
|
720
757
|
export interface BeforeToolCallResult {
|
|
721
758
|
block?: boolean;
|
|
722
759
|
reason?: string;
|
|
723
760
|
args?: Record<string, unknown>;
|
|
761
|
+
additionalContext?: string;
|
|
724
762
|
}
|
|
725
763
|
/**
|
|
726
764
|
* Partial override returned from `afterToolCall`.
|
|
@@ -867,6 +905,16 @@ export type ToolApproval = ToolApprovalDecision | ((args: unknown) => ToolApprov
|
|
|
867
905
|
* Apps can extend via declaration merging.
|
|
868
906
|
*/
|
|
869
907
|
export interface AgentToolContext {
|
|
908
|
+
/**
|
|
909
|
+
* Attach trusted, agent-authored instructions to the next provider request.
|
|
910
|
+
* The host emits them after tool results with developer/system priority where
|
|
911
|
+
* the selected transport supports it. Do not use this channel for raw tool
|
|
912
|
+
* output, retrieved documents, web content, or other untrusted data; return
|
|
913
|
+
* those through the ordinary tool result instead. Hosts populate it from
|
|
914
|
+
* {@link ToolCallContext.addAdditionalContext} (or their own collector for
|
|
915
|
+
* calls dispatched outside the loop); absent when the host has no sink.
|
|
916
|
+
*/
|
|
917
|
+
addAdditionalContext?(context: string): void;
|
|
870
918
|
/** Present only while the matching outer tool owns its finalized stream session. */
|
|
871
919
|
[SPECULATIVE_STREAM_SESSION]?: ToolSpeculationStreamSession;
|
|
872
920
|
}
|
|
@@ -903,6 +951,12 @@ export interface AgentTool<TParameters extends TSchema = TSchema, TDetails = any
|
|
|
903
951
|
loadMode?: ToolLoadMode;
|
|
904
952
|
/** Short one-line summary used for tool discovery indexes. */
|
|
905
953
|
summary?: string;
|
|
954
|
+
/**
|
|
955
|
+
* On-demand documentation topics (`topic → markdown`), readable as
|
|
956
|
+
* `xd://<tool>/<topic>`. Lets a tool keep large sub-surfaces out of its
|
|
957
|
+
* description and advertise only a one-line pointer per topic.
|
|
958
|
+
*/
|
|
959
|
+
docTopics?(): Readonly<Record<string, string>>;
|
|
906
960
|
/**
|
|
907
961
|
* Concurrency mode for tool scheduling when multiple calls are in one turn.
|
|
908
962
|
* - "shared": can run alongside other shared tools (default)
|
|
@@ -926,7 +980,7 @@ export interface AgentTool<TParameters extends TSchema = TSchema, TDetails = any
|
|
|
926
980
|
* cleanly (e.g. `job` poll), so the abort surfaces the tool's current
|
|
927
981
|
* snapshot rather than corrupting a side effect. Every other call runs to
|
|
928
982
|
* completion even when steering is queued; the message lands at the next
|
|
929
|
-
* batch boundary. Honored
|
|
983
|
+
* batch boundary. Honored in both `interruptMode`s.
|
|
930
984
|
*/
|
|
931
985
|
interruptible?: boolean | ((args: Partial<Static<TParameters>>) => boolean);
|
|
932
986
|
/**
|
package/package.json
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-agent-core",
|
|
4
|
-
"version": "18.
|
|
4
|
+
"version": "18.3.1",
|
|
5
5
|
"description": "General-purpose agent with transport abstraction, state management, and attachment support",
|
|
6
6
|
"homepage": "https://omp.sh",
|
|
7
|
-
"author":
|
|
7
|
+
"author": {
|
|
8
|
+
"name": "Stencil Labs, Inc.",
|
|
9
|
+
"url": "https://stencil.so"
|
|
10
|
+
},
|
|
8
11
|
"contributors": [
|
|
9
12
|
"Mario Zechner"
|
|
10
13
|
],
|
|
@@ -35,16 +38,16 @@
|
|
|
35
38
|
"fmt": "oxfmt --no-error-on-unmatched-pattern 'src/**/*.{ts,tsx}' '{test,bench,examples,scripts}/**/*.ts' '*.ts'"
|
|
36
39
|
},
|
|
37
40
|
"dependencies": {
|
|
38
|
-
"@oh-my-pi/pi-ai": "18.
|
|
39
|
-
"@oh-my-pi/pi-catalog": "18.
|
|
40
|
-
"@oh-my-pi/pi-natives": "18.
|
|
41
|
-
"@oh-my-pi/pi-utils": "18.
|
|
42
|
-
"@oh-my-pi/pi-wire": "18.
|
|
43
|
-
"@oh-my-pi/snapcompact": "18.
|
|
41
|
+
"@oh-my-pi/pi-ai": "18.3.1",
|
|
42
|
+
"@oh-my-pi/pi-catalog": "18.3.1",
|
|
43
|
+
"@oh-my-pi/pi-natives": "18.3.1",
|
|
44
|
+
"@oh-my-pi/pi-utils": "18.3.1",
|
|
45
|
+
"@oh-my-pi/pi-wire": "18.3.1",
|
|
46
|
+
"@oh-my-pi/snapcompact": "18.3.1",
|
|
44
47
|
"@opentelemetry/api": "^1.9.1"
|
|
45
48
|
},
|
|
46
49
|
"devDependencies": {
|
|
47
|
-
"@oh-my-pi/omptype": "18.
|
|
50
|
+
"@oh-my-pi/omptype": "18.3.1",
|
|
48
51
|
"@opentelemetry/context-async-hooks": "^2.9.0",
|
|
49
52
|
"@opentelemetry/sdk-trace-base": "^2.9.0",
|
|
50
53
|
"@types/bun": "^1.3.14"
|