pi-openai-toolkit 0.24.0 → 0.24.1

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 (37) hide show
  1. package/LICENSE +21 -21
  2. package/NOTICE +83 -83
  3. package/config.schema.json +264 -264
  4. package/docs/configuration.md +304 -304
  5. package/package.json +123 -123
  6. package/src/compact-client.ts +412 -412
  7. package/src/compaction-projection.ts +35 -35
  8. package/src/config/legacy.ts +469 -469
  9. package/src/config/migration.ts +115 -115
  10. package/src/config/notifications.ts +46 -42
  11. package/src/config/policy.ts +115 -115
  12. package/src/config/v2.ts +1128 -1128
  13. package/src/config.ts +320 -320
  14. package/src/debug.ts +238 -238
  15. package/src/deferred-tool-carryover.ts +282 -282
  16. package/src/details-store.ts +151 -151
  17. package/src/extension-runtime.ts +1095 -1095
  18. package/src/image-generation/extension.ts +189 -189
  19. package/src/image-generation/protocol.ts +579 -579
  20. package/src/image-generation/types.ts +300 -300
  21. package/src/native-fallback.ts +140 -140
  22. package/src/payload-rewrite.ts +515 -515
  23. package/src/pi-context-hook.ts +117 -117
  24. package/src/remote-v2-client.ts +811 -811
  25. package/src/request-context-cache.ts +155 -155
  26. package/src/responses-headers.ts +180 -180
  27. package/src/retired-remote-windows.ts +35 -35
  28. package/src/runtime.ts +875 -875
  29. package/src/serializer.ts +882 -882
  30. package/src/supported-environment.ts +27 -27
  31. package/src/types.ts +541 -541
  32. package/src/web-search/alpha-client.ts +876 -876
  33. package/src/web-search/extension.ts +530 -530
  34. package/src/web-search/payload.ts +299 -299
  35. package/src/web-search/prompt.ts +60 -60
  36. package/src/web-search/types.ts +166 -166
  37. package/tsconfig.check.json +50 -50
