From db8cbfe96f668cfe3e06c25b5514d6ba26fb715a Mon Sep 17 00:00:00 2001 From: Jack Chiang Date: Sat, 5 Sep 2026 16:28:28 +0800 Subject: [PATCH 1/4] chore: copy Claude configuration to Codex (#96) Co-authored-by: OpenAI Codex <215057067+openai-codex[bot]@users.noreply.github.com> --- AGENTS.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index eab08c5f..3ed71d86 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -213,3 +213,6 @@ Co-authored-by: OpenAI Codex <215057067+openai-codex[bot]@users.noreply.github.c ``` - Keep controlled comment-only first-pass automation separate from the privileged maintainer-only mention workflow. + +## Codex 工具適配 +瀏覽器操作使用當前 Codex browser/computer-use 工具;原文的 Claude MCP 工具名稱只描述操作意圖,不能直接呼叫。提問依目前模式使用可用工具;Git 操作仍需當次明確授權。 From 694eef1f9dd5ecd90e4ee38ce3ae023349c53ae0 Mon Sep 17 00:00:00 2001 From: Abdul Kapti Date: Sun, 6 Sep 2026 22:44:29 +0530 Subject: [PATCH 2/4] feat: add AWS Bedrock in-region Claude pricing (#95) Adds an opt-in AWS Bedrock in-region pricing backend for Claude usage estimates, with settings, cache invalidation, localization, and tests.\n\nCo-authored-by: akapti <18787762+akapti@users.noreply.github.com> --- CHANGELOG.md | 5 ++ package-lock.json | 4 +- package.json | 2 +- src/extension.ts | 28 ++++++- src/i18n.ts | 7 ++ src/pricing.ts | 116 +++++++++++++++++++++++++++++ src/settings.ts | 12 +++ src/test/pricing.test.ts | 118 +++++++++++++++++++++++++++++- src/types.ts | 1 + tests/ui/claude-settings.spec.mjs | 32 ++++++++ 10 files changed, 320 insertions(+), 5 deletions(-) create mode 100644 tests/ui/claude-settings.spec.mjs diff --git a/CHANGELOG.md b/CHANGELOG.md index dce931ae..92e18381 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ upstream release: 1.0.8). Format follows [Keep a Changelog](https://keepachangel ## [2.3.0] — Unreleased ### Fixed +- **AWS Bedrock in-region Claude pricing** — the selectable Bedrock table now + covers Claude Opus 4.5, 4.6, 4.7, 4.8, Sonnet 4.5, Sonnet 4.6, Sonnet 5, + Opus 5, and Haiku 4.5, including separate 5-minute and 1-hour cache-write + rates. Changing the pricing backend invalidates the incremental cost + aggregate so existing logs are recalculated immediately. - **Codex Today is now the configured calendar day** — the first Codex tab now pairs its day total with exact hourly API-equivalent cost and a separate token-composition view. Its additive schema-3 current-day sidecar scans only diff --git a/package-lock.json b/package-lock.json index 10caceca..9690caac 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "claude-code-usage", - "version": "2.1.1", + "version": "2.3.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "claude-code-usage", - "version": "2.1.1", + "version": "2.3.1", "license": "MIT", "devDependencies": { "@axe-core/playwright": "4.12.1", diff --git a/package.json b/package.json index 642769d1..23861493 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "claude-code-usage", "displayName": "Claude Code Usage", "description": "Local Claude Code and Codex usage insights in the VS Code status bar", - "version": "2.1.1", + "version": "2.3.1", "publisher": "GrowthJack", "author": "GrowthJack", "license": "MIT", diff --git a/src/extension.ts b/src/extension.ts index d878cbb8..b4ecc355 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -18,7 +18,7 @@ import { StatusBarManager } from './statusBar'; import { UsageWebviewProvider } from './webview'; import { I18n } from './i18n'; import { resolveTimeZone } from './dateKeys'; -import { fetchLatestPricing } from './pricing'; +import { fetchLatestPricing, setPricingBackend } from './pricing'; import { ClaudeApiClient } from './claudeApiClient'; import { buildOptimizerSystemPrompt, @@ -150,6 +150,7 @@ export class ClaudeCodeUsageExtension { private quotaColdRetryDone: boolean = false; private claudeProfileGeneration: number = 0; private claudeWeeklyQuotaHistory: WeeklyQuotaObservation[] = []; + private activePricingBackend: ExtensionConfig['pricingBackend'] = 'anthropic'; private codexProvider: CodexProvider; private readonly codexSalt: string; private codexView: CodexUsageView | null = null; @@ -304,6 +305,7 @@ export class ClaudeCodeUsageExtension { private async refreshPricing(): Promise { try { const result = await fetchLatestPricing(); + this.invalidateClaudeUsagePricingCache(); vscode.window.showInformationMessage(`${I18n.t.popup.pricingUpdated} (${result.updated})`); // Force a full recompute so the new prices take effect. void this.refreshData(true, 'pricing'); @@ -756,6 +758,8 @@ export class ClaudeCodeUsageExtension { private loadConfiguration(): void { const config = this.getConfiguration(); + this.activePricingBackend = config.pricingBackend; + setPricingBackend(config.pricingBackend); I18n.setLanguage(config.language as any); I18n.setDecimalPlaces(config.decimalPlaces); I18n.setTokenDecimalPlaces(config.tokenDecimalPlaces); @@ -791,6 +795,7 @@ export class ClaudeCodeUsageExtension { return { refreshInterval: s.get('refreshInterval'), dataDirectory: s.get('dataDirectory'), + pricingBackend: s.get<'anthropic' | 'aws-bedrock-in-region'>('pricingBackend'), codexEnabled: s.get('codex.enabled'), codexDataDirectory: s.get('codex.dataDirectory'), codexFileWatchSeconds: @@ -1109,6 +1114,12 @@ export class ClaudeCodeUsageExtension { private onConfigurationChanged(): void { const config = this.getConfiguration(); + const pricingBackendChanged = this.activePricingBackend !== config.pricingBackend; + this.activePricingBackend = config.pricingBackend; + setPricingBackend(config.pricingBackend); + if (pricingBackendChanged) { + this.invalidateClaudeUsagePricingCache(); + } I18n.setLanguage(config.language as any); I18n.setDecimalPlaces(config.decimalPlaces); I18n.setTokenDecimalPlaces(config.tokenDecimalPlaces); @@ -1136,6 +1147,21 @@ export class ClaudeCodeUsageExtension { }); } + /** + * Pricing is part of every cached UsageData aggregate. The incremental + * loader normally skips unchanged JSONL files, so changing the pricing + * backend would otherwise update only the displayed rate labels while + * leaving totalCost/modelBreakdown.cost at the old rates. + */ + private invalidateClaudeUsagePricingCache(): void { + this.cache.claudeIndex = createClaudeUsageIndex(); + this.cache.records = []; + this.cache.contentAnalysis = null; + this.cache.manifest = null; + this.cache.dataDirectory = null; + this.cache.lastUpdate = new Date(0); + } + /** Keep quota credentials on the same Claude profile as this window's logs. * A profile switch invalidates every in-memory quota/backoff value because it * belongs to a different account. */ diff --git a/src/i18n.ts b/src/i18n.ts index 483775c5..e82ecc8d 100644 --- a/src/i18n.ts +++ b/src/i18n.ts @@ -2919,6 +2919,7 @@ const SETTINGS_I18N: Partial { // Opus current tier: $5 / $25 / $6.25 / $0.50 per million in/out/write/read. @@ -83,3 +88,114 @@ test('getModelPricing falls back to the right family for an unknown snapshot', ( assert.ok(pricing, 'expected a fallback pricing object, got null'); assert.equal(pricing!.input_cost_per_token, 5 / 1_000_000); }); + +test('AWS Bedrock mode prices Claude 5 models with in-region rates', () => { + setPricingBackend('aws-bedrock-in-region'); + try { + for (const model of [ + 'claude-sonnet-5', + 'us.anthropic.claude-sonnet-5-v1:0', + 'claude-sonnet-5[1m]', + ]) { + const pricing = getModelPricing(model); + assert.ok(pricing, `expected Bedrock pricing for ${model}, got null`); + assert.equal(pricing!.input_cost_per_token, 2.2 / 1_000_000, model); + assert.equal(pricing!.output_cost_per_token, 11 / 1_000_000, model); + assert.equal(pricing!.cache_creation_input_token_cost, 2.75 / 1_000_000, model); + assert.equal(pricing!.cache_creation_1h_input_token_cost, 4.4 / 1_000_000, model); + assert.equal(pricing!.cache_read_input_token_cost, 0.22 / 1_000_000, model); + } + + const cost = calculateCostFromTokens( + { + input_tokens: 1_000_000, + output_tokens: 1_000_000, + cache_creation_input_tokens: 1_000_000, + cache_read_input_tokens: 1_000_000, + }, + 'claude-sonnet-5', + ); + assert.ok(Math.abs(cost - 16.17) < 1e-9, `expected ~16.17, got ${cost}`); + } finally { + setPricingBackend('anthropic'); + } +}); + +test('AWS Bedrock mode prices Claude Opus 4.5-4.8 with in-region rates', () => { + setPricingBackend('aws-bedrock-in-region'); + try { + for (const model of [ + 'claude-opus-4-8', + 'claude-opus-4-7', + 'claude-opus-4-6', + 'claude-opus-4-5-20251101', + 'us.anthropic.claude-opus-4-8-v1:0', + ]) { + const pricing = getModelPricing(model); + assert.ok(pricing, `expected Bedrock pricing for ${model}, got null`); + assert.equal(pricing!.input_cost_per_token, 5.5 / 1_000_000, model); + assert.equal(pricing!.output_cost_per_token, 27.5 / 1_000_000, model); + assert.equal(pricing!.cache_creation_input_token_cost, 6.875 / 1_000_000, model); + assert.equal(pricing!.cache_creation_1h_input_token_cost, 11 / 1_000_000, model); + assert.equal(pricing!.cache_read_input_token_cost, 0.55 / 1_000_000, model); + } + } finally { + setPricingBackend('anthropic'); + } +}); + +test('AWS Bedrock mode prices Claude Sonnet 4.5 and 4.6 with in-region rates', () => { + setPricingBackend('aws-bedrock-in-region'); + try { + for (const model of [ + 'claude-sonnet-4-6', + 'claude-sonnet-4-5-20250929', + 'claude-sonnet-4-5', + 'us.anthropic.claude-sonnet-4-6-v1:0', + 'us.anthropic.claude-sonnet-4-5-20250929-v1:0', + 'claude-sonnet-4.5', + 'claude_sonnet_4_5', + ]) { + const pricing = getModelPricing(model); + assert.ok(pricing, `expected Bedrock pricing for ${model}, got null`); + assert.equal(pricing!.input_cost_per_token, 3.3 / 1_000_000, model); + assert.equal(pricing!.output_cost_per_token, 16.5 / 1_000_000, model); + assert.equal(pricing!.cache_creation_input_token_cost, 4.125 / 1_000_000, model); + assert.equal(pricing!.cache_creation_1h_input_token_cost, 6.6 / 1_000_000, model); + assert.equal(pricing!.cache_read_input_token_cost, 0.33 / 1_000_000, model); + } + + const cost = calculateCostFromTokens( + { + input_tokens: 1_000_000, + output_tokens: 1_000_000, + cache_creation_input_tokens: 1_000_000, + cache_read_input_tokens: 1_000_000, + }, + 'claude-sonnet-4-5-20250929', + ); + assert.ok(Math.abs(cost - 24.255) < 1e-9, `expected ~24.255, got ${cost}`); + } finally { + setPricingBackend('anthropic'); + } +}); + +test('Claude Sonnet 4.5 switches between direct and Bedrock rates for the same model id', () => { + const model = 'claude-sonnet-4-5-20250929'; + + setPricingBackend('anthropic'); + const direct = getModelPricing(model); + assert.ok(direct); + assert.equal(direct.input_cost_per_token, 3 / 1_000_000); + assert.equal(direct.output_cost_per_token, 15 / 1_000_000); + + setPricingBackend('aws-bedrock-in-region'); + try { + const bedrock = getModelPricing(model); + assert.ok(bedrock); + assert.equal(bedrock.input_cost_per_token, 3.3 / 1_000_000); + assert.equal(bedrock.output_cost_per_token, 16.5 / 1_000_000); + } finally { + setPricingBackend('anthropic'); + } +}); diff --git a/src/types.ts b/src/types.ts index d3cc91d5..06e2d7ff 100644 --- a/src/types.ts +++ b/src/types.ts @@ -268,6 +268,7 @@ export interface UsageAttribution { export interface ExtensionConfig { refreshInterval: number; dataDirectory: string; + pricingBackend: 'anthropic' | 'aws-bedrock-in-region'; codexEnabled: boolean; codexDataDirectory: string; codexFileWatchSeconds: number; diff --git a/tests/ui/claude-settings.spec.mjs b/tests/ui/claude-settings.spec.mjs new file mode 100644 index 00000000..9c52c760 --- /dev/null +++ b/tests/ui/claude-settings.spec.mjs @@ -0,0 +1,32 @@ +import { test, expect, openClaude, openCodex } from './support/app.mjs'; + +test('Claude pricing backend is available only in Claude settings', async ({ page }) => { + await openClaude(page); + await page.locator('#tab-settings').click(); + + const backend = page.locator('#set_pricingBackend'); + await expect(backend).toBeVisible(); + await expect(page.locator('label[for="set_pricingBackend"]')).toHaveText('Claude pricing backend'); + await expect(backend.locator('option')).toHaveText([ + 'Anthropic direct API', + 'AWS Bedrock (in-region)', + ]); + await expect(backend).toHaveValue('anthropic'); + + await openCodex(page); + await page.locator('#tab-settings').click(); + await expect(page.locator('#set_pricingBackend')).toHaveCount(0); +}); + +test('Claude pricing backend dropdown posts the selected Bedrock backend', async ({ page }) => { + await openClaude(page); + await page.locator('#tab-settings').click(); + + await page.locator('#set_pricingBackend').selectOption('aws-bedrock-in-region'); + + await expect.poll(() => page.evaluate(() => window.__ccuPostedMessages.at(-1))).toEqual({ + command: 'updateSetting', + key: 'pricingBackend', + value: 'aws-bedrock-in-region', + }); +}); From 087bae5e90b38fcdd35ae1852ee657c33fa9e444 Mon Sep 17 00:00:00 2001 From: Carl <74130028+Carl723000@users.noreply.github.com> Date: Mon, 7 Sep 2026 01:39:20 +0800 Subject: [PATCH 3/4] release: prepare v2.3.1 (#97) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Validated release candidate: Node 940/940 effective passes with one capability skip, Playwright 129/129, and Release Package passed. Includes the v2.3.1 UI, migration, quota-confidence, model-pricing, documentation, and roadmap closure described in the PR.\n\n---\n🤖 Generated with [OpenAI Codex](https://developers.openai.com/codex/) --- .github/scripts/vsixPolicy.mjs | 4 + .github/scripts/vsixPolicy.test.mjs | 20 + .vscodeignore | 10 + AGENTS.zh-CN.md | 6 + AI-ADVICE-EFFECTIVENESS-V2.3.1.md | 132 + AI-ADVICE-INTEGRATION-DESIGN-V2.3.1.md | 120 + AI-ADVICE-INTEGRATION-DESIGN-V2.3.1.zh-CN.md | 92 + AI-ADVICE-INTEGRATION-V2.3.1.md | 284 + AI-ADVICE-INTEGRATION-V2.3.1.zh-CN.md | 263 + ARCHITECTURE-zh-CN.md | 83 +- ARCHITECTURE.md | 120 +- CHANGELOG.md | 251 +- CLAUDE-COLD-AB-V2.3.1.md | 15 + CLAUDE-COLD-AB-V2.3.1.zh-CN.md | 15 + LOCAL-DATA.md | 116 + LOCAL-DATA.zh-CN.md | 93 + README-en.md | 83 +- README-id.md | 76 +- README-ja.md | 73 +- README-ko.md | 73 +- README-zh-CN.md | 100 +- README-zh-TW.md | 76 +- README.md | 267 +- V2.3.1-CANDIDATE-CONTRACT.md | 60 + V2.3.1-CANDIDATE-CONTRACT.zh-CN.md | 60 + .../2026-09-01-v2.3.1-goal-ledger.en.md | 341 + .../2026-09-01-v2.3.1-goal-ledger.zh-CN.md | 341 + docs/roadmap/provider-parity-v2.4.md | 69 + docs/roadmap/provider-parity-v2.4.zh-CN.md | 63 + ...26-09-02-v2.5.3-project-ai-usage-review.md | 156 + ...02-v2.5.3-project-ai-usage-review.zh-CN.md | 156 + .../2026-09-02-v2.3.1-local-data-contract.md | 178 + ...-09-02-v2.3.1-local-data-contract.zh-CN.md | 167 + .../2026-09-03-v2.3.2-overnight-review.md | 335 + images/v2.3.1/claude-today-zh-CN-dark.png | Bin 0 -> 84934 bytes images/v2.3.1/codex-overview-zh-CN-dark.png | Bin 0 -> 81287 bytes .../v2.3.1/codex-weekly-estimate-en-dark.png | Bin 0 -> 50088 bytes images/v2.3.1/compare-heatmap-en-light.png | Bin 0 -> 120039 bytes package-lock.json | 4 +- package.json | 47 +- src/adviceEffectiveness/adapters.ts | 597 ++ src/adviceEffectiveness/comparison.ts | 239 + src/adviceEffectiveness/comparisonPairing.ts | 562 ++ .../comparisonProduction.ts | 413 ++ src/adviceEffectiveness/comparisonResult.ts | 615 ++ src/adviceEffectiveness/contract.ts | 374 ++ .../evidencePreparation.ts | 166 + src/adviceEffectiveness/feedback.ts | 166 + src/adviceEffectiveness/integration.ts | 140 + src/adviceEffectiveness/legacyBridge.ts | 131 + .../legacyPersonalization.ts | 276 + src/adviceEffectiveness/modelExperiment.ts | 125 + src/adviceEffectiveness/payload.ts | 499 ++ src/adviceEffectiveness/preparedRequest.ts | 254 + src/adviceEffectiveness/remoteAdvice.ts | 113 + src/adviceEffectiveness/structuredOutput.ts | 346 + .../versionedPersistence.ts | 723 +++ src/advisor.ts | 243 +- src/backgroundWorkState.ts | 466 ++ src/claudeApiClient.ts | 81 +- src/claudeIncrementalIndex.ts | 215 +- src/codexStatus.ts | 104 +- src/codexViewComponents.ts | 11 + src/combinedHeatmap.ts | 242 + src/combinedHeatmapSvg.ts | 188 + src/dataLoader.ts | 269 +- src/dateKeys.ts | 5 + src/extension.ts | 3193 ++++++++-- src/githubHeatmapPublish.ts | 178 + src/heatmapSvg.ts | 148 +- src/httpClient.ts | 55 +- src/i18n.ts | 980 ++- src/localDataControls.ts | 245 + src/optimizerRequest.ts | 133 + src/pricing.ts | 64 +- src/promptOrigin.ts | 40 + src/providers/codex/codexDedup.ts | 23 +- src/providers/codex/codexFilePassPool.ts | 44 +- src/providers/codex/codexIdentity.ts | 4 +- src/providers/codex/codexIndex.ts | 751 ++- src/providers/codex/codexIndexClient.ts | 103 +- src/providers/codex/codexIndexWorker.ts | 4 +- src/providers/codex/codexParser.ts | 137 +- src/providers/codex/codexPeriodIndex.ts | 193 +- src/providers/codex/codexProvider.ts | 206 +- src/providers/codex/codexQuotaHistory.ts | 195 + src/providers/codex/codexUsage.ts | 172 +- src/providers/codex/codexWorkerProtocol.ts | 3 + src/quotaObservationStore.ts | 1103 ++++ src/resourceOwnership.ts | 204 + src/settings.ts | 390 +- src/statusBar.ts | 89 +- src/test/adviceAdapters.test.ts | 312 + src/test/adviceComparison.test.ts | 106 + src/test/adviceComparisonPairing.test.ts | 148 + src/test/adviceComparisonProduction.test.ts | 239 + src/test/adviceComparisonResult.test.ts | 189 + src/test/adviceContract.test.ts | 96 + src/test/adviceEffectivenessI18n.test.ts | 106 + src/test/adviceEvidencePreparation.test.ts | 144 + src/test/adviceFeedback.test.ts | 72 + src/test/adviceIntegration.test.ts | 152 + src/test/adviceLegacyBridge.test.ts | 133 + src/test/adviceLegacyPersonalization.test.ts | 134 + src/test/adviceModelExperiment.test.ts | 117 + src/test/adviceOutput.test.ts | 74 + src/test/advicePayload.test.ts | 143 + src/test/advicePersistence.test.ts | 625 ++ src/test/advicePreparedRequest.test.ts | 150 + src/test/advicePrivacy.test.ts | 64 + src/test/adviceRemoteInvocation.test.ts | 106 + src/test/adviceRuntimePolicy.test.ts | 88 + src/test/adviceStateSerialization.test.ts | 616 ++ src/test/adviceTestFixtures.ts | 130 + src/test/backgroundWorkState.test.ts | 299 + src/test/claudeApiClient.test.ts | 73 +- src/test/claudeIncrementalIndex.test.ts | 247 + src/test/codexDedup.test.ts | 33 + src/test/codexIdentity.test.ts | 25 + src/test/codexIndex.test.ts | 450 +- src/test/codexIndexClient.test.ts | 122 +- src/test/codexParser.test.ts | 80 + src/test/codexPeriodIndex.test.ts | 40 + src/test/codexProvider.test.ts | 149 +- src/test/codexQuotaHistory.test.ts | 138 + src/test/codexStatus.test.ts | 89 +- src/test/codexUsage.test.ts | 334 +- src/test/codexWebviewFixtures.ts | 69 +- src/test/combinedHeatmap.test.ts | 274 + src/test/combinedHeatmapI18n.test.ts | 60 + src/test/dataLoader.test.ts | 386 +- src/test/dateKeys.test.ts | 10 + src/test/extensionRefreshRouting.test.ts | 10 +- src/test/extensionWindowActivity.test.ts | 1358 +++- src/test/githubHeatmapPublish.test.ts | 113 + src/test/heatmapSvg.test.ts | 146 + src/test/i18nTimezone.test.ts | 12 + src/test/localDataControls.test.ts | 410 ++ src/test/localDataExtension.test.ts | 330 + src/test/optimizerPreparedFlow.test.ts | 84 + src/test/pricing.test.ts | 41 +- src/test/promptOrigin.test.ts | 36 + src/test/providerSelection.test.ts | 66 +- src/test/quotaI18n.test.ts | 27 + src/test/quotaObservationStore.test.ts | 526 ++ src/test/releaseAnnouncements.test.ts | 12 +- src/test/repositoryPolicy.test.ts | 107 +- src/test/resourceOwnership.test.ts | 160 + src/test/resourceOwnershipIntegration.test.ts | 59 + src/test/settingsPrivacy.test.ts | 445 ++ src/test/statusBarThresholds.test.ts | 122 + src/test/usageDateLabels.test.ts | 1 + src/test/webviewDateTime.test.ts | 169 + src/test/webviewWorkflowSummary.test.ts | 116 + src/test/weeklyValue.test.ts | 413 +- src/types.ts | 45 +- src/usageDateLabels.ts | 2 +- src/webview.ts | 5594 +++++++++++++++-- src/weeklyValue.ts | 342 +- tests/ui/advice-effectiveness.spec.mjs | 694 ++ tests/ui/chart-date-labels.spec.mjs | 60 + tests/ui/codex-accessibility.spec.mjs | 203 +- tests/ui/codex-interactions.spec.mjs | 200 +- tests/ui/codex-responsive.spec.mjs | 68 +- tests/ui/codex-visual.spec.mjs | 486 +- tests/ui/combined-heatmap.spec.mjs | 216 + tests/ui/local-data-controls.spec.mjs | 137 + .../support/advice-effectiveness-fixture.cjs | 183 + tests/ui/support/app.mjs | 14 +- tests/ui/support/capture-readme.mjs | 90 + tests/ui/support/render-harness.cjs | 171 +- tests/ui/support/server.mjs | 41 +- 172 files changed, 38569 insertions(+), 2131 deletions(-) create mode 100644 AI-ADVICE-EFFECTIVENESS-V2.3.1.md create mode 100644 AI-ADVICE-INTEGRATION-DESIGN-V2.3.1.md create mode 100644 AI-ADVICE-INTEGRATION-DESIGN-V2.3.1.zh-CN.md create mode 100644 AI-ADVICE-INTEGRATION-V2.3.1.md create mode 100644 AI-ADVICE-INTEGRATION-V2.3.1.zh-CN.md create mode 100644 CLAUDE-COLD-AB-V2.3.1.md create mode 100644 CLAUDE-COLD-AB-V2.3.1.zh-CN.md create mode 100644 LOCAL-DATA.md create mode 100644 LOCAL-DATA.zh-CN.md create mode 100644 V2.3.1-CANDIDATE-CONTRACT.md create mode 100644 V2.3.1-CANDIDATE-CONTRACT.zh-CN.md create mode 100644 docs/releases/2026-09-01-v2.3.1-goal-ledger.en.md create mode 100644 docs/releases/2026-09-01-v2.3.1-goal-ledger.zh-CN.md create mode 100644 docs/roadmap/provider-parity-v2.4.md create mode 100644 docs/roadmap/provider-parity-v2.4.zh-CN.md create mode 100644 docs/superpowers/plans/2026-09-02-v2.5.3-project-ai-usage-review.md create mode 100644 docs/superpowers/plans/2026-09-02-v2.5.3-project-ai-usage-review.zh-CN.md create mode 100644 docs/superpowers/specs/2026-09-02-v2.3.1-local-data-contract.md create mode 100644 docs/superpowers/specs/2026-09-02-v2.3.1-local-data-contract.zh-CN.md create mode 100644 docs/superpowers/specs/2026-09-03-v2.3.2-overnight-review.md create mode 100644 images/v2.3.1/claude-today-zh-CN-dark.png create mode 100644 images/v2.3.1/codex-overview-zh-CN-dark.png create mode 100644 images/v2.3.1/codex-weekly-estimate-en-dark.png create mode 100644 images/v2.3.1/compare-heatmap-en-light.png create mode 100644 src/adviceEffectiveness/adapters.ts create mode 100644 src/adviceEffectiveness/comparison.ts create mode 100644 src/adviceEffectiveness/comparisonPairing.ts create mode 100644 src/adviceEffectiveness/comparisonProduction.ts create mode 100644 src/adviceEffectiveness/comparisonResult.ts create mode 100644 src/adviceEffectiveness/contract.ts create mode 100644 src/adviceEffectiveness/evidencePreparation.ts create mode 100644 src/adviceEffectiveness/feedback.ts create mode 100644 src/adviceEffectiveness/integration.ts create mode 100644 src/adviceEffectiveness/legacyBridge.ts create mode 100644 src/adviceEffectiveness/legacyPersonalization.ts create mode 100644 src/adviceEffectiveness/modelExperiment.ts create mode 100644 src/adviceEffectiveness/payload.ts create mode 100644 src/adviceEffectiveness/preparedRequest.ts create mode 100644 src/adviceEffectiveness/remoteAdvice.ts create mode 100644 src/adviceEffectiveness/structuredOutput.ts create mode 100644 src/adviceEffectiveness/versionedPersistence.ts create mode 100644 src/backgroundWorkState.ts create mode 100644 src/combinedHeatmap.ts create mode 100644 src/combinedHeatmapSvg.ts create mode 100644 src/githubHeatmapPublish.ts create mode 100644 src/localDataControls.ts create mode 100644 src/optimizerRequest.ts create mode 100644 src/promptOrigin.ts create mode 100644 src/providers/codex/codexQuotaHistory.ts create mode 100644 src/quotaObservationStore.ts create mode 100644 src/resourceOwnership.ts create mode 100644 src/test/adviceAdapters.test.ts create mode 100644 src/test/adviceComparison.test.ts create mode 100644 src/test/adviceComparisonPairing.test.ts create mode 100644 src/test/adviceComparisonProduction.test.ts create mode 100644 src/test/adviceComparisonResult.test.ts create mode 100644 src/test/adviceContract.test.ts create mode 100644 src/test/adviceEffectivenessI18n.test.ts create mode 100644 src/test/adviceEvidencePreparation.test.ts create mode 100644 src/test/adviceFeedback.test.ts create mode 100644 src/test/adviceIntegration.test.ts create mode 100644 src/test/adviceLegacyBridge.test.ts create mode 100644 src/test/adviceLegacyPersonalization.test.ts create mode 100644 src/test/adviceModelExperiment.test.ts create mode 100644 src/test/adviceOutput.test.ts create mode 100644 src/test/advicePayload.test.ts create mode 100644 src/test/advicePersistence.test.ts create mode 100644 src/test/advicePreparedRequest.test.ts create mode 100644 src/test/advicePrivacy.test.ts create mode 100644 src/test/adviceRemoteInvocation.test.ts create mode 100644 src/test/adviceRuntimePolicy.test.ts create mode 100644 src/test/adviceStateSerialization.test.ts create mode 100644 src/test/adviceTestFixtures.ts create mode 100644 src/test/backgroundWorkState.test.ts create mode 100644 src/test/codexQuotaHistory.test.ts create mode 100644 src/test/combinedHeatmap.test.ts create mode 100644 src/test/combinedHeatmapI18n.test.ts create mode 100644 src/test/githubHeatmapPublish.test.ts create mode 100644 src/test/localDataControls.test.ts create mode 100644 src/test/localDataExtension.test.ts create mode 100644 src/test/optimizerPreparedFlow.test.ts create mode 100644 src/test/promptOrigin.test.ts create mode 100644 src/test/quotaObservationStore.test.ts create mode 100644 src/test/resourceOwnership.test.ts create mode 100644 src/test/resourceOwnershipIntegration.test.ts create mode 100644 src/test/settingsPrivacy.test.ts create mode 100644 src/test/webviewDateTime.test.ts create mode 100644 src/test/webviewWorkflowSummary.test.ts create mode 100644 tests/ui/advice-effectiveness.spec.mjs create mode 100644 tests/ui/chart-date-labels.spec.mjs create mode 100644 tests/ui/combined-heatmap.spec.mjs create mode 100644 tests/ui/local-data-controls.spec.mjs create mode 100644 tests/ui/support/advice-effectiveness-fixture.cjs create mode 100644 tests/ui/support/capture-readme.mjs diff --git a/.github/scripts/vsixPolicy.mjs b/.github/scripts/vsixPolicy.mjs index bfe9b1ff..5564194e 100644 --- a/.github/scripts/vsixPolicy.mjs +++ b/.github/scripts/vsixPolicy.mjs @@ -18,6 +18,10 @@ const FORBIDDEN_ENTRY_PATTERNS = [ /^extension\/node_modules(?:\/|$)/i, /^extension\/issue87-.*\.cjs$/i, /^extension\/out\/claudeIncrementalPrototype\.js$/i, + /^extension\/out\/adviceEffectiveness\/(?:evidencePreparation|feedback|legacyBridge|legacyPersonalization|modelExperiment)\.js$/i, + /^extension\/out\/(?:adviceDemoSample|adviceSummary)\.js$/i, + /^extension\/(?:AI-ADVICE-.*V2\.3\.1|V2\.3\.1-CANDIDATE-CONTRACT(?:\.zh-CN)?)\.md$/i, + /^extension\/CLAUDE-COLD-AB-V2\.3\.1(?:\.zh-CN)?\.md$/i, /(?:^|\/)\.env(?:\.|$)/i, /\.(?:pem|key|p12|pfx)$/i, ]; diff --git a/.github/scripts/vsixPolicy.test.mjs b/.github/scripts/vsixPolicy.test.mjs index 8b95bf91..0fbc97b9 100644 --- a/.github/scripts/vsixPolicy.test.mjs +++ b/.github/scripts/vsixPolicy.test.mjs @@ -73,6 +73,26 @@ test('VSIX policy rejects internal Superpowers review material', () => { ); }); +test('VSIX policy rejects dormant v2.3.1 preparation seams and review documents', () => { + for (const entry of [ + 'extension/out/adviceEffectiveness/evidencePreparation.js', + 'extension/out/adviceEffectiveness/feedback.js', + 'extension/out/adviceEffectiveness/legacyBridge.js', + 'extension/out/adviceEffectiveness/legacyPersonalization.js', + 'extension/out/adviceEffectiveness/modelExperiment.js', + 'extension/out/adviceDemoSample.js', + 'extension/out/adviceSummary.js', + 'extension/AI-ADVICE-INTEGRATION-V2.3.1.md', + 'extension/V2.3.1-CANDIDATE-CONTRACT.zh-CN.md', + ]) { + assert.throws( + () => assertSafeVsixEntries([...minimalEntries, entry]), + /forbidden VSIX entry/, + entry, + ); + } +}); + test('VSIX policy rejects unsafe or non-canonical archive paths', () => { for (const entry of [ '/extension/out/extra.js', diff --git a/.vscodeignore b/.vscodeignore index ec2a3674..ce7fa89c 100644 --- a/.vscodeignore +++ b/.vscodeignore @@ -20,6 +20,16 @@ CONTRIBUTING.md probe-*.mjs issue87-*.cjs out/claudeIncrementalPrototype.js +out/adviceEffectiveness/evidencePreparation.js +out/adviceEffectiveness/feedback.js +out/adviceEffectiveness/legacyBridge.js +out/adviceEffectiveness/legacyPersonalization.js +out/adviceEffectiveness/modelExperiment.js +out/adviceDemoSample.js +out/adviceSummary.js +AI-ADVICE-*V2.3.1*.md +CLAUDE-COLD-AB-V2.3.1*.md +V2.3.1-CANDIDATE-CONTRACT*.md docs/** .gitignore diff --git a/AGENTS.zh-CN.md b/AGENTS.zh-CN.md index 524e0cab..86e2e667 100644 --- a/AGENTS.zh-CN.md +++ b/AGENTS.zh-CN.md @@ -177,3 +177,9 @@ Co-authored-by: OpenAI Codex <215057067+openai-codex[bot]@users.noreply.github.c ``` - comment-only 的受控 first-pass automation 与 maintainer-only 的高权限 mention workflow 必须保持分离。 + +## Codex 工具适配 + +浏览器操作使用当前可用的 Codex browser/computer-use 工具;原文中的 Claude MCP +工具名称只描述操作意图,不能直接调用。提问按当前模式使用可用工具;Git 操作仍须 +获得当次明确授权。 diff --git a/AI-ADVICE-EFFECTIVENESS-V2.3.1.md b/AI-ADVICE-EFFECTIVENESS-V2.3.1.md new file mode 100644 index 00000000..0f8d8ee0 --- /dev/null +++ b/AI-ADVICE-EFFECTIVENESS-V2.3.1.md @@ -0,0 +1,132 @@ +# v2.3.1 AI 建议有效性:候选实现说明 + +## 状态与边界 + +当前 worktree 是从正式 `v2.3.0` 基线选择性移植并继续收敛的 v2.3.1 本地候选;版本号、 +release workflow 与发布入口保持不变,尚未发布。 + +候选代码已经进入 `extension.ts`、`webview.ts` 与生产 VSIX 路径,但相关功能默认关闭。 +关闭、隐藏或未授权时不创建 AI timer、watcher、worker、network 或额外日志扫描。启用后, +只有用户先检查完整请求预览、再另行点击“发送”,才会调用现有用户配置的 BYOK endpoint。 +旧 `Get AI Advice` 命令仅打开这一统一界面;Prompt Optimizer 也使用同一 prepare → preview → +explicit Send → strict parse → local feedback 边界,不再保留一次性持久授权或宽松 parser fallback。 + +## 新增核心 + +### `src/adviceEffectiveness/contract.ts` + +`AdviceContract` 是 provider-neutral 的 v1 契约,统一保存: + +- host 生成的本地 observation 与 evidence; +- direct / correlational / proxy 证据强度; +- AI 只能引用已有证据的 explanation; +- 带 `when` 与 stop condition 的 conditional action; +- 带最小可比任务数与质量 guardrail 的 success criterion; +- host 盖章的 privacy、source、confidence、quality flags 与 provenance。 + +模型不能提供 observation、evidence、privacy 或 provenance。`createAdviceContract()` +只在整个引用图和隐私声明一致时返回 `{ ok: true }`。空 recommendations 是合法的 +“没有足够证据下结论”,不会自动补通用建议。 + +### `src/adviceEffectiveness/payload.ts` + +`buildAdviceAggregateSnapshot()` 只消费现有 `UsageData` aggregate,并通过 allow-list +重建 DTO。完整/自定义 model ID 被压成 `opus / sonnet / haiku / fable / other`;raw +record、project label/path、cwd、session、prompt、response、credential 没有目标字段。 +可远程发送的 metric 还有单独 allow-list;新增 metric 必须经过显式隐私评审。low/unknown +confidence 或仍带 quality flag 的 source 会在 payload 生成前失败,不会要求模型填补缺口。 + +`prepareAdvicePayload()` 默认生成 `aggregates-only` 的内层 evidence body,且序列化结果中完全没有 +`promptSamples` key。只有调用者额外提供: + +```ts +{ consent: 'explicit', samples: [{ text: '...' }] } +``` + +才会加入受数量、单条长度和总长度限制的 prompt 文本;sample 上的 `cwd` 等多余字段仍会被 +丢弃。`advice.userContext` 与 prompt sample 共用独立于 aggregate 的 personalization consent; +未授权时两者都不进入 body,授权后必须逐字出现在完整预览中。 + +生产路径再由 `preparedRequest.ts` 把内层 evidence body 封装为完整 Anthropic / OpenAI-compatible +provider HTTP JSON body。该 body 只序列化一次,生成 host-retained `PreparedAiInvocation`、 +`canonicalBytes` 与 SHA-256;预览从同一 bytes 解码,实际发送和同一次发送内的 retry / curl +fallback 继续复用该 Prepared bytes。API key 只在发送边界加入 header,不进入预览 body。 + +### `src/adviceEffectiveness/versionedPersistence.ts` + +生产反馈统一写入 `ccu.adviceEffectiveness.localState` v2 envelope,只接受 +`helpful / not-helpful / applied`、格式验证后的 advice/recommendation ID 与本地 epoch 时间。 +它不接受自由文本、prompt、payload、解释、endpoint、path 或 session ID。helpful 与 +not-helpful 互斥,applied 独立;再次点击已选状态会撤回并在全部状态清空时删除该条记录。 +Advice 的每条 recommendation 与 Prompt Optimizer 的每次结果都走同一个写入函数。 +损坏、未知版本或带额外字段的数据 fail closed,写失败后不会用旧快照覆盖新决定。 + +### `src/adviceEffectiveness/comparison.ts` + +`compareAdviceEffectiveness()` 是无时间/IO 依赖的 paired comparison。只有以下条件全部 +满足,才可能返回 `improved`: + +- task kind、complexity、provider、model family、effort、metric definition、单位、方向、 + quality rubric 在 pair 内和 cohort 间一致; +- coverage complete,confidence 不是 low/unknown,无未允许的 quality flag; +- 达到预先声明的最小 pair 数,数值均为有限值且 baseline 非零; +- before/after 质量证据已知,after 全部通过 rubric; +- 主指标达到阈值,同时 minimum quality 与最大允许退化两个 guardrail 都守住。 + +任何可比性、覆盖、置信度、数值或质量证据问题都返回 `insufficient-evidence`。主指标 +改善但质量退化返回 `quality-guardrail-failed`;没有达到预设幅度只返回 +`no-demonstrated-improvement`,不声称建议造成伤害或收益。 + +`comparisonPairing.ts`、`comparisonProduction.ts` 与 `comparisonResult.ts` 把这条纯比较边界接入 +真实、已净化的 provider/task-level 物化 aggregate。只有 scope、task kind、complexity、provider、 +model、effort、metric definition 与 quality rubric 全部相同,且 applied 时间能可靠划分前后时, +才形成 comparable pair。版本化 result envelope 只保存 schema、provider、measurement / recommendation +version、guardrail、replay eligibility、sample 与 result;prompt、response、session、path、正文和未知字段 +没有目标字段。无可靠 pair 时 UI 明确显示“证据不足”。 + +### `src/adviceEffectiveness/structuredOutput.ts` + +`parseStructuredAdviceOutput()` 只接受一个严格 JSON 值,并逐层拒绝未知/缺失字段、错误 +schema version、非法枚举、超长/超量内容、重复 ID、空 action/criterion 和未知证据引用。 +任一 recommendation 非法会拒绝整批。它不会剥 Markdown fence、修 JSON、接受尾随 prose、 +返回 partial batch,或把 raw 输出 fallback 成看似有效的建议;错误结果也不回显 raw 模型 +正文。`recommendations: []` 是明确的 no-conclusion 成功结果。 + +## 当前生产闭环 + +1. **本地证据**:Claude 只消费 `claudeIncrementalIndex` 已物化的同窗口 aggregate / session + summary;Codex 只消费现有结构化数值 insight 与任务级物化 aggregate,不新增日志扫描器。 +2. **独立 consent**:Advice 的 aggregate consent 与 prompt-personalization consent 分开持久化; + 默认是 aggregates-only。Optimizer 只包含用户主动粘贴的 draft,不使用持久网络授权。 +3. **exact preview / explicit Send**:host 保留完整 Prepared provider body;Webview 只持有 opaque + snapshot ID、精确 body、byte count、content type、data mode 与 SHA-256。只有另行 Send 点击会联网。 +4. **唯一生产 sender**:Advice 与 Optimizer 都进入 `sendPreparedModelRequest()` → + `sendPreparedAiInvocation()`,且只能使用配置的 BYOK key / endpoint。 +5. **strict response**:Advice 只接受 exact-shape JSON 与已知 evidence references;Optimizer 只接受 + 完整 marker / 三行 settings contract。任一失败都不显示 raw 或自由文本 fallback。 +6. **可撤销本地 feedback**:每条 Advice recommendation 和 Optimizer result 共用 v2 bounded ledger; + 不接 analytics、telemetry、contribution endpoint 或 advice payload。 +7. **有效性评估**:只有 applied 后出现严格可比的前后任务时才写 pair / result envelope;否则显示 + `insufficient-evidence`。结果是受控描述性证据,不是因果证明。 + +## 排除项与剩余边界 + +- `claudeCodeUsage.getAdvice` 只打开统一 Content 界面,不再构建旧 summary 或直接调用模型。 +- `legacyBridge.ts`、`adviceSummary.ts`、旧 demo / personalization / experiment 准备模块被排除出 VSIX, + 且 `extension.ts` / `webview.ts` 没有生产调用点;它们不能成为默认或第三条 sender。 +- 生产 backend 固定为 BYOK `api`。Claude Code OAuth/subscription 凭据仅服务既有 quota/usage, + 不用于 Advice 或 Optimizer,也不存在后台模型请求。 +- Codex remote advice 仍保持关闭;Codex recommendation 与 comparison 使用本地结构数据。 +- comparison 不做显著性或因果识别;缺少可靠 cohort、质量证据或最小样本时保持证据不足。 +- feedback 不存自由文本、不同步、不发 telemetry;Optimizer feedback 当前不触发 comparable-task comparison。 + +## 验证 + +测试平铺在 `src/test/` 与 `tests/ui/`,覆盖契约引用、完整 Prepared preview/send object identity、 +默认 aggregates-only、prompt 独立 opt-in、Optimizer 关闭态与严格 parser、敌意隐私字段注入、 +可撤销本地 feedback、真实 pairing / 版本化 result envelope、可比性 / quality guardrail 以及 +未知字段 fail-closed。候选验证仍按仓库流程运行: + +```bash +npm test +``` diff --git a/AI-ADVICE-INTEGRATION-DESIGN-V2.3.1.md b/AI-ADVICE-INTEGRATION-DESIGN-V2.3.1.md new file mode 100644 index 00000000..53fb5e0b --- /dev/null +++ b/AI-ADVICE-INTEGRATION-DESIGN-V2.3.1.md @@ -0,0 +1,120 @@ +# v2.3.1 AI Advice Effectiveness: Experimental UI Design Plan + +> Status: v2.3.1 integration candidate only. The feature flag is off by default; +> this document is not a current-release commitment. + +## Goal and boundaries + +The UI answers one question: can a user see which local evidence supports an +advice candidate, exactly what would leave the device, and how effectiveness +would be judged before opting in? It extends the existing AI Advice and Prompt +Optimizer cards on the Content tab instead of adding a third top-level entry. + +Both features are off by default and create no AI network work while disabled. +When enabled, AI Advice and Prompt Optimizer use the same interaction grammar: +prepare a complete provider HTTP JSON body, preview its exact UTF-8 bytes, require +a separate explicit Send click, strictly parse the response, and offer reversible +local helpful / not-helpful / applied feedback. There is no automatic or default +model request. + +## Visual system + +- **Color:** use existing VS Code theme tokens only. Background and copy use + `--vscode-editor-background` / `--vscode-foreground`; borders and secondary + copy use `--vscode-panel-border` / `--vscode-descriptionForeground`; actions + and focus use `--vscode-button-background` / `--vscode-focusBorder`; the + snapshot uses `--vscode-editorWidget-background`. Light+ and Dark+ remain + host-owned, with no new gradient or hard-coded brand accent. +- **Type:** explanatory copy uses `--vscode-font-family` at the existing 13px + density. Metrics, byte counts, and JSON use `--vscode-editor-font-family`. + Small 10–11px status stamps never carry meaning by color alone. +- **Spacing:** reuse `.action-card`, `.model-details-stacked`, and the existing + button/input rhythm. Avoid a new oversized KPI-card language. +- **Signature:** one continuous evidence spine joins five ordered stages. The + sealed snapshot header reads `SEALED · N BYTES · SHA-256`, and its body is + decoded verbatim from the same canonical UTF-8 bytes. + +## Information architecture and wireframe + +Wide layout: + +```text +┌ AI advice effectiveness (experimental) ──────────────────────────┐ +│ [Observed] ─── [Evidence] ─── [Advice] ─── [Action] ─── [Result] │ +│ values strength conditional local feedback │ +│ sample/guardrail│ +│ │ +│ □ Allow aggregates only □ Allow prompt samples (separate, off) │ +│ [Create sealed snapshot] │ +│ ▾ SEALED · 1248 BYTES · SHA-256 · AGGREGATES ONLY │ +│ {"schemaVersion":1,...} │ +│ [Send prepared request] (disabled until a valid preview exists) │ +│ Helpful ○ Not helpful ○ Applied ○ │ +└──────────────────────────────────────────────────────────────────┘ +``` + +At narrow widths the five stages stack vertically and the spine becomes a left +rail. Consent controls and actions wrap naturally. JSON scrolls horizontally; +actual transmitted bytes are never compressed or elided. + +## Interaction and accessibility + +- Aggregate consent and prompt-personalization consent are separate values in + version-migrated VS Code `globalState`. Both start off; prompt consent is + disabled until aggregate consent is selected. Every included prompt is + visible in the sealed snapshot. +- “Create sealed snapshot” asks the extension host to prepare the object. The + host rechecks the flag and consent, serializes the complete provider body once, + and retains its canonical UTF-8 bytes. Preview text, byte count, and SHA-256 all + come from that object. The separately clicked Send action resolves the opaque + snapshot ID and passes the same Prepared object to the sole production BYOK + transport; it cannot rebuild a request from webview text. +- Prompt Optimizer has the same preview and Send stages. Its body contains only + the pasted draft plus the fixed optimizer system instruction and configured + model fields; it has no persistent “first-run authorization.” +- Advice output uses the strict evidence-referencing JSON parser. Optimizer output + uses its strict marker/settings parser. Neither repairs malformed output or + falls back to free-form prose. +- Helpful / not helpful / applied are native buttons with `aria-pressed` for + every advice recommendation and for an Optimizer result. Selecting an active + button again withdraws that value. Helpful and not helpful are mutually + exclusive; applied is independent. All mutations use the same bounded local + `globalState` envelope and never enter a remote payload. +- Every action is keyboard reachable. Headings, lists, `fieldset` / `legend`, + `aria-live`, and VS Code focus tokens give screen-reader and focus semantics. + Consent, disclosure, and provider UI state survive a webview reload. +- The result stage says “insufficient evidence” until comparable sample and + quality guardrails are met; a one-off movement is never called effective. + +## Content and privacy rules + +- The default route carries same-window aggregate numbers, coarse model families, + allow-listed metrics, confidence, and evidence references only. It never accepts + or serializes raw sessions, per-record data, prompts, responses, project paths, + cwd values, or stable session IDs. +- Long sessions use elapsed-duration as a proxy; large context uses numeric token + thresholds. A conditional “start a new session / `/clear` at a stable task + boundary” action appears only when deterministic thresholds and sample rules pass. +- Topic drift is semantic. It may enter the AI Advice model request only after + separate prompt-personalization consent, with every included prompt visible in + the exact preview. The host never infers drift from body-free signals. +- Strict parse failure, unknown persistence versions, damaged state, or incomplete + evidence closes the result and never produces a permissive fallback suggestion. +- `claudeCodeUsage.getAdvice` only reveals the unified Content surface. It does + not call a model, build a legacy summary, or open a separate Markdown result. +- `legacyBridge.ts`, `adviceSummary.ts`, and the demo/preparation modules have no + production import and are excluded from the VSIX. They are neither a default + transport nor a third sender. +- Only the configured Anthropic/OpenAI-compatible BYOK endpoint may receive these + requests. Claude Code OAuth/subscription credentials are not an AI backend, and + no preview, refresh, timer, or background task sends a model request. + +## Design critique and revision + +An early direction risked becoming another generic dashboard and a third isolated +experience. The revision embeds the evidence chain in the existing Advice card and +applies the same preview, Send, strict-result, and feedback grammar to Prompt +Optimizer. The only new visual signature is the sealed byte snapshot because it +directly supports privacy review. Narrow layouts, keyboard use, screen readers, and +theme changes remain candidate checks; with either feature disabled, its host +message seam fails closed and creates no AI network resource. diff --git a/AI-ADVICE-INTEGRATION-DESIGN-V2.3.1.zh-CN.md b/AI-ADVICE-INTEGRATION-DESIGN-V2.3.1.zh-CN.md new file mode 100644 index 00000000..2d1ab032 --- /dev/null +++ b/AI-ADVICE-INTEGRATION-DESIGN-V2.3.1.zh-CN.md @@ -0,0 +1,92 @@ +# v2.3.1 AI 建议有效性:实验界面设计计划 + +> 状态:仅用于 v2.3.1 候选集成。功能开关默认关闭;本文不代表当前发布承诺。 + +## 设计目标与边界 + +界面服务于一个明确问题:用户能否先看清“哪些本地证据支持哪条建议、若继续会发送什么、 +应用后怎样判断是否有效”,再决定是否参与实验。它扩展 Content 页已有的 AI Advice 与 Prompt +Optimizer 卡片,不增加第三个顶层入口。 + +两项功能都默认关闭,关闭时不创建任何 AI 网络工作。启用后,AI Advice 与 Prompt Optimizer +使用同一种交互语法:准备完整 provider HTTP JSON body、精确预览其 UTF-8 bytes、要求用户另行 +明确点击“发送”、严格解析响应,并提供可撤销的本地“有用 / 无用 / 已应用”反馈。不存在自动或 +默认模型请求。 + +## 视觉系统 + +- **颜色**:只使用现有 VS Code 主题令牌。背景与正文使用 + `--vscode-editor-background` / `--vscode-foreground`;边界和次级文本使用 + `--vscode-panel-border` / `--vscode-descriptionForeground`;交互和焦点使用 + `--vscode-button-background` / `--vscode-focusBorder`;快照使用 + `--vscode-editorWidget-background` 与编辑器等宽字体。Light+ 与 Dark+ 均由宿主主题决定, + 不引入品牌外的渐变或硬编码强调色。 +- **字体**:说明文字沿用 `--vscode-font-family` 与现有 13px 密度;指标、字节数和 JSON + 使用 `--vscode-editor-font-family`。小型状态章保持 10–11px,但不承载唯一语义。 +- **空间**:沿用 `.action-card`、`.model-details-stacked`、现有按钮与输入组件的间距, + 保持 Content 页的信息密度,不新增“大号 KPI 卡片”风格。 +- **标志性元素**:一条连续的“证据脊柱”连接五个有序阶段;密封快照标题显示 + `SEALED · N BYTES · SHA-256`,正文逐字呈现同一组 canonical UTF-8 bytes。 + +## 信息架构与草图 + +宽屏: + +```text +┌ AI 建议有效性(实验)──────────────────────────────────────────────┐ +│ [观察] ─── [证据] ─── [建议] ─── [行动] ─── [结果] │ +│ 数值 强度 有条件 本地反馈 样本/质量护栏 │ +│ │ +│ □ 仅允许聚合数据 □ 允许附加用户 prompt(默认关,独立同意) │ +│ [生成密封快照] │ +│ ▾ SEALED · 1248 BYTES · SHA-256 · AGGREGATES ONLY │ +│ {"schemaVersion":1,...} │ +│ [发送已准备请求](只有有效预览后才启用) │ +│ 有用 ○ 无用 ○ 已应用 ○ │ +└──────────────────────────────────────────────────────────────────┘ +``` + +窄屏时五个阶段改为纵向排列,脊柱成为左侧竖线;同意项和按钮自然换行,JSON 区域横向滚动, +不压缩或省略发送字节。 + +## 交互与可访问性 + +- 两项同意分开保存于带版本迁移的 VS Code `globalState`:聚合数据默认未同意;prompt 个性化默认关闭, + 且只有聚合同意后才可选择。prompt 的正文必然出现在密封快照中。 +- “生成密封快照”只向 extension host 请求准备对象。host 再次验证实验开关与同意状态,只序列化 + 一次完整 provider body,并保留 canonical UTF-8 bytes;预览正文、byte count 与 SHA-256 都由 + 该对象派生。用户另行点击“发送”后,host 才通过 opaque snapshot ID 取回同一个 Prepared 对象, + 交给唯一生产 BYOK transport;不能从 webview 文本重建请求。 +- Prompt Optimizer 使用相同的预览与发送阶段。其 body 只包含用户粘贴的 draft、固定 optimizer + system instruction 与已配置模型字段;不存在持久的“首次运行授权”。 +- Advice 输出只进入严格的 evidence-reference JSON parser;Optimizer 输出只进入严格的 marker / settings + parser。两者都不修复畸形输出,也不 fallback 到自由文本。 +- 每条 Advice recommendation 和每次 Optimizer 结果都使用原生 `