diff --git a/src/api/strategy.js b/src/api/strategy.js
index 3b29a13e..ee4e7bad 100644
--- a/src/api/strategy.js
+++ b/src/api/strategy.js
@@ -16,6 +16,7 @@ const api = {
verifyCode: '/api/strategies/verify',
aiGenerate: '/api/strategies/generate',
aiWorkspace: '/api/strategies/ai-workspace',
+ aiWorkspaceAccountModels: '/api/strategies/ai-workspace/account-models',
scriptTemplates: '/api/strategies/script-templates',
reviewReport: '/api/strategies/review-report',
reviewReportHistory: '/api/strategies/review-report/history',
@@ -253,6 +254,13 @@ export function runStrategyAiTurn (data) {
})
}
+export function getStrategyAiAccountModels () {
+ return request({
+ url: api.aiWorkspaceAccountModels,
+ method: 'get'
+ })
+}
+
export function setStrategyAiCandidateStatus (changeId, status) {
return request({
url: `${api.aiWorkspace}/changes/${changeId}/status`,
diff --git a/src/views/strategy-ide/index.vue b/src/views/strategy-ide/index.vue
index cf4a93d9..0d9a59fa 100644
--- a/src/views/strategy-ide/index.vue
+++ b/src/views/strategy-ide/index.vue
@@ -303,6 +303,24 @@
+
+
+ ChatGPT account
+ API key
+
+
+
+ {{ item.name || item.id }}
+
+
+
{{ aiAuthMode === 'chatgpt_account' ? 'ChatGPT plan ยท no API-key fallback' : 'Explicit API billing mode' }}
+
item.id === this.aiAccountModel)) {
+ this.aiAccountModel = this.aiAccountModels[0].id
+ }
+ } catch (e) {
+ this.aiAccountModels = []
+ } finally {
+ this.aiAccountModelsLoading = false
+ }
+ },
async loadStrategyAiWorkspace (sourceId) {
const id = Number(sourceId || 0)
if (!id || this.scriptCodeHidden) {
@@ -1433,6 +1472,8 @@ export default {
existingCode,
interactionMode,
generationMode: 'authoring',
+ authMode: this.aiAuthMode,
+ model: this.aiAuthMode === 'chatgpt_account' ? this.aiAccountModel : undefined,
context: { source: 'strategy_ide' }
})
const data = (res && res.data) || {}
@@ -3054,6 +3095,18 @@ export default {
background: #fff;
}
+.strategy-ai-auth-row {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ margin-bottom: 8px;
+}
+
+.strategy-ai-auth-hint {
+ color: #7f8a9b;
+ font-size: 12px;
+}
+
.strategy-ai-workspace--collapsed {
min-height: 42px;
height: 42px;
diff --git a/tests/unit/strategyAiWorkspace.test.mjs b/tests/unit/strategyAiWorkspace.test.mjs
index 2446ec8d..34cdccb3 100644
--- a/tests/unit/strategyAiWorkspace.test.mjs
+++ b/tests/unit/strategyAiWorkspace.test.mjs
@@ -39,6 +39,17 @@ test('strategy AI workspace keeps memory source-bound and uses explicit candidat
assert.match(page, /currentAssetType \(\) \{[\s\S]*this\.aiStrategyPrompt = ''[\s\S]*this\.aiInteractionMode = 'auto'/)
})
+test('strategy AI workspace exposes explicit account and API-key modes without fallback', () => {
+ const api = read('src/api/strategy.js')
+ const page = read('src/views/strategy-ide/index.vue')
+ assert.match(page, /value="chatgpt_account">ChatGPT account/)
+ assert.match(page, /value="api_key">API key/)
+ assert.match(page, /no API-key fallback/)
+ assert.match(page, /authMode: this\.aiAuthMode/)
+ assert.match(page, /model: this\.aiAuthMode === 'chatgpt_account'/)
+ assert.match(api, /aiWorkspaceAccountModels: '\/api\/strategies\/ai-workspace\/account-models'/)
+})
+
test('hidden purchased strategies show a locked AI workspace and never load or send source context', () => {
const page = read('src/views/strategy-ide/index.vue')
assert.match(page, /v-if="scriptCodeHidden" class="strategy-ai-locked"/)