codex-copilot-dx 0.6.5 → 0.6.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/bin/cli.mjs +1 -0
- package/package.json +1 -1
- package/scripts/benchmark.mjs +58 -1
- package/src/adapter.mjs +1 -0
- package/src/cli-auth.mjs +2 -11
- package/src/cli-models.mjs +14 -7
- package/src/copilot-client.mjs +3 -1
- package/src/doctor.mjs +9 -8
- package/src/http-transport.mjs +114 -13
- package/src/image-optimization.mjs +33 -11
- package/src/legacy-command-warning.mjs +3 -2
- package/src/models.mjs +14 -10
- package/src/observability.mjs +18 -1
- package/src/pm-studio-setup.mjs +28 -4
- package/src/pm-studio-status.mjs +103 -9
- package/src/profile-routing.mjs +7 -16
- package/src/profile-runtime.mjs +12 -3
- package/src/response-history.mjs +12 -2
- package/src/responses-byte-budget.mjs +53 -26
- package/src/responses-chat-payload.mjs +35 -16
- package/src/responses-content.mjs +36 -4
- package/src/responses-handler.mjs +47 -27
- package/src/responses-image-limit.mjs +16 -2
- package/src/responses-image-pressure.mjs +10 -4
- package/src/responses-request.mjs +28 -10
package/src/models.mjs
CHANGED
|
@@ -95,11 +95,6 @@ function numericLimit(value, fallback) {
|
|
|
95
95
|
return Number.isFinite(value) && value > 0 ? value : fallback;
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
function hasMessagesEndpoint(model) {
|
|
99
|
-
const endpoints = Array.isArray(model?.supported_endpoints) ? model.supported_endpoints : [];
|
|
100
|
-
return endpoints.includes("/v1/messages") || endpoints.includes("/chat/completions");
|
|
101
|
-
}
|
|
102
|
-
|
|
103
98
|
function hasOpenAIEndpoint(model) {
|
|
104
99
|
const endpoints = Array.isArray(model?.supported_endpoints) ? model.supported_endpoints : [];
|
|
105
100
|
return endpoints.includes("/responses") || endpoints.includes("/v1/responses") || endpoints.includes("/chat/completions");
|
|
@@ -140,13 +135,22 @@ function uniqueIds(models, predicate) {
|
|
|
140
135
|
return ids;
|
|
141
136
|
}
|
|
142
137
|
|
|
143
|
-
function
|
|
138
|
+
export function isClaudeCopilotCatalogEntry(model) {
|
|
144
139
|
const id = String(model?.id || "").trim();
|
|
145
|
-
const vendor = String(model?.vendor || "").toLowerCase();
|
|
146
|
-
return id
|
|
147
|
-
|
|
140
|
+
const vendor = String(model?.vendor || model?.owned_by || "").trim().toLowerCase();
|
|
141
|
+
return Boolean(id) && (id.toLowerCase().startsWith("claude-") || vendor === "anthropic");
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export function isClaudeCopilotModel(model) {
|
|
145
|
+
const policy = String(model?.policy?.state || "").trim().toLowerCase();
|
|
146
|
+
const declaredOwners = [model?.vendor, model?.owned_by]
|
|
147
|
+
.map((value) => String(value || "").trim().toLowerCase())
|
|
148
|
+
.filter(Boolean);
|
|
149
|
+
return isClaudeCopilotCatalogEntry(model)
|
|
150
|
+
&& declaredOwners.every((owner) => owner === "anthropic")
|
|
148
151
|
&& model?.model_picker_enabled !== false
|
|
149
|
-
&&
|
|
152
|
+
&& (!policy || policy === "enabled")
|
|
153
|
+
&& modelSupportsChatCompletions(model);
|
|
150
154
|
}
|
|
151
155
|
|
|
152
156
|
export function gptModelIdsFromCopilotModels(models) {
|
package/src/observability.mjs
CHANGED
|
@@ -150,6 +150,15 @@ function safeClientRuntimeStatus(client) {
|
|
|
150
150
|
};
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
+
function safeProfileCurrent(checkProfileCurrent) {
|
|
154
|
+
if (typeof checkProfileCurrent !== "function") return false;
|
|
155
|
+
try {
|
|
156
|
+
return checkProfileCurrent() === true;
|
|
157
|
+
} catch {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
153
162
|
export function runtimeStatusPayload({
|
|
154
163
|
metrics,
|
|
155
164
|
streamPerformance,
|
|
@@ -161,6 +170,7 @@ export function runtimeStatusPayload({
|
|
|
161
170
|
codexModelRegistry,
|
|
162
171
|
claudeModelRegistry,
|
|
163
172
|
claudeMode = "inherited",
|
|
173
|
+
isClaudeProfileCurrent,
|
|
164
174
|
} = {}) {
|
|
165
175
|
const memory = process.memoryUsage();
|
|
166
176
|
const resolvedClaudeMode = claudeMode === "isolated" ? "isolated" : "inherited";
|
|
@@ -172,6 +182,8 @@ export function runtimeStatusPayload({
|
|
|
172
182
|
const claudeModels = resolvedClaudeMode === "isolated"
|
|
173
183
|
? modelRegistryStatus(claudeModelRegistry)
|
|
174
184
|
: codexModels;
|
|
185
|
+
const claudeProfileCurrent = resolvedClaudeMode === "isolated"
|
|
186
|
+
&& safeProfileCurrent(isClaudeProfileCurrent);
|
|
175
187
|
return {
|
|
176
188
|
...adapterHealthPayload(),
|
|
177
189
|
uptime_ms: Math.round(process.uptime() * 1000),
|
|
@@ -192,7 +204,12 @@ export function runtimeStatusPayload({
|
|
|
192
204
|
models: codexModels,
|
|
193
205
|
profiles: {
|
|
194
206
|
codex: { mode: "legacy", client: codexRuntime, models: codexModels },
|
|
195
|
-
claude: {
|
|
207
|
+
claude: {
|
|
208
|
+
mode: resolvedClaudeMode,
|
|
209
|
+
profile_current: claudeProfileCurrent,
|
|
210
|
+
client: claudeRuntime,
|
|
211
|
+
models: claudeModels,
|
|
212
|
+
},
|
|
196
213
|
},
|
|
197
214
|
routing: profileRouting({ claudeMode: resolvedClaudeMode }),
|
|
198
215
|
limits: {
|
package/src/pm-studio-setup.mjs
CHANGED
|
@@ -396,6 +396,19 @@ function backupName(recipe) {
|
|
|
396
396
|
return `${recipe.id}-${recipe.sourceAsarSha256.slice(0, 12)}`;
|
|
397
397
|
}
|
|
398
398
|
|
|
399
|
+
export function pmStudioBackupRoot(home = os.homedir()) {
|
|
400
|
+
return path.join(home, ".local", "share", "codex-copilot-dx", "pm-studio-backups");
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
export function pmStudioPatchManifestPath({
|
|
404
|
+
home = os.homedir(),
|
|
405
|
+
backupRoot = pmStudioBackupRoot(home),
|
|
406
|
+
recipe,
|
|
407
|
+
} = {}) {
|
|
408
|
+
if (!recipe) throw new TypeError("PM Studio patch recipe is required");
|
|
409
|
+
return path.join(backupRoot, backupName(recipe), "manifest.json");
|
|
410
|
+
}
|
|
411
|
+
|
|
399
412
|
function backupManifest(recipe, createdAt) {
|
|
400
413
|
return {
|
|
401
414
|
schema_version: 1,
|
|
@@ -512,13 +525,24 @@ function writePatchedBinaryRecord({ backup, inspection, operations }) {
|
|
|
512
525
|
backup.manifest = manifest;
|
|
513
526
|
}
|
|
514
527
|
|
|
515
|
-
function assertPatchedBinaryRecord({ inspection, manifestPath }) {
|
|
528
|
+
export function assertPatchedBinaryRecord({ inspection, manifestPath, recipe }) {
|
|
516
529
|
let manifest;
|
|
517
530
|
try {
|
|
518
531
|
manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
|
519
532
|
} catch {
|
|
520
533
|
throw setupError("PM_STUDIO_PATCH_RECORD_INVALID", "PM Studio patched-binary record is missing or invalid");
|
|
521
534
|
}
|
|
535
|
+
if (recipe && (manifest?.schema_version !== 1
|
|
536
|
+
|| manifest?.kind !== "ccdx-pm-studio-backup"
|
|
537
|
+
|| manifest?.recipe_id !== recipe.id
|
|
538
|
+
|| manifest?.app?.bundle_identifier !== recipe.bundleIdentifier
|
|
539
|
+
|| manifest?.app?.version !== recipe.version
|
|
540
|
+
|| manifest?.app?.build !== recipe.build
|
|
541
|
+
|| manifest?.patched?.asar_sha256 !== recipe.patchedAsarSha256
|
|
542
|
+
|| manifest?.patched?.asar_header_sha256 !== recipe.patchedHeaderSha256
|
|
543
|
+
|| !integrityMatches(manifest?.patched?.electron_asar_integrity, "SHA256", recipe.patchedHeaderSha256))) {
|
|
544
|
+
throw setupError("PM_STUDIO_PATCH_RECORD_INVALID", "PM Studio patched-binary record does not match the installed patch recipe");
|
|
545
|
+
}
|
|
522
546
|
const binaries = manifest?.patched?.binaries;
|
|
523
547
|
if (!binaries
|
|
524
548
|
|| binaries.main_executable_sha256 !== inspection.executableIntegrity.executableSha256
|
|
@@ -593,7 +617,7 @@ export async function runPmStudioSetup({
|
|
|
593
617
|
commandName = "ccdx",
|
|
594
618
|
appPath = DEFAULT_PM_STUDIO_APP_PATH,
|
|
595
619
|
home = os.homedir(),
|
|
596
|
-
backupRoot =
|
|
620
|
+
backupRoot = pmStudioBackupRoot(home),
|
|
597
621
|
recipes = PM_STUDIO_RECIPES,
|
|
598
622
|
operations: operationOverrides = {},
|
|
599
623
|
logger = (line) => console.log(line),
|
|
@@ -629,7 +653,7 @@ export async function runPmStudioSetup({
|
|
|
629
653
|
throw setupError("PM_STUDIO_BACKUP_INVALID", "Patched PM Studio has no matching verified backup; no files were changed");
|
|
630
654
|
}
|
|
631
655
|
const backup = validateBackup({ backupDir: finalBackupDir, recipe, operations });
|
|
632
|
-
assertPatchedBinaryRecord({ inspection: source, manifestPath: backup.manifestPath });
|
|
656
|
+
assertPatchedBinaryRecord({ inspection: source, manifestPath: backup.manifestPath, recipe });
|
|
633
657
|
emit(`[OK] PM Studio ${recipe.version} build ${recipe.build} is already patched; no backup or signing was repeated.`);
|
|
634
658
|
emit(`[OK] Verified backup: ${backup.backupAppPath}`);
|
|
635
659
|
emit(`[OK] Backup manifest: ${backup.manifestPath}`);
|
|
@@ -700,7 +724,7 @@ export async function runPmStudioSetup({
|
|
|
700
724
|
installed.issues);
|
|
701
725
|
}
|
|
702
726
|
try {
|
|
703
|
-
assertPatchedBinaryRecord({ inspection: installed, manifestPath: backup.manifestPath });
|
|
727
|
+
assertPatchedBinaryRecord({ inspection: installed, manifestPath: backup.manifestPath, recipe });
|
|
704
728
|
} catch (error) {
|
|
705
729
|
throw setupError("PM_STUDIO_INSTALL_VERIFY_FAILED",
|
|
706
730
|
`Installed PM Studio differs from the verified staging record; do not launch it. ${recoveryMessage(backup)}`,
|
package/src/pm-studio-status.mjs
CHANGED
|
@@ -7,16 +7,27 @@ import {
|
|
|
7
7
|
} from "./auth-profile.mjs";
|
|
8
8
|
import { PM_STUDIO_RECIPES } from "./pm-studio-asar.mjs";
|
|
9
9
|
import {
|
|
10
|
+
assertPatchedBinaryRecord,
|
|
10
11
|
createPmStudioSetupOperations,
|
|
11
12
|
DEFAULT_PM_STUDIO_APP_PATH,
|
|
12
13
|
inspectPmStudioApp,
|
|
13
14
|
PM_STUDIO_CLAUDE_AUTH_COMMAND,
|
|
15
|
+
pmStudioBackupRoot,
|
|
16
|
+
pmStudioPatchManifestPath,
|
|
14
17
|
} from "./pm-studio-setup.mjs";
|
|
18
|
+
import { readAdapterStatus } from "./cli-status.mjs";
|
|
15
19
|
import { checkRunningAdapter } from "./running-adapter.mjs";
|
|
16
20
|
import { status } from "./status.mjs";
|
|
17
21
|
|
|
18
22
|
const PM_RELAY_HOST = "127.0.0.1";
|
|
19
23
|
const PM_RELAY_PORT = 2026;
|
|
24
|
+
const PM_RELAY_CAPABILITY = "pm_studio_relay_v1";
|
|
25
|
+
const PM_RELAY_ROUTE_NAMES = Object.freeze([
|
|
26
|
+
"pm_models",
|
|
27
|
+
"pm_chat_completions",
|
|
28
|
+
"pm_responses",
|
|
29
|
+
"pm_embeddings",
|
|
30
|
+
]);
|
|
20
31
|
|
|
21
32
|
function selectRecipe(metadata, recipes) {
|
|
22
33
|
return recipes.find((recipe) => recipe.version === String(metadata.version)
|
|
@@ -42,15 +53,52 @@ function inspectClaudeProfile(home, readCredentials) {
|
|
|
42
53
|
}
|
|
43
54
|
}
|
|
44
55
|
|
|
56
|
+
function inspectAdapterRuntime(adapter, runtimeStatus) {
|
|
57
|
+
const data = runtimeStatus?.data;
|
|
58
|
+
const health = adapter?.data;
|
|
59
|
+
const issues = [];
|
|
60
|
+
if (!data || typeof data !== "object") {
|
|
61
|
+
return { ok: false, baseUrl: runtimeStatus?.baseUrl || adapter?.baseUrl, data: null, issues: ["runtime status payload is missing"] };
|
|
62
|
+
}
|
|
63
|
+
if (data.version !== health?.version
|
|
64
|
+
|| data.protocol_version !== health?.protocol_version
|
|
65
|
+
|| data.instance_id !== health?.instance_id
|
|
66
|
+
|| data.pid !== health?.pid) {
|
|
67
|
+
issues.push("health and runtime status do not describe the same adapter instance");
|
|
68
|
+
}
|
|
69
|
+
const capabilities = new Set(Array.isArray(data.capabilities) ? data.capabilities : []);
|
|
70
|
+
if (!capabilities.has(PM_RELAY_CAPABILITY)) issues.push("PM Studio relay capability is missing");
|
|
71
|
+
if (data.profiles?.claude?.mode !== "isolated") issues.push("Claude profile mode is not isolated");
|
|
72
|
+
if (data.profiles?.claude?.profile_current !== true) {
|
|
73
|
+
issues.push("running Claude credentials do not match the current isolated profile");
|
|
74
|
+
}
|
|
75
|
+
if (data.routing?.responses !== "codex") issues.push("/v1/responses is not routed to Codex");
|
|
76
|
+
if (data.routing?.messages !== "claude") issues.push("/v1/messages is not routed to Claude");
|
|
77
|
+
const routes = data.requests?.by_route;
|
|
78
|
+
for (const routeName of PM_RELAY_ROUTE_NAMES) {
|
|
79
|
+
if (!routes || typeof routes !== "object" || !Object.hasOwn(routes, routeName)) {
|
|
80
|
+
issues.push(`runtime status does not expose ${routeName}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
...runtimeStatus,
|
|
85
|
+
ok: issues.length === 0,
|
|
86
|
+
issues,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
45
90
|
export async function inspectPmStudioStatus({
|
|
46
91
|
appPath = DEFAULT_PM_STUDIO_APP_PATH,
|
|
47
92
|
home = os.homedir(),
|
|
93
|
+
backupRoot = pmStudioBackupRoot(home),
|
|
48
94
|
recipes = PM_STUDIO_RECIPES,
|
|
49
95
|
operationOverrides = {},
|
|
50
96
|
existsSync = fs.existsSync,
|
|
51
97
|
inspectApp = inspectPmStudioApp,
|
|
98
|
+
verifyPatchRecord = assertPatchedBinaryRecord,
|
|
52
99
|
readClaudeCredentials = readAuthProfileCredentials,
|
|
53
100
|
checkRunningAdapterFn = checkRunningAdapter,
|
|
101
|
+
readAdapterStatusFn = readAdapterStatus,
|
|
54
102
|
} = {}) {
|
|
55
103
|
const operations = createPmStudioSetupOperations(operationOverrides);
|
|
56
104
|
let app;
|
|
@@ -69,9 +117,24 @@ export async function inspectPmStudioStatus({
|
|
|
69
117
|
bundleIdentifier: String(metadata.bundleIdentifier),
|
|
70
118
|
};
|
|
71
119
|
const recipe = selectRecipe(normalized, recipes);
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
120
|
+
if (recipe) {
|
|
121
|
+
app = { ...inspectApp({ appPath, recipe, operations }), recipe: recipe.id };
|
|
122
|
+
if (app.state === "patched") {
|
|
123
|
+
const manifestPath = pmStudioPatchManifestPath({ home, backupRoot, recipe });
|
|
124
|
+
try {
|
|
125
|
+
verifyPatchRecord({ inspection: app, manifestPath, recipe });
|
|
126
|
+
app.patchRecord = { valid: true, manifestPath, reason: "" };
|
|
127
|
+
} catch (error) {
|
|
128
|
+
const reason = safeMessage(error);
|
|
129
|
+
app.patchRecipeMatched = true;
|
|
130
|
+
app.patchRecord = { valid: false, manifestPath, reason };
|
|
131
|
+
app.state = "drift";
|
|
132
|
+
app.issues = [...(Array.isArray(app.issues) ? app.issues : []), reason];
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
} else {
|
|
136
|
+
app = { state: "unsupported", metadata: normalized, issues: [], recipe: "" };
|
|
137
|
+
}
|
|
75
138
|
} catch (error) {
|
|
76
139
|
app = { state: "error", metadata: null, issues: [safeMessage(error)], recipe: "" };
|
|
77
140
|
}
|
|
@@ -84,16 +147,40 @@ export async function inspectPmStudioStatus({
|
|
|
84
147
|
} catch (error) {
|
|
85
148
|
adapter = { ok: false, error };
|
|
86
149
|
}
|
|
87
|
-
|
|
88
|
-
|
|
150
|
+
let runtime = { ok: false, baseUrl: adapter?.baseUrl, data: null, issues: ["adapter health check failed"] };
|
|
151
|
+
if (adapter?.ok === true) {
|
|
152
|
+
try {
|
|
153
|
+
runtime = inspectAdapterRuntime(adapter, await readAdapterStatusFn({
|
|
154
|
+
host: PM_RELAY_HOST,
|
|
155
|
+
port: PM_RELAY_PORT,
|
|
156
|
+
}));
|
|
157
|
+
} catch (error) {
|
|
158
|
+
runtime = { ok: false, baseUrl: adapter.baseUrl, data: null, issues: [safeMessage(error)] };
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
const operational = app.state === "patched"
|
|
162
|
+
&& app.patchRecord?.valid === true
|
|
163
|
+
&& claude.valid
|
|
164
|
+
&& adapter?.ok === true
|
|
165
|
+
&& runtime.ok === true;
|
|
166
|
+
return { ok: operational, appPath, app, claude, adapter, runtime };
|
|
89
167
|
}
|
|
90
168
|
|
|
91
169
|
function appStatusLine(result, commandName) {
|
|
92
170
|
const { app } = result;
|
|
93
171
|
const version = app.metadata ? `${app.metadata.version} build ${app.metadata.build}` : "";
|
|
94
|
-
if (app.state === "patched"
|
|
172
|
+
if (app.state === "patched" && app.patchRecord?.valid === true) {
|
|
173
|
+
return status("ok", `PM Studio ${version} is patched and verified against its installed patch record`);
|
|
174
|
+
}
|
|
175
|
+
if (app.state === "patched") {
|
|
176
|
+
const reason = app.patchRecord?.reason || "installed patch record is missing";
|
|
177
|
+
return status("err", `PM Studio ${version} matches the patch recipe, but its installed patch record is not verified: ${reason}`);
|
|
178
|
+
}
|
|
95
179
|
if (app.state === "clean") return status("warn", `PM Studio ${version} is supported but not patched; run ${commandName} pms setup`);
|
|
96
180
|
if (app.state === "unsupported") return status("err", `PM Studio ${version} has no exact patch recipe; no files will be changed`);
|
|
181
|
+
if (app.state === "drift" && app.patchRecipeMatched) {
|
|
182
|
+
return status("err", `PM Studio ${version} matches the patch recipe, but its installed patch record is not verified: ${app.issues.join("; ")}`);
|
|
183
|
+
}
|
|
97
184
|
if (app.state === "drift") return status("err", `PM Studio ${version} does not match the clean or patched recipe: ${app.issues.join("; ")}`);
|
|
98
185
|
if (app.state === "not_installed") return status("warn", `PM Studio is not installed at ${result.appPath}`);
|
|
99
186
|
return status("err", `PM Studio could not be inspected: ${app.issues.join("; ")}`);
|
|
@@ -111,14 +198,21 @@ export function formatPmStudioStatus(result, { commandName = "ccdx" } = {}) {
|
|
|
111
198
|
const reason = result.claude.configured && result.claude.reason ? ` (${result.claude.reason})` : "";
|
|
112
199
|
lines.push(status("err", `Isolated Claude profile is not ready${reason}; run ${PM_STUDIO_CLAUDE_AUTH_COMMAND}`));
|
|
113
200
|
}
|
|
114
|
-
if (result.adapter?.ok) {
|
|
115
|
-
lines.push(status("ok", `PM relay
|
|
201
|
+
if (result.adapter?.ok && result.runtime?.ok) {
|
|
202
|
+
lines.push(status("ok", `PM relay and isolated Claude routing are verified at ${result.adapter.baseUrl}`));
|
|
203
|
+
} else if (result.adapter?.ok) {
|
|
204
|
+
const reason = result.runtime?.issues?.join("; ") || "runtime status is unavailable";
|
|
205
|
+
lines.push(status("err", `The adapter is running at ${result.adapter.baseUrl}, but PM relay routing is not ready: ${reason}; stop it and run ${commandName} start`));
|
|
116
206
|
} else if (result.adapter?.incompatible) {
|
|
117
207
|
lines.push(status("err", `The adapter at ${result.adapter.baseUrl} is incompatible; stop it and run ${commandName} start`));
|
|
118
208
|
} else {
|
|
119
209
|
lines.push(status("warn", `PM relay is not running at http://${PM_RELAY_HOST}:${PM_RELAY_PORT}; run ${commandName} start`));
|
|
120
210
|
}
|
|
121
|
-
|
|
211
|
+
if (result.runtime?.ok) {
|
|
212
|
+
lines.push(status("info", "Routing: PM GPT uses the PM Studio bearer; eligible Claude chat uses the isolated Claude profile"));
|
|
213
|
+
} else {
|
|
214
|
+
lines.push(status("info", "Expected routing: PM GPT uses the PM Studio bearer; eligible Claude chat uses the isolated Claude profile"));
|
|
215
|
+
}
|
|
122
216
|
return lines.join("\n");
|
|
123
217
|
}
|
|
124
218
|
|
package/src/profile-routing.mjs
CHANGED
|
@@ -1,22 +1,13 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isClaudeCopilotCatalogEntry,
|
|
3
|
+
isClaudeCopilotModel,
|
|
4
|
+
} from "./models.mjs";
|
|
5
|
+
|
|
1
6
|
function catalogData(catalog) {
|
|
2
7
|
if (Array.isArray(catalog)) return catalog;
|
|
3
8
|
return Array.isArray(catalog?.data) ? catalog.data : [];
|
|
4
9
|
}
|
|
5
10
|
|
|
6
|
-
function isClaudeCatalogEntry(model) {
|
|
7
|
-
const id = String(model?.id || "").trim();
|
|
8
|
-
const vendor = String(model?.vendor || "").trim().toLowerCase();
|
|
9
|
-
return Boolean(id) && (vendor === "anthropic" || id.toLowerCase().startsWith("claude-"));
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
function isAllowedClaudeModel(model) {
|
|
13
|
-
return isClaudeCatalogEntry(model)
|
|
14
|
-
&& String(model?.vendor || "").trim().toLowerCase() === "anthropic"
|
|
15
|
-
&& model?.model_picker_enabled !== false
|
|
16
|
-
&& Array.isArray(model?.supported_endpoints)
|
|
17
|
-
&& model.supported_endpoints.includes("/chat/completions");
|
|
18
|
-
}
|
|
19
|
-
|
|
20
11
|
export function profileRouting({ claudeMode = "inherited", claudeConfigured = false } = {}) {
|
|
21
12
|
const isolated = claudeMode === "isolated" || claudeConfigured === true;
|
|
22
13
|
return Object.freeze({ responses: "codex", messages: isolated ? "claude" : "codex" });
|
|
@@ -44,9 +35,9 @@ export function createPmStudioModelRouter({ getCatalog, isClaudeEnabled } = {})
|
|
|
44
35
|
const known = new Set();
|
|
45
36
|
for (const model of catalogData(catalog)) {
|
|
46
37
|
const id = String(model?.id || "").trim();
|
|
47
|
-
if (!id || !
|
|
38
|
+
if (!id || !isClaudeCopilotCatalogEntry(model)) continue;
|
|
48
39
|
known.add(id);
|
|
49
|
-
if (enabled &&
|
|
40
|
+
if (enabled && isClaudeCopilotModel(model) && !allowed.has(id)) allowed.set(id, model);
|
|
50
41
|
}
|
|
51
42
|
cachedCatalog = catalog;
|
|
52
43
|
cachedEnabled = enabled;
|
package/src/profile-runtime.mjs
CHANGED
|
@@ -46,6 +46,15 @@ export function createProfileRuntime({
|
|
|
46
46
|
};
|
|
47
47
|
const credentials = readCredentials();
|
|
48
48
|
const claudeProfile = safeProfileStatus(credentials);
|
|
49
|
+
const claudeCredentialFingerprint = credentials.valid
|
|
50
|
+
? githubTokenFingerprint(credentials.token)
|
|
51
|
+
: "";
|
|
52
|
+
const isClaudeProfileCurrent = () => {
|
|
53
|
+
if (!claudeCredentialFingerprint) return false;
|
|
54
|
+
const current = readCredentials();
|
|
55
|
+
return current.valid === true
|
|
56
|
+
&& githubTokenFingerprint(current.token) === claudeCredentialFingerprint;
|
|
57
|
+
};
|
|
49
58
|
let codexCredentialFingerprint = "";
|
|
50
59
|
try {
|
|
51
60
|
const codexCredentials = readAuthProfileCredentials(AUTH_PROFILE_CODEX, { home });
|
|
@@ -65,6 +74,7 @@ export function createProfileRuntime({
|
|
|
65
74
|
claudeProfile,
|
|
66
75
|
codexCredentialFingerprint,
|
|
67
76
|
claudeCredentialFingerprint: "",
|
|
77
|
+
isClaudeProfileCurrent,
|
|
68
78
|
});
|
|
69
79
|
}
|
|
70
80
|
|
|
@@ -86,8 +96,7 @@ export function createProfileRuntime({
|
|
|
86
96
|
claudeMode: "isolated",
|
|
87
97
|
claudeProfile,
|
|
88
98
|
codexCredentialFingerprint,
|
|
89
|
-
claudeCredentialFingerprint
|
|
90
|
-
|
|
91
|
-
: "",
|
|
99
|
+
claudeCredentialFingerprint,
|
|
100
|
+
isClaudeProfileCurrent,
|
|
92
101
|
});
|
|
93
102
|
}
|
package/src/response-history.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { httpError } from "./http-transport.mjs";
|
|
2
|
+
import { clearResponsesToolOutputPartsCache } from "./responses-content.mjs";
|
|
2
3
|
import { loadRuntimeConfig, parsePositiveInteger, RUNTIME_DEFAULTS } from "./runtime-config.mjs";
|
|
3
4
|
|
|
4
5
|
const DEFAULT_MAX_BYTES = RUNTIME_DEFAULTS.responseHistoryMaxBytes;
|
|
@@ -201,17 +202,26 @@ export function responseHistoryMaterializedBytes(responseId) {
|
|
|
201
202
|
return chain.reduce((bytes, entry) => bytes + entry.bytes, 0);
|
|
202
203
|
}
|
|
203
204
|
|
|
204
|
-
export function materializeResponseHistory(responseId) {
|
|
205
|
+
export function materializeResponseHistory(responseId, { assertActive } = {}) {
|
|
206
|
+
assertActive?.();
|
|
205
207
|
const { chain, rootId } = responseHistoryChain(responseId);
|
|
206
208
|
const items = [];
|
|
207
|
-
for (
|
|
209
|
+
for (let index = 0; index < chain.length; index += 1) {
|
|
210
|
+
if ((index & 63) === 0) assertActive?.();
|
|
211
|
+
const entry = chain[chain.length - index - 1];
|
|
212
|
+
items.push(...entry.inputItems, ...entry.outputItems);
|
|
213
|
+
}
|
|
214
|
+
assertActive?.();
|
|
208
215
|
const materialized = cloneJson(items);
|
|
216
|
+
assertActive?.();
|
|
209
217
|
touchTree(rootId);
|
|
210
218
|
return materialized;
|
|
211
219
|
}
|
|
212
220
|
|
|
213
221
|
export function rememberResponseHistoryNode({ id, parentId, inputItems, outputItems, takeOwnership = false }) {
|
|
214
222
|
if (!id || !Array.isArray(inputItems)) return;
|
|
223
|
+
clearResponsesToolOutputPartsCache(inputItems);
|
|
224
|
+
clearResponsesToolOutputPartsCache(outputItems);
|
|
215
225
|
const bytes = jsonByteLength([inputItems, outputItems]);
|
|
216
226
|
if (bytes > maxBytes) {
|
|
217
227
|
if (histories.has(id)) removeSubtree(id);
|