fix(mcp): flow-order signals in tool results: ask the bank, import history first (#1955)
E2E #5: the agent skipped the bank question (generic picker link) and went straight to connect cards for a company whose fiscal year started 13 months ago (history import should come first). The skill says both, but the agent follows tool results more reliably than skill prose, so the rules now live in the results themselves: - gnubok_lookup_company still_to_ask gains two flow questions: which bank (for the bank= deep link) and whether the bookkeeping lived in a previous system (SIE import BEFORE bank). - gnubok_create_company's confirmed result computes days of history in the fiscal period: past 90 days it emits history_note (PSD2 cannot reach it; run gnubok_sie_preflight first) and reorders the remaining- setup message to (1) SIE import, (2) bank, (3) Skatteverket. - gnubok_connect_bank called without bank= now opens its instructions with a nudge to ask for the bank and re-call for the direct consent link. Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Jakob Wennberg
Claude Fable 5
parent
cd9fb5b717
commit
6f0f4d1eea
@@ -74,6 +74,18 @@ describe('onboarding connect-link tools', () => {
|
||||
expect(result.connect_url).toBe('https://app.example.test/import?mode=psd2')
|
||||
})
|
||||
|
||||
it('bank: nudges the agent to ask for the bank when none was passed, and not when one was', async () => {
|
||||
const bare = (await bankTool.execute({}, COMPANY_ID, 'user-1', {
|
||||
from: listClient([]).from,
|
||||
} as never)) as Record<string, unknown>
|
||||
expect(bare.instructions).toContain('BETTER LINK AVAILABLE')
|
||||
|
||||
const named = (await bankTool.execute({ bank: 'Swedbank' }, COMPANY_ID, 'user-1', {
|
||||
from: listClient([]).from,
|
||||
} as never)) as Record<string, unknown>
|
||||
expect(named.instructions).not.toContain('BETTER LINK AVAILABLE')
|
||||
})
|
||||
|
||||
it('bank: reports an active connection', async () => {
|
||||
const { from } = listClient([
|
||||
{ id: 'c1', bank_name: 'Swedbank', status: 'active', created_at: '2026-08-01T00:00:00Z' },
|
||||
|
||||
@@ -118,6 +118,35 @@ describe('gnubok_create_company', () => {
|
||||
expect((result.next as Record<string, unknown>).tool).toBe('gnubok_load_skill')
|
||||
})
|
||||
|
||||
it('orders history import before the bank when the fiscal period started over 90 days ago', async () => {
|
||||
// The default calendar-year setup starts 1 January: from February on the
|
||||
// period has >90 days of history the bank cannot deliver, so the created
|
||||
// result must point at SIE-first ordering. Frozen mid-year so the test
|
||||
// does not flip in January.
|
||||
vi.useFakeTimers()
|
||||
vi.setSystemTime(new Date(`${new Date().getFullYear()}-08-15T12:00:00Z`))
|
||||
try {
|
||||
const supabase = supabaseWithTeam(TEAM_ID)
|
||||
mocks.createCompanyCore.mockImplementation(
|
||||
async (_client: unknown, _input: unknown, createRow: () => Promise<{ data: unknown; error: unknown }>) => {
|
||||
const { data } = await createRow()
|
||||
return { companyId: data as string }
|
||||
}
|
||||
)
|
||||
const result = (await tool.execute(
|
||||
{ ...setup, confirm: true },
|
||||
'',
|
||||
'user-1',
|
||||
supabase as never
|
||||
)) as Record<string, unknown>
|
||||
|
||||
expect(result.history_note).toContain('gnubok_sie_preflight')
|
||||
expect(result.message).toContain('IN ORDER')
|
||||
} finally {
|
||||
vi.useRealTimers()
|
||||
}
|
||||
})
|
||||
|
||||
it('refuses a VAT-registered company without a moms period before touching the database', async () => {
|
||||
const supabase = supabaseWithTeam(TEAM_ID)
|
||||
await expect(
|
||||
|
||||
@@ -86,6 +86,10 @@ describe('gnubok_lookup_company', () => {
|
||||
// accounting_method defaults by form in create_company (flagged in its
|
||||
// preview) and is deliberately not a question here.
|
||||
expect(ask.some((q) => q.startsWith('accounting_method'))).toBe(false)
|
||||
// The two flow questions (E2E #5): bank for the direct consent link,
|
||||
// previous system for the import-before-bank ordering.
|
||||
expect(ask.some((q) => q.startsWith('bank ('))).toBe(true)
|
||||
expect(ask.some((q) => q.startsWith('previous_system'))).toBe(true)
|
||||
// Registry facts are confirmed, never re-asked.
|
||||
expect(ask.some((q) => q.startsWith('entity_type'))).toBe(false)
|
||||
expect(ask.some((q) => q.startsWith('vat_registered'))).toBe(false)
|
||||
|
||||
Reference in New Issue
Block a user