vibe-coding-master 0.7.43 → 0.7.45
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 +64 -39
- package/dist/backend/adapters/claude-adapter.js +2 -2
- package/dist/backend/adapters/codex-bridge-adapter.js +213 -0
- package/dist/backend/api/app-settings-routes.js +6 -6
- package/dist/backend/api/artifact-routes.js +3 -0
- package/dist/backend/api/harness-routes.js +16 -0
- package/dist/backend/api/task-routes.js +1 -0
- package/dist/backend/api/workflow-control-routes.js +12 -0
- package/dist/backend/cli/install-vcm-harness.js +21 -0
- package/dist/backend/server.js +11 -9
- package/dist/backend/services/app-settings-service.js +11 -11
- package/dist/backend/services/artifact-service.js +7 -30
- package/dist/backend/services/auto-memory-service.js +640 -12
- package/dist/backend/services/claude-hook-service.js +80 -2
- package/dist/backend/services/{ccr-integration-service.js → codex-bridge-integration-service.js} +71 -71
- package/dist/backend/services/gate-review-service.js +173 -74
- package/dist/backend/services/harness-feedback-service.js +76 -78
- package/dist/backend/services/harness-service.js +27 -3
- package/dist/backend/services/runtime-coordinator-service.js +2 -1
- package/dist/backend/services/session-service.js +16 -16
- package/dist/backend/services/status-service.js +1 -0
- package/dist/backend/services/translation-worker-service.js +19 -4
- package/dist/backend/services/usage-analytics-service.js +3 -3
- package/dist/backend/services/workflow-control-service.js +96 -12
- package/dist/backend/templates/handoff.js +44 -2
- package/dist/backend/templates/harness/architect-agent.js +13 -7
- package/dist/backend/templates/harness/architect-scaffold-worker-agent.js +1 -1
- package/dist/backend/templates/harness/check-scaffold-ledger.js +234 -10
- package/dist/backend/templates/harness/claude-root.js +3 -2
- package/dist/backend/templates/harness/coder-agent.js +8 -0
- package/dist/backend/templates/harness/gate-review.js +144 -49
- package/dist/backend/templates/harness/harness-engineer-agent.js +25 -10
- package/dist/backend/templates/harness/project-manager-agent.js +16 -10
- package/dist/backend/templates/harness/resolve-durable-doc-assignment.js +60 -0
- package/dist/backend/templates/harness/tester-agent.js +13 -0
- package/dist/backend/templates/harness/vcm-ask-user-skill.js +82 -0
- package/dist/backend/templates/harness/vcm-code-navigation-skill.js +7 -5
- package/dist/backend/templates/harness/vcm-task-state-skill.js +2 -2
- package/dist/backend/templates/harness/vcm-workflow-review-skill.js +1 -1
- package/dist/shared/types/session.js +28 -22
- package/dist/shared/types/workflow.js +1 -0
- package/dist/shared/validation/artifact-check.js +3 -3
- package/dist/shared/validation/artifact-contract.js +1 -1
- package/dist/shared/validation/artifact-registry.js +16 -0
- package/dist-frontend/assets/index-DDmygnV6.css +32 -0
- package/dist-frontend/assets/{index-VW9tYPP5.js → index-nAV6toi8.js} +47 -47
- package/dist-frontend/index.html +2 -2
- package/package.json +1 -1
- package/scripts/claude-plugins/vcm-lsp-bridge/.claude-plugin/plugin.json +21 -6
- package/scripts/{ccr-api-key-helper.mjs → codex-bridge-api-key-helper.mjs} +1 -1
- package/scripts/harness-tools/vcm-artifact +1 -2
- package/scripts/harness-tools/vcm-bash-guard +1 -1
- package/dist/backend/adapters/ccr-gateway-adapter.js +0 -172
- package/dist-frontend/assets/index-B0d4Z6ny.css +0 -32
|
@@ -158,7 +158,7 @@ export function createClaudeHookService(deps) {
|
|
|
158
158
|
? await deps.harnessFeedbackService?.handleTaskRetrospectiveHook(context.project.repoRoot, {
|
|
159
159
|
taskSlug: activeTask.taskSlug,
|
|
160
160
|
eventName,
|
|
161
|
-
|
|
161
|
+
memoryReviewStatus: memoryState?.status ?? "idle"
|
|
162
162
|
})
|
|
163
163
|
: false;
|
|
164
164
|
if (memoryHandled || retrospectiveHandled) {
|
|
@@ -212,6 +212,16 @@ export function createClaudeHookService(deps) {
|
|
|
212
212
|
if (memoryResult) {
|
|
213
213
|
return memoryResult;
|
|
214
214
|
}
|
|
215
|
+
const prompt = stringOrUndefined(input.event.prompt);
|
|
216
|
+
if (input.role === "project-manager"
|
|
217
|
+
&& prompt
|
|
218
|
+
&& isDirectUserPrompt(prompt)
|
|
219
|
+
&& deps.workflowControlService) {
|
|
220
|
+
const workflowState = await deps.workflowControlService.getState(createWorkflowControlContext(context));
|
|
221
|
+
if (workflowState.awaitingUser) {
|
|
222
|
+
await deps.workflowControlService.resolveUserInput(createWorkflowControlContext(context));
|
|
223
|
+
}
|
|
224
|
+
}
|
|
215
225
|
const session = await deps.sessionService.recordClaudeHookEvent(context.project.repoRoot, {
|
|
216
226
|
taskSlug: context.taskSlug,
|
|
217
227
|
role: input.role,
|
|
@@ -266,7 +276,7 @@ export function createClaudeHookService(deps) {
|
|
|
266
276
|
handoffDir: context.task.handoffDir,
|
|
267
277
|
taskSlug: context.taskSlug,
|
|
268
278
|
role: input.role,
|
|
269
|
-
prompt
|
|
279
|
+
prompt
|
|
270
280
|
});
|
|
271
281
|
if (submitted) {
|
|
272
282
|
await deps.architectRestartService?.recordRouteAccepted(context.project.repoRoot, context.taskSlug, submitted);
|
|
@@ -298,6 +308,7 @@ export function createClaudeHookService(deps) {
|
|
|
298
308
|
return completedHookResult(input, eventName);
|
|
299
309
|
}
|
|
300
310
|
await clearStopFailureRecoveryState(context, input.role);
|
|
311
|
+
let pmAwaitingUser = false;
|
|
301
312
|
if (options.allowBlock && deps.jobGuard) {
|
|
302
313
|
const verdict = await deps.jobGuard.evaluateStop({
|
|
303
314
|
repoRoot: context.project.repoRoot,
|
|
@@ -319,12 +330,42 @@ export function createClaudeHookService(deps) {
|
|
|
319
330
|
};
|
|
320
331
|
}
|
|
321
332
|
}
|
|
333
|
+
if (input.role === "project-manager" && deps.workflowControlService) {
|
|
334
|
+
const workflowState = await deps.workflowControlService.getState(createWorkflowControlContext(context));
|
|
335
|
+
if (workflowState.awaitingUser) {
|
|
336
|
+
pmAwaitingUser = true;
|
|
337
|
+
}
|
|
338
|
+
else {
|
|
339
|
+
const lastAssistantMessage = stringOrUndefined(input.event.last_assistant_message);
|
|
340
|
+
if (options.allowBlock && lastAssistantMessage && asksUserQuestion(lastAssistantMessage)) {
|
|
341
|
+
return {
|
|
342
|
+
ok: true,
|
|
343
|
+
eventName,
|
|
344
|
+
taskSlug: context.taskSlug,
|
|
345
|
+
role: input.role,
|
|
346
|
+
sessionUpdated: false,
|
|
347
|
+
dispatchedCount: 0,
|
|
348
|
+
stopDecision: {
|
|
349
|
+
behavior: "block",
|
|
350
|
+
reason: "You asked the user a question without registering the wait. Use vcm-ask-user with the exact question, then ask that question and stop. Do not advance or route the workflow."
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
322
356
|
await deps.roleStallDetector?.recordHook({
|
|
323
357
|
...createStallContext(context),
|
|
324
358
|
role: input.role,
|
|
325
359
|
eventName,
|
|
326
360
|
event: input.event
|
|
327
361
|
});
|
|
362
|
+
if (pmAwaitingUser) {
|
|
363
|
+
return recordTurnEnd(input, context, eventName, {
|
|
364
|
+
dispatchRouteFiles: false,
|
|
365
|
+
notifyGateway: true,
|
|
366
|
+
settleGuard: false
|
|
367
|
+
});
|
|
368
|
+
}
|
|
328
369
|
return recordTurnEnd(input, context, eventName, {
|
|
329
370
|
dispatchRouteFiles: !isReviewerRoleName(input.role),
|
|
330
371
|
notifyGateway: true,
|
|
@@ -554,6 +595,17 @@ export function createClaudeHookService(deps) {
|
|
|
554
595
|
if (!deps.autoMemoryService || !(await deps.autoMemoryService.isRoleMemoryTurn(context.taskRepoRoot, input.role))) {
|
|
555
596
|
return undefined;
|
|
556
597
|
}
|
|
598
|
+
const prompt = stringOrUndefined(input.event.prompt);
|
|
599
|
+
if (eventName === "UserPromptSubmit"
|
|
600
|
+
&& input.role === "project-manager"
|
|
601
|
+
&& prompt
|
|
602
|
+
&& isDirectUserPrompt(prompt)
|
|
603
|
+
&& deps.workflowControlService) {
|
|
604
|
+
const workflowState = await deps.workflowControlService.getState(createWorkflowControlContext(context));
|
|
605
|
+
if (workflowState.awaitingUser) {
|
|
606
|
+
await deps.workflowControlService.resolveUserInput(createWorkflowControlContext(context));
|
|
607
|
+
}
|
|
608
|
+
}
|
|
557
609
|
const session = await deps.sessionService.recordClaudeHookEvent(context.project.repoRoot, {
|
|
558
610
|
taskSlug: context.taskSlug,
|
|
559
611
|
role: input.role,
|
|
@@ -593,6 +645,10 @@ export function createClaudeHookService(deps) {
|
|
|
593
645
|
role: input.role,
|
|
594
646
|
eventName
|
|
595
647
|
});
|
|
648
|
+
if (eventName === "Stop") {
|
|
649
|
+
const memoryState = await deps.autoMemoryService.getState(context.project.repoRoot, context.taskRepoRoot);
|
|
650
|
+
await deps.harnessFeedbackService?.completeWaitingTaskRetrospective(context.project.repoRoot, context.taskSlug, memoryState.status);
|
|
651
|
+
}
|
|
596
652
|
return {
|
|
597
653
|
ok: true,
|
|
598
654
|
eventName,
|
|
@@ -613,6 +669,14 @@ export function createClaudeHookService(deps) {
|
|
|
613
669
|
...(stoppedRole ? { stoppedRole } : {})
|
|
614
670
|
};
|
|
615
671
|
}
|
|
672
|
+
function createWorkflowControlContext(context) {
|
|
673
|
+
return {
|
|
674
|
+
taskRepoRoot: context.taskRepoRoot,
|
|
675
|
+
stateRoot: context.config.stateRoot,
|
|
676
|
+
handoffDir: context.task.handoffDir,
|
|
677
|
+
taskSlug: context.taskSlug
|
|
678
|
+
};
|
|
679
|
+
}
|
|
616
680
|
async function isCurrentRoleHook(context, input, eventName) {
|
|
617
681
|
const current = await deps.sessionService.getRoleSession(context.project.repoRoot, context.taskSlug, input.role);
|
|
618
682
|
return Boolean(current && matchesRoleHookSession(current, {
|
|
@@ -1017,3 +1081,17 @@ function normalizeDiagnosticString(value, maxLength) {
|
|
|
1017
1081
|
function stringOrUndefined(value) {
|
|
1018
1082
|
return typeof value === "string" ? value : undefined;
|
|
1019
1083
|
}
|
|
1084
|
+
function isDirectUserPrompt(prompt) {
|
|
1085
|
+
const normalized = prompt.trim();
|
|
1086
|
+
return normalized.length > 0 && !/^\[VCM(?:\s|\])/i.test(normalized);
|
|
1087
|
+
}
|
|
1088
|
+
function asksUserQuestion(message) {
|
|
1089
|
+
const withoutCodeBlocks = message.replace(/```[\s\S]*?```/g, "");
|
|
1090
|
+
const lines = withoutCodeBlocks
|
|
1091
|
+
.split(/\r?\n/)
|
|
1092
|
+
.map((line) => line.trim())
|
|
1093
|
+
.filter(Boolean);
|
|
1094
|
+
return lines.some((line) => /[??]["')\]}】”’]*$/.test(line)
|
|
1095
|
+
|| /^(?:please\s+(?:answer|choose|confirm|decide|provide|select|tell)|(?:can|could|do|does|is|are|should|will|would)\s+you\b)/i.test(line)
|
|
1096
|
+
|| /^(?:请(?:回答|选择|确认|决定|提供|告知)|你(?:是否|能否|要不要|可否)|是否需要你|需要你(?:选择|确认|决定|提供|告知))/.test(line));
|
|
1097
|
+
}
|
package/dist/backend/services/{ccr-integration-service.js → codex-bridge-integration-service.js}
RENAMED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import { CCR_GATEWAY_BASE_URL, CCR_GPT_AUTO_COMPACT_PERCENT, CCR_GPT_AUTO_COMPACT_WINDOW_TOKENS, CCR_GPT_EFFECTIVE_CONTEXT_TOKENS, CCR_GPT_MODEL_ID, createSessionModelOptions, isCcrSessionModel } from "../../shared/types/session.js";
|
|
2
1
|
import path from "node:path";
|
|
3
2
|
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { CODEX_BRIDGE_AUTO_COMPACT_PERCENT, CODEX_BRIDGE_AUTO_COMPACT_WINDOW_TOKENS, CODEX_BRIDGE_BASE_URL, CODEX_BRIDGE_EFFECTIVE_CONTEXT_TOKENS, createSessionModelOptions, getCodexBridgeModelId, isCodexBridgeSessionModel } from "../../shared/types/session.js";
|
|
4
4
|
import { VcmError } from "../errors.js";
|
|
5
5
|
import { resolveVcmDataDir } from "../vcm-data-dir.js";
|
|
6
6
|
const DEFAULT_CACHE_TTL_MS = 10_000;
|
|
7
|
-
const DEFAULT_API_KEY_HELPER_PATH = fileURLToPath(new URL("../../../scripts/
|
|
8
|
-
export function
|
|
7
|
+
const DEFAULT_API_KEY_HELPER_PATH = fileURLToPath(new URL("../../../scripts/codex-bridge-api-key-helper.mjs", import.meta.url));
|
|
8
|
+
export function createCodexBridgeIntegrationService(deps) {
|
|
9
9
|
const now = deps.now ?? (() => new Date());
|
|
10
10
|
const cacheTtlMs = deps.cacheTtlMs ?? DEFAULT_CACHE_TTL_MS;
|
|
11
11
|
const baseEnv = deps.baseEnv ?? process.env;
|
|
12
|
-
const configDir = deps.configDir ?? path.join(resolveVcmDataDir(baseEnv), "claude", "
|
|
12
|
+
const configDir = deps.configDir ?? path.join(resolveVcmDataDir(baseEnv), "claude", "codex-bridge");
|
|
13
13
|
let cachedProbe;
|
|
14
14
|
let inFlight;
|
|
15
15
|
async function probe(force = false) {
|
|
16
|
-
const settings = await deps.settings.
|
|
16
|
+
const settings = await deps.settings.getCodexBridgeIntegrationSettings();
|
|
17
17
|
if (!settings.apiKey) {
|
|
18
18
|
throw missingApiKeyError();
|
|
19
19
|
}
|
|
@@ -24,7 +24,7 @@ export function createCcrIntegrationService(deps) {
|
|
|
24
24
|
if (inFlight) {
|
|
25
25
|
return inFlight;
|
|
26
26
|
}
|
|
27
|
-
inFlight = deps.
|
|
27
|
+
inFlight = deps.bridge.probe(settings.apiKey).then((result) => {
|
|
28
28
|
const checkedAtDate = now();
|
|
29
29
|
cachedProbe = {
|
|
30
30
|
result,
|
|
@@ -38,14 +38,15 @@ export function createCcrIntegrationService(deps) {
|
|
|
38
38
|
return inFlight;
|
|
39
39
|
}
|
|
40
40
|
async function buildStatus(options = {}) {
|
|
41
|
-
const settings = await deps.settings.
|
|
41
|
+
const settings = await deps.settings.getCodexBridgeIntegrationSettings();
|
|
42
42
|
if (!settings.enabled) {
|
|
43
43
|
return createStatus({
|
|
44
44
|
enabled: false,
|
|
45
45
|
apiKeyConfigured: Boolean(settings.apiKey),
|
|
46
46
|
connectionState: "disabled",
|
|
47
47
|
modelAvailable: false,
|
|
48
|
-
|
|
48
|
+
models: [],
|
|
49
|
+
error: settings.apiKey ? undefined : "Save a Codex Bridge API key before enabling Codex models."
|
|
49
50
|
});
|
|
50
51
|
}
|
|
51
52
|
if (!cachedProbe && options.refreshIfMissing) {
|
|
@@ -56,7 +57,8 @@ export function createCcrIntegrationService(deps) {
|
|
|
56
57
|
enabled: true,
|
|
57
58
|
apiKeyConfigured: true,
|
|
58
59
|
connectionState: "checking",
|
|
59
|
-
modelAvailable: false
|
|
60
|
+
modelAvailable: false,
|
|
61
|
+
models: []
|
|
60
62
|
});
|
|
61
63
|
}
|
|
62
64
|
return createStatus({
|
|
@@ -64,13 +66,14 @@ export function createCcrIntegrationService(deps) {
|
|
|
64
66
|
apiKeyConfigured: true,
|
|
65
67
|
connectionState: cachedProbe.result.connectionState,
|
|
66
68
|
modelAvailable: cachedProbe.result.modelAvailable,
|
|
69
|
+
models: cachedProbe.result.models,
|
|
67
70
|
checkedAt: cachedProbe.checkedAt,
|
|
68
71
|
error: cachedProbe.result.error
|
|
69
72
|
});
|
|
70
73
|
}
|
|
71
74
|
return {
|
|
72
75
|
async initialize() {
|
|
73
|
-
const settings = await deps.settings.
|
|
76
|
+
const settings = await deps.settings.getCodexBridgeIntegrationSettings();
|
|
74
77
|
if (settings.enabled) {
|
|
75
78
|
await probe(true);
|
|
76
79
|
}
|
|
@@ -80,15 +83,15 @@ export function createCcrIntegrationService(deps) {
|
|
|
80
83
|
},
|
|
81
84
|
async updateSettings(input) {
|
|
82
85
|
if (input.apiKey !== undefined && typeof input.apiKey !== "string") {
|
|
83
|
-
throw invalidSettingsError("
|
|
86
|
+
throw invalidSettingsError("Codex Bridge API key must be a string.");
|
|
84
87
|
}
|
|
85
88
|
if (input.enabled !== undefined && typeof input.enabled !== "boolean") {
|
|
86
|
-
throw invalidSettingsError("
|
|
89
|
+
throw invalidSettingsError("Codex Bridge enabled state must be a boolean.");
|
|
87
90
|
}
|
|
88
91
|
if (input.clearApiKey !== undefined && typeof input.clearApiKey !== "boolean") {
|
|
89
|
-
throw invalidSettingsError("
|
|
92
|
+
throw invalidSettingsError("Codex Bridge clear-key state must be a boolean.");
|
|
90
93
|
}
|
|
91
|
-
const current = await deps.settings.
|
|
94
|
+
const current = await deps.settings.getCodexBridgeIntegrationSettings();
|
|
92
95
|
const clearApiKey = input.clearApiKey === true;
|
|
93
96
|
const nextApiKey = clearApiKey
|
|
94
97
|
? ""
|
|
@@ -99,7 +102,7 @@ export function createCcrIntegrationService(deps) {
|
|
|
99
102
|
if (nextEnabled && !nextApiKey) {
|
|
100
103
|
throw missingApiKeyError();
|
|
101
104
|
}
|
|
102
|
-
await deps.settings.
|
|
105
|
+
await deps.settings.updateCodexBridgeIntegrationSettings({
|
|
103
106
|
enabled: nextEnabled,
|
|
104
107
|
apiKey: nextApiKey
|
|
105
108
|
});
|
|
@@ -114,64 +117,65 @@ export function createCcrIntegrationService(deps) {
|
|
|
114
117
|
return buildStatus();
|
|
115
118
|
},
|
|
116
119
|
async checkConnection() {
|
|
117
|
-
const settings = await deps.settings.
|
|
120
|
+
const settings = await deps.settings.getCodexBridgeIntegrationSettings();
|
|
118
121
|
if (!settings.enabled) {
|
|
119
122
|
throw new VcmError({
|
|
120
|
-
code: "
|
|
121
|
-
message: "
|
|
123
|
+
code: "CODEX_BRIDGE_DISABLED",
|
|
124
|
+
message: "Codex Bridge models are disabled.",
|
|
122
125
|
statusCode: 409,
|
|
123
|
-
hint: "Save the
|
|
126
|
+
hint: "Save the Codex Bridge API key and enable Codex models before checking the connection."
|
|
124
127
|
});
|
|
125
128
|
}
|
|
126
129
|
await probe(true);
|
|
127
130
|
return buildStatus();
|
|
128
131
|
},
|
|
129
132
|
async getLaunchEnvironment(model) {
|
|
130
|
-
if (!
|
|
133
|
+
if (!isCodexBridgeSessionModel(model)) {
|
|
131
134
|
return buildNativeLaunchEnvironment(baseEnv);
|
|
132
135
|
}
|
|
133
|
-
const settings = await deps.settings.
|
|
136
|
+
const settings = await deps.settings.getCodexBridgeIntegrationSettings();
|
|
134
137
|
if (!settings.enabled) {
|
|
135
138
|
throw new VcmError({
|
|
136
|
-
code: "
|
|
137
|
-
message: "
|
|
139
|
+
code: "CODEX_BRIDGE_DISABLED",
|
|
140
|
+
message: "Codex Bridge models are disabled.",
|
|
138
141
|
statusCode: 409,
|
|
139
|
-
hint: "Enable
|
|
142
|
+
hint: "Enable Codex Bridge in VCM Settings before starting this session."
|
|
140
143
|
});
|
|
141
144
|
}
|
|
142
145
|
const checked = await probe();
|
|
143
|
-
|
|
146
|
+
const modelId = getCodexBridgeModelId(model);
|
|
147
|
+
const modelAvailable = checked.result.models.some((available) => available.id === modelId);
|
|
148
|
+
if (checked.result.connectionState !== "available" || !modelAvailable) {
|
|
144
149
|
throw new VcmError({
|
|
145
|
-
code: "
|
|
146
|
-
message: checked.result.error ?? `
|
|
150
|
+
code: "CODEX_BRIDGE_MODEL_UNAVAILABLE",
|
|
151
|
+
message: checked.result.error ?? `Codex Bridge model ${modelId} is unavailable.`,
|
|
147
152
|
statusCode: 409,
|
|
148
|
-
hint: "Check
|
|
153
|
+
hint: "Check Codex Bridge on port 3456, refresh its Codex login, and verify the selected model."
|
|
149
154
|
});
|
|
150
155
|
}
|
|
151
|
-
const
|
|
152
|
-
const
|
|
153
|
-
const noProxy = mergeNoProxy(deps.baseEnv?.NO_PROXY ?? deps.baseEnv?.no_proxy ?? process.env.NO_PROXY ?? process.env.no_proxy,
|
|
156
|
+
const bridgeBaseUrl = checked.result.baseUrl ?? CODEX_BRIDGE_BASE_URL;
|
|
157
|
+
const bridgeHost = new URL(bridgeBaseUrl).hostname;
|
|
158
|
+
const noProxy = mergeNoProxy(deps.baseEnv?.NO_PROXY ?? deps.baseEnv?.no_proxy ?? process.env.NO_PROXY ?? process.env.no_proxy, bridgeHost);
|
|
154
159
|
return {
|
|
155
|
-
ANTHROPIC_BASE_URL:
|
|
156
|
-
ANTHROPIC_API_BASE_URL:
|
|
157
|
-
CLAUDE_AGENT_API_BASE_URL:
|
|
160
|
+
ANTHROPIC_BASE_URL: bridgeBaseUrl,
|
|
161
|
+
ANTHROPIC_API_BASE_URL: bridgeBaseUrl,
|
|
162
|
+
CLAUDE_AGENT_API_BASE_URL: bridgeBaseUrl,
|
|
158
163
|
ANTHROPIC_AUTH_TOKEN: undefined,
|
|
159
164
|
ANTHROPIC_API_KEY: undefined,
|
|
160
|
-
ANTHROPIC_MODEL:
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
ANTHROPIC_SMALL_FAST_MODEL: CCR_GPT_MODEL_ID,
|
|
165
|
+
ANTHROPIC_MODEL: modelId,
|
|
166
|
+
CODEX_BRIDGE_CLAUDE_MODEL: modelId,
|
|
167
|
+
ANTHROPIC_SMALL_FAST_MODEL: modelId,
|
|
164
168
|
CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY: "1",
|
|
165
|
-
CLAUDE_CODE_MAX_CONTEXT_TOKENS: String(
|
|
166
|
-
CLAUDE_CODE_AUTO_COMPACT_WINDOW: String(
|
|
167
|
-
CLAUDE_AUTOCOMPACT_PCT_OVERRIDE: String(
|
|
169
|
+
CLAUDE_CODE_MAX_CONTEXT_TOKENS: String(CODEX_BRIDGE_EFFECTIVE_CONTEXT_TOKENS),
|
|
170
|
+
CLAUDE_CODE_AUTO_COMPACT_WINDOW: String(CODEX_BRIDGE_AUTO_COMPACT_WINDOW_TOKENS),
|
|
171
|
+
CLAUDE_AUTOCOMPACT_PCT_OVERRIDE: String(CODEX_BRIDGE_AUTO_COMPACT_PERCENT),
|
|
168
172
|
CLAUDE_CONFIG_DIR: configDir,
|
|
169
173
|
NO_PROXY: noProxy,
|
|
170
174
|
no_proxy: noProxy
|
|
171
175
|
};
|
|
172
176
|
},
|
|
173
177
|
async getLaunchSettingsOverride(model) {
|
|
174
|
-
if (!
|
|
178
|
+
if (!isCodexBridgeSessionModel(model)) {
|
|
175
179
|
return undefined;
|
|
176
180
|
}
|
|
177
181
|
return {
|
|
@@ -180,51 +184,50 @@ export function createCcrIntegrationService(deps) {
|
|
|
180
184
|
}
|
|
181
185
|
};
|
|
182
186
|
}
|
|
183
|
-
const
|
|
187
|
+
const BRIDGE_BASE_URL_KEYS = [
|
|
184
188
|
"ANTHROPIC_BASE_URL",
|
|
185
189
|
"ANTHROPIC_API_BASE_URL",
|
|
186
190
|
"CLAUDE_AGENT_API_BASE_URL"
|
|
187
191
|
];
|
|
188
|
-
const
|
|
192
|
+
const BRIDGE_MODEL_KEYS = [
|
|
189
193
|
"ANTHROPIC_MODEL",
|
|
190
194
|
"ANTHROPIC_SMALL_FAST_MODEL"
|
|
191
195
|
];
|
|
192
|
-
const
|
|
193
|
-
"
|
|
194
|
-
"CODEXL_CLAUDE_CODE_MODEL",
|
|
196
|
+
const BRIDGE_ONLY_ENV_KEYS = [
|
|
197
|
+
"CODEX_BRIDGE_CLAUDE_MODEL",
|
|
195
198
|
"CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY"
|
|
196
199
|
];
|
|
197
200
|
export function buildNativeLaunchEnvironment(baseEnv) {
|
|
198
201
|
const environment = {};
|
|
199
|
-
let
|
|
202
|
+
let inheritedBridgeTakeover = false;
|
|
200
203
|
if (baseEnv.CLAUDE_CONFIG_DIR?.trim()) {
|
|
201
204
|
environment.CLAUDE_CONFIG_DIR = baseEnv.CLAUDE_CONFIG_DIR.trim();
|
|
202
205
|
}
|
|
203
|
-
for (const key of
|
|
204
|
-
if (
|
|
206
|
+
for (const key of BRIDGE_BASE_URL_KEYS) {
|
|
207
|
+
if (isCodexBridgeUrl(baseEnv[key])) {
|
|
205
208
|
environment[key] = undefined;
|
|
206
|
-
|
|
209
|
+
inheritedBridgeTakeover = true;
|
|
207
210
|
}
|
|
208
211
|
}
|
|
209
|
-
for (const key of
|
|
210
|
-
if (
|
|
212
|
+
for (const key of BRIDGE_MODEL_KEYS) {
|
|
213
|
+
if (isCodexBridgeModelName(baseEnv[key], baseEnv.CODEX_BRIDGE_CLAUDE_MODEL)) {
|
|
211
214
|
environment[key] = undefined;
|
|
212
|
-
|
|
215
|
+
inheritedBridgeTakeover = true;
|
|
213
216
|
}
|
|
214
217
|
}
|
|
215
|
-
for (const key of
|
|
218
|
+
for (const key of BRIDGE_ONLY_ENV_KEYS) {
|
|
216
219
|
if (baseEnv[key] !== undefined) {
|
|
217
220
|
environment[key] = undefined;
|
|
218
|
-
|
|
221
|
+
inheritedBridgeTakeover = true;
|
|
219
222
|
}
|
|
220
223
|
}
|
|
221
|
-
if (
|
|
224
|
+
if (inheritedBridgeTakeover) {
|
|
222
225
|
environment.ANTHROPIC_AUTH_TOKEN = undefined;
|
|
223
226
|
environment.ANTHROPIC_API_KEY = undefined;
|
|
224
227
|
}
|
|
225
228
|
return environment;
|
|
226
229
|
}
|
|
227
|
-
function
|
|
230
|
+
function isCodexBridgeUrl(value) {
|
|
228
231
|
if (!value) {
|
|
229
232
|
return false;
|
|
230
233
|
}
|
|
@@ -237,32 +240,29 @@ function isCcrGatewayUrl(value) {
|
|
|
237
240
|
return false;
|
|
238
241
|
}
|
|
239
242
|
}
|
|
240
|
-
function
|
|
241
|
-
|
|
242
|
-
return false;
|
|
243
|
-
}
|
|
244
|
-
return value === CCR_GPT_MODEL_ID
|
|
245
|
-
|| value.startsWith("anthropic/claude-ccr-h");
|
|
243
|
+
function isCodexBridgeModelName(value, bridgeModel) {
|
|
244
|
+
return Boolean(value && bridgeModel && value === bridgeModel);
|
|
246
245
|
}
|
|
247
246
|
function createStatus(input) {
|
|
247
|
+
const { models, ...status } = input;
|
|
248
248
|
const reason = input.error
|
|
249
|
-
?? (input.enabled ? "
|
|
249
|
+
?? (input.enabled ? "Codex Bridge models are unavailable." : "Enable Codex Bridge in Settings.");
|
|
250
250
|
return {
|
|
251
|
-
...
|
|
252
|
-
modelOptions: createSessionModelOptions(input.enabled && input.modelAvailable
|
|
251
|
+
...status,
|
|
252
|
+
modelOptions: createSessionModelOptions(models, input.enabled && input.modelAvailable ? undefined : reason)
|
|
253
253
|
};
|
|
254
254
|
}
|
|
255
255
|
function missingApiKeyError() {
|
|
256
256
|
return new VcmError({
|
|
257
|
-
code: "
|
|
258
|
-
message: "
|
|
257
|
+
code: "CODEX_BRIDGE_API_KEY_MISSING",
|
|
258
|
+
message: "Codex Bridge API key is not configured.",
|
|
259
259
|
statusCode: 400,
|
|
260
|
-
hint: "Save the
|
|
260
|
+
hint: "Save the Codex Bridge API key before enabling Codex models."
|
|
261
261
|
});
|
|
262
262
|
}
|
|
263
263
|
function invalidSettingsError(message) {
|
|
264
264
|
return new VcmError({
|
|
265
|
-
code: "
|
|
265
|
+
code: "CODEX_BRIDGE_SETTINGS_INVALID",
|
|
266
266
|
message,
|
|
267
267
|
statusCode: 400
|
|
268
268
|
});
|