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
@@ -1,161 +0,0 @@
1
- import { createHash } from "node:crypto";
2
- import type { Api, Model } from "@earendil-works/pi-ai";
3
- import type { ModelRegistry } from "@earendil-works/pi-coding-agent";
4
- import { parseModelSpec } from "../runtime";
5
- import { CLASSIFIER_SYSTEM_PROMPT } from "./prompt";
6
- import { boundReviewText, type ClassifierRisk, type FastDecisionReason } from "./types";
7
-
8
- /** One completed classification of the trajectory, tagged to a tool-call position. */
9
- export type ScoreRecord = {
10
- risk: ClassifierRisk;
11
- /** Tool-call counter value when the sample was taken. */
12
- scoredAtCall: number;
13
- /** Fingerprint of the user turns the score was based on. */
14
- authorizationVersion: string;
15
- sampledAt: number;
16
- };
17
-
18
- /**
19
- * Mutable per-session tracker for the non-blocking pre-scorer.
20
- *
21
- * The counters reproduce Codex's staleness rule: the agent keeps running while a
22
- * classification is in flight, so a score is only trustworthy if it is recent
23
- * enough and nothing the user said has changed underneath it.
24
- */
25
- export type ScoreTracker = {
26
- latestCallIndex: number;
27
- latestScoredIndex: number;
28
- latestFailedIndex: number;
29
- score?: ScoreRecord;
30
- };
31
-
32
- export function createScoreTracker(): ScoreTracker {
33
- return { latestCallIndex: 0, latestScoredIndex: 0, latestFailedIndex: 0 };
34
- }
35
-
36
- /** Reset everything: a new turn or a disengaged mode invalidates prior scoring. */
37
- export function resetScoreTracker(tracker: ScoreTracker): void {
38
- tracker.latestCallIndex = 0;
39
- tracker.latestScoredIndex = 0;
40
- tracker.latestFailedIndex = 0;
41
- tracker.score = undefined;
42
- }
43
-
44
- /**
45
- * Fingerprint of the user-authored conversation. Any new or edited user turn makes
46
- * an existing score stale, because the score's authorization premise changed.
47
- */
48
- export function authorizationVersion(userText: string): string {
49
- return createHash("sha256").update(userText).digest("hex");
50
- }
51
-
52
- export function recordScoredCall(tracker: ScoreTracker, score: ScoreRecord): void {
53
- tracker.score = score;
54
- tracker.latestScoredIndex = Math.max(tracker.latestScoredIndex, score.scoredAtCall);
55
- }
56
-
57
- export function recordFailedCall(tracker: ScoreTracker, scoredAtCall: number): void {
58
- tracker.latestFailedIndex = Math.max(tracker.latestFailedIndex, scoredAtCall);
59
- }
60
-
61
- export type FastDecisionInput = {
62
- tracker: ScoreTracker;
63
- /** Tool-call counter value for the call being gated. */
64
- currentCallIndex: number;
65
- authorizationVersion: string;
66
- maxLag: number;
67
- /** Retries after a denial and widened gates must not take the fast path. */
68
- requiresSynchronousReview?: boolean;
69
- };
70
-
71
- /**
72
- * Decide whether a cached classification can satisfy this gated call without
73
- * running the blocking reviewer. Anything other than `low_risk` defers to the
74
- * synchronous review; the classifier never denies an action on its own.
75
- */
76
- export function fastApprovalEligible(input: FastDecisionInput): {
77
- eligible: boolean;
78
- reason: FastDecisionReason;
79
- } {
80
- const defer = (reason: FastDecisionReason) => ({ eligible: false, reason });
81
-
82
- if (input.requiresSynchronousReview) return defer("requires_synchronous_review");
83
- if (input.tracker.latestFailedIndex > input.tracker.latestScoredIndex) return defer("scoring_failure");
84
-
85
- const score = input.tracker.score;
86
- if (!score) return defer("missing_score");
87
-
88
- const lag = input.currentCallIndex - score.scoredAtCall;
89
- if (lag < 0 || lag > input.maxLag) return defer("stale_score");
90
- if (score.authorizationVersion !== input.authorizationVersion) return defer("authorization_changed");
91
- if (score.risk === "low") return { eligible: true, reason: "low_risk" };
92
- return defer("elevated_risk");
93
- }
94
-
95
- export type ClassificationResult =
96
- | { kind: ClassifierRisk; model: string }
97
- | { kind: "failed"; reason: string };
98
-
99
- /**
100
- * Run one non-blocking classification. Output is a single token; anything else is a
101
- * failed sample rather than a verdict, so a confused classifier costs a review and
102
- * never an approval or a denial.
103
- */
104
- export async function classifyTrajectory(params: {
105
- registry: Pick<ModelRegistry, "find" | "complete">;
106
- modelSpec: string;
107
- prompt: string;
108
- timeoutMs: number;
109
- signal?: AbortSignal;
110
- }): Promise<ClassificationResult> {
111
- const parsed = parseModelSpec(params.modelSpec);
112
- if (!parsed) {
113
- return { kind: "failed", reason: `autoMode.classifier.model "${params.modelSpec}" is not provider/model-id.` };
114
- }
115
- const model = params.registry.find(parsed.provider, parsed.modelId) as Model<Api> | undefined;
116
- if (!model) {
117
- return { kind: "failed", reason: `Classifier model ${params.modelSpec} is not available.` };
118
- }
119
- const label = boundReviewText(`${model.provider}/${model.id}`, 256);
120
-
121
- const controller = new AbortController();
122
- const abortFromCaller = () => controller.abort();
123
- if (params.signal?.aborted) controller.abort();
124
- else params.signal?.addEventListener("abort", abortFromCaller);
125
- const timer = setTimeout(() => controller.abort(), params.timeoutMs);
126
-
127
- try {
128
- const response = await params.registry.complete(
129
- model,
130
- {
131
- systemPrompt: CLASSIFIER_SYSTEM_PROMPT,
132
- messages: [
133
- {
134
- role: "user",
135
- content: [{ type: "text", text: params.prompt }],
136
- timestamp: Date.now(),
137
- },
138
- ],
139
- },
140
- { signal: controller.signal, cacheRetention: "none" },
141
- );
142
- if (controller.signal.aborted) return { kind: "failed", reason: "Classifier was cancelled or timed out." };
143
- if (response.stopReason === "error" || response.stopReason === "aborted") {
144
- return { kind: "failed", reason: response.errorMessage ?? `Classifier stopped: ${response.stopReason}` };
145
- }
146
- const text = response.content
147
- .filter((block): block is { type: "text"; text: string } => block.type === "text")
148
- .map((block) => block.text)
149
- .join("")
150
- .trim()
151
- .toLowerCase();
152
- if (/^high\b/.test(text)) return { kind: "high", model: label };
153
- if (/^low\b/.test(text)) return { kind: "low", model: label };
154
- return { kind: "failed", reason: `Classifier returned unreadable output: ${boundReviewText(text, 80)}` };
155
- } catch (error) {
156
- return { kind: "failed", reason: error instanceof Error ? error.message : String(error) };
157
- } finally {
158
- clearTimeout(timer);
159
- params.signal?.removeEventListener("abort", abortFromCaller);
160
- }
161
- }