@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 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 local OpenAI-compatible
43
- * servers are not undercut by a shorter `PI_STREAM_FIRST_EVENT_TIMEOUT_MS`
44
- * or the global default during prompt processing.
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 `undefined` when an explicit env knob disables the watchdog.
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.2",
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.2",
42
- "@oh-my-pi/pi-utils": "17.1.2",
43
- "@oh-my-pi/pi-wire": "17.1.2",
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
  },
@@ -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
- return advertisedTools.map(tool => {
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 ?? getOpenAIStreamFirstEventTimeoutMs(idleTimeoutMs);
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 ?? getOpenAIStreamFirstEventTimeoutMs(idleTimeoutMs);
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 local OpenAI-compatible
72
- * servers are not undercut by a shorter `PI_STREAM_FIRST_EVENT_TIMEOUT_MS`
73
- * or the global default during prompt processing.
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 `undefined` when an explicit env knob disables the watchdog.
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 undefined;
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
  }