fix(gameplay): use universe-generated AI companions
Some checks failed
CI / validate (pull_request) Waiting to run
CI / validate (push) Failing after 34m2s

Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
2026-08-22 14:50:14 +05:00
parent 56bddafbe8
commit f6c0fbcfeb
12 changed files with 632 additions and 124 deletions

View File

@@ -8,6 +8,17 @@ const worldFixture = () => ({
startingLocation: { id: 'location', kind: 'location' as const, name: 'Gate', summary: 'A station beyond known space.', tags: [] as string[], secrets: [] as string[] },
npcs: [1, 2, 3].map(index => ({ id: `npc-${index}`, kind: 'npc' as const, name: `NPC ${index}`, summary: 'A useful person with their own agenda.', tags: [], secrets: [] })),
factions: [1, 2].map(index => ({ id: `faction-${index}`, kind: 'faction' as const, name: `Faction ${index}`, summary: 'An organization pursuing a hidden objective.', tags: [], secrets: [] })),
companions: [1, 2, 3].map(index => ({
name: `Companion ${index}`,
concept: `A universe-specific companion with role ${index}.`,
abilities: { str: 10, dex: 12, con: 11, int: 13, wis: 12, cha: 10 },
hp: 11,
maxHp: 11,
defense: 12,
proficiency: 2,
inventory: ['Field kit'],
persona: { voice: 'Distinctive and clear.', motivation: 'Help the party.', flaw: 'Has a private agenda.', bond: 'Believes in the party.' },
})),
hook: 'A sufficiently long hook that immediately gives the party something to investigate.',
hiddenThreat: 'A hidden threat waits beyond the gate.',
openingScene: 'The gate opens without warning, and an impossible signal calls every hero by name.',
@@ -34,7 +45,10 @@ describe('worker AI providers', () => {
})
vi.stubGlobal('fetch', fetchMock)
const { generateWorld } = await import('./ai')
await expect(generateWorld([{ role: 'user', content: 'Create a science fiction frontier.' }])).resolves.toMatchObject({ title: 'Test Reach' })
await expect(generateWorld([{ role: 'user', content: 'Create a science fiction frontier.' }])).resolves.toMatchObject({
title: 'Test Reach',
companions: [{ name: 'Companion 1' }, { name: 'Companion 2' }, { name: 'Companion 3' }],
})
expect(fetchMock).toHaveBeenCalledWith('https://api.deepseek.com/chat/completions', expect.any(Object))
})

View File

@@ -89,7 +89,7 @@ async function structuredRequest<T>(name: string, schema: JsonSchema, messages:
export async function generateWorld(messages: Array<{ role: 'user' | 'assistant'; content: string }>): Promise<WorldStarter> {
assert13Plus(...messages.map(message => message.content))
const world = await structuredRequest('world_starter', WorldStarterSchema.toJSONSchema(), [
{ role: 'system', content: 'You are the Dungeons & Ground world coauthor. Create an original 13+ private TTRPG setting in any requested genre. Never use protected settings or characters. Return exactly three NPCs and two factions. Make the opening immediately playable.' },
{ role: 'system', content: 'You are the Dungeons & Ground world coauthor. Create an original 13+ private TTRPG setting in any requested genre. Never use protected settings or characters. Return exactly three NPCs, two factions, and three distinct persistent AI companion heroes created specifically for this universe. Give each companion complete playable mechanics, useful genre-appropriate equipment, and a persona that produces interesting party choices without overriding the human player. Make the opening immediately playable.' },
...messages,
], value => WorldStarterSchema.parse(value))
// Moderate the entire typed object, including entity summaries, secrets and