@oh-my-pi/pi-ai 17.1.7 → 17.2.0

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.
Files changed (62) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/dist/types/auth-broker/server.d.ts +5 -0
  3. package/dist/types/auth-broker/types.d.ts +4 -0
  4. package/dist/types/auth-storage.d.ts +26 -1
  5. package/dist/types/error/classes.d.ts +5 -1
  6. package/dist/types/error/rate-limit.d.ts +5 -4
  7. package/dist/types/providers/anthropic-client.d.ts +13 -1
  8. package/dist/types/providers/anthropic.d.ts +9 -1
  9. package/dist/types/providers/cursor/exec-modern.d.ts +98 -0
  10. package/dist/types/providers/cursor-pi-args.d.ts +99 -0
  11. package/dist/types/providers/cursor.d.ts +53 -3
  12. package/dist/types/providers/error-message.d.ts +2 -4
  13. package/dist/types/providers/openai-codex-responses.d.ts +10 -0
  14. package/dist/types/providers/openai-responses-wire.d.ts +8 -0
  15. package/dist/types/providers/openai-shared.d.ts +13 -1
  16. package/dist/types/registry/exa.d.ts +8 -0
  17. package/dist/types/registry/registry.d.ts +7 -1
  18. package/dist/types/registry/xai.d.ts +4 -1
  19. package/dist/types/stream.d.ts +2 -0
  20. package/dist/types/types.d.ts +101 -1
  21. package/dist/types/usage/umans.d.ts +2 -0
  22. package/dist/types/utils/block-symbols.d.ts +11 -0
  23. package/dist/types/utils/harmony-leak.d.ts +15 -7
  24. package/dist/types/utils/schema/normalize.d.ts +1 -0
  25. package/dist/types/utils.d.ts +8 -0
  26. package/package.json +4 -4
  27. package/src/auth-broker/client.ts +5 -1
  28. package/src/auth-broker/server.ts +103 -9
  29. package/src/auth-broker/snapshot-cache.ts +12 -3
  30. package/src/auth-broker/types.ts +6 -0
  31. package/src/auth-storage.ts +447 -28
  32. package/src/error/classes.ts +98 -3
  33. package/src/error/flags.ts +6 -1
  34. package/src/error/rate-limit.ts +18 -12
  35. package/src/providers/amazon-bedrock.ts +3 -3
  36. package/src/providers/anthropic-client.ts +24 -2
  37. package/src/providers/anthropic.ts +31 -6
  38. package/src/providers/cursor/exec-modern.ts +495 -0
  39. package/src/providers/cursor/proto/agent.proto +1007 -0
  40. package/src/providers/cursor-pi-args.ts +135 -0
  41. package/src/providers/cursor.ts +1138 -66
  42. package/src/providers/devin.ts +2 -1
  43. package/src/providers/error-message.ts +3 -1
  44. package/src/providers/google-vertex.ts +3 -5
  45. package/src/providers/openai-codex-responses.ts +109 -27
  46. package/src/providers/openai-completions.ts +19 -4
  47. package/src/providers/openai-responses-wire.ts +8 -0
  48. package/src/providers/openai-responses.ts +12 -1
  49. package/src/providers/openai-shared.ts +83 -14
  50. package/src/registry/api-key-validation.ts +18 -34
  51. package/src/registry/exa.ts +19 -0
  52. package/src/registry/novita.ts +6 -3
  53. package/src/registry/registry.ts +3 -1
  54. package/src/registry/xai.ts +17 -1
  55. package/src/stream.ts +3 -4
  56. package/src/types.ts +115 -0
  57. package/src/usage/umans.ts +192 -0
  58. package/src/utils/block-symbols.ts +12 -0
  59. package/src/utils/harmony-leak.ts +32 -0
  60. package/src/utils/idle-iterator.ts +2 -2
  61. package/src/utils/schema/normalize.ts +140 -38
  62. package/src/utils.ts +173 -3
