@oh-my-pi/pi-ai 15.13.1 → 15.13.3
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 +52 -0
- package/dist/types/grammar/anthropic.d.ts +9 -0
- package/dist/types/grammar/catalog.d.ts +3 -0
- package/dist/types/grammar/coercion.d.ts +23 -0
- package/dist/types/grammar/deepseek.d.ts +14 -0
- package/dist/types/grammar/examples.d.ts +2 -0
- package/dist/types/grammar/factory.d.ts +3 -0
- package/dist/types/grammar/gemini.d.ts +16 -0
- package/dist/types/grammar/gemma.d.ts +14 -0
- package/dist/types/grammar/glm.d.ts +9 -0
- package/dist/types/grammar/harmony.d.ts +8 -0
- package/dist/types/grammar/hermes.d.ts +9 -0
- package/dist/types/grammar/history.d.ts +3 -0
- package/dist/types/grammar/index.d.ts +8 -0
- package/dist/types/grammar/inventory.d.ts +12 -0
- package/dist/types/grammar/kimi.d.ts +13 -0
- package/dist/types/grammar/owned-stream.d.ts +4 -0
- package/dist/types/grammar/pi.d.ts +9 -0
- package/dist/types/grammar/qwen3.d.ts +9 -0
- package/dist/types/grammar/rendering.d.ts +30 -0
- package/dist/types/grammar/thinking.d.ts +6 -0
- package/dist/types/grammar/types.d.ts +67 -0
- package/dist/types/grammar/xml.d.ts +9 -0
- package/dist/types/providers/cursor.d.ts +47 -1
- package/dist/types/registry/azure.d.ts +4 -0
- package/dist/types/registry/registry.d.ts +3 -0
- package/dist/types/types.d.ts +28 -0
- package/dist/types/utils/harmony-leak.d.ts +118 -0
- package/dist/types/utils/schema/index.d.ts +1 -0
- package/dist/types/utils/schema/typescript.d.ts +18 -0
- package/dist/types/utils/stream-markup-healing.d.ts +10 -15
- package/package.json +11 -3
- package/src/grammar/anthropic.md +31 -0
- package/src/grammar/anthropic.ts +521 -0
- package/src/grammar/catalog.ts +27 -0
- package/src/grammar/coercion.ts +136 -0
- package/src/grammar/deepseek.md +23 -0
- package/src/grammar/deepseek.ts +535 -0
- package/src/grammar/examples.ts +33 -0
- package/src/grammar/factory.ts +34 -0
- package/src/grammar/gemini.md +35 -0
- package/src/grammar/gemini.ts +440 -0
- package/src/grammar/gemma.md +23 -0
- package/src/grammar/gemma.ts +237 -0
- package/src/grammar/glm.md +32 -0
- package/src/grammar/glm.ts +384 -0
- package/src/grammar/harmony.md +30 -0
- package/src/grammar/harmony.ts +272 -0
- package/src/grammar/hermes.md +24 -0
- package/src/grammar/hermes.ts +171 -0
- package/src/grammar/history.ts +81 -0
- package/src/grammar/index.ts +8 -0
- package/src/grammar/inventory.ts +28 -0
- package/src/grammar/kimi.md +23 -0
- package/src/grammar/kimi.ts +198 -0
- package/src/grammar/owned-stream.ts +423 -0
- package/src/grammar/pi.md +49 -0
- package/src/grammar/pi.ts +585 -0
- package/src/grammar/prompt-template.md +12 -0
- package/src/grammar/qwen3.md +27 -0
- package/src/grammar/qwen3.ts +203 -0
- package/src/grammar/rendering.ts +313 -0
- package/src/grammar/thinking.ts +91 -0
- package/src/grammar/types.ts +55 -0
- package/src/grammar/xml.md +22 -0
- package/src/grammar/xml.ts +33 -0
- package/src/providers/cursor.ts +54 -11
- package/src/providers/openai-codex-responses.ts +170 -90
- package/src/registry/azure.ts +6 -0
- package/src/registry/registry.ts +2 -0
- package/src/types.ts +32 -0
- package/src/utils/harmony-leak.ts +456 -0
- package/src/utils/schema/index.ts +1 -0
- package/src/utils/schema/typescript.ts +198 -0
- package/src/utils/stream-markup-healing.ts +59 -496
- package/src/utils/validation.ts +98 -22
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,58 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [15.13.3] - 2026-06-15
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added the `gemini` in-band tool-call syntax with Python-style ```tool_code``` blocks and `default_api` invocations
|
|
10
|
+
- Added the `gemma` token-delimited in-band tool-call syntax using `<|tool_call>` and `<|tool_response>` blocks
|
|
11
|
+
- Added `gemini` and `gemma` to owned stream tool-result token detection so their tool responses are recognized
|
|
12
|
+
- Fixed truncated Gemini and Gemma tool blocks from being emitted as plain text during streaming
|
|
13
|
+
- Added the Azure OpenAI provider definition (`azure`) to the registry; `AZURE_OPENAI_API_KEY` resolves as its env-var API key via the catalog provider table.
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- Gemini tool-call examples now render without the `default_api.` namespace prefix, keeping `<example>` blocks concise. The live wire format still uses `default_api.` per the Gemini grammar.
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- Fixed duplicate tool call projections by deduplicating provider-native `toolCall` events against in-band `tool_code` calls and keeping only the first real channel
|
|
22
|
+
- Dropped nameless native `toolCall` events so they no longer appear as surfaced tool calls in owned-mode streams
|
|
23
|
+
- Fixed truncated Gemini and Gemma tool blocks from being emitted as plain text during streaming
|
|
24
|
+
- Fixed Gemini/Gemma in-band tool-call parsing around Python comments, raw/unicode string literals, and Gemma close-token text inside string values.
|
|
25
|
+
|
|
26
|
+
## [15.13.2] - 2026-06-15
|
|
27
|
+
|
|
28
|
+
### Added
|
|
29
|
+
|
|
30
|
+
- Added `jsonSchemaToTypeScript` to `@oh-my-pi/pi-ai/utils/schema` to render JSON Schema argument shapes as compact, human-readable TypeScript-style signatures
|
|
31
|
+
- Added the generic `ToolExample` type (`ToolCallExample`/`ToolCompareExample`/`ToolNoteExample`, parameterized over a tool's argument shape) and an `examples` property on the `Tool` interface for defining tool-call examples once as data.
|
|
32
|
+
- Added `renderToolExamples` (via `@oh-my-pi/pi-ai/grammar`) to render a tool's examples into an `<examples>` block in the model's native tool-call syntax, with an optional `_i` intent-field placeholder injected when intent tracing is active.
|
|
33
|
+
- Added per-grammar `renderToolCall` rendering of a single tool-call invocation (the inner element only, without the parallel-call block envelope), distinct from `renderAssistantToolCalls` which renders a complete block of one or more parallel calls.
|
|
34
|
+
- Added a `GrammarRenderOptions.example` flag to `renderToolCall`: when set, the invocation renders as the bare payload — Harmony emits just the JSON arguments, dropping the verbose `<|start|>…<|message|>…<|call|>` envelope — so `renderToolExamples` keeps `<examples>` blocks legible.
|
|
35
|
+
- Added an `abortOnFabrication` parameter to `wrapInbandToolStream` (default `true`): when `false`, a fabricated in-band tool-result continuation is discarded without aborting the provider request instead of cutting the turn short.
|
|
36
|
+
- Added `@oh-my-pi/pi-ai/utils/harmony-leak` export with helpers to detect, audit, and recover GPT-5 Harmony tool-call header leaks
|
|
37
|
+
- Added the `@oh-my-pi/pi-ai/grammar` public entrypoint for grammar factories, prompt/call rendering, in-band scanning, history encoding, and related typed utilities
|
|
38
|
+
- Added a unified in-band tool-call grammar engine with syntax-owned scanners, prompts, history rendering, tool-result rendering, and stream adaptation for GLM, Hermes/Qwen, Kimi, XML/Anthropic, DeepSeek, Harmony, and pi-native formats.
|
|
39
|
+
|
|
40
|
+
### Changed
|
|
41
|
+
|
|
42
|
+
- Changed Harmony in-band tool-call rendering to omit the `<|constrain|>json` marker before the payload in `commentary` channel calls
|
|
43
|
+
- Changed tool inventory rendering to present each tool’s `Parameters` section as a simplified TypeScript-style signature derived from its wire schema
|
|
44
|
+
- Added raw in-band tool-call block capture to parsed owned tool calls so debugging can inspect the exact model-emitted call syntax.
|
|
45
|
+
- Moved the canonical `ToolCallSyntax` union to `@oh-my-pi/pi-catalog/identity` and re-exported it from `@oh-my-pi/pi-ai/grammar` so the catalog can own the syntax vocabulary without an `@oh-my-pi/pi-ai` runtime import; all existing import paths are unchanged.
|
|
46
|
+
- Made tool-call argument validation more lenient for schema-directed scalar coercions, including object/array stringification and 0/1 boolean coercion.
|
|
47
|
+
- Changed `renderToolInventory` (the verbose system-prompt inventory and `/dump`) to render each tool as a `# Tool: <name>` markdown section instead of a `<tool name="…">…</tool>` wrapper.
|
|
48
|
+
|
|
49
|
+
### Fixed
|
|
50
|
+
|
|
51
|
+
- Fixed Harmony leak handling support by adding `recoverHarmonyToolCall` plus leak-detection workflows for contaminated assistant messages so recoverable tool-call arguments can be safely truncated and retried
|
|
52
|
+
- Fixed false-positive gating in Harmony leak heuristics using signal-based checks so unrelated text containing `to=functions...` is not treated as leaked tool-call markup
|
|
53
|
+
- Routed Kimi, DeepSeek DSML, and plain thinking markup healing through the shared in-band scanners so provider leak repair and owned tool calling parse the same wire formats.
|
|
54
|
+
- Fixed Cursor provider (`cursor-agent` API) streaming dropping large MCP tool-call arguments — most visibly the built-in `task` tool's `tasks` array on multi-subagent dispatches, which failed downstream schema validation with `tasks: Invalid input: expected array, received undefined`. Two upstream behaviors were fighting the stream handler in `packages/ai/src/providers/cursor.ts`: (1) `args_text_delta` carries the *cumulative* args text so far per `agent.proto`, but the handler concatenated each snapshot onto the buffer, garbling the JSON; (2) `tool_call_completed` carries an `McpArgs` map that omits oversized parameters entirely and downgrades unparsable values to their raw string fallback, but the handler unconditionally overwrote the streamed args with that map. The handler now strips the already-buffered prefix from each `args_text_delta` snapshot (falling back to append when the snapshot doesn't extend the buffer) and merges the decoded `McpArgs` map into the streamed args — preserving streamed keys the completion frame omits and the structured value when the completion frame downgrades to a string. ([#2615](https://github.com/can1357/oh-my-pi/issues/2615))
|
|
55
|
+
- Fixed Codex Responses stream mis-routing interleaved `function_call_arguments.delta` events when more than one tool call was open concurrently. The runtime tracked a singleton `currentItem`/`currentBlock`, so every delta — regardless of `item_id` — was appended to whichever item was most recently added, and `output_item.done` for the earlier call then overwrote a sibling's stored arguments (visible as `tasks: Invalid input: expected array, received undefined` on the `task` tool). Open items are now keyed by `item_id` with `output_index` fallback; deltas/done events route to the matching block, late deltas whose item already closed are dropped instead of corrupting a sibling, and `toolcall_*` stream events emit the right `contentIndex` per call ([#2619](https://github.com/can1357/oh-my-pi/issues/2619)).
|
|
56
|
+
|
|
5
57
|
## [15.13.1] - 2026-06-15
|
|
6
58
|
|
|
7
59
|
### Fixed
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Grammar, InbandScanEvent, InbandScanner, InbandScannerOptions } from "./types";
|
|
2
|
+
export declare class AnthropicInbandScanner implements InbandScanner {
|
|
3
|
+
#private;
|
|
4
|
+
constructor(options?: InbandScannerOptions);
|
|
5
|
+
feed(text: string): InbandScanEvent[];
|
|
6
|
+
flush(): InbandScanEvent[];
|
|
7
|
+
}
|
|
8
|
+
declare const grammar: Grammar;
|
|
9
|
+
export default grammar;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { InbandTool } from "./types";
|
|
2
|
+
export interface ToolArgShape {
|
|
3
|
+
stringArgs: Set<string>;
|
|
4
|
+
properties: Record<string, unknown>;
|
|
5
|
+
parameterOrder: string[];
|
|
6
|
+
}
|
|
7
|
+
export declare function buildArgShapes(tools?: readonly InbandTool[]): Map<string, ToolArgShape>;
|
|
8
|
+
export declare function buildStringArgsResolver(tools?: readonly InbandTool[]): (toolName: string) => ReadonlySet<string>;
|
|
9
|
+
export declare function resolveToolSchema(tool: InbandTool): Record<string, unknown>;
|
|
10
|
+
export declare function isStringOnlySchema(schema: unknown): boolean;
|
|
11
|
+
export declare function collectSchemaTypes(schema: unknown, out?: Set<string>, depth?: number): Set<string>;
|
|
12
|
+
export declare function jsonTypeOf(value: unknown): string;
|
|
13
|
+
export declare function decodeValue(raw: string): unknown;
|
|
14
|
+
export declare function coerceValue(raw: string, schema: unknown): unknown;
|
|
15
|
+
export declare function isArraySchema(schema: unknown): boolean;
|
|
16
|
+
export declare function isObjectSchema(schema: unknown): boolean;
|
|
17
|
+
export declare function getObjectProperties(schema: unknown): Record<string, unknown>;
|
|
18
|
+
export declare function getArrayItemSchema(schema: unknown): unknown;
|
|
19
|
+
export declare function mintToolCallId(): string;
|
|
20
|
+
export declare function partialSuffixOverlap(text: string, tag: string): number;
|
|
21
|
+
export declare function partialSuffixOverlapAny(text: string, tags: readonly string[]): number;
|
|
22
|
+
export declare function normalizeKimiFunctionName(rawId: string): string;
|
|
23
|
+
export declare function asRecord(value: unknown): Record<string, unknown>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Grammar, InbandScanEvent, InbandScanner, InbandScannerOptions } from "./types";
|
|
2
|
+
export declare const DEEPSEEK_TOOL_CALLS_BEGIN = "<\uFF5Ctool\u2581calls\u2581begin\uFF5C>";
|
|
3
|
+
export declare const DEEPSEEK_TOOL_CALLS_END = "<\uFF5Ctool\u2581calls\u2581end\uFF5C>";
|
|
4
|
+
export declare const DEEPSEEK_TOOL_CALL_BEGIN = "<\uFF5Ctool\u2581call\u2581begin\uFF5C>";
|
|
5
|
+
export declare const DEEPSEEK_TOOL_CALL_END = "<\uFF5Ctool\u2581call\u2581end\uFF5C>";
|
|
6
|
+
export declare const DEEPSEEK_TOOL_SEPARATOR = "<\uFF5Ctool\u2581sep\uFF5C>";
|
|
7
|
+
export declare class DeepSeekInbandScanner implements InbandScanner {
|
|
8
|
+
#private;
|
|
9
|
+
constructor(options?: InbandScannerOptions);
|
|
10
|
+
feed(text: string): InbandScanEvent[];
|
|
11
|
+
flush(): InbandScanEvent[];
|
|
12
|
+
}
|
|
13
|
+
declare const grammar: Grammar;
|
|
14
|
+
export default grammar;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { Grammar, InbandScanner, InbandScannerOptions, ToolCallSyntax } from "./types";
|
|
2
|
+
export declare function getInbandGrammar(syntax: ToolCallSyntax): Grammar;
|
|
3
|
+
export declare function createInbandScanner(syntax: ToolCallSyntax, options?: InbandScannerOptions): InbandScanner;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Grammar, InbandScanEvent, InbandScanner } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Scanner for the hosted-Gemini / Gemma 3 Pythonic tool-calling convention
|
|
4
|
+
* (see `docs/toolconv/gemini.md`). Tool calls arrive as a ```` ```tool_code ````
|
|
5
|
+
* fenced block whose body is one or more Python call expressions, e.g.
|
|
6
|
+
* `print(default_api.search(pattern="x", skip=40))`. Like the qwen3 scanner we
|
|
7
|
+
* buffer the whole block until its closing fence, then parse all calls at once
|
|
8
|
+
* (no incremental argument deltas — Python literals are not worth streaming).
|
|
9
|
+
*/
|
|
10
|
+
export declare class GeminiInbandScanner implements InbandScanner {
|
|
11
|
+
#private;
|
|
12
|
+
feed(text: string): InbandScanEvent[];
|
|
13
|
+
flush(): InbandScanEvent[];
|
|
14
|
+
}
|
|
15
|
+
declare const grammar: Grammar;
|
|
16
|
+
export default grammar;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Grammar, InbandScanEvent, InbandScanner } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Scanner for the Gemma 4 token-delimited tool-calling convention (see
|
|
4
|
+
* `docs/toolconv/gemma.md`). Each call is one `<|tool_call>call:NAME{…}<tool_call|>`
|
|
5
|
+
* block whose argument list is `key:value` pairs; string values are wrapped in
|
|
6
|
+
* the `<|"|>` token rather than ASCII quotes, so splitting must skip those spans.
|
|
7
|
+
*/
|
|
8
|
+
export declare class GemmaInbandScanner implements InbandScanner {
|
|
9
|
+
#private;
|
|
10
|
+
feed(text: string): InbandScanEvent[];
|
|
11
|
+
flush(): InbandScanEvent[];
|
|
12
|
+
}
|
|
13
|
+
declare const grammar: Grammar;
|
|
14
|
+
export default grammar;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Grammar, InbandScanEvent, InbandScanner, InbandScannerOptions } from "./types";
|
|
2
|
+
export declare class GLMInbandScanner implements InbandScanner {
|
|
3
|
+
#private;
|
|
4
|
+
constructor(options?: InbandScannerOptions);
|
|
5
|
+
feed(text: string): InbandScanEvent[];
|
|
6
|
+
flush(): InbandScanEvent[];
|
|
7
|
+
}
|
|
8
|
+
declare const grammar: Grammar;
|
|
9
|
+
export default grammar;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Grammar, InbandScanEvent, InbandScanner } from "./types";
|
|
2
|
+
export declare class HarmonyInbandScanner implements InbandScanner {
|
|
3
|
+
#private;
|
|
4
|
+
feed(text: string): InbandScanEvent[];
|
|
5
|
+
flush(): InbandScanEvent[];
|
|
6
|
+
}
|
|
7
|
+
declare const grammar: Grammar;
|
|
8
|
+
export default grammar;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Grammar, InbandScanEvent, InbandScanner, InbandScannerOptions } from "./types";
|
|
2
|
+
export declare class HermesInbandScanner implements InbandScanner {
|
|
3
|
+
#private;
|
|
4
|
+
constructor(options?: InbandScannerOptions);
|
|
5
|
+
feed(text: string): InbandScanEvent[];
|
|
6
|
+
flush(): InbandScanEvent[];
|
|
7
|
+
}
|
|
8
|
+
declare const grammar: Grammar;
|
|
9
|
+
export default grammar;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { InbandTool } from "./types";
|
|
2
|
+
/**
|
|
3
|
+
* Human-readable per-tool inventory: each tool renders as a `# Tool: <name>`
|
|
4
|
+
* section with its description, a simplified TypeScript-style parameter
|
|
5
|
+
* signature (derived from the wire JSON Schema), and examples in the model's
|
|
6
|
+
* native tool-call syntax. Shared by the verbose system-prompt inventory and
|
|
7
|
+
* `/dump` so both render the catalog the same way.
|
|
8
|
+
*
|
|
9
|
+
* `model` is a model id; the native example syntax is resolved from it
|
|
10
|
+
* (`preferredToolSyntax`, which falls back to XML for empty/unknown ids).
|
|
11
|
+
*/
|
|
12
|
+
export declare function renderToolInventory(tools: readonly InbandTool[], model: string): string;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Grammar, InbandScanEvent, InbandScanner } from "./types";
|
|
2
|
+
export declare const KIMI_SECTION_BEGIN = "<|tool_calls_section_begin|>";
|
|
3
|
+
export declare const KIMI_SECTION_END = "<|tool_calls_section_end|>";
|
|
4
|
+
export declare const KIMI_CALL_BEGIN = "<|tool_call_begin|>";
|
|
5
|
+
export declare const KIMI_CALL_END = "<|tool_call_end|>";
|
|
6
|
+
export declare const KIMI_ARG_BEGIN = "<|tool_call_argument_begin|>";
|
|
7
|
+
export declare class KimiInbandScanner implements InbandScanner {
|
|
8
|
+
#private;
|
|
9
|
+
feed(text: string): InbandScanEvent[];
|
|
10
|
+
flush(): InbandScanEvent[];
|
|
11
|
+
}
|
|
12
|
+
declare const grammar: Grammar;
|
|
13
|
+
export default grammar;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { AssistantMessage, AssistantMessageEventStream as AssistantMessageEventStreamType } from "../types";
|
|
2
|
+
import type { InbandTool, ToolCallSyntax } from "./types";
|
|
3
|
+
export declare function parseInbandToolMessage(message: AssistantMessage, syntax: ToolCallSyntax, tools: readonly InbandTool[]): AssistantMessage;
|
|
4
|
+
export declare function wrapInbandToolStream(inner: AssistantMessageEventStreamType, tools: readonly InbandTool[], syntax: ToolCallSyntax, onAbort?: () => void, abortOnFabrication?: boolean): AssistantMessageEventStreamType;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Grammar, InbandScanEvent, InbandScanner, InbandScannerOptions } from "./types";
|
|
2
|
+
export declare class PiNativeInbandScanner implements InbandScanner {
|
|
3
|
+
#private;
|
|
4
|
+
constructor(options?: InbandScannerOptions);
|
|
5
|
+
feed(text: string): InbandScanEvent[];
|
|
6
|
+
flush(): InbandScanEvent[];
|
|
7
|
+
}
|
|
8
|
+
declare const grammar: Grammar;
|
|
9
|
+
export default grammar;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Grammar, InbandScanEvent, InbandScanner, InbandScannerOptions } from "./types";
|
|
2
|
+
export declare class Qwen3InbandScanner implements InbandScanner {
|
|
3
|
+
#private;
|
|
4
|
+
constructor(options?: InbandScannerOptions);
|
|
5
|
+
feed(text: string): InbandScanEvent[];
|
|
6
|
+
flush(): InbandScanEvent[];
|
|
7
|
+
}
|
|
8
|
+
declare const grammar: Grammar;
|
|
9
|
+
export default grammar;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ToolCall } from "../types";
|
|
2
|
+
import type { GrammarRenderOptions, GrammarToolResult } from "./types";
|
|
3
|
+
export declare function renderGlmInvocation(call: ToolCall, options?: GrammarRenderOptions): string;
|
|
4
|
+
export declare function renderGlmToolCalls(calls: readonly ToolCall[], options?: GrammarRenderOptions): string;
|
|
5
|
+
export declare function renderGlmToolResults(results: readonly GrammarToolResult[]): string;
|
|
6
|
+
export declare function renderHermesInvocation(call: ToolCall, _options?: GrammarRenderOptions): string;
|
|
7
|
+
export declare function renderHermesToolCalls(calls: readonly ToolCall[], options?: GrammarRenderOptions): string;
|
|
8
|
+
export declare function renderKimiInvocation(call: ToolCall, _options?: GrammarRenderOptions): string;
|
|
9
|
+
export declare function renderKimiToolCalls(calls: readonly ToolCall[]): string;
|
|
10
|
+
export declare function renderKimiToolResults(results: readonly GrammarToolResult[]): string;
|
|
11
|
+
export declare function renderDeepSeekInvocation(call: ToolCall, _options?: GrammarRenderOptions): string;
|
|
12
|
+
export declare function renderDeepSeekToolCalls(calls: readonly ToolCall[], options?: GrammarRenderOptions): string;
|
|
13
|
+
export declare function renderDeepSeekToolResults(results: readonly GrammarToolResult[]): string;
|
|
14
|
+
export declare function renderHarmonyInvocation(call: ToolCall, options?: GrammarRenderOptions): string;
|
|
15
|
+
export declare function renderHarmonyToolCalls(calls: readonly ToolCall[], options?: GrammarRenderOptions): string;
|
|
16
|
+
export declare function renderHarmonyToolResults(results: readonly GrammarToolResult[]): string;
|
|
17
|
+
export declare function renderAnthropicInvocation(call: ToolCall, options?: GrammarRenderOptions): string;
|
|
18
|
+
export declare function renderAnthropicToolCalls(calls: readonly ToolCall[], options?: GrammarRenderOptions): string;
|
|
19
|
+
export declare function renderAnthropicToolResults(results: readonly GrammarToolResult[]): string;
|
|
20
|
+
export declare function renderXmlInvocation(call: ToolCall, options?: GrammarRenderOptions): string;
|
|
21
|
+
export declare function renderXmlToolCalls(calls: readonly ToolCall[], options?: GrammarRenderOptions): string;
|
|
22
|
+
export declare function renderPiNativeInvocation(call: ToolCall, options?: GrammarRenderOptions): string;
|
|
23
|
+
export declare function renderPiNativeToolCalls(calls: readonly ToolCall[], options?: GrammarRenderOptions): string;
|
|
24
|
+
export declare function renderToolResponseResults(results: readonly GrammarToolResult[]): string;
|
|
25
|
+
export declare function renderGeminiInvocation(call: ToolCall, options?: GrammarRenderOptions): string;
|
|
26
|
+
export declare function renderGeminiToolCalls(calls: readonly ToolCall[], options?: GrammarRenderOptions): string;
|
|
27
|
+
export declare function renderGeminiToolResults(results: readonly GrammarToolResult[]): string;
|
|
28
|
+
export declare function renderGemmaInvocation(call: ToolCall, _options?: GrammarRenderOptions): string;
|
|
29
|
+
export declare function renderGemmaToolCalls(calls: readonly ToolCall[], options?: GrammarRenderOptions): string;
|
|
30
|
+
export declare function renderGemmaToolResults(results: readonly GrammarToolResult[]): string;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { ToolCallSyntax } from "@oh-my-pi/pi-catalog/identity";
|
|
2
|
+
import type { Context, ToolCall } from "../types";
|
|
3
|
+
export type { ToolCallSyntax };
|
|
4
|
+
export type InbandScanEvent = {
|
|
5
|
+
type: "text";
|
|
6
|
+
text: string;
|
|
7
|
+
} | {
|
|
8
|
+
type: "thinkingStart";
|
|
9
|
+
} | {
|
|
10
|
+
type: "thinkingDelta";
|
|
11
|
+
delta: string;
|
|
12
|
+
} | {
|
|
13
|
+
type: "thinkingEnd";
|
|
14
|
+
thinking: string;
|
|
15
|
+
} | {
|
|
16
|
+
type: "toolStart";
|
|
17
|
+
id: string;
|
|
18
|
+
name: string;
|
|
19
|
+
} | {
|
|
20
|
+
type: "toolArgDelta";
|
|
21
|
+
id: string;
|
|
22
|
+
name: string;
|
|
23
|
+
key: string;
|
|
24
|
+
delta: string;
|
|
25
|
+
} | {
|
|
26
|
+
type: "toolEnd";
|
|
27
|
+
id: string;
|
|
28
|
+
name: string;
|
|
29
|
+
arguments: Record<string, unknown>;
|
|
30
|
+
rawBlock?: string;
|
|
31
|
+
};
|
|
32
|
+
export interface InbandScanner {
|
|
33
|
+
feed(text: string): InbandScanEvent[];
|
|
34
|
+
flush(): InbandScanEvent[];
|
|
35
|
+
}
|
|
36
|
+
export interface GrammarToolResult {
|
|
37
|
+
readonly id: string;
|
|
38
|
+
readonly name: string;
|
|
39
|
+
readonly index: number;
|
|
40
|
+
readonly text: string;
|
|
41
|
+
readonly isError: boolean;
|
|
42
|
+
}
|
|
43
|
+
export interface GrammarRenderOptions {
|
|
44
|
+
readonly tools?: readonly InbandTool[];
|
|
45
|
+
readonly example?: boolean;
|
|
46
|
+
}
|
|
47
|
+
export interface Grammar {
|
|
48
|
+
readonly syntax: ToolCallSyntax;
|
|
49
|
+
readonly prompt: string;
|
|
50
|
+
createScanner(options?: InbandScannerOptions): InbandScanner;
|
|
51
|
+
/** Render a single tool-call invocation — the inner element only, WITHOUT any parallel-call block envelope (e.g. anthropic's `<function_calls>` / kimi's section wrapper). */
|
|
52
|
+
renderToolCall(call: ToolCall, options?: GrammarRenderOptions): string;
|
|
53
|
+
/** Render a batch of (parallel) tool calls as one complete block, including whatever envelope the syntax wraps multiple calls in. */
|
|
54
|
+
renderAssistantToolCalls(calls: readonly ToolCall[], options?: GrammarRenderOptions): string;
|
|
55
|
+
renderToolResults(results: readonly GrammarToolResult[], options?: GrammarRenderOptions): string;
|
|
56
|
+
}
|
|
57
|
+
export interface InbandScannerOptions {
|
|
58
|
+
/** string-typed arg names for a tool → read verbatim. Ignored by JSON-carrying syntaxes. */
|
|
59
|
+
stringArgs?: (toolName: string) => ReadonlySet<string>;
|
|
60
|
+
/** Full tool schemas for schema-driven syntaxes such as GLM XML and pi-native. */
|
|
61
|
+
tools?: readonly InbandTool[];
|
|
62
|
+
/** XML only: parse pipe-wrapped DeepSeek DSML tags vs plain Anthropic invoke/parameter tags. */
|
|
63
|
+
xmlTagset?: "anthropic" | "dsml";
|
|
64
|
+
/** Emit thinking markers as thinking events instead of visible text when the syntax defines them. */
|
|
65
|
+
parseThinking?: boolean;
|
|
66
|
+
}
|
|
67
|
+
export type InbandTool = NonNullable<Context["tools"]>[number];
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Grammar, InbandScanEvent, InbandScanner, InbandScannerOptions } from "./types";
|
|
2
|
+
export declare class XmlInbandScanner implements InbandScanner {
|
|
3
|
+
#private;
|
|
4
|
+
constructor(options?: InbandScannerOptions);
|
|
5
|
+
feed(text: string): InbandScanEvent[];
|
|
6
|
+
flush(): InbandScanEvent[];
|
|
7
|
+
}
|
|
8
|
+
declare const grammar: Grammar;
|
|
9
|
+
export default grammar;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type JsonValue } from "@bufbuild/protobuf";
|
|
2
|
-
import type { CursorExecHandlerResult, CursorExecHandlers, CursorToolResultHandler, Message, StreamFunction, StreamOptions, ToolResultMessage } from "../types";
|
|
2
|
+
import type { AssistantMessage, CursorExecHandlerResult, CursorExecHandlers, CursorToolResultHandler, Message, StreamFunction, StreamOptions, TextContent, ThinkingContent, ToolCall, ToolResultMessage } from "../types";
|
|
3
|
+
import { AssistantMessageEventStream } from "../utils/event-stream";
|
|
3
4
|
export declare const CURSOR_API_URL = "https://api2.cursor.sh";
|
|
4
5
|
export declare const CURSOR_CLIENT_VERSION = "cli-2026.01.09-231024f";
|
|
5
6
|
export interface CursorOptions extends StreamOptions {
|
|
@@ -9,11 +10,56 @@ export interface CursorOptions extends StreamOptions {
|
|
|
9
10
|
onToolResult?: CursorToolResultHandler;
|
|
10
11
|
}
|
|
11
12
|
export declare const streamCursor: StreamFunction<"cursor-agent">;
|
|
13
|
+
export type ToolCallState = ToolCall & {
|
|
14
|
+
index: number;
|
|
15
|
+
partialJson?: string;
|
|
16
|
+
kind: "mcp" | "todo";
|
|
17
|
+
};
|
|
18
|
+
export interface BlockState {
|
|
19
|
+
currentTextBlock: (TextContent & {
|
|
20
|
+
index: number;
|
|
21
|
+
}) | null;
|
|
22
|
+
currentThinkingBlock: (ThinkingContent & {
|
|
23
|
+
index: number;
|
|
24
|
+
}) | null;
|
|
25
|
+
currentToolCall: ToolCallState | null;
|
|
26
|
+
firstTokenTime: number | undefined;
|
|
27
|
+
setTextBlock: (b: (TextContent & {
|
|
28
|
+
index: number;
|
|
29
|
+
}) | null) => void;
|
|
30
|
+
setThinkingBlock: (b: (ThinkingContent & {
|
|
31
|
+
index: number;
|
|
32
|
+
}) | null) => void;
|
|
33
|
+
setToolCall: (t: ToolCallState | null) => void;
|
|
34
|
+
setFirstTokenTime: () => void;
|
|
35
|
+
}
|
|
36
|
+
export interface UsageState {
|
|
37
|
+
sawTokenDelta: boolean;
|
|
38
|
+
}
|
|
12
39
|
/** Exported for tests: verifies handler is invoked with correct `this` when passed as bound. */
|
|
13
40
|
export declare function resolveExecHandler<TArgs, TResult>(args: TArgs, handler: ((args: TArgs) => Promise<CursorExecHandlerResult<TResult>>) | undefined, onToolResult: CursorToolResultHandler | undefined, buildFromToolResult: (toolResult: ToolResultMessage) => TResult, buildRejected: (reason: string) => TResult, buildError: (error: string) => TResult): Promise<{
|
|
14
41
|
execResult: TResult;
|
|
15
42
|
toolResult?: ToolResultMessage;
|
|
16
43
|
}>;
|
|
44
|
+
/**
|
|
45
|
+
* Merge the decoded completion-frame `McpArgs` map into the args assembled
|
|
46
|
+
* from streamed `args_text_delta` snapshots.
|
|
47
|
+
*
|
|
48
|
+
* The completion frame is authoritative for the scalars it carries — but it
|
|
49
|
+
* can omit oversized parameters entirely and can downgrade a structured value
|
|
50
|
+
* to its raw string fallback when `decodeMcpArgValue` cannot parse it as
|
|
51
|
+
* JSON. Overwriting the streamed args wholesale therefore loses data (e.g.
|
|
52
|
+
* the task tool's `tasks` array on multi-subagent dispatches, issue #2615).
|
|
53
|
+
*
|
|
54
|
+
* Rules per key:
|
|
55
|
+
* - completion key absent → keep the streamed value.
|
|
56
|
+
* - completion is a string while the streamed value is structured (object or
|
|
57
|
+
* array) → keep the streamed value (the completion frame downgraded it).
|
|
58
|
+
* - otherwise → completion wins.
|
|
59
|
+
*/
|
|
60
|
+
export declare function mergeCursorMcpToolCallArgs(streamed: Record<string, unknown> | undefined, completion: Record<string, unknown> | undefined): Record<string, unknown>;
|
|
61
|
+
/** Exported for tests: drives one Cursor interaction update through the streaming state machine. */
|
|
62
|
+
export declare function processInteractionUpdate(update: any, output: AssistantMessage, stream: AssistantMessageEventStream, state: BlockState, usageState: UsageState): void;
|
|
17
63
|
/**
|
|
18
64
|
* Build `ConversationStateStructure.rootPromptMessagesJson` blob IDs for the
|
|
19
65
|
* system prompt plus prior conversation history, as JSON blobs matching
|
|
@@ -25,6 +25,9 @@ declare const ALL: ({
|
|
|
25
25
|
readonly refreshToken: (credentials: import("./oauth").OAuthCredentials) => Promise<import("./oauth").OAuthCredentials>;
|
|
26
26
|
readonly callbackPort: 54545;
|
|
27
27
|
readonly pasteCodeFlow: true;
|
|
28
|
+
} | {
|
|
29
|
+
readonly id: "azure";
|
|
30
|
+
readonly name: "Azure OpenAI";
|
|
28
31
|
} | {
|
|
29
32
|
readonly id: "cerebras";
|
|
30
33
|
readonly name: "Cerebras";
|
package/dist/types/types.d.ts
CHANGED
|
@@ -344,6 +344,11 @@ export interface ToolCall {
|
|
|
344
344
|
arguments: Record<string, any>;
|
|
345
345
|
thoughtSignature?: string;
|
|
346
346
|
intent?: string;
|
|
347
|
+
/**
|
|
348
|
+
* Verbatim in-band syntax block that produced this synthetic `ptc_*` call.
|
|
349
|
+
* Present only for owned prompt/tool-call formats; provider-native calls omit it.
|
|
350
|
+
*/
|
|
351
|
+
rawBlock?: string;
|
|
347
352
|
/**
|
|
348
353
|
* Original wire-level name when the tool was invoked via OpenAI's custom-tool
|
|
349
354
|
* mechanism (e.g., `apply_patch`). Set by `openai-responses` on receive so
|
|
@@ -483,6 +488,20 @@ export type TSchema = ZodType | TJsonSchema;
|
|
|
483
488
|
export type Static<S> = S extends ZodType ? z.infer<S> : S extends {
|
|
484
489
|
static: infer T;
|
|
485
490
|
} ? T : unknown;
|
|
491
|
+
export interface ToolCallExample<TArgs = Record<string, unknown>> {
|
|
492
|
+
caption?: string;
|
|
493
|
+
call: TArgs;
|
|
494
|
+
}
|
|
495
|
+
export interface ToolCompareExample<TArgs = Record<string, unknown>> {
|
|
496
|
+
caption?: string;
|
|
497
|
+
bad: TArgs;
|
|
498
|
+
good: TArgs;
|
|
499
|
+
}
|
|
500
|
+
export interface ToolNoteExample {
|
|
501
|
+
caption: string;
|
|
502
|
+
note?: string;
|
|
503
|
+
}
|
|
504
|
+
export type ToolExample<TArgs = Record<string, unknown>> = ToolCallExample<TArgs> | ToolCompareExample<TArgs> | ToolNoteExample;
|
|
486
505
|
export interface Tool<TParameters extends TSchema = TSchema> {
|
|
487
506
|
name: string;
|
|
488
507
|
description: string;
|
|
@@ -509,6 +528,15 @@ export interface Tool<TParameters extends TSchema = TSchema> {
|
|
|
509
528
|
* calls route correctly. Absent for regular JSON function tools.
|
|
510
529
|
*/
|
|
511
530
|
customWireName?: string;
|
|
531
|
+
/**
|
|
532
|
+
* Illustrative calls/notes; the AI layer renders them into an `<examples>`
|
|
533
|
+
* block in the model's native tool-call syntax and appends to the wire
|
|
534
|
+
* description. Author `call`/`bad`/`good` as plain argument objects WITHOUT
|
|
535
|
+
* `_i` — when intent tracing injects `_i` into the schema, the renderer adds
|
|
536
|
+
* a placeholder `_i` automatically. Type each tool's `examples` against its
|
|
537
|
+
* own schema (e.g. `readonly ToolExample<z.input<typeof schema>>[]`).
|
|
538
|
+
*/
|
|
539
|
+
examples?: readonly ToolExample[];
|
|
512
540
|
}
|
|
513
541
|
export interface Context {
|
|
514
542
|
systemPrompt?: string[];
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GPT-5 Harmony-header leakage detection and recovery.
|
|
3
|
+
*
|
|
4
|
+
* Background and policy: see `docs/ERRATA-GPT5-HARMONY.md`. This module
|
|
5
|
+
* implements §3 of that document: detection by signal fusion, plus a
|
|
6
|
+
* truncate-and-resume primitive for the `edit` tool when its input is in
|
|
7
|
+
* hashline DSL form. Other tools and surfaces fall through to
|
|
8
|
+
* abort-and-retry handled by the agent loop.
|
|
9
|
+
*/
|
|
10
|
+
import type { AssistantMessage, Model, ToolCall } from "../types";
|
|
11
|
+
declare const SIGNAL_ORDER: readonly ["M", "C", "G", "S", "B", "R", "T"];
|
|
12
|
+
export type HarmonySignalClass = "H" | (typeof SIGNAL_ORDER)[number];
|
|
13
|
+
export type HarmonySurface = "assistant_text" | "assistant_thinking" | "tool_arg";
|
|
14
|
+
export interface HarmonySignal {
|
|
15
|
+
classes: HarmonySignalClass[];
|
|
16
|
+
start: number;
|
|
17
|
+
end: number;
|
|
18
|
+
text: string;
|
|
19
|
+
}
|
|
20
|
+
export interface HarmonyDetection {
|
|
21
|
+
surface: HarmonySurface;
|
|
22
|
+
contentIndex?: number;
|
|
23
|
+
toolName?: string;
|
|
24
|
+
toolCallId?: string;
|
|
25
|
+
signals: HarmonySignal[];
|
|
26
|
+
}
|
|
27
|
+
export interface HarmonyAuditEvent {
|
|
28
|
+
action: "truncate_resume" | "abort_retry" | "escalated";
|
|
29
|
+
surface: HarmonySurface;
|
|
30
|
+
signal: string;
|
|
31
|
+
retryN: number;
|
|
32
|
+
model: string;
|
|
33
|
+
provider: string;
|
|
34
|
+
toolName?: string;
|
|
35
|
+
removedLen: number;
|
|
36
|
+
removedSha8: string;
|
|
37
|
+
removedPreview: string;
|
|
38
|
+
removedBlob?: string;
|
|
39
|
+
}
|
|
40
|
+
export interface HarmonyRecoveredToolCall {
|
|
41
|
+
message: AssistantMessage;
|
|
42
|
+
removed: string;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Whether to run leak detection on responses from this model. We default-on
|
|
46
|
+
* for every openai-codex model rather than enumerating ids, so a future
|
|
47
|
+
* gpt-5.6 (or whatever) doesn't silently bypass the mitigation. Detection
|
|
48
|
+
* itself is cheap; the cost of missing a leak on a new model is not.
|
|
49
|
+
*/
|
|
50
|
+
export declare function isHarmonyLeakMitigationTarget(model: Model): boolean;
|
|
51
|
+
export declare function signalListLabel(signals: readonly HarmonySignal[]): string;
|
|
52
|
+
/**
|
|
53
|
+
* Detect harmony-protocol leakage in `text`. Returns undefined if clean.
|
|
54
|
+
*
|
|
55
|
+
* Trip rule: `H` alone, or `M` paired with at least one co-signal
|
|
56
|
+
* (`C`/`G`/`S`/`B`/`R`/`T`). Bare `M` does not trip — this document, its
|
|
57
|
+
* tests, and bug reports legitimately carry the marker.
|
|
58
|
+
*
|
|
59
|
+
* The `tool_arg` surface is held to a stricter rule. A tool argument is
|
|
60
|
+
* arbitrary file/data content that can legitimately carry the marker, a
|
|
61
|
+
* channel word, harmony control tokens, or a non-Latin script run (editing
|
|
62
|
+
* these very fixtures does exactly that). The only robust leak signal there
|
|
63
|
+
* is content trailing the structurally-valid parse, so a `tool_arg` detection
|
|
64
|
+
* additionally requires the `T` co-signal. Absent a `parsedEnd` boundary `T`
|
|
65
|
+
* is never set, so `tool_arg` scanning stays inert and a legitimate codex tool
|
|
66
|
+
* call is never hard-aborted. `assistant_text`/`assistant_thinking` keep the
|
|
67
|
+
* base rule.
|
|
68
|
+
*
|
|
69
|
+
* `parsedEnd`, when supplied, marks the byte at which a structurally valid
|
|
70
|
+
* tool-argument parse ends; markers at or past it set the `T` co-signal.
|
|
71
|
+
* `contentIndex`/`toolName`/`toolCallId` flow through to the returned
|
|
72
|
+
* detection for downstream auditing.
|
|
73
|
+
*/
|
|
74
|
+
export declare function detectHarmonyLeak(text: string, surface: HarmonySurface, options?: {
|
|
75
|
+
parsedEnd?: number;
|
|
76
|
+
contentIndex?: number;
|
|
77
|
+
toolName?: string;
|
|
78
|
+
toolCallId?: string;
|
|
79
|
+
}): HarmonyDetection | undefined;
|
|
80
|
+
/**
|
|
81
|
+
* Scan an assistant message's content blocks; return the first detection.
|
|
82
|
+
*
|
|
83
|
+
* `toolArgParseEnd`, when supplied, resolves the byte offset at which a tool
|
|
84
|
+
* call's structurally-valid argument parse ends (the `T` co-signal in
|
|
85
|
+
* {@link detectHarmonyLeak}). Callers that can parse a tool's argument DSL pass
|
|
86
|
+
* it to enable `tool_arg` leak detection; omitting it keeps that surface inert
|
|
87
|
+
* — the safe default the agent loop relies on, since it cannot bound a streamed
|
|
88
|
+
* tool DSL and must never hard-abort a legitimate tool call.
|
|
89
|
+
*/
|
|
90
|
+
export declare function detectHarmonyLeakInAssistantMessage(message: AssistantMessage, toolArgParseEnd?: (toolCall: ToolCall) => number | undefined): HarmonyDetection | undefined;
|
|
91
|
+
/**
|
|
92
|
+
* Truncate a contaminated tool call at the start of the contaminated line and
|
|
93
|
+
* append the tool's recovery sentinel. Returns a recovered AssistantMessage
|
|
94
|
+
* (containing only the cleaned tool call), a synthetic continuation user
|
|
95
|
+
* message asking the model to re-issue the rest, and the removed substring
|
|
96
|
+
* for auditing. Returns undefined when the tool is not recovery-eligible or
|
|
97
|
+
* the truncation would leave nothing meaningful to dispatch.
|
|
98
|
+
*
|
|
99
|
+
* `providerPayload` is dropped from the recovered message: for Codex the
|
|
100
|
+
* encrypted reasoning blob is opaque/signed and we cannot validate that it is
|
|
101
|
+
* uncontaminated. The model re-reasons on the next turn.
|
|
102
|
+
*/
|
|
103
|
+
export declare function recoverHarmonyToolCall(message: AssistantMessage, detection: HarmonyDetection): HarmonyRecoveredToolCall | undefined;
|
|
104
|
+
/**
|
|
105
|
+
* Return the contaminated substring from `message` for audit purposes when
|
|
106
|
+
* recovery is not applicable (abort path). Walks from the first detected
|
|
107
|
+
* signal to end-of-content within the relevant block. Returns "" if the
|
|
108
|
+
* detection cannot be resolved against the message.
|
|
109
|
+
*/
|
|
110
|
+
export declare function extractHarmonyRemoved(message: AssistantMessage, detection: HarmonyDetection): string;
|
|
111
|
+
export declare function createHarmonyAuditEvent(params: {
|
|
112
|
+
action: HarmonyAuditEvent["action"];
|
|
113
|
+
detection: HarmonyDetection;
|
|
114
|
+
model: Model;
|
|
115
|
+
retryN: number;
|
|
116
|
+
removed: string;
|
|
117
|
+
}): HarmonyAuditEvent;
|
|
118
|
+
export {};
|