pi-openai-toolkit 0.20.7 → 0.21.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 (55) hide show
  1. package/LICENSE +21 -21
  2. package/NOTICE +83 -83
  3. package/README.md +8 -26
  4. package/README.zh.md +8 -26
  5. package/config.schema.json +278 -366
  6. package/docs/configuration.md +294 -310
  7. package/docs/internals.md +166 -175
  8. package/package.json +3 -13
  9. package/src/compact-client.ts +412 -389
  10. package/src/compaction-errors.ts +96 -0
  11. package/src/compaction-projection.ts +35 -35
  12. package/src/config/legacy.ts +519 -717
  13. package/src/config/migration.ts +103 -100
  14. package/src/config/notifications.ts +36 -28
  15. package/src/config/policy.ts +10 -18
  16. package/src/config/v2.ts +1167 -1380
  17. package/src/config.ts +310 -319
  18. package/src/context-management/codex-provider.ts +270 -270
  19. package/src/context-management/history-notes.ts +357 -357
  20. package/src/context-management/manual-compact.ts +0 -11
  21. package/src/context-management/tools.ts +371 -371
  22. package/src/context-management/types.ts +168 -168
  23. package/src/context-management/window-manager.ts +801 -801
  24. package/src/debug.ts +238 -237
  25. package/src/deferred-tool-carryover.ts +282 -282
  26. package/src/details-store.ts +151 -151
  27. package/src/extension-runtime.ts +1662 -1623
  28. package/src/image-generation/extension.ts +189 -189
  29. package/src/image-generation/protocol.ts +579 -579
  30. package/src/image-generation/types.ts +300 -300
  31. package/src/native-fallback.ts +4 -132
  32. package/src/payload-rewrite.ts +515 -518
  33. package/src/pi-context-hook.ts +117 -117
  34. package/src/remote-v2-client.ts +811 -814
  35. package/src/request-context-cache.ts +155 -155
  36. package/src/responses-headers.ts +180 -180
  37. package/src/runtime.ts +865 -832
  38. package/src/serializer.ts +882 -882
  39. package/src/supported-environment.ts +27 -27
  40. package/src/types.ts +565 -648
  41. package/src/web-search/alpha-client.ts +876 -876
  42. package/src/web-search/extension.ts +530 -530
  43. package/src/web-search/payload.ts +299 -299
  44. package/src/web-search/prompt.ts +60 -60
  45. package/src/web-search/types.ts +166 -166
  46. package/tsconfig.check.json +50 -54
  47. package/src/auto-mode/classifier.ts +0 -161
  48. package/src/auto-mode/extension.ts +0 -691
  49. package/src/auto-mode/model-selection-guard.ts +0 -52
  50. package/src/auto-mode/policy.ts +0 -146
  51. package/src/auto-mode/prompt.ts +0 -279
  52. package/src/auto-mode/reviewer.ts +0 -325
  53. package/src/auto-mode/tool-review-tui.ts +0 -211
  54. package/src/auto-mode/transcript.ts +0 -135
  55. package/src/auto-mode/types.ts +0 -137