package/CHANGELOG.md CHANGED
@@ -2,6 +2,70 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [17.2.0] - 2026-07-30
6
+
7
+ ### Added
8
+
9
+ - Added first-class parentTurnId support for nested Codex requests, allowing stream options and metadata helpers to accept and safely propagate the initiating turn's ID.
10
+ - Added preservation of the Codex `encrypted_function_args` plaintext-collaboration marker on replayed function calls, keeping server-marked plaintext tool arguments from being reinterpreted as encrypted on subsequent turns.
11
+ - Added interactive Exa API-key login through `/login exa`, opening the official API-key dashboard and saving pasted keys to the credential store ([#1798](https://github.com/can1357/oh-my-pi/issues/1798)).
12
+ - Cursor's modern exec wire protocol is now handled end to end. `agent.proto` models the frames current Cursor CLI builds emit — the seven Pi tools (`ExecServerMessage` 45-51), hooks, subagents, allowlist prechecks, MCP state, smart-mode classification, canvas diagnostics, conversation search, agent-store conflicts and git diff — and every one of them gets a typed answer. The Pi frames run their local equivalents (`read`/`bash`/`edit`/`write`/`grep`/`glob`); the rest answer with the error, not-found or empty-but-valid variant that is actually true of this client. Frames this build cannot name at all now raise `ExecClientControlMessage.throw` with `unknown_exec_variant`, and recognised frames with no truthful answer (`git_diff_request`, whose `GetDiffResponse` has no error variant) raise `exec_variant_unsupported`, instead of a silent ack that leaves the server waiting.
13
+ - `lsp` is advertised in the MCP tool catalog again. It was filtered out as a Cursor-native tool, but the native `diagnostics` frame covers one of roughly ten LSP actions, so the other nine were unreachable.
14
+ - Added `pinSessionOAuthAccount` support for backdating the sticky's last-use timestamp (`options.lastUsedAtMs`), so pins restored from persisted sessions keep the provider's warm-window semantics: resumes inside the prompt-cache TTL reuse the account, stale resumes still re-rank.
15
+
16
+ ### Changed
17
+
18
+ - Codex turn metadata now reserves the codex-rs `code_mode_tool_names` key, preventing caller-supplied client metadata extras from colliding with the core-owned field.
19
+ - Codex SSE requests to the official endpoint now use zstd-compressed bodies by default to match the official client, which can be disabled with PI_CODEX_ZSTD=0.
20
+ - API-key validation now preserves provider HTTP status and retry headers, allowing authentication, rate-limit, and server failures to retain their original error classifications.
21
+ - The Cursor Pi arg translation (`piReadPath`, `piJoinPath`, `piLsPath`, `piEscapeRegexLiteral`, `piLimit`) moved to `providers/cursor-pi-args`, re-exported from `providers/cursor/exec-modern` so existing imports are unaffected. The legacy pi shim shares these helpers and is compiled into the bundled virtual module registry, where a nested `providers/<dir>/<mod>` specifier is unresolvable under bunfs — and importing them from the exec module would drag the whole protobuf graph in for two string functions.
22
+
23
+ ### Fixed
24
+
25
+ - Fixed Novita login rejecting valid API keys belonging to Developer and Basic team members by validating against the chat completions endpoint instead of the billing balance endpoint.
26
+ - Fixed Cursor resource_exhausted errors being incorrectly classified as QUOTA_EXHAUSTED (which caused 30-minute credential blocks), mapping them to MODEL_CAPACITY_EXHAUSTED with a shorter backoff instead.
27
+ - Fixed a crash in Amazon Bedrock and Devin providers when Context.systemPrompt is passed as a bare string.
28
+ - Fixed aborted usage-limit recovery incorrectly blocking credentials or waiting on local usage fetches after the session had already changed.
29
+ - Fixed Codex WebSocket sessions echoing stale or missing turn states by capturing x-codex-turn-state refreshes from response metadata event headers.
30
+ - Fixed Harmony-dialect models (e.g., gpt-5.x, openai-codex) failing with invalid_prompt or "Request blocked" errors by escaping reserved control tokens in untrusted user and tool-result text.
31
+ - Fixed named forced tool_choice not being enforced on string-only OpenAI-compatible hosts (such as llama.cpp and LM Studio) by narrowing the advertised tools to the forced tool.
32
+ - Fixed direct Anthropic Claude Opus requests failing with HTTP 400 when the endpoint rejects strict tool fields.
33
+ - Fixed usage-based credential ranking for Anthropic accounts where a missing long-window (7-day) metric was incorrectly treated as a short-window metric.
34
+ - Fixed legacy Codex usage blocks continuing to gate all models after per-meter backoff was introduced, splitting the old shared scope into independent chat and spark blocks while maintaining backward compatibility with older clients and database schemas.
35
+ - Fixed Anthropic retry loops ignoring `maxRetryDelayMs` for long server `retry-after` hints, so over-budget delays surface immediately without losing response details or abort cleanup ([#7003](https://github.com/can1357/oh-my-pi/issues/7003)).
36
+ - Added interactive xAI API-key login with key validation through the xAI models endpoint.
37
+ - Fixed Google Gemini and Vertex tool declarations carrying numeric, boolean, object-valued, or mixed `enum` arrays that the Google Schema wire type cannot represent. Unsupported enums are omitted while valid string enums remain constrained.
38
+ - Umans usage provider: fetches `GET /v1/usage` and surfaces the rolling 5h request window + concurrency limits in `/usage`, `omp usage`, and the TUI status bar.
39
+ - Fixed ranged legacy Cursor reads reporting the returned window byte length as the full file size.
40
+ - Updated the Cursor client build advertisement to activate the modern exec-frame protocol handled by this provider.
41
+ - Fixed a windowed Cursor `read` reporting the window's line count as the file's. `total_lines` and `file_size` were derived from the payload, which is the whole file only for an unranged read — a 20-line page of a 100-line file answered `total_lines: 20`, which a paginating server reads as the end of the file. The count now comes from the read's own record of the file (`details.meta.truncation.totalLines`), falling back to counting the payload when the read returned the file whole.
42
+ - Fixed a `pi_grep` that hit the native backend's internal match ceiling answering as an unqualified success. `GrepTool` folds that cap into the flat `details.truncated` alone, setting neither `details.truncation` nor `perFileLimitReached` — the two fields the Pi result was built from — so the one truncation a caller can neither detect nor page around was the one it was never told about. The flat flag is now translated into a `PiTruncation`, and only when no specific cap already reported itself.
43
+ - Fixed a `pi_grep` frame's `context` and `limit` vanishing from the transcript. The bridge honors both by building a scoped `grep`, but neither is expressible in the model-facing schema, so the synthesized block recorded a plain pattern/path search — replaying a context-widened or capped search as an ordinary grep sitting beside output no ordinary grep produces. Both are now recorded on the block.
44
+ - Fixed a Cursor MCP resource listing shrinking to a count in the transcript. The full URI/name/mime catalog goes out on the wire, but the paired local result recorded `Listed N MCP resource(s)` — and rebuilt history is serialized from that result, so one reload later the model knew it had seen N resources and could name none of them. The paired result now lists what the answer carried.
45
+ - Fixed the `pi_read` range translation padding the slice it asks for. `piReadPath` composed a plain `:N+K` selector, which the local `read` tool expands by one leading and three trailing context line — so a frame naming offset 5/limit 20 received lines 4-27. Ranged Pi reads now compose `:raw:N+K`; the wire result is an opaque output string, so the line-number gutter `raw` also drops carries nothing the contract needs.
46
+ - Fixed four Cursor exec frames answering with a result whose oneof was never set. In proto3 that is not an empty result — the server reads it as "the tool ran and produced nothing", indistinguishable from real success. `listMcpResourcesExecResult`, `readMcpResourceExecResult`, `recordScreenResult` and `computerUseResult` now send `ListMcpResourcesSuccess{resources: []}`, `ReadMcpResourceNotFound{uri}`, `RecordScreenFailure` and `ComputerUseError` respectively.
47
+ - The MCP resource frames now answer from the host instead of a fixed verdict. `CursorExecHandlers` gained `listMcpResources`/`readMcpResource`, so a host holding live MCP connections advertises them; the empty catalog and `not_found` above remain the answer when no handler is supplied. A handler that throws surfaces as `ListMcpResourcesError`/`ReadMcpResourceError` rather than collapsing into "none exist", which the model cannot retry. A read carrying `download_path` forwards it and answers with `ReadMcpResourceSuccess.download_path` and no content, which is what that mode means.
48
+ - Fixed Cursor `connect_scm` calls losing their repository and settling on a fabricated verdict. The target rides in the `ConnectScmArgs.target` oneof, so reading a flat `github` property always saw `undefined`; and the authoritative `success`/`error`/`rejected` result only arrives on the completion frame, so answering at the announcement persisted a fixed failure for every call — including the ones the server went on to accept. The block now opens on the start frame and settles from the completion's decoded result.
49
+ - Fixed interleaved Cursor tool calls corrupting each other. The stream decoder tracked a single "current" block and settled it on any `toolCallCompleted`, ignoring the envelope's `call_id`: a completion for one call closed whichever block happened to be open and paired it with the wrong result, and `start A, start B` orphaned A entirely so its own completion settled B while A was never paired — which strips the whole interaction from every rebuilt transcript. Open blocks are now retained per envelope `call_id`, and end-of-stream closes all of them rather than only the last.
50
+ - Fixed a Cursor `search_conversations` call leaving no transcript block. The frame is answered from a fixed verdict, so nothing downstream pairs a result for it, and an unpaired call takes its whole interaction out of every rebuilt transcript.
51
+ - Fixed a Cursor `read_mcp_resource` call leaving no transcript block. The frame runs locally — and in download mode writes a workspace file — but synthesized no tool call and paired no result, so the read was invisible in the UI and absent from every rebuilt history; a resource download could mutate the workspace with nothing on record. The frame now synthesizes a `read_mcp_resource` block (not `read`: it is a remote MCP operation, and the name drives rendering and prune semantics) and pairs a result on success, not-found and error alike. Frames answered without a handler still synthesize nothing, since nothing ran.
52
+ - Fixed a Cursor `list_mcp_resources` call leaving no transcript block. The model consumed the catalog, but the frame synthesized no tool call and paired no result — its streamed `ListMcpResourcesToolCall` announcement was equally unrecognized — so the listing was invisible in the UI and absent from every rebuilt history. Frames a handler answered now synthesize a `list_mcp_resources` block and pair a result derived from the same answer that went on the wire; frames answered from the fixed no-handler catalog still synthesize nothing, since nothing ran.
53
+ - Fixed an unavailable `pi_edit`/`pi_write` answering with the error variant. Both results model refusal and failure as separate oneof cases, and a denial reported as `error` reads as "the tool ran and broke" — inviting a retry of an operation that was never permitted. A frame whose tool is not granted, or whose handler produced nothing, now answers with `PiEditExecRejected`/`PiWriteExecRejected`; execution failures keep the error variant.
54
+ - Fixed a Cursor MCP approval probe actually running the tool. A modern `mcpArgs` frame carrying `smart_mode_approval_only` asks only whether a call would be permitted, not for the call itself. The decoder dropped the flag, so the frame ran a side-effecting MCP tool the user had not been asked about, then ran it again when the real call followed. The flag is now carried through and the probe is answered from the host's policy without executing: approved only for a definite allow, refused for a deny, for a mode that demands a prompt the frame cannot raise, and for a tool the session does not have. No transcript block is synthesized either, since nothing ran.
55
+ - Fixed the Cursor stream's end-of-transport cleanup erasing the arguments of every block still open. Blocks whose args arrive whole (todo, connect-SCM, MCP) never feed the streamed partial-JSON buffer, and reparsing an absent buffer yields `{}`, so a truncated or disconnected turn rebuilt those calls with no arguments at all. Only blocks that actually streamed their args are reparsed now.
56
+ - Fixed a Cursor stream dying mid-turn stranding the call it left open. `connect_scm` and native todo blocks are stamped resolved the moment they open, so the agent loop synthesizes no placeholder and only their completion frame pairs a result — a transport that closed first left the card animating and the call unpaired, which takes the whole interaction out of every rebuilt transcript. The terminal-error path now closes open blocks and pairs those server-owned calls with an interrupted result; the flush ran only on clean completion before, which is not the path a dying stream takes. Exec-settled MCP blocks are left alone, since the dispatch that ran them owns their result.
57
+ - Fixed the Pi exec frames displaying a different operation than the one they run. The provider synthesized its transcript block from a second, hand-rolled translation of the frame args, so `pi_read`'s `offset`/`limit` were shown as a whole-file read, `pi_grep`'s `literal` pattern as an unescaped regex, and `pi_find`'s path/glob join differed from the executed one. Both sides now share a single translation.
58
+ - Fixed the streamed `pi_*_tool_call` announcements that modern builds send alongside each exec frame being unrecognized. The exec channel already synthesizes those blocks when it runs the tool; the duplicate was avoided only because the decoder recognized none of the variants, which would have started double-rendering as soon as any one was added.
59
+ - Fixed `pi_bash` results reaching Cursor clipped with no truncation notice. Two truncation records exist locally: `read`/`grep` set `details.truncation`, which carries an explicit `truncated` flag, while `bash` sets `details.meta.truncation`, whose record has no such flag — its presence is the signal. `piTruncation` read only the first shape and required the flag, so every real Bash truncation was dropped and the server was told the clipped output was complete. Both shapes now translate, and an explicit `truncated: false` still suppresses the field.
60
+
61
+ ## [17.1.8] - 2026-07-28
62
+
63
+ ### Fixed
64
+
65
+ - Fixed an HTTP 400 error when resuming or replaying OpenAI history after an interrupted native Computer Use turn.
66
+ - Fixed connection 404 errors when using Google Vertex AI in multi-region locations (eu and us) by correctly resolving regional endpoint (REP) hosts.
67
+ - Fixed a resource leak in SqliteAuthCredentialStore.close() where unclosed prepared statements kept the SQLite connection alive, preventing database file cleanup (especially on Windows where files remained locked).
68
+
5
69
  ## [17.1.7] - 2026-07-27
6
70
 
7
71
  ### Changed
@@ -20,6 +20,11 @@ export interface AuthBrokerServerOptions {
20
20
  * without long sleeps. Default {@link DEFAULT_STREAM_KEEPALIVE_MS}.
21
21
  */
22
22
  streamKeepaliveMs?: number;
23
+ /**
24
+ * Override cross-process SQLite change polling in milliseconds.
25
+ * Internal-only — tests use a short interval. Default 250ms.
26
+ */
27
+ externalChangePollMs?: number;
23
28
  }
24
29
  export interface AuthBrokerServerHandle {
25
30
  /** Bound URL (`http://host:port`). */
@@ -131,6 +131,10 @@ export type SnapshotStreamEvent = SnapshotStreamSnapshotEvent | SnapshotStreamEn
131
131
  * unauthenticated for liveness probes; everything else requires a bearer.
132
132
  */
133
133
  export declare const AUTH_BROKER_API_PREFIX = "/v1";
134
+ /** Request header used by clients to advertise optional auth-broker protocol features. */
135
+ export declare const AUTH_BROKER_CAPABILITIES_HEADER = "OMP-Auth-Broker-Capabilities";
136
+ /** Client understands independent Codex `chat` and `spark` credential-block scopes. */
137
+ export declare const AUTH_BROKER_CAPABILITY_CODEX_METER_BLOCK_SCOPES = "codex-meter-block-scopes";
134
138
  /** Default port when none is configured. Loopback-only, no external exposure. */
135
139
  export declare const DEFAULT_AUTH_BROKER_BIND = "127.0.0.1:8765";
136
140
  /** Default broker→provider refresh skew. Refresh credentials this close to expiry. */
@@ -257,6 +257,13 @@ export interface CredentialRefreshLeaseFence {
257
257
  }
258
258
  export interface AuthCredentialStore {
259
259
  close(): void;
260
+ /**
261
+ * Stateful probe for commits made by another process to the backing store.
262
+ * Returns true once per observed change.
263
+ */
264
+ pollExternalChanges?(): boolean;
265
+ /** Record the current auth revision after a local mutation already notified consumers. */
266
+ acknowledgeLocalChanges?(): void;
260
267
  /** Optional hook to notify the underlying store that usage report cache is stale. */
261
268
  invalidateUsageCache?(signal?: AbortSignal): Promise<void>;
262
269
  listAuthCredentials(provider?: string): StoredAuthCredential[];
@@ -711,6 +718,11 @@ export declare class AuthStorage {
711
718
  */
712
719
  close(): void;
713
720
  getGeneration(): number;
721
+ /**
722
+ * Reload state after another process commits to the backing store, then
723
+ * notify snapshot consumers even when only credential blocks changed.
724
+ */
725
+ pollExternalChanges(): Promise<boolean>;
714
726
  onGenerationChanged(listener: (generation: number) => void): () => void;
715
727
  offGenerationChanged(listener: (generation: number) => void): void;
716
728
  /**
@@ -1041,8 +1053,15 @@ export declare class AuthStorage {
1041
1053
  * The durable credential id keeps the pin stable across credential refreshes,
1042
1054
  * storage reordering, and process restarts. Normal auth retry and usage-limit
1043
1055
  * handling may still route around an unavailable account.
1056
+ *
1057
+ * `options.lastUsedAtMs` backdates the sticky's last-use timestamp so a pin
1058
+ * restored from a persisted session keeps the provider's warm-window
1059
+ * semantics: a resume inside the prompt-cache TTL reuses the account, a
1060
+ * stale resume still re-ranks.
1044
1061
  */
1045
- pinSessionOAuthAccount(provider: string, sessionId: string, credentialId: number): boolean;
1062
+ pinSessionOAuthAccount(provider: string, sessionId: string, credentialId: number, options?: {
1063
+ lastUsedAtMs?: number;
1064
+ }): boolean;
1046
1065
  /**
1047
1066
  * Resolve every stored OAuth credential for `provider` independently.
1048
1067
  *
@@ -1314,5 +1333,11 @@ export declare class SqliteAuthCredentialStore implements AuthCredentialStore {
1314
1333
  * Delete all credentials for a provider.
1315
1334
  */
1316
1335
  deleteProvider(provider: string): void;
1336
+ /**
1337
+ * SQLite increments `data_version` when another connection commits. Own
1338
+ * writes leave it unchanged and already notify AuthStorage directly.
1339
+ */
1340
+ pollExternalChanges(): boolean;
1341
+ acknowledgeLocalChanges(): void;
1317
1342
  close(): void;
1318
1343
  }
@@ -31,12 +31,16 @@ export declare class OpenAIHttpError extends ProviderHttpError {
31
31
  code: string | undefined;
32
32
  };
33
33
  }
34
+ /** Test-only control for the otherwise bounded Anthropic error-body drain. */
35
+ export declare const __anthropicApiErrorForTesting: {
36
+ setBodyReadTimeoutMs(timeoutMs: number | undefined): void;
37
+ };
34
38
  /** Non-2xx response from the Anthropic API. */
35
39
  export declare class AnthropicApiError extends ProviderHttpError {
36
40
  readonly headers: Headers;
37
41
  readonly requestId: string | null;
38
42
  constructor(status: number, message: string, headers: Headers);
39
- static fromResponse(response: Response): Promise<AnthropicApiError>;
43
+ static fromResponse(response: Response, signal?: AbortSignal): Promise<AnthropicApiError>;
40
44
  }
41
45
  /** Network-level failure (DNS, TLS, socket reset) after retries were exhausted. */
42
46
  export declare class AnthropicConnectionError extends Error {
@@ -5,11 +5,12 @@
5
5
  export type RateLimitReason = "QUOTA_EXHAUSTED" | "RATE_LIMIT_EXCEEDED" | "MODEL_CAPACITY_EXHAUSTED" | "SERVER_ERROR" | "UNKNOWN";
6
6
  /**
7
7
  * Classify a rate-limit error message into a reason category.
8
- * Priority order: QUOTA (Antigravity "quota will reset") > MODEL_CAPACITY > QUOTA (account) >
9
- * RATE_LIMIT > QUOTA (generic) > SERVER_ERROR > UNKNOWN.
8
+ * Priority order: explicit details in a resource-exhausted error > QUOTA
9
+ * (Antigravity "quota will reset") > MODEL_CAPACITY > QUOTA (account) >
10
+ * RATE_LIMIT > QUOTA (generic) > SERVER_ERROR > bare resource-exhausted > UNKNOWN.
10
11
  *
11
- * "resource exhausted" maps to MODEL_CAPACITY (transient, short wait)
12
- * "quota exceeded" / "quota will reset" maps to QUOTA_EXHAUSTED (long wait, switch account)
12
+ * Bare "resource exhausted" / "resource_exhausted" maps to MODEL_CAPACITY (transient, short wait).
13
+ * Explicit details such as "quota exceeded" retain their normal classification.
13
14
  */
14
15
  export declare function parseRateLimitReason(errorMessage: string): RateLimitReason;
15
16
  /**
@@ -9,6 +9,12 @@ export interface AnthropicRequestOptions {
9
9
  timeout?: number;
10
10
  /** Per-request retry budget override. */
11
11
  maxRetries?: number;
12
+ /**
13
+ * Maximum delay in milliseconds to wait for a server-directed retry. If the
14
+ * server's `retry-after` hint exceeds this value, the retry is declined and
15
+ * the original error is surfaced. Non-positive values disable the cap. Defaults to 60000.
16
+ */
17
+ maxRetryDelayMs?: number;
12
18
  /** Per-request headers merged after client defaults. */
13
19
  headers?: Record<string, string>;
14
20
  }
@@ -38,6 +44,12 @@ export interface AnthropicClientOptions {
38
44
  authToken?: string | null;
39
45
  baseURL?: string | null;
40
46
  maxRetries?: number;
47
+ /**
48
+ * Maximum delay in milliseconds to wait for a server-directed retry. If the
49
+ * server's `retry-after` hint exceeds this value, the retry is declined and
50
+ * the original error is surfaced. Non-positive values disable the cap. Defaults to 60000.
51
+ */
52
+ maxRetryDelayMs?: number;
41
53
  /** Pre-response timeout in milliseconds. Defaults to 10 minutes. */
42
54
  timeout?: number;
43
55
  defaultHeaders?: Record<string, string>;
@@ -45,7 +57,7 @@ export interface AnthropicClientOptions {
45
57
  fetchOptions?: AnthropicFetchOptions;
46
58
  }
47
59
  /** Server-suggested delay (`retry-after-ms`, then `retry-after` seconds or HTTP date). */
48
- export declare function retryDelayFromHeaders(headers: Headers | undefined): number | undefined;
60
+ export declare function retryDelayFromHeaders(headers: Pick<Headers, "get"> | undefined): number | undefined;
49
61
  export declare function calculateAnthropicRetryDelayMs(attempt: number): number;
50
62
  /**
51
63
  * Lazy in-flight request handle. The HTTP request starts on the first
@@ -1,4 +1,4 @@
1
- import type { FetchImpl, Message, Model, ProviderSessionState, ServiceTier, SimpleStreamOptions, StreamFunction, StreamOptions, Usage } from "../types.js";
1
+ import type { Api, FetchImpl, Message, Model, ProviderSessionState, ServiceTier, SimpleStreamOptions, StreamFunction, StreamOptions, Usage } from "../types.js";
2
2
  import { type AnthropicFetchOptions, type AnthropicMessagesClientLike } from "./anthropic-client.js";
3
3
  import { type FallbackParam, type MessageParam, type TextBlockParam } from "./anthropic-wire.js";
4
4
  export type AnthropicHeaderOptions = {
@@ -26,6 +26,12 @@ type AnthropicCacheControl = NonNullable<TextBlockParam["cache_control"]>;
26
26
  * hasn't been materialized yet.
27
27
  */
28
28
  export declare function clearAnthropicFastModeFallback(providerSessionState: Map<string, ProviderSessionState> | undefined): void;
29
+ /**
30
+ * Whether the direct Anthropic model's endpoint-scoped fast-mode fallback is
31
+ * currently active. Reading the map directly is intentional: inspection must
32
+ * not materialize a state entry for a model that has never streamed.
33
+ */
34
+ export declare function isAnthropicFastModeFallbackDisabled(providerSessionState: Map<string, ProviderSessionState> | undefined, model: Model<Api>): boolean;
29
35
  export * from "./claude-code-fingerprint.js";
30
36
  export declare function mapStainlessOs(platform: string): "MacOS" | "Windows" | "Linux" | "FreeBSD" | `Other::${string}`;
31
37
  export declare function mapStainlessArch(arch: string): "x64" | "arm64" | "x86" | `other::${string}`;
@@ -156,6 +162,7 @@ export type AnthropicClientOptionsArgs = {
156
162
  thinkingDisplay?: AnthropicThinkingDisplay;
157
163
  disableStrictTools?: boolean;
158
164
  fetch?: FetchImpl;
165
+ maxRetryDelayMs?: number;
159
166
  claudeCodeSessionId?: string;
160
167
  };
161
168
  export type AnthropicClientOptionsResult = {
@@ -164,6 +171,7 @@ export type AnthropicClientOptionsResult = {
164
171
  authToken?: string | null;
165
172
  baseURL?: string;
166
173
  maxRetries: number;
174
+ maxRetryDelayMs?: number;
167
175
  defaultHeaders: Record<string, string>;
168
176
  fetch?: FetchImpl;
169
177
  fetchOptions?: AnthropicFetchOptions;
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Proto builders for the modern Cursor CLI exec frames (`ExecServerMessage`
3
+ * 27-31, 36-38, 40-55).
4
+ *
5
+ * Split out of `cursor.ts` because these are pure `create(...)` shapes with no
6
+ * transport, stream, or block-state coupling: the dispatcher decides *which*
7
+ * answer a frame gets, this module knows *what* that answer looks like on the
8
+ * wire. Every builder returns a result whose oneof case is set — an
9
+ * `ExecClientMessage` carrying a result with an unset oneof is a fake success
10
+ * the server reads as "the tool ran and produced nothing".
11
+ */
12
+ import { type ExecuteHookRequest, type ExecuteHookResult, type McpStateExecResult, type McpToolDefinition, type PiBashExecResult, type PiEditExecResult, type PiFindExecResult, type PiGrepExecResult, type PiLsExecResult, type PiReadExecResult, type PiTruncation, type PiWriteExecResult } from "@oh-my-pi/pi-catalog/discovery/cursor-gen/agent_pb";
13
+ import type { ToolResultMessage } from "../../types.js";
14
+ /**
15
+ * The pure arg translation lives in `../cursor-pi-args` so the legacy pi shim
16
+ * can share it without pulling this module's protobuf graph into the bundled
17
+ * virtual registry. Re-exported here because this is where the frame builders
18
+ * and their translation are consumed together.
19
+ */
20
+ export { piEscapeRegexLiteral, piGrepSkip, piJoinPath, piLimit, piLsPath, piReadDisplayPath, piReadPath, piTimeout, } from "../cursor-pi-args.js";
21
+ /** Flatten a tool result's content into the single `output` string the Pi frames carry. */
22
+ export declare function piOutputText(toolResult: ToolResultMessage): string;
23
+ /**
24
+ * Translate a local tool's truncation summary into `PiTruncation`.
25
+ *
26
+ * Two shapes reach here. `read`/`grep` set `details.truncation`
27
+ * (`TruncationResult`), which carries an explicit `truncated` boolean. `bash`
28
+ * sets `details.meta.truncation` (`TruncationMeta`), which has **no** such
29
+ * flag — its presence *is* the signal, and requiring the boolean silently
30
+ * dropped every Bash truncation, handing Cursor clipped output with no notice
31
+ * that it was clipped.
32
+ *
33
+ * Returns `undefined` when nothing was truncated: the field is `optional` on
34
+ * every Pi success message, and emitting a zeroed `PiTruncation` would tell the
35
+ * server the output was trimmed to nothing.
36
+ */
37
+ export declare function piTruncation(toolResult: ToolResultMessage): PiTruncation | undefined;
38
+ export declare function buildPiReadResult(toolResult: ToolResultMessage): PiReadExecResult;
39
+ export declare function buildPiReadError(error: string): PiReadExecResult;
40
+ export declare function buildPiBashResult(toolResult: ToolResultMessage): PiBashExecResult;
41
+ export declare function buildPiBashError(error: string): PiBashExecResult;
42
+ /**
43
+ * `PiEditExecSuccess` requires `diff` and `patch` alongside `output`. The local
44
+ * `edit` tool reports them under `details`; when it does not, the strings stay
45
+ * empty rather than being faked from the output text.
46
+ */
47
+ export declare function buildPiEditResult(toolResult: ToolResultMessage): PiEditExecResult;
48
+ export declare function buildPiEditError(error: string): PiEditExecResult;
49
+ /**
50
+ * A refusal is not an execution failure: `PiEditExecResult` models them as
51
+ * separate variants, and answering a denied call with `error` reads as "the
52
+ * edit ran and broke", which invites a retry of an operation that was never
53
+ * permitted.
54
+ */
55
+ export declare function buildPiEditRejected(reason: string): PiEditExecResult;
56
+ export declare function buildPiWriteResult(toolResult: ToolResultMessage): PiWriteExecResult;
57
+ export declare function buildPiWriteError(error: string): PiWriteExecResult;
58
+ /** Same variant split as {@link buildPiEditRejected}. */
59
+ export declare function buildPiWriteRejected(reason: string): PiWriteExecResult;
60
+ export declare function buildPiGrepResult(toolResult: ToolResultMessage): PiGrepExecResult;
61
+ export declare function buildPiGrepError(error: string): PiGrepExecResult;
62
+ export declare function buildPiFindResult(toolResult: ToolResultMessage): PiFindExecResult;
63
+ export declare function buildPiFindError(error: string): PiFindExecResult;
64
+ export declare function buildPiLsResult(toolResult: ToolResultMessage): PiLsExecResult;
65
+ export declare function buildPiLsError(error: string): PiLsExecResult;
66
+ /**
67
+ * Answer `mcpStateExecArgs` (frame 36) from the catalog already advertised in
68
+ * `RequestContext.tools`.
69
+ *
70
+ * This client hosts no MCP servers of its own: every forwarded tool is a local
71
+ * pi-agent tool published under a synthetic `providerIdentifier`. Regrouping
72
+ * the same list keeps the server's view of "which servers exist and what do
73
+ * they expose" consistent with what it was told at context time, instead of
74
+ * claiming zero servers while tool calls for them keep arriving.
75
+ *
76
+ * `serverIdentifiers` filters the answer when the server asks about specific
77
+ * servers. `kickOnly` is a restart request — there is nothing to restart, so it
78
+ * is answered with the same state rather than an error.
79
+ */
80
+ export declare function buildMcpStateResult(tools: McpToolDefinition[], serverIdentifiers: readonly string[]): McpStateExecResult;
81
+ /**
82
+ * Build the neutral response for a hook query: the matching response case with
83
+ * every field unset.
84
+ *
85
+ * This client runs no Cursor hooks, and every field of every response variant
86
+ * is `optional` — so an empty response of the right case means "no hook had
87
+ * anything to say", which is exactly true. It is NOT the unset-oneof fake
88
+ * success: the case itself is set, only the payload is empty.
89
+ *
90
+ * `ExecuteHookRequest` and `ExecuteHookResponse` are parallel oneofs whose case
91
+ * names line up, but the two unions are unrelated to the compiler: a `switch`
92
+ * is what makes each pairing individually type-checked, and it forces a
93
+ * deliberate branch when a future regen adds a request case.
94
+ *
95
+ * Returns `null` for a request case this build does not model, which the
96
+ * dispatcher answers with `ExecClientThrow` rather than guessing a case.
97
+ */
98
+ export declare function buildNeutralHookResult(request: ExecuteHookRequest | undefined): ExecuteHookResult | null;
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Translate a Pi frame's args into the local tool kwargs that run it.
3
+ *
4
+ * Shared deliberately by three consumers: the provider synthesizes a display
5
+ * block from these, the coding-agent bridge executes with them, and the legacy
6
+ * pi shim performs the identical translation for the old wire. Separate
7
+ * hand-rolled copies drift, and the drift is invisible — the transcript shows
8
+ * one operation while a different one runs.
9
+ *
10
+ * Kept apart from `cursor/exec-modern.ts` on purpose: these are pure
11
+ * string/path functions with no protobuf coupling, while that module pulls in
12
+ * `@bufbuild/protobuf` and the generated `agent_pb` graph. The legacy shim is
13
+ * compiled into the bundled virtual module registry, so importing it from a
14
+ * nested path would drag the whole exec implementation in with it — and
15
+ * `./providers/*` is a single-segment wildcard export that cannot serve a
16
+ * nested specifier under bunfs (issue #3442).
17
+ *
18
+ * Every `optional int32` here is presence-sensitive: `0` is a supplied value,
19
+ * not "unset", so it must never be folded into a default.
20
+ */
21
+ /**
22
+ * A `pi_read` range composed onto the path as `read`'s inline `:raw:N+K`
23
+ * selector.
24
+ *
25
+ * `read` exposes no range kwargs, so an uncomposed range reads the whole file.
26
+ * `offset` is a 1-indexed start clamped like the reference's
27
+ * `Math.max(0, offset - 1)` over 0-indexed lines; `limit` is a line count.
28
+ * `null` marks a present `limit: 0` — zero lines, which no selector expresses
29
+ * and which must not degrade into a whole-file read.
30
+ *
31
+ * The range is `raw` because a plain `:N+K` deliberately pads with one leading
32
+ * and three trailing context lines: helpful for a human reading a snippet,
33
+ * wrong for a caller that asked for exactly `limit` lines from `offset`. The
34
+ * wire result is an opaque `output` string, so the hashline and line-number
35
+ * gutter that `raw` also drops carry nothing the frame's contract needs.
36
+ * A range-free read keeps the ordinary form — whole-file reads want them.
37
+ */
38
+ export declare function piReadPath(readPath: string, offset?: number, limit?: number): string | null;
39
+ /**
40
+ * The same range as {@link piReadPath}, rendered for a transcript block rather
41
+ * than for execution.
42
+ *
43
+ * Differs only at `limit: 0`, where `piReadPath` returns `null` because no
44
+ * selector reads zero lines and the frame is answered with empty output
45
+ * directly. The block still has to say so: falling back to the bare path there
46
+ * would record a whole-file read whose result is empty, which is the widest
47
+ * possible gap between what a rebuilt transcript shows and what happened.
48
+ * `+0` is never executed — it exists to be read.
49
+ */
50
+ export declare function piReadDisplayPath(readPath: string, offset?: number, limit?: number): string;
51
+ /**
52
+ * A legacy `grep` frame's pagination `offset` as the local tool's file `skip`.
53
+ *
54
+ * `grep` paginates by file and reports "use skip=N for the next page" in that
55
+ * same unit, so the offset maps across directly. A present `0` means "start at
56
+ * the beginning", which is the unskipped search rather than a skip of zero.
57
+ *
58
+ * Shared because both the executing bridge and the provider's transcript
59
+ * synthesis need it: a block showing an unskipped search beside a result from
60
+ * a later file window misreports what was searched.
61
+ */
62
+ export declare function piGrepSkip(offset?: number): number | undefined;
63
+ /**
64
+ * Join a Pi frame's optional `path` with the `glob`/`pattern` it scopes.
65
+ *
66
+ * The local `grep`/`glob` tools take one combined path spec. An absolute
67
+ * pattern ignores the path, and an absent or `.` path leaves the pattern
68
+ * standing alone rather than building a `./`- or `//`-prefixed spec.
69
+ *
70
+ * Uses `node:path` rather than string surgery so Windows absolutes (`C:\…`,
71
+ * UNC) are recognised and separators stay normalized.
72
+ */
73
+ export declare function piJoinPath(basePath: string | undefined, pattern: string): string;
74
+ /**
75
+ * The path a `pi_ls` frame lists.
76
+ *
77
+ * The frame's `limit` is deliberately NOT mapped. It caps directory *entries*
78
+ * (the reference does a flat `readdir` and slices the entry array), while the
79
+ * local `read` tool renders a depth-2 tree with per-directory caps and elision
80
+ * summaries and applies a selector as a *rendered line* slice. Nested rows,
81
+ * headers and "N more" lines all count toward that slice, so `:1+K` would cap
82
+ * a different unit while looking honored — worse than leaving it unset, which
83
+ * at least reports the local listing's own truncation faithfully.
84
+ */
85
+ export declare function piLsPath(basePath: string | undefined): string;
86
+ /** Escape a literal string so the regex-only local `grep` tool matches it verbatim. */
87
+ export declare function piEscapeRegexLiteral(value: string): string;
88
+ /** Clamp a present `optional int32` result cap the way the reference does; `undefined` stays unset. */
89
+ export declare function piLimit(limit: number | undefined): number | undefined;
90
+ /**
91
+ * A `pi_bash` frame's timeout as the local `bash` tool's kwarg.
92
+ *
93
+ * Presence-sensitive like every other `optional int32` here, and unusually
94
+ * load-bearing: `bash` documents `timeout: 0` as "disables the command
95
+ * deadline", so folding a supplied `0` into `undefined` applies the 300s
96
+ * default and kills exactly the long-running command that asked not to be.
97
+ * Negative values have no local meaning and fall back to the default.
98
+ */
99
+ export declare function piTimeout(timeout: number | undefined): number | undefined;
@@ -3,10 +3,10 @@ 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
5
  import type { AssistantMessage, CursorExecHandlerResult, CursorExecHandlers, CursorExecPairing, CursorTodoSyncHandler, CursorToolResultHandler, Message, StreamFunction, StreamOptions, TextContent, ThinkingContent, Tool, ToolCall, ToolResultMessage } from "../types.js";
6
- import { kCursorExecResolved, kStreamingBlockIndex, kStreamingBlockKind, kStreamingLastParseLen, kStreamingPartialJson } from "../utils/block-symbols.js";
6
+ import { kCursorExecResolved, kStreamingBlockIndex, kStreamingBlockKind, kStreamingEnvelopeId, 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";
9
- export declare const CURSOR_CLIENT_VERSION = "cli-2026.01.09-231024f";
9
+ export declare const CURSOR_CLIENT_VERSION = "cli-2026.07.23-e383d2b";
10
10
  export interface CursorOptions extends StreamOptions {
11
11
  customSystemPrompt?: string;
12
12
  conversationId?: string;
@@ -32,7 +32,8 @@ export type ToolCallState = ToolCall & {
32
32
  [kStreamingBlockIndex]: number;
33
33
  [kStreamingPartialJson]?: string;
34
34
  [kStreamingLastParseLen]?: number;
35
- [kStreamingBlockKind]: "mcp" | "todo" | "cursor-exec";
35
+ [kStreamingBlockKind]: "mcp" | "todo" | "cursor-exec" | "connect-scm";
36
+ [kStreamingEnvelopeId]?: string;
36
37
  [kCursorExecResolved]?: true;
37
38
  };
38
39
  export interface BlockState {
@@ -43,6 +44,17 @@ export interface BlockState {
43
44
  [kStreamingBlockIndex]: number;
44
45
  }) | null;
45
46
  currentToolCall: ToolCallState | null;
47
+ /**
48
+ * Open streamed tool-call blocks, keyed by the interaction envelope's
49
+ * `call_id`.
50
+ *
51
+ * Cursor interleaves calls: two `toolCallStarted` frames can arrive before
52
+ * either completes. A single "current" slot would let the second overwrite
53
+ * the first, orphaning a block that nothing then settles. Every keyed block
54
+ * stays reachable until its own completion, and `currentToolCall` remains
55
+ * only as the fallback for frames that carry no `call_id`.
56
+ */
57
+ openToolCalls: Map<string, ToolCallState>;
46
58
  /** MCP call IDs synthesized from exec frames before their redundant streamed block arrives. */
47
59
  resolvedMcpToolCallIds: Set<string>;
48
60
  firstTokenTime: number | undefined;
@@ -102,6 +114,44 @@ export declare function resolveExecHandler<TArgs, TResult>(args: TArgs, handler:
102
114
  * specific recovery hint.
103
115
  */
104
116
  export declare function emptyGrepPatternRejection(pattern: string | undefined, glob: string | undefined): string | null;
117
+ /**
118
+ * Retain a freshly opened streamed tool-call block.
119
+ *
120
+ * Keyed by the interaction envelope's `call_id`, which is the only key every
121
+ * `ToolCall*Update` for that call shares. The block's own `id` is deliberately
122
+ * not the key: MCP, Pi and connect-SCM blocks are filed under the id carried
123
+ * inside the call's `args`, because that is what the exec channel pairs its
124
+ * result under and what the transcript files the visible block under.
125
+ *
126
+ * `currentToolCall` is still set, as the fallback for frames that carry no
127
+ * `call_id` (proto3-optional, and unset on what older builds send).
128
+ */
129
+ /**
130
+ * Close every tool-call block still open when the stream ends.
131
+ *
132
+ * Not just the last one started: with interleaved calls several can be open at
133
+ * once, and an unclosed block leaves its live card animating and its call
134
+ * unpaired.
135
+ *
136
+ * Only blocks fed by a streamed argument buffer get reparsed. Todo,
137
+ * connect-SCM and MCP-settled frames arrive with complete `arguments` and
138
+ * never set the partial buffer; `parseStreamingJson(undefined)` returns `{}`,
139
+ * so reparsing unconditionally would erase the arguments of every such block
140
+ * caught open by a truncated stream.
141
+ *
142
+ * Server-owned blocks are also paired here. `connect-scm` and `todo` are
143
+ * stamped {@link kCursorExecResolved} the moment they open, so `agent-loop.ts`
144
+ * synthesizes no placeholder for them and only their `toolCallCompleted` frame
145
+ * pairs a result. A transport that closes before that frame would leave the
146
+ * call unpaired, and `buildSessionContext` strips a dangling call from every
147
+ * rebuilt transcript — the interaction disappears. An interrupted result is
148
+ * emitted instead.
149
+ *
150
+ * MCP blocks are excluded even when resolved: the exec dispatch that marked
151
+ * them owns their result, and `drainInFlightDispatches` awaits it before this
152
+ * runs, so pairing here would duplicate one against the same `toolCallId`.
153
+ */
154
+ export declare function flushOpenToolCalls(output: AssistantMessage, stream: AssistantMessageEventStream, state: BlockState): void;
105
155
  /**
106
156
  * Merge the decoded completion-frame `McpArgs` map into the args assembled
107
157
  * from streamed `args_text_delta` snapshots.
@@ -1,10 +1,8 @@
1
1
  import type { Api, Model } from "../types.js";
2
2
  export declare function createProviderErrorMessage(model: Model<Api>, err: unknown): {
3
3
  role: "assistant";
4
- content: {
5
- type: "text";
6
- text: string;
7
- }[];
4
+ content: never[];
5
+ errorMessage: string;
8
6
  api: Api;
9
7
  provider: string;
10
8
  model: string;
@@ -27,6 +27,14 @@ export interface OpenAICodexResponsesOptions extends StreamOptions {
27
27
  * keys are ignored; extras are never emitted as top-level metadata fields.
28
28
  */
29
29
  clientMetadata?: Record<string, string>;
30
+ /**
31
+ * Turn id of the initiating (parent) Codex turn for nested requests such as
32
+ * subagent spawns (codex-rs `parent_turn_id`, #35835). Emitted both as the
33
+ * flat `client_metadata.parent_turn_id` key and inside the
34
+ * `x-codex-turn-metadata` JSON blob; blank values are ignored. The key is
35
+ * reserved: `clientMetadata` extras cannot supply it.
36
+ */
37
+ parentTurnId?: string;
30
38
  /**
31
39
  * Invoked when the server streams a `response.metadata` event carrying
32
40
  * ChatGPT moderation metadata (`metadata.openai_chatgpt_moderation_metadata`)
@@ -44,6 +52,8 @@ export interface OpenAICodexCompatibilityMetadataOptions {
44
52
  startNewTurn?: boolean;
45
53
  turnStartedAtUnixMs?: number;
46
54
  clientMetadata?: Readonly<Record<string, string>>;
55
+ /** Parent Codex turn id for nested requests; see {@link OpenAICodexResponsesOptions.parentTurnId}. */
56
+ parentTurnId?: string;
47
57
  /** Add the direct installation header required by `/responses/compact`. */
48
58
  includeInstallationHeader?: boolean;
49
59
  }
@@ -2453,6 +2453,14 @@ export interface ResponseFunctionToolCall {
2453
2453
  * The namespace of the function to run.
2454
2454
  */
2455
2455
  namespace?: string;
2456
+ /**
2457
+ * Plaintext-argument marker for collaboration tool calls (codex-rs
2458
+ * `encrypted_function_args`, #35845): an empty array marks the arguments as
2459
+ * plaintext agent-message payloads rather than encrypted blobs. Preserved
2460
+ * verbatim across history replay so the backend keeps treating them as
2461
+ * plaintext; omitted entirely for ordinary function calls.
2462
+ */
2463
+ encrypted_function_args?: string[];
2456
2464
  /**
2457
2465
  * The status of the item. One of `in_progress`, `completed`, or `incomplete`.
2458
2466
  * Populated when items are returned via API.