@@ -1,115 +1,115 @@
1
- import { resolveToolkitConfig } from "../config";
2
- import type { LoadedToolkitConfig } from "../types";
3
- import {
4
- RETIRED_AUTO_MODE_ISSUE, RETIRED_AUTO_MODE_MESSAGE,
5
- RETIRED_REMOTE_WINDOWS_ISSUE, RETIRED_REMOTE_WINDOWS_MODE_ISSUE,
6
- RETIRED_REMOTE_WINDOWS_CONFIG_MESSAGE, RETIRED_REMOTE_WINDOWS_MODE_MESSAGE,
7
- type ConfigOrigin,
8
- } from "./policy";
9
- import { resolveV2Config } from "./v2";
10
-
11
- export type MigrationPreview = {
12
- status: "ready" | "needs-review" | "already-v2" | "unavailable";
13
- candidate?: Record<string, unknown>;
14
- /** Controlled explanations, never arbitrary raw input values. */
15
- reasons: string[];
16
- /** Unknown names are masked; paths never expose unknown values. */
17
- unmappedPaths: string[];
18
- /** Candidate leaf -> origin in the unchanged legacy source (or built-in). */
19
- origins?: Record<string, ConfigOrigin>;
20
- };
21
-
22
- function modelRef(key: string): { provider: string; id: string; api: string } {
23
- const slash = key.indexOf("/");
24
- return { provider: key.slice(0, slash), id: key.slice(slash + 1), api: "openai-responses" };
25
- }
26
-
27
- /** Read-only analysis: never edits a source file or claims a lossy candidate is equivalent. */
28
- export function previewToolkitMigration(loaded: LoadedToolkitConfig): MigrationPreview {
29
- if (loaded.document?.format === "v2") {
30
- const retired = resolveToolkitConfig(loaded).issues.filter((issue) =>
31
- [RETIRED_AUTO_MODE_ISSUE, RETIRED_REMOTE_WINDOWS_ISSUE, RETIRED_REMOTE_WINDOWS_MODE_ISSUE].some((code) => code === issue.code));
32
- const removedMode = retired.some((issue) => issue.code === RETIRED_REMOTE_WINDOWS_MODE_ISSUE);
33
- return { status: removedMode ? "needs-review" : "already-v2", reasons: ["The document already uses schemaVersion 2.",
34
- ...(retired.some((issue) => issue.code === RETIRED_AUTO_MODE_ISSUE) ? [RETIRED_AUTO_MODE_MESSAGE] : []),
35
- ...(retired.some((issue) => issue.code === RETIRED_REMOTE_WINDOWS_ISSUE) ? [RETIRED_REMOTE_WINDOWS_CONFIG_MESSAGE] : []),
36
- ...(removedMode ? [RETIRED_REMOTE_WINDOWS_MODE_MESSAGE] : [])], unmappedPaths: retired.map((issue) => issue.path) };
37
- }
38
- if (loaded.document?.format === "invalid" || loaded.document?.format === "missing") {
39
- return { status: "unavailable", reasons: ["A readable legacy document is required; no candidate was produced."], unmappedPaths: [] };
40
- }
41
- const baseline = resolveToolkitConfig(loaded);
42
- const legacy = baseline.config;
43
- const { compatibility: _compatibility, diagnostics, ...defaults } = structuredClone(baseline.policy);
44
- const models: Record<string, unknown> = Object.create(null);
45
- const origins: Record<string, ConfigOrigin> = Object.create(null);
46
- const unmappedPaths = new Set(baseline.issues.map((issue) => issue.path));
47
- for (const [leaf, origin] of Object.entries(baseline.origins)) {
48
- if (leaf.startsWith("compatibility.")) continue;
49
- origins[leaf.startsWith("diagnostics.") ? leaf : `defaults.${leaf}`] = origin;
50
- }
51
- const keys = new Set([
52
- ...legacy.webSearch.models,
53
- ...Object.keys(legacy.webSearch.routes ?? {}),
54
- ...legacy.compaction.gatewayContextModels,
55
- ]);
56
- for (const key of keys) {
57
- const resolved = resolveToolkitConfig(loaded, modelRef(key));
58
- const override: Record<string, unknown> = {};
59
- if (resolved.policy.webSearch.route !== defaults.webSearch.route) override.webSearch = resolved.policy.webSearch;
60
- if (resolved.policy.compatibility.transport !== "standard") override.compatibility = resolved.policy.compatibility;
61
- if (Object.keys(override).length > 0) {
62
- models[key] = override;
63
- for (const [feature, fields] of Object.entries(override)) {
64
- for (const field of Object.keys(fields as Record<string, unknown>)) {
65
- const leaf = `${feature}.${field}`;
66
- origins[`models[${JSON.stringify(key)}].${leaf}`] = resolved.origins[leaf];
67
- }
68
- }
69
- }
70
- }
71
- const candidate = { schemaVersion: 2, defaults, models, diagnostics };
72
- const reasons: string[] = [];
73
- // Retired sections are never migrated; their presence is a controlled removal reason.
74
- if (baseline.issues.some((issue) => issue.code === RETIRED_AUTO_MODE_ISSUE)) {
75
- reasons.push(RETIRED_AUTO_MODE_MESSAGE + " No Auto Mode settings were migrated; review the removal before applying this candidate.");
76
- unmappedPaths.add("autoMode");
77
- }
78
- if (loaded.warnings.length || baseline.issues.length) {
79
- reasons.push("Legacy warnings, unknown fields or normalized invalid values require review. Their original contents remain in the untouched source file.");
80
- }
81
- if (legacy.webSearch.enabled && legacy.webSearch.defaultRoute === undefined &&
82
- legacy.webSearch.models.some((key) => !Object.prototype.hasOwnProperty.call(legacy.webSearch.routes ?? {}, key))) {
83
- reasons.push("Legacy hosted allowlists ignore unsupported APIs and malformed payloads; explicit v2 hosted routes fail closed. This candidate is not behavior-equivalent on those paths.");
84
- }
85
- if (!legacy.webSearch.enabled && (legacy.webSearch.models.length || Object.keys(legacy.webSearch.routes ?? {}).length || legacy.webSearch.defaultRoute)) {
86
- reasons.push("Disabled legacy search contains dormant routes or a model list; simplified v2 policy does not retain that enable-later intent.");
87
- if (legacy.webSearch.models.length) unmappedPaths.add("webSearch.models");
88
- if (Object.keys(legacy.webSearch.routes ?? {}).length) unmappedPaths.add("webSearch.routes");
89
- if (legacy.webSearch.defaultRoute) unmappedPaths.add("webSearch.defaultRoute");
90
- }
91
- if (legacy.webSearch.models.length && (legacy.webSearch.defaultRoute !== undefined ||
92
- legacy.webSearch.models.some((key) => Object.hasOwn(legacy.webSearch.routes ?? {}, key)))) {
93
- unmappedPaths.add("webSearch.models");
94
- }
95
- if (baseline.issues.some((issue) => issue.code === RETIRED_REMOTE_WINDOWS_MODE_ISSUE)) {
96
- reasons.push(`${RETIRED_REMOTE_WINDOWS_MODE_MESSAGE} The preview uses Pi ownership only as a safe review placeholder; it is not behavior-equivalent. Choose a supported mode explicitly before any manual apply.`);
97
- unmappedPaths.add("compaction.contextManagement");
98
- }
99
- if (baseline.issues.some((issue) => issue.code === RETIRED_REMOTE_WINDOWS_ISSUE)) {
100
- reasons.push(`${RETIRED_REMOTE_WINDOWS_CONFIG_MESSAGE} No retired ancillary settings were migrated; review their removal.`);
101
- }
102
- // Gateway affinity is shared across retained compaction and image features.
103
- if (legacy.compaction.gatewayContextModels.length) {
104
- reasons.push("Gateway compatibility is now shared across features; review synthetic compaction/image affinity before using this candidate.");
105
- }
106
- const candidateChecks = [resolveV2Config(candidate, undefined), ...[...keys].map((key) => resolveV2Config(candidate, key))];
107
- if (candidateChecks.some((resolved) => resolved.issues.length)) reasons.push("The candidate has v2 validation issues; it cannot be applied as-is.");
108
- return {
109
- status: reasons.length ? "needs-review" : "ready",
110
- candidate,
111
- origins,
112
- unmappedPaths: [...unmappedPaths],
113
- reasons: reasons.length ? reasons : ["Recognized active policies map without a known semantic difference. Preview only; no source file was changed."],
114
- };
115
- }
1
+ import { resolveToolkitConfig } from "../config";
2
+ import type { LoadedToolkitConfig } from "../types";
3
+ import {
4
+ RETIRED_AUTO_MODE_ISSUE, RETIRED_AUTO_MODE_MESSAGE,
5
+ RETIRED_REMOTE_WINDOWS_ISSUE, RETIRED_REMOTE_WINDOWS_MODE_ISSUE,
6
+ RETIRED_REMOTE_WINDOWS_CONFIG_MESSAGE, RETIRED_REMOTE_WINDOWS_MODE_MESSAGE,
7
+ type ConfigOrigin,
8
+ } from "./policy";
9
+ import { resolveV2Config } from "./v2";
10
+
11
+ export type MigrationPreview = {
12
+ status: "ready" | "needs-review" | "already-v2" | "unavailable";
13
+ candidate?: Record<string, unknown>;
14
+ /** Controlled explanations, never arbitrary raw input values. */
15
+ reasons: string[];
16
+ /** Unknown names are masked; paths never expose unknown values. */
17
+ unmappedPaths: string[];
18
+ /** Candidate leaf -> origin in the unchanged legacy source (or built-in). */
19
+ origins?: Record<string, ConfigOrigin>;
20
+ };
21
+
22
+ function modelRef(key: string): { provider: string; id: string; api: string } {
23
+ const slash = key.indexOf("/");
24
+ return { provider: key.slice(0, slash), id: key.slice(slash + 1), api: "openai-responses" };
25
+ }
26
+
27
+ /** Read-only analysis: never edits a source file or claims a lossy candidate is equivalent. */
28
+ export function previewToolkitMigration(loaded: LoadedToolkitConfig): MigrationPreview {
29
+ if (loaded.document?.format === "v2") {
30
+ const retired = resolveToolkitConfig(loaded).issues.filter((issue) =>
31
+ [RETIRED_AUTO_MODE_ISSUE, RETIRED_REMOTE_WINDOWS_ISSUE, RETIRED_REMOTE_WINDOWS_MODE_ISSUE].some((code) => code === issue.code));
32
+ const removedMode = retired.some((issue) => issue.code === RETIRED_REMOTE_WINDOWS_MODE_ISSUE);
33
+ return { status: removedMode ? "needs-review" : "already-v2", reasons: ["The document already uses schemaVersion 2.",
34
+ ...(retired.some((issue) => issue.code === RETIRED_AUTO_MODE_ISSUE) ? [RETIRED_AUTO_MODE_MESSAGE] : []),
35
+ ...(retired.some((issue) => issue.code === RETIRED_REMOTE_WINDOWS_ISSUE) ? [RETIRED_REMOTE_WINDOWS_CONFIG_MESSAGE] : []),
36
+ ...(removedMode ? [RETIRED_REMOTE_WINDOWS_MODE_MESSAGE] : [])], unmappedPaths: retired.map((issue) => issue.path) };
37
+ }
38
+ if (loaded.document?.format === "invalid" || loaded.document?.format === "missing") {
39
+ return { status: "unavailable", reasons: ["A readable legacy document is required; no candidate was produced."], unmappedPaths: [] };
40
+ }
41
+ const baseline = resolveToolkitConfig(loaded);
42
+ const legacy = baseline.config;
43
+ const { compatibility: _compatibility, diagnostics, ...defaults } = structuredClone(baseline.policy);
44
+ const models: Record<string, unknown> = Object.create(null);
45
+ const origins: Record<string, ConfigOrigin> = Object.create(null);
46
+ const unmappedPaths = new Set(baseline.issues.map((issue) => issue.path));
47
+ for (const [leaf, origin] of Object.entries(baseline.origins)) {
48
+ if (leaf.startsWith("compatibility.")) continue;
49
+ origins[leaf.startsWith("diagnostics.") ? leaf : `defaults.${leaf}`] = origin;
50
+ }
51
+ const keys = new Set([
52
+ ...legacy.webSearch.models,
53
+ ...Object.keys(legacy.webSearch.routes ?? {}),
54
+ ...legacy.compaction.gatewayContextModels,
55
+ ]);
56
+ for (const key of keys) {
57
+ const resolved = resolveToolkitConfig(loaded, modelRef(key));
58
+ const override: Record<string, unknown> = {};
59
+ if (resolved.policy.webSearch.route !== defaults.webSearch.route) override.webSearch = resolved.policy.webSearch;
60
+ if (resolved.policy.compatibility.transport !== "standard") override.compatibility = resolved.policy.compatibility;
61
+ if (Object.keys(override).length > 0) {
62
+ models[key] = override;
63
+ for (const [feature, fields] of Object.entries(override)) {
64
+ for (const field of Object.keys(fields as Record<string, unknown>)) {
65
+ const leaf = `${feature}.${field}`;
66
+ origins[`models[${JSON.stringify(key)}].${leaf}`] = resolved.origins[leaf];
67
+ }
68
+ }
69
+ }
70
+ }
71
+ const candidate = { schemaVersion: 2, defaults, models, diagnostics };
72
+ const reasons: string[] = [];
73
+ // Retired sections are never migrated; their presence is a controlled removal reason.
74
+ if (baseline.issues.some((issue) => issue.code === RETIRED_AUTO_MODE_ISSUE)) {
75
+ reasons.push(RETIRED_AUTO_MODE_MESSAGE + " No Auto Mode settings were migrated; review the removal before applying this candidate.");
76
+ unmappedPaths.add("autoMode");
77
+ }
78
+ if (loaded.warnings.length || baseline.issues.length) {
79
+ reasons.push("Legacy warnings, unknown fields or normalized invalid values require review. Their original contents remain in the untouched source file.");
80
+ }
81
+ if (legacy.webSearch.enabled && legacy.webSearch.defaultRoute === undefined &&
82
+ legacy.webSearch.models.some((key) => !Object.prototype.hasOwnProperty.call(legacy.webSearch.routes ?? {}, key))) {
83
+ reasons.push("Legacy hosted allowlists ignore unsupported APIs and malformed payloads; explicit v2 hosted routes fail closed. This candidate is not behavior-equivalent on those paths.");
84
+ }
85
+ if (!legacy.webSearch.enabled && (legacy.webSearch.models.length || Object.keys(legacy.webSearch.routes ?? {}).length || legacy.webSearch.defaultRoute)) {
86
+ reasons.push("Disabled legacy search contains dormant routes or a model list; simplified v2 policy does not retain that enable-later intent.");
87
+ if (legacy.webSearch.models.length) unmappedPaths.add("webSearch.models");
88
+ if (Object.keys(legacy.webSearch.routes ?? {}).length) unmappedPaths.add("webSearch.routes");
89
+ if (legacy.webSearch.defaultRoute) unmappedPaths.add("webSearch.defaultRoute");
90
+ }
91
+ if (legacy.webSearch.models.length && (legacy.webSearch.defaultRoute !== undefined ||
92
+ legacy.webSearch.models.some((key) => Object.hasOwn(legacy.webSearch.routes ?? {}, key)))) {
93
+ unmappedPaths.add("webSearch.models");
94
+ }
95
+ if (baseline.issues.some((issue) => issue.code === RETIRED_REMOTE_WINDOWS_MODE_ISSUE)) {
96
+ reasons.push(`${RETIRED_REMOTE_WINDOWS_MODE_MESSAGE} The preview uses Pi ownership only as a safe review placeholder; it is not behavior-equivalent. Choose a supported mode explicitly before any manual apply.`);
97
+ unmappedPaths.add("compaction.contextManagement");
98
+ }
99
+ if (baseline.issues.some((issue) => issue.code === RETIRED_REMOTE_WINDOWS_ISSUE)) {
100
+ reasons.push(`${RETIRED_REMOTE_WINDOWS_CONFIG_MESSAGE} No retired ancillary settings were migrated; review their removal.`);
101
+ }
102
+ // Gateway affinity is shared across retained compaction and image features.
103
+ if (legacy.compaction.gatewayContextModels.length) {
104
+ reasons.push("Gateway compatibility is now shared across features; review synthetic compaction/image affinity before using this candidate.");
105
+ }
106
+ const candidateChecks = [resolveV2Config(candidate, undefined), ...[...keys].map((key) => resolveV2Config(candidate, key))];
107
+ if (candidateChecks.some((resolved) => resolved.issues.length)) reasons.push("The candidate has v2 validation issues; it cannot be applied as-is.");
108
+ return {
109
+ status: reasons.length ? "needs-review" : "ready",
110
+ candidate,
111
+ origins,
112
+ unmappedPaths: [...unmappedPaths],
113
+ reasons: reasons.length ? reasons : ["Recognized active policies map without a known semantic difference. Preview only; no source file was changed."],
114
+ };
115
+ }
@@ -1,42 +1,46 @@
1
- import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
2
- import type { ResolvedToolkitConfig } from "../config";
3
- import {
4
- RETIRED_AUTO_MODE_ISSUE, RETIRED_AUTO_MODE_MESSAGE,
5
- RETIRED_REMOTE_WINDOWS_ISSUE, RETIRED_REMOTE_WINDOWS_MODE_ISSUE,
6
- RETIRED_REMOTE_WINDOWS_CONFIG_MESSAGE, RETIRED_REMOTE_WINDOWS_MODE_MESSAGE,
7
- type ConfigIssue,
8
- } from "./policy";
9
- import { redactValue } from "../debug";
10
-
11
- const lastNotice = new WeakMap<object, string>();
12
-
13
- /** Controlled human text for a known code; never built from user-supplied configuration values. */
14
- function issueExplanation(issue: ConfigIssue): string {
15
- if (issue.code === RETIRED_AUTO_MODE_ISSUE) return RETIRED_AUTO_MODE_MESSAGE;
16
- if (issue.code === RETIRED_REMOTE_WINDOWS_ISSUE) return RETIRED_REMOTE_WINDOWS_CONFIG_MESSAGE;
17
- if (issue.code === RETIRED_REMOTE_WINDOWS_MODE_ISSUE) return RETIRED_REMOTE_WINDOWS_MODE_MESSAGE;
18
- return issue.code;
19
- }
20
-
21
- export function formatConfigIssues(issues: readonly ConfigIssue[], limit = 12): string {
22
- const lines = issues.slice(0, limit).map((issue) =>
23
- `${issue.severity}: ${issue.path.slice(0, 220)} — ${issueExplanation(issue)}`);
24
- if (issues.length > limit) lines.push(`${issues.length - limit} more issue(s); inspect the configuration file.`);
25
- return String(redactValue(lines.join("\n")));
26
- }
27
-
28
- /** Per-session bounded notice, independent of feature enablement/debug. Never logs raw config. */
29
- export function notifyConfigIssues(ctx: ExtensionContext, resolved: ResolvedToolkitConfig): void {
30
- const owner = ctx.sessionManager ?? ctx;
31
- if (resolved.issues.length === 0) {
32
- lastNotice.delete(owner);
33
- return;
34
- }
35
- if (!ctx.hasUI || typeof ctx.ui?.notify !== "function") return;
36
- const message = formatConfigIssues(resolved.issues, 4);
37
- const fingerprint = `${resolved.source ?? "defaults"}\n${resolved.modelKey ?? ""}\n${message}`;
38
- if (lastNotice.get(owner) === fingerprint) return;
39
- lastNotice.set(owner, fingerprint);
40
- ctx.ui.notify(`Toolkit configuration:\n${message}\nUse /toolkit-config validate for details.`,
41
- resolved.issues.some((issue) => issue.severity === "error") ? "error" : "warning");
42
- }
1
+ import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
2
+ import type { ResolvedToolkitConfig } from "../config";
3
+ import {
4
+ RETIRED_AUTO_MODE_ISSUE, RETIRED_AUTO_MODE_MESSAGE,
5
+ RETIRED_REMOTE_WINDOWS_ISSUE, RETIRED_REMOTE_WINDOWS_MODE_ISSUE,
6
+ RETIRED_REMOTE_WINDOWS_CONFIG_MESSAGE, RETIRED_REMOTE_WINDOWS_MODE_MESSAGE,
7
+ type ConfigIssue,
8
+ } from "./policy";
9
+ import { redactValue } from "../debug";
10
+ import { detectRetiredRemoteWindows } from "../retired-remote-windows";
11
+
12
+ const lastNotice = new WeakMap<object, string>();
13
+
14
+ /** Controlled human text for a known code; never built from user-supplied configuration values. */
15
+ function issueExplanation(issue: ConfigIssue): string {
16
+ if (issue.code === RETIRED_AUTO_MODE_ISSUE) return RETIRED_AUTO_MODE_MESSAGE;
17
+ if (issue.code === RETIRED_REMOTE_WINDOWS_ISSUE) return RETIRED_REMOTE_WINDOWS_CONFIG_MESSAGE;
18
+ if (issue.code === RETIRED_REMOTE_WINDOWS_MODE_ISSUE) return RETIRED_REMOTE_WINDOWS_MODE_MESSAGE;
19
+ return issue.code;
20
+ }
21
+
22
+ export function formatConfigIssues(issues: readonly ConfigIssue[], limit = 12): string {
23
+ const lines = issues.slice(0, limit).map((issue) =>
24
+ `${issue.severity}: ${issue.path.slice(0, 220)} — ${issueExplanation(issue)}`);
25
+ if (issues.length > limit) lines.push(`${issues.length - limit} more issue(s); inspect the configuration file.`);
26
+ return String(redactValue(lines.join("\n")));
27
+ }
28
+
29
+ /** Per-session bounded notice, independent of feature enablement/debug. Never logs raw config. */
30
+ export function notifyConfigIssues(ctx: ExtensionContext, resolved: ResolvedToolkitConfig): void {
31
+ const owner = ctx.sessionManager ?? ctx;
32
+ if (!ctx.hasUI || typeof ctx.ui?.notify !== "function") return;
33
+ const hasRetiredWindows = resolved.issues.some((issue) => issue.code === RETIRED_REMOTE_WINDOWS_ISSUE)
34
+ && detectRetiredRemoteWindows(ctx.sessionManager.getBranch()) !== undefined;
35
+ const issues = resolved.issues.filter((issue) => issue.code !== RETIRED_REMOTE_WINDOWS_ISSUE || hasRetiredWindows);
36
+ if (issues.length === 0) {
37
+ lastNotice.delete(owner);
38
+ return;
39
+ }
40
+ const message = formatConfigIssues(issues, 4);
41
+ const fingerprint = `${resolved.source ?? "defaults"}\n${resolved.modelKey ?? ""}\n${message}`;
42
+ if (lastNotice.get(owner) === fingerprint) return;
43
+ lastNotice.set(owner, fingerprint);
44
+ ctx.ui.notify(`Toolkit configuration:\n${message}\nUse /toolkit-config validate for details.`,
45
+ issues.some((issue) => issue.severity === "error") ? "error" : "warning");
46
+ }
@@ -1,115 +1,115 @@
1
- import {
2
- DEFAULT_COMPACTION_CONFIG,
3
- DEFAULT_IMAGE_GENERATION_MODEL,
4
- type NativeFallbackConfig,
5
- type RemoteV2ContextSource,
6
- } from "../types";
7
-
8
- export type ConfigFeature = "context" | "webSearch" | "imageGeneration" | "compatibility" | "responsesWebSocket" | "diagnostics";
9
- export type SearchRoute = "unmanaged" | "local" | "hosted" | "standalone-alpha";
10
- export type DiagnosticsConfig = {
11
- level: "error" | "warn" | "info" | "debug";
12
- notifyOnLoad: boolean;
13
- captureRequests: boolean;
14
- /** Captures compact responses, not ordinary conversation responses. */
15
- captureResponses: boolean;
16
- redactSensitiveData: boolean;
17
- artifactRoot: string;
18
- };
19
- export type ContextPolicy = {
20
- mode: "pi" | "remote-compaction";
21
- remoteCompaction: {
22
- model: string | null;
23
- inputSource: RemoteV2ContextSource;
24
- allowContinuityBreak: boolean;
25
- apis: string[];
26
- };
27
- nativeFallback: Omit<NativeFallbackConfig, "model"> & { model: string | null };
28
- };
29
- export type EffectiveToolkitPolicy = {
30
- context: ContextPolicy;
31
- webSearch: { route: SearchRoute };
32
- imageGeneration: { enabled: boolean; defaultModel: string; allowedModels: string[] };
33
- responsesWebSocket: { enabled: boolean };
34
- compatibility: { transport: "standard" | "codex-gateway" };
35
- diagnostics: DiagnosticsConfig;
36
- };
37
- export type ConfigOrigin = {
38
- kind: "builtin" | "defaults" | "model" | "legacy";
39
- path?: string;
40
- source?: string;
41
- };
42
- export type ConfigIssue = {
43
- severity: "error" | "warning";
44
- /** Controlled code; do not include raw input values in messages. */
45
- code: string;
46
- path: string;
47
- feature: ConfigFeature | "document";
48
- /** Issues for a different model do not invalidate the selected model. */
49
- modelKey?: string;
50
- };
51
- /**
52
- * Stable code for a configuration section that was removed from the Toolkit. A retired section is
53
- * ignored in any shape and must never invalidate another feature, so it is reported as a warning.
54
- */
55
- export const RETIRED_AUTO_MODE_ISSUE = "retired.autoMode" as const;
56
- /** Controlled explanation of the retirement; never contains user-supplied values. */
57
- export const RETIRED_AUTO_MODE_MESSAGE =
58
- "Auto Mode has been removed; these settings are ignored and tool calls are no longer reviewed by it.";
59
- /** Opaque ancillary settings are warning-only; explicitly selected retired modes block context. */
60
- export const RETIRED_REMOTE_WINDOWS_ISSUE = "retired.remoteWindows" as const;
61
- export const RETIRED_REMOTE_WINDOWS_MODE_ISSUE = "retired.remoteWindows.mode" as const;
62
- export const RETIRED_REMOTE_WINDOWS_CONFIG_MESSAGE =
63
- "Remote Windows has been removed; these settings are ignored. No window behavior or automatic migration is available.";
64
- export const RETIRED_REMOTE_WINDOWS_MODE_MESSAGE =
65
- "Remote Windows has been removed. Context operations are blocked until you explicitly select pi or remote-compaction. No replacement mode was activated.";
66
-
67
- export type ResolvedToolkitPolicy = {
68
- policy: EffectiveToolkitPolicy;
69
- origins: Record<string, ConfigOrigin>;
70
- issues: ConfigIssue[];
71
- invalidFeatures: ConfigFeature[];
72
- };
73
- export type ConfigDocumentSnapshot = {
74
- format: "legacy" | "v2" | "missing" | "invalid";
75
- configPath: string;
76
- /** In-memory only; never render arbitrary raw configuration values. */
77
- raw?: unknown;
78
- issues: ConfigIssue[];
79
- };
80
- export const CONFIG_FEATURES: readonly ConfigFeature[] = [
81
- "context", "webSearch", "imageGeneration", "compatibility", "responsesWebSocket", "diagnostics",
82
- ];
83
-
84
- /** Independent per-operation defaults, preserving the existing shipped policy. */
85
- export function createPolicyDefaults(): EffectiveToolkitPolicy {
86
- const context = DEFAULT_COMPACTION_CONFIG;
87
- return {
88
- context: {
89
- mode: "remote-compaction",
90
- remoteCompaction: {
91
- model: null,
92
- inputSource: context.remoteV2ContextSource,
93
- allowContinuityBreak: context.allowCompactionContinuityBreak,
94
- apis: [...context.responsesApis],
95
- },
96
- nativeFallback: { ...context.nativeFallback, model: null },
97
- },
98
- webSearch: { route: "unmanaged" },
99
- imageGeneration: {
100
- enabled: false,
101
- defaultModel: DEFAULT_IMAGE_GENERATION_MODEL,
102
- allowedModels: [DEFAULT_IMAGE_GENERATION_MODEL],
103
- },
104
- responsesWebSocket: { enabled: false },
105
- compatibility: { transport: "standard" },
106
- diagnostics: {
107
- level: "info",
108
- notifyOnLoad: context.notifyOnLoad,
109
- captureRequests: context.logProviderPayloads,
110
- captureResponses: context.logCompactResponses,
111
- redactSensitiveData: context.redactSensitiveData,
112
- artifactRoot: context.artifactRoot,
113
- },
114
- };
115
- }
1
+ import {
2
+ DEFAULT_COMPACTION_CONFIG,
3
+ DEFAULT_IMAGE_GENERATION_MODEL,
4
+ type NativeFallbackConfig,
5
+ type RemoteV2ContextSource,
6
+ } from "../types";
7
+
8
+ export type ConfigFeature = "context" | "webSearch" | "imageGeneration" | "compatibility" | "responsesWebSocket" | "diagnostics";
9
+ export type SearchRoute = "unmanaged" | "local" | "hosted" | "standalone-alpha";
10
+ export type DiagnosticsConfig = {
11
+ level: "error" | "warn" | "info" | "debug";
12
+ notifyOnLoad: boolean;
13
+ captureRequests: boolean;
14
+ /** Captures compact responses, not ordinary conversation responses. */
15
+ captureResponses: boolean;
16
+ redactSensitiveData: boolean;
17
+ artifactRoot: string;
18
+ };
19
+ export type ContextPolicy = {
20
+ mode: "pi" | "remote-compaction";
21
+ remoteCompaction: {
22
+ model: string | null;
23
+ inputSource: RemoteV2ContextSource;
24
+ allowContinuityBreak: boolean;
25
+ apis: string[];
26
+ };
27
+ nativeFallback: Omit<NativeFallbackConfig, "model"> & { model: string | null };
28
+ };
29
+ export type EffectiveToolkitPolicy = {
30
+ context: ContextPolicy;
31
+ webSearch: { route: SearchRoute };
32
+ imageGeneration: { enabled: boolean; defaultModel: string; allowedModels: string[] };
33
+ responsesWebSocket: { enabled: boolean };
34
+ compatibility: { transport: "standard" | "codex-gateway" };
35
+ diagnostics: DiagnosticsConfig;
36
+ };
37
+ export type ConfigOrigin = {
38
+ kind: "builtin" | "defaults" | "model" | "legacy";
39
+ path?: string;
40
+ source?: string;
41
+ };
42
+ export type ConfigIssue = {
43
+ severity: "error" | "warning";
44
+ /** Controlled code; do not include raw input values in messages. */
45
+ code: string;
46
+ path: string;
47
+ feature: ConfigFeature | "document";
48
+ /** Issues for a different model do not invalidate the selected model. */
49
+ modelKey?: string;
50
+ };
51
+ /**
52
+ * Stable code for a configuration section that was removed from the Toolkit. A retired section is
53
+ * ignored in any shape and must never invalidate another feature, so it is reported as a warning.
54
+ */
55
+ export const RETIRED_AUTO_MODE_ISSUE = "retired.autoMode" as const;
56
+ /** Controlled explanation of the retirement; never contains user-supplied values. */
57
+ export const RETIRED_AUTO_MODE_MESSAGE =
58
+ "Auto Mode has been removed; these settings are ignored and tool calls are no longer reviewed by it.";
59
+ /** Opaque ancillary settings are warning-only; explicitly selected retired modes block context. */
60
+ export const RETIRED_REMOTE_WINDOWS_ISSUE = "retired.remoteWindows" as const;
61
+ export const RETIRED_REMOTE_WINDOWS_MODE_ISSUE = "retired.remoteWindows.mode" as const;
62
+ export const RETIRED_REMOTE_WINDOWS_CONFIG_MESSAGE =
63
+ "Remote Windows has been removed; these settings are ignored. No window behavior or automatic migration is available.";
64
+ export const RETIRED_REMOTE_WINDOWS_MODE_MESSAGE =
65
+ "Remote Windows has been removed. Context operations are blocked until you explicitly select pi or remote-compaction. No replacement mode was activated.";
66
+
67
+ export type ResolvedToolkitPolicy = {
68
+ policy: EffectiveToolkitPolicy;
69
+ origins: Record<string, ConfigOrigin>;
70
+ issues: ConfigIssue[];
71
+ invalidFeatures: ConfigFeature[];
72
+ };
73
+ export type ConfigDocumentSnapshot = {
74
+ format: "legacy" | "v2" | "missing" | "invalid";
75
+ configPath: string;
76
+ /** In-memory only; never render arbitrary raw configuration values. */
77
+ raw?: unknown;
78
+ issues: ConfigIssue[];
79
+ };
80
+ export const CONFIG_FEATURES: readonly ConfigFeature[] = [
81
+ "context", "webSearch", "imageGeneration", "compatibility", "responsesWebSocket", "diagnostics",
82
+ ];
83
+
84
+ /** Independent per-operation defaults, preserving the existing shipped policy. */
85
+ export function createPolicyDefaults(): EffectiveToolkitPolicy {
86
+ const context = DEFAULT_COMPACTION_CONFIG;
87
+ return {
88
+ context: {
89
+ mode: "remote-compaction",
90
+ remoteCompaction: {
91
+ model: null,
92
+ inputSource: context.remoteV2ContextSource,
93
+ allowContinuityBreak: context.allowCompactionContinuityBreak,
94
+ apis: [...context.responsesApis],
95
+ },
96
+ nativeFallback: { ...context.nativeFallback, model: null },
97
+ },
98
+ webSearch: { route: "unmanaged" },
99
+ imageGeneration: {
100
+ enabled: false,
101
+ defaultModel: DEFAULT_IMAGE_GENERATION_MODEL,
102
+ allowedModels: [DEFAULT_IMAGE_GENERATION_MODEL],
103
+ },
104
+ responsesWebSocket: { enabled: false },
105
+ compatibility: { transport: "standard" },
106
+ diagnostics: {
107
+ level: "info",
108
+ notifyOnLoad: context.notifyOnLoad,
109
+ captureRequests: context.logProviderPayloads,
110
+ captureResponses: context.logCompactResponses,
111
+ redactSensitiveData: context.redactSensitiveData,
112
+ artifactRoot: context.artifactRoot,
113
+ },
114
+ };
115
+ }