Task 10 — product-expert-operator-agent (PR 11)
Gate: Phase 0 merged
Vuex module: frontend/src/store/modules/product/expert/operator-agent/index.js
New file: frontend/src/stores/product-expert-operator-agent.js
Persistence: sessionId → localStorage (cleared on logout)
Cross-store dependency: getCapabilities reads account.team.id via _account-bridge.js
10.1 — Create the Pinia store
// frontend/src/stores/product-expert-operator-agent.js
import { defineStore } from 'pinia'
import { markRaw } from 'vue'
import expertApi from '../api/expert.js'
import { useAccountBridge } from './_account-bridge.js'
export const useProductExpertOperatorAgentStore = defineStore('product-expert-operator-agent', {
state: () => ({
sessionId: null,
messages: [],
sessionStartTime: null,
sessionWarningShown: false,
sessionExpiredShown: false,
sessionCheckTimer: null,
capabilityServers: [], // NOTE: named capabilityServers to avoid conflict with the capabilities getter
selectedCapabilities: []
}),
getters: {
capabilities: (state) => state.capabilityServers.map(c => ({
...c,
toolCount: c.resources.length + c.tools.length + c.prompts.length
}))
},
actions: {
reset () {
if (this.sessionCheckTimer) clearInterval(this.sessionCheckTimer)
Object.assign(this, {
sessionId: null, messages: [], sessionStartTime: null,
sessionWarningShown: false, sessionExpiredShown: false,
sessionCheckTimer: null, capabilityServers: [], selectedCapabilities: []
})
},
setSelectedCapabilities (caps) { this.selectedCapabilities = caps },
setSessionCheckTimer (timer) { this.sessionCheckTimer = markRaw(timer) },
async getCapabilities () {
const { team } = useAccountBridge()
const data = await expertApi.getCapabilities({ context: { teamId: team.id } })
this.capabilityServers = data.servers || []
// NOTE: waitWhile() guard from the Vuex module is intentionally dropped —
// it was a hacky workaround for issue #6520 / #6519 and is not carried forward.
}
},
persist: {
pick: ['sessionId'],
storage: localStorage
}
})
10.2 — No component consumers
Only accessed by the parent product-expert store.
10.3 — Do not delete the Vuex module yet
Delete with the parent in PR 12.
Logout bridge: uncomment useProductExpertOperatorAgentStore().$reset() in the Vuex logout action (Task 0.7).
10.4 — Write store tests
// frontend/src/tests/stores/product-expert-operator-agent.spec.js
import { setActivePinia, createPinia } from 'pinia'
import { describe, it, expect, beforeEach, vi } from 'vitest'
import { useProductExpertOperatorAgentStore } from '@/stores/product-expert-operator-agent.js'
import * as bridge from '@/stores/_account-bridge.js'
import expertApi from '@/api/expert.js'
describe('product-expert-operator-agent store', () => {
beforeEach(() => {
setActivePinia(createPinia())
vi.spyOn(bridge, 'useAccountBridge').mockReturnValue({ team: { id: 'team-1' } })
})
it('initializes with empty capabilities and messages', () => {
const store = useProductExpertOperatorAgentStore()
expect(store.capabilities).toEqual([])
expect(store.messages).toEqual([])
})
it('getCapabilities fetches and stores server list', async () => {
const store = useProductExpertOperatorAgentStore()
const server = { id: 'srv-1', resources: [], tools: [], prompts: [] }
vi.spyOn(expertApi, 'getCapabilities').mockResolvedValue({ servers: [server] })
await store.getCapabilities()
// capabilities getter maps capabilityServers and adds toolCount
expect(store.capabilities).toEqual([{ ...server, toolCount: 0 }])
expect(store.capabilityServers).toEqual([server])
})
it('reset clears timer and resets state', () => {
const store = useProductExpertOperatorAgentStore()
const fakeTimer = setInterval(() => {}, 9999)
const clearSpy = vi.spyOn(globalThis, 'clearInterval')
store.setSessionCheckTimer(fakeTimer)
store.reset()
expect(clearSpy).toHaveBeenCalledWith(fakeTimer)
expect(store.sessionId).toBeNull()
clearInterval(fakeTimer)
})
})
10.5 — Export from stores index
export { useProductExpertOperatorAgentStore } from './product-expert-operator-agent.js'
Task 10 —
product-expert-operator-agent(PR 11)Gate: Phase 0 merged
Vuex module:
frontend/src/store/modules/product/expert/operator-agent/index.jsNew file:
frontend/src/stores/product-expert-operator-agent.jsPersistence:
sessionId→ localStorage (cleared on logout)Cross-store dependency:
getCapabilitiesreadsaccount.team.idvia_account-bridge.js10.1 — Create the Pinia store
10.2 — No component consumers
Only accessed by the parent
product-expertstore.10.3 — Do not delete the Vuex module yet
Delete with the parent in PR 12.
Logout bridge: uncomment
useProductExpertOperatorAgentStore().$reset()in the Vuex logout action (Task 0.7).10.4 — Write store tests
10.5 — Export from stores index