feat: coordinate persona language shell planning
This commit is contained in:
parent
ebe12763f1
commit
d9abf241a6
8 changed files with 357 additions and 3 deletions
|
|
@ -0,0 +1,194 @@
|
|||
import { describe, expect, it, vi } from 'vitest'
|
||||
import type { AiModelProvider } from './aiTargets'
|
||||
import type { LanguageGoalProjection } from './languageOperatingModel'
|
||||
import type { PersonaLanguageGoalBinding } from './personaLanguageGoal'
|
||||
import type {
|
||||
PersonaRepositoryBinding,
|
||||
PersonaRepositoryBindingResolution,
|
||||
} from './personaRepositoryBinding'
|
||||
import { planPersonaLanguageShellGoal } from './personaLanguageShellController'
|
||||
|
||||
const provider: AiModelProvider = {
|
||||
id: 'ollama-local',
|
||||
name: 'Local Ollama',
|
||||
kind: 'ollama',
|
||||
base_url: 'http://127.0.0.1:11434/v1',
|
||||
api_key_storage: 'none',
|
||||
models: [{
|
||||
id: 'qwen3:8b',
|
||||
capabilities: {
|
||||
streaming: true,
|
||||
tools: false,
|
||||
vision: false,
|
||||
json_mode: true,
|
||||
reasoning: true,
|
||||
},
|
||||
}],
|
||||
}
|
||||
|
||||
const repositoryBinding: PersonaRepositoryBinding = {
|
||||
personaId: 'ICE-P-ZY001',
|
||||
repositoryPath: '/persona/zhuyuan',
|
||||
gitHead: 'a'.repeat(40),
|
||||
repositoryClean: true,
|
||||
brainEntry: 'BRAIN.md',
|
||||
currentCheckpoint: 'CURRENT.hdlp',
|
||||
humanResponsibilitySubject: 'BINGSHUO',
|
||||
modelProviderId: provider.id,
|
||||
modelId: provider.models[0].id,
|
||||
modelBaseUrl: provider.base_url!,
|
||||
organContracts: [{
|
||||
organId: 'fact-sense-001',
|
||||
kind: 'FACT_SENSE',
|
||||
mode: 'READ_ONLY',
|
||||
paths: ['BRAIN.md'],
|
||||
inputSchema: 'input/v1',
|
||||
outputSchema: 'output/v1',
|
||||
permissions: ['repository.read'],
|
||||
modelInferenceAllowed: true,
|
||||
realityActionsAllowed: false,
|
||||
activatable: true,
|
||||
implementationState: 'IMPLEMENTED',
|
||||
}],
|
||||
cognitiveGravity: {
|
||||
schema: 'hololake.persona-cognitive-gravity-evidence/v1',
|
||||
subjectPersonaId: 'ICE-P-ZY001',
|
||||
sourcePath: 'B0.hdlp',
|
||||
sourceHash: 'b'.repeat(64),
|
||||
frameSchema: 'guanghu.zhuyuan-cognitive-gravity-frame/v1',
|
||||
},
|
||||
}
|
||||
|
||||
const plannedGoal: LanguageGoalProjection = {
|
||||
originalUtterance: '继续当前开发',
|
||||
realPurpose: '从事实恢复当前阶段并继续安全开发',
|
||||
status: 'ready',
|
||||
executable: true,
|
||||
confirmationRequired: false,
|
||||
detectedBoundaries: [],
|
||||
evidenceSources: ['current repository'],
|
||||
missingKnowledge: [],
|
||||
causalModel: 'read current facts before changing source',
|
||||
partnerGuidance: 'continue the bounded source-only stage',
|
||||
requestAssessment: 'valid within the current lane',
|
||||
executionDisposition: { decision: 'PROCEED', reason: 'bounded and reversible' },
|
||||
worldIntegrity: {
|
||||
protectedAssets: ['language-home continuity'],
|
||||
harmPath: 'none in this read-only deliberation',
|
||||
authorityBoundary: 'no reality action',
|
||||
reversibility: 'no mutation performed',
|
||||
saferAlternatives: [],
|
||||
},
|
||||
}
|
||||
|
||||
function boundResolution(): PersonaRepositoryBindingResolution {
|
||||
return {
|
||||
phase: 'bound',
|
||||
inspectedRepositoryCount: 1,
|
||||
failures: [],
|
||||
binding: repositoryBinding,
|
||||
}
|
||||
}
|
||||
|
||||
function input() {
|
||||
return {
|
||||
utterance: '继续当前开发',
|
||||
personaId: 'ICE-P-ZY001',
|
||||
repositoryPaths: ['/persona/zhuyuan'],
|
||||
providers: [provider],
|
||||
personaAuthorized: true,
|
||||
requestId: 'REQ-001',
|
||||
modelInstanceId: 'MODEL-INSTANCE-001',
|
||||
developmentId: 'DEV-20260811-010',
|
||||
sourceLanguageAnchor: 'current-human-utterance',
|
||||
}
|
||||
}
|
||||
|
||||
describe('planPersonaLanguageShellGoal', () => {
|
||||
it.each(['unavailable', 'unbound', 'ambiguous', 'error'] as const)(
|
||||
'fails closed at repository phase %s before device identity or cognition',
|
||||
async (phase) => {
|
||||
const resolution = phase === 'error'
|
||||
? { phase, inspectedRepositoryCount: 1, failures: [], code: 'DISCOVERY_FAILED' } as const
|
||||
: phase === 'unavailable'
|
||||
? { phase, inspectedRepositoryCount: 0, failures: [] } as const
|
||||
: { phase, inspectedRepositoryCount: 1, failures: [] } as const
|
||||
const loadDeviceIdentity = vi.fn()
|
||||
const planGoal = vi.fn()
|
||||
|
||||
const result = await planPersonaLanguageShellGoal({
|
||||
...input(),
|
||||
resolveRepository: vi.fn().mockResolvedValue(resolution),
|
||||
loadDeviceIdentity,
|
||||
planGoal,
|
||||
})
|
||||
|
||||
expect(result.phase).toBe(phase)
|
||||
expect(loadDeviceIdentity).not.toHaveBeenCalled()
|
||||
expect(planGoal).not.toHaveBeenCalled()
|
||||
},
|
||||
)
|
||||
|
||||
it('does not create a device identity when the manifest-pinned model is unavailable', async () => {
|
||||
const loadDeviceIdentity = vi.fn()
|
||||
const planGoal = vi.fn()
|
||||
|
||||
const result = await planPersonaLanguageShellGoal({
|
||||
...input(),
|
||||
providers: [],
|
||||
resolveRepository: vi.fn().mockResolvedValue(boundResolution()),
|
||||
loadDeviceIdentity,
|
||||
planGoal,
|
||||
})
|
||||
|
||||
expect(result).toEqual({ phase: 'model_unavailable' })
|
||||
expect(loadDeviceIdentity).not.toHaveBeenCalled()
|
||||
expect(planGoal).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('binds repository, model, device and B0 deliberation without executing a reality action', async () => {
|
||||
const loadDeviceIdentity = vi.fn().mockResolvedValue({
|
||||
schema: 'guanghu.router-device/v1',
|
||||
device_id: 'HL-DEVICE-001',
|
||||
public_key: 'public-key',
|
||||
})
|
||||
const planGoal = vi.fn(async ({ binding }: { binding: PersonaLanguageGoalBinding }) => {
|
||||
expect(binding.wake.repositoryPath).toBe(repositoryBinding.repositoryPath)
|
||||
expect(binding.wake.nodeId).toBe('HL-DEVICE-001')
|
||||
expect(binding.provider.id).toBe(provider.id)
|
||||
return plannedGoal
|
||||
})
|
||||
|
||||
const result = await planPersonaLanguageShellGoal({
|
||||
...input(),
|
||||
resolveRepository: vi.fn().mockResolvedValue(boundResolution()),
|
||||
loadDeviceIdentity,
|
||||
planGoal,
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
phase: 'planned',
|
||||
binding: expect.objectContaining({ requestId: 'REQ-001' }),
|
||||
goal: plannedGoal,
|
||||
})
|
||||
expect(loadDeviceIdentity).toHaveBeenCalledTimes(1)
|
||||
expect(planGoal).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('returns a stable binding error and never invokes cognition for invalid device evidence', async () => {
|
||||
const planGoal = vi.fn()
|
||||
|
||||
const result = await planPersonaLanguageShellGoal({
|
||||
...input(),
|
||||
resolveRepository: vi.fn().mockResolvedValue(boundResolution()),
|
||||
loadDeviceIdentity: vi.fn().mockResolvedValue({ schema: 'unknown' }),
|
||||
planGoal,
|
||||
})
|
||||
|
||||
expect(result).toEqual({
|
||||
phase: 'binding_error',
|
||||
code: 'PERSONA_LANGUAGE_DEVICE_IDENTITY_INVALID',
|
||||
})
|
||||
expect(planGoal).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
Loading…
Reference in a new issue