@@ -0,0 +1,96 @@
1
+ /** Content-free diagnostics shared by the compaction HTTP/stream boundaries. */
2
+ export type CompactionErrorInfo = {
3
+ errorCode?: string;
4
+ errorType?: string;
5
+ errorParam?: string;
6
+ /** Controlled text, never a provider-authored message. */
7
+ errorMessage?: string;
8
+ errorCategory?: "auth" | "quota" | "protocol";
9
+ };
10
+
11
+ const HINTS: Record<string, string> = {
12
+ subscription_sharing_unsupported_capability: "This OpenAI subscription-sharing grant does not authorize compaction. Use a separately authenticated supported API-key or native Codex route; credentials are not interchangeable.",
13
+ hardened_oauth_rule_missing: "This OAuth grant does not authorize this operation. Select a separately authenticated supported route; Toolkit will not switch credentials.",
14
+ rejected_by_hardened_oauth_boundary: "This OAuth grant does not authorize this operation. Select a separately authenticated supported route; Toolkit will not switch credentials.",
15
+ missing_scope: "The credential lacks the required Responses authorization scope. Authenticate the intended route separately.",
16
+ invalid_api_key: "The selected route rejected its credential. Check authentication for that route.",
17
+ insufficient_quota: "The selected route has insufficient quota. Check its billing and quota.",
18
+ rate_limit_exceeded: "The selected route is rate limited. Retry later.",
19
+ unsupported_capability: "The endpoint does not support the requested compaction protocol.",
20
+ unsupported_compaction: "The endpoint does not support the requested compaction protocol.",
21
+ unsupported_protocol: "The endpoint does not support the requested compaction protocol.",
22
+ unsupported_parameter: "The endpoint rejected a compaction request parameter.",
23
+ unknown_parameter: "The endpoint rejected a compaction request parameter.",
24
+ invalid_request_error: "The endpoint rejected the compaction request.",
25
+ };
26
+ const PROTOCOL_CODES = new Set(["unsupported_capability", "unsupported_compaction", "unsupported_protocol", "unsupported_parameter", "unknown_parameter"]);
27
+ const SAFE_TYPES = new Set(["invalid_request_error", "authentication_error", "permission_error", "rate_limit_error", "insufficient_quota", "server_error"]);
28
+ const SAFE_PARAMS = new Set(["input", "model", "compaction_trigger", "stream", "store", "tools", "instructions", "reasoning", "text"]);
29
+
30
+ function isRecord(value: unknown): value is Record<string, unknown> {
31
+ return !!value && typeof value === "object" && !Array.isArray(value);
32
+ }
33
+
34
+ export function decodeCompactionError(value: unknown): CompactionErrorInfo {
35
+ if (!isRecord(value)) return {};
36
+ const source = isRecord(value.error) ? value.error
37
+ : isRecord(value.response) && isRecord(value.response.error) ? value.response.error : value;
38
+ // Unknown identifiers may themselves contain secrets. Only public vocabulary
39
+ // is emitted; classification can conservatively exclude an unknown auth code.
40
+ const code = typeof source.code === "string" && source.code.length <= 128 ? source.code : undefined;
41
+ const type = typeof source.type === "string" && source.type.length <= 128 ? source.type : undefined;
42
+ const excluded = [code, type].filter((item): item is string => !!item).join(" ");
43
+ const missingScope = typeof source.message === "string" && source.message.length <= 4096 && /missing (?:required )?scopes?\b/i.test(source.message);
44
+ const errorCategory = missingScope || /auth|oauth|permission|subscription|scope|access_denied|forbidden|api_key|token|credential|invalid_grant/i.test(excluded) ? "auth"
45
+ : /quota|billing|rate_limit|too_many_requests/i.test(excluded) ? "quota"
46
+ : code && PROTOCOL_CODES.has(code) ? "protocol" : undefined;
47
+ const errorCode = code && Object.hasOwn(HINTS, code) ? code : undefined;
48
+ return {
49
+ ...(errorCode ? { errorCode } : {}),
50
+ ...(type && SAFE_TYPES.has(type) ? { errorType: type } : {}),
51
+ ...(typeof source.param === "string" && SAFE_PARAMS.has(source.param) ? { errorParam: source.param } : {}),
52
+ ...(missingScope ? { errorMessage: HINTS.missing_scope } : errorCode ? { errorMessage: HINTS[errorCode] } : {}),
53
+ ...(errorCategory ? { errorCategory } : {}),
54
+ };
55
+ }
56
+
57
+ /** One protocol probe, not a transport/auth retry. Unknown failures fail closed. */
58
+ export function qualifiesForStandaloneCompaction(failure: CompactionErrorInfo & { reason: string; status?: number }): boolean {
59
+ if (failure.errorCategory === "auth" || failure.errorCategory === "quota") return false;
60
+ if (failure.reason === "non-2xx") return [400, 404, 405, 422, 501].includes(failure.status ?? 0);
61
+ if (failure.reason === "error-event") return failure.errorCategory === "protocol";
62
+ return failure.status !== undefined && failure.status >= 200 && failure.status < 300 && [
63
+ "empty-body", "invalid-sse", "missing-completed-event", "incomplete-response",
64
+ "invalid-compaction-count", "malformed-compaction-item", "conflicting-compaction-item",
65
+ "invalid-compaction-metadata", "invalid-event-order", "duplicate-completed-event",
66
+ ].includes(failure.reason);
67
+ }
68
+
69
+ /** Body collection must be cancellable even with a stalled mocked/custom transport. */
70
+ export async function readCompactionResponseText(response: Response, signal?: AbortSignal): Promise<string> {
71
+ if (signal?.aborted) throw new DOMException("Compaction aborted", "AbortError");
72
+ const reader = response.body?.getReader();
73
+ if (!reader) return "";
74
+ const decoder = new TextDecoder();
75
+ const parts: string[] = [];
76
+ let onAbort: (() => void) | undefined;
77
+ const aborted = new Promise<never>((_resolve, reject) => {
78
+ onAbort = () => reject(new DOMException("Compaction aborted", "AbortError"));
79
+ signal?.addEventListener("abort", onAbort, { once: true });
80
+ });
81
+ try {
82
+ while (true) {
83
+ if (signal?.aborted) throw new DOMException("Compaction aborted", "AbortError");
84
+ const chunk = await Promise.race([reader.read(), aborted]);
85
+ if (signal?.aborted) throw new DOMException("Compaction aborted", "AbortError");
86
+ parts.push(chunk.done ? decoder.decode() : decoder.decode(chunk.value, { stream: true }));
87
+ if (chunk.done) return parts.join("");
88
+ }
89
+ } finally {
90
+ if (onAbort) signal?.removeEventListener("abort", onAbort);
91
+ // Abort/completion never waits for transport cleanup. Cancel the reader,
92
+ // not the locked body, and observe late rejection before releasing it.
93
+ try { void reader.cancel().catch(() => undefined); } catch { /* Custom transport cleanup. */ }
94
+ finally { try { reader.releaseLock(); } catch { /* Preserve the operation outcome. */ } }
95
+ }
96
+ }
@@ -1,35 +1,35 @@
1
- import type {
2
- NativeCompactionDetails,
3
- NativeCompactionInputProvenance,
4
- } from "./types";
5
-
6
- /** The configured Pi context-hook projector is unavailable or failed. */
7
- export const COMPACTION_PROJECTION_UNAVAILABLE = "no-supported-projection-seam" as const;
8
- /** The current session context could not be read before projection. */
9
- export const COMPACTION_SESSION_CONTEXT_UNAVAILABLE = "session-context-unavailable" as const;
10
- /** A projected recursive context did not contain the current compaction summary. */
11
- export const COMPACTION_PROJECTED_CONTEXT_UNAVAILABLE = "projected-compaction-context-unavailable" as const;
12
- /** The latest opaque checkpoint was created with no or a different input marker. */
13
- export const COMPACTION_CHECKPOINT_PROVENANCE_UNAVAILABLE = "unverified-compaction-checkpoint" as const;
14
-
15
- /** Persisted edits changed input already sealed inside an opaque checkpoint. */
16
- export const COMPACTION_CHECKPOINT_CONTEXT_EDITED = "checkpoint-context-edited" as const;
17
-
18
- export type UnprojectedCompactionReason =
19
- | typeof COMPACTION_CHECKPOINT_CONTEXT_EDITED
20
- | typeof COMPACTION_PROJECTION_UNAVAILABLE
21
- | typeof COMPACTION_SESSION_CONTEXT_UNAVAILABLE
22
- | typeof COMPACTION_CHECKPOINT_PROVENANCE_UNAVAILABLE
23
- | typeof COMPACTION_PROJECTED_CONTEXT_UNAVAILABLE;
24
-
25
- /**
26
- * A Remote V2 checkpoint is safe for the current replay chain only when its
27
- * marker matches the configured input source. Structural persistence validation
28
- * intentionally remains more permissive for old session files.
29
- */
30
- export function hasVerifiedCompactionInputProvenance(
31
- details: Pick<NativeCompactionDetails, "inputProvenance"> | undefined,
32
- expected: NativeCompactionInputProvenance,
33
- ): boolean {
34
- return details?.inputProvenance === expected;
35
- }
1
+ import type {
2
+ NativeCompactionDetails,
3
+ NativeCompactionInputProvenance,
4
+ } from "./types";
5
+
6
+ /** The configured Pi context-hook projector is unavailable or failed. */
7
+ export const COMPACTION_PROJECTION_UNAVAILABLE = "no-supported-projection-seam" as const;
8
+ /** The current session context could not be read before projection. */
9
+ export const COMPACTION_SESSION_CONTEXT_UNAVAILABLE = "session-context-unavailable" as const;
10
+ /** A projected recursive context did not contain the current compaction summary. */
11
+ export const COMPACTION_PROJECTED_CONTEXT_UNAVAILABLE = "projected-compaction-context-unavailable" as const;
12
+ /** The latest opaque checkpoint was created with no or a different input marker. */
13
+ export const COMPACTION_CHECKPOINT_PROVENANCE_UNAVAILABLE = "unverified-compaction-checkpoint" as const;
14
+
15
+ /** Persisted edits changed input already sealed inside an opaque checkpoint. */
16
+ export const COMPACTION_CHECKPOINT_CONTEXT_EDITED = "checkpoint-context-edited" as const;
17
+
18
+ export type UnprojectedCompactionReason =
19
+ | typeof COMPACTION_CHECKPOINT_CONTEXT_EDITED
20
+ | typeof COMPACTION_PROJECTION_UNAVAILABLE
21
+ | typeof COMPACTION_SESSION_CONTEXT_UNAVAILABLE
22
+ | typeof COMPACTION_CHECKPOINT_PROVENANCE_UNAVAILABLE
23
+ | typeof COMPACTION_PROJECTED_CONTEXT_UNAVAILABLE;
24
+
25
+ /**
26
+ * A Remote V2 checkpoint is safe for the current replay chain only when its
27
+ * marker matches the configured input source. Structural persistence validation
28
+ * intentionally remains more permissive for old session files.
29
+ */
30
+ export function hasVerifiedCompactionInputProvenance(
31
+ details: Pick<NativeCompactionDetails, "inputProvenance"> | undefined,
32
+ expected: NativeCompactionInputProvenance,
33
+ ): boolean {
34
+ return details?.inputProvenance === expected;
35
+ }