@oh-my-pi/pi-ai 17.1.2 → 17.1.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 +7 -0
- package/dist/types/providers/cursor.d.ts +2 -1
- package/dist/types/utils/idle-iterator.d.ts +6 -4
- package/package.json +4 -4
- package/src/providers/cursor.ts +11 -2
- package/src/providers/openai-completions.ts +2 -1
- package/src/providers/openai-responses.ts +2 -1
- package/src/utils/idle-iterator.ts +8 -6
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [17.1.3] - 2026-07-24
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Fixed Cursor sessions exposing `ast_edit` (and other staged-preview `xd://` devices) without a reachable resolver: the built-in `write` tool — which carries the `xd://resolve` / `xd://reject` transport that finalizes a staged preview — was filtered out of Cursor's forwarded catalog, so previews could never be resolved and the session aborted after three forced `write` turns. `write` is now re-included in the forwarded catalog whenever pi-agent devices are advertised ([#6536](https://github.com/can1357/oh-my-pi/issues/6536)).
|
|
10
|
+
- Fixed OpenAI Responses and chat-completions streams honoring per-model first-event watchdog policy, allowing local llama.cpp-style backends to process arbitrarily large prompts without a premature client cancellation ([#6524](https://github.com/can1357/oh-my-pi/issues/6524)).
|
|
11
|
+
|
|
5
12
|
## [17.1.2] - 2026-07-24
|
|
6
13
|
|
|
7
14
|
### Added
|
|
@@ -2,7 +2,7 @@ import http2 from "node:http2";
|
|
|
2
2
|
import { type JsonValue } from "@bufbuild/protobuf";
|
|
3
3
|
import type { McpToolDefinition } from "@oh-my-pi/pi-catalog/discovery/cursor-gen/agent_pb";
|
|
4
4
|
import { type AgentServerMessage, type ConversationStateStructure } from "@oh-my-pi/pi-catalog/discovery/cursor-gen/agent_pb";
|
|
5
|
-
import type { AssistantMessage, CursorExecHandlerResult, CursorExecHandlers, CursorToolResultHandler, Message, StreamFunction, StreamOptions, TextContent, ThinkingContent, ToolCall, ToolResultMessage } from "../types.js";
|
|
5
|
+
import type { AssistantMessage, CursorExecHandlerResult, CursorExecHandlers, CursorToolResultHandler, Message, StreamFunction, StreamOptions, TextContent, ThinkingContent, Tool, ToolCall, ToolResultMessage } from "../types.js";
|
|
6
6
|
import { kCursorExecResolved, kStreamingBlockIndex, kStreamingBlockKind, kStreamingLastParseLen, kStreamingPartialJson } from "../utils/block-symbols.js";
|
|
7
7
|
import { AssistantMessageEventStream } from "../utils/event-stream.js";
|
|
8
8
|
export declare const CURSOR_API_URL = "https://api2.cursor.sh";
|
|
@@ -119,6 +119,7 @@ export declare function mergeCursorMcpToolCallArgs(streamed: Record<string, unkn
|
|
|
119
119
|
export declare function synthesizeCursorExecToolCall(output: AssistantMessage, stream: AssistantMessageEventStream, state: BlockState, toolCallId: string, toolName: string, args: Record<string, unknown>): void;
|
|
120
120
|
/** Exported for tests: drives one Cursor interaction update through the streaming state machine. */
|
|
121
121
|
export declare function processInteractionUpdate(update: any, output: AssistantMessage, stream: AssistantMessageEventStream, state: BlockState, usageState: UsageState): void;
|
|
122
|
+
export declare function buildMcpToolDefinitions(tools: Tool[] | undefined): McpToolDefinition[];
|
|
122
123
|
/**
|
|
123
124
|
* Build `ConversationStateStructure.rootPromptMessagesJson` blob IDs for the
|
|
124
125
|
* system prompt plus prior conversation history, as JSON blobs matching
|
|
@@ -39,11 +39,13 @@ export declare function getStreamFirstEventTimeoutMs(idleTimeoutMs?: number, fal
|
|
|
39
39
|
* `"0"` disable) wins outright. Otherwise the resolved idle (caller-supplied
|
|
40
40
|
* `idleTimeoutMs` — which itself already encompasses per-call
|
|
41
41
|
* `streamIdleTimeoutMs` or `PI_OPENAI_STREAM_IDLE_TIMEOUT_MS` resolved
|
|
42
|
-
* upstream) floors the first-event budget so slow
|
|
43
|
-
*
|
|
44
|
-
*
|
|
42
|
+
* upstream) floors the first-event budget so slow OpenAI-compatible servers
|
|
43
|
+
* are not undercut by a shorter `PI_STREAM_FIRST_EVENT_TIMEOUT_MS` or the
|
|
44
|
+
* global default during prompt processing. A zero per-provider fallback
|
|
45
|
+
* disables the first-event watchdog unless an environment override is set.
|
|
45
46
|
*
|
|
46
|
-
* Returns `
|
|
47
|
+
* Returns `0` when an explicit env knob or per-provider fallback disables the
|
|
48
|
+
* watchdog, preserving the sentinel through iterator timeout resolution.
|
|
47
49
|
*/
|
|
48
50
|
export declare function getOpenAIStreamFirstEventTimeoutMs(idleTimeoutMs?: number, fallbackMs?: number): number | undefined;
|
|
49
51
|
/**
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-ai",
|
|
4
|
-
"version": "17.1.
|
|
4
|
+
"version": "17.1.3",
|
|
5
5
|
"description": "Unified LLM API with automatic model discovery and provider configuration",
|
|
6
6
|
"homepage": "https://omp.sh",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -38,9 +38,9 @@
|
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@bufbuild/protobuf": "^2.12.1",
|
|
41
|
-
"@oh-my-pi/pi-catalog": "17.1.
|
|
42
|
-
"@oh-my-pi/pi-utils": "17.1.
|
|
43
|
-
"@oh-my-pi/pi-wire": "17.1.
|
|
41
|
+
"@oh-my-pi/pi-catalog": "17.1.3",
|
|
42
|
+
"@oh-my-pi/pi-utils": "17.1.3",
|
|
43
|
+
"@oh-my-pi/pi-wire": "17.1.3",
|
|
44
44
|
"arktype": "2.2.3",
|
|
45
45
|
"zod": "^4"
|
|
46
46
|
},
|
package/src/providers/cursor.ts
CHANGED
|
@@ -2401,7 +2401,7 @@ function readCursorBlob(blobStore: Map<string, Uint8Array>, blobId: Uint8Array):
|
|
|
2401
2401
|
|
|
2402
2402
|
const CURSOR_NATIVE_TOOL_NAMES = new Set(["bash", "read", "write", "delete", "ls", "grep", "lsp", "todo"]);
|
|
2403
2403
|
|
|
2404
|
-
function buildMcpToolDefinitions(tools: Tool[] | undefined): McpToolDefinition[] {
|
|
2404
|
+
export function buildMcpToolDefinitions(tools: Tool[] | undefined): McpToolDefinition[] {
|
|
2405
2405
|
if (!tools || tools.length === 0) {
|
|
2406
2406
|
return [];
|
|
2407
2407
|
}
|
|
@@ -2411,7 +2411,16 @@ function buildMcpToolDefinitions(tools: Tool[] | undefined): McpToolDefinition[]
|
|
|
2411
2411
|
return [];
|
|
2412
2412
|
}
|
|
2413
2413
|
|
|
2414
|
-
|
|
2414
|
+
// The `write` tool doubles as the xd:// transport: forwarded devices such as
|
|
2415
|
+
// `ast_edit` stage previews finalized only by writing a reason to xd://resolve
|
|
2416
|
+
// or xd://reject. Cursor's native catalog may expose no write path, so
|
|
2417
|
+
// re-include the built-in `write` (dropped as native above) whenever pi-agent
|
|
2418
|
+
// devices are advertised — otherwise a staged preview can never be resolved
|
|
2419
|
+
// and the SoftToolRequirement('write') escalation aborts the turn.
|
|
2420
|
+
const writeTool = tools.find(tool => tool.name === "write");
|
|
2421
|
+
const forwarded = writeTool ? [...advertisedTools, writeTool] : advertisedTools;
|
|
2422
|
+
|
|
2423
|
+
return forwarded.map(tool => {
|
|
2415
2424
|
const jsonSchema = toolWireSchema(tool);
|
|
2416
2425
|
const schemaValue: JsonValue =
|
|
2417
2426
|
jsonSchema && typeof jsonSchema === "object"
|
|
@@ -630,7 +630,8 @@ const streamOpenAICompletionsOnce = (
|
|
|
630
630
|
const idleTimeoutFallbackMs = model.compat.streamIdleTimeoutMs;
|
|
631
631
|
const idleTimeoutMs = options?.streamIdleTimeoutMs ?? getOpenAIStreamIdleTimeoutMs(idleTimeoutFallbackMs);
|
|
632
632
|
const firstEventTimeoutMs =
|
|
633
|
-
options?.streamFirstEventTimeoutMs ??
|
|
633
|
+
options?.streamFirstEventTimeoutMs ??
|
|
634
|
+
getOpenAIStreamFirstEventTimeoutMs(idleTimeoutMs, model.compat.streamFirstEventTimeoutMs);
|
|
634
635
|
const requestTimeoutMs =
|
|
635
636
|
firstEventTimeoutMs !== undefined && firstEventTimeoutMs > 0 ? firstEventTimeoutMs : undefined;
|
|
636
637
|
const { copilotPremiumRequests, baseUrl, headers, query, requestHeaders } = createRequestSetup(
|
|
@@ -495,7 +495,8 @@ const streamOpenAIResponsesOnce = (
|
|
|
495
495
|
const idleTimeoutMs =
|
|
496
496
|
options?.streamIdleTimeoutMs ?? getOpenAIStreamIdleTimeoutMs(model.compat.streamIdleTimeoutMs);
|
|
497
497
|
const firstEventTimeoutMs =
|
|
498
|
-
options?.streamFirstEventTimeoutMs ??
|
|
498
|
+
options?.streamFirstEventTimeoutMs ??
|
|
499
|
+
getOpenAIStreamFirstEventTimeoutMs(idleTimeoutMs, model.compat.streamFirstEventTimeoutMs);
|
|
499
500
|
const requestTimeoutMs =
|
|
500
501
|
firstEventTimeoutMs !== undefined && firstEventTimeoutMs > 0 ? firstEventTimeoutMs : undefined;
|
|
501
502
|
const requestUrl = `${resolvedBaseUrl}/responses`;
|
|
@@ -68,11 +68,13 @@ export function getStreamFirstEventTimeoutMs(
|
|
|
68
68
|
* `"0"` disable) wins outright. Otherwise the resolved idle (caller-supplied
|
|
69
69
|
* `idleTimeoutMs` — which itself already encompasses per-call
|
|
70
70
|
* `streamIdleTimeoutMs` or `PI_OPENAI_STREAM_IDLE_TIMEOUT_MS` resolved
|
|
71
|
-
* upstream) floors the first-event budget so slow
|
|
72
|
-
*
|
|
73
|
-
*
|
|
71
|
+
* upstream) floors the first-event budget so slow OpenAI-compatible servers
|
|
72
|
+
* are not undercut by a shorter `PI_STREAM_FIRST_EVENT_TIMEOUT_MS` or the
|
|
73
|
+
* global default during prompt processing. A zero per-provider fallback
|
|
74
|
+
* disables the first-event watchdog unless an environment override is set.
|
|
74
75
|
*
|
|
75
|
-
* Returns `
|
|
76
|
+
* Returns `0` when an explicit env knob or per-provider fallback disables the
|
|
77
|
+
* watchdog, preserving the sentinel through iterator timeout resolution.
|
|
76
78
|
*/
|
|
77
79
|
export function getOpenAIStreamFirstEventTimeoutMs(
|
|
78
80
|
idleTimeoutMs?: number,
|
|
@@ -80,10 +82,10 @@ export function getOpenAIStreamFirstEventTimeoutMs(
|
|
|
80
82
|
): number | undefined {
|
|
81
83
|
const openAIFirstEventRaw = $env.PI_OPENAI_STREAM_FIRST_EVENT_TIMEOUT_MS;
|
|
82
84
|
if (openAIFirstEventRaw !== undefined) {
|
|
83
|
-
return normalizeIdleTimeoutMs(openAIFirstEventRaw, fallbackMs);
|
|
85
|
+
return normalizeIdleTimeoutMs(openAIFirstEventRaw, fallbackMs) ?? 0;
|
|
84
86
|
}
|
|
85
87
|
const base = normalizeIdleTimeoutMs($env.PI_STREAM_FIRST_EVENT_TIMEOUT_MS, fallbackMs);
|
|
86
|
-
if (base === undefined) return
|
|
88
|
+
if (base === undefined || base <= 0) return 0;
|
|
87
89
|
if (idleTimeoutMs === undefined || idleTimeoutMs <= 0) return base;
|
|
88
90
|
return Math.max(base, idleTimeoutMs);
|
|
89
91
|
}
|