2026-08-12 06:10:49 +08:00
|
|
|
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
2026-08-12 03:09:48 +08:00
|
|
|
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'
|
|
|
|
|
|
2026-08-12 06:10:49 +08:00
|
|
|
const native = vi.hoisted(() => ({ invoke: vi.fn() }))
|
|
|
|
|
|
|
|
|
|
vi.mock('@tauri-apps/api/core', () => ({ invoke: native.invoke }))
|
|
|
|
|
vi.mock('../mock-tauri', () => ({ isTauri: () => false, mockInvoke: native.invoke }))
|
|
|
|
|
|
2026-08-12 03:09:48 +08:00
|
|
|
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],
|
|
|
|
|
requestId: 'REQ-001',
|
|
|
|
|
modelInstanceId: 'MODEL-INSTANCE-001',
|
|
|
|
|
developmentId: 'DEV-20260811-010',
|
|
|
|
|
sourceLanguageAnchor: 'current-human-utterance',
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
describe('planPersonaLanguageShellGoal', () => {
|
2026-08-12 06:10:49 +08:00
|
|
|
beforeEach(() => native.invoke.mockReset())
|
|
|
|
|
|
2026-08-12 03:09:48 +08:00
|
|
|
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)
|
2026-08-12 04:36:45 +08:00
|
|
|
expect(planGoal).toHaveBeenCalledWith(expect.objectContaining({ personaAuthorized: false }))
|
|
|
|
|
})
|
|
|
|
|
|
2026-08-12 06:10:49 +08:00
|
|
|
it('enables persona-primary planning only after the native verifier accepts the exact receipt', async () => {
|
2026-08-12 04:36:45 +08:00
|
|
|
const planGoal = vi.fn().mockResolvedValue(plannedGoal)
|
2026-08-12 06:10:49 +08:00
|
|
|
const candidateReceipt = {
|
|
|
|
|
schema: 'hololake.persona-control-authorization/v2',
|
|
|
|
|
outcome: 'VERIFIED',
|
|
|
|
|
authorizationId: 'AUTH-001',
|
|
|
|
|
}
|
|
|
|
|
native.invoke.mockResolvedValue({
|
|
|
|
|
status: 'VERIFIED',
|
|
|
|
|
reason: null,
|
|
|
|
|
sourceCommit: 'c'.repeat(40),
|
|
|
|
|
signerId: 'GH-AIOS-AUTHORIZER-001',
|
|
|
|
|
authorizationEnabled: true,
|
|
|
|
|
})
|
2026-08-12 04:36:45 +08:00
|
|
|
|
|
|
|
|
await planPersonaLanguageShellGoal({
|
|
|
|
|
...input(),
|
2026-08-12 06:10:49 +08:00
|
|
|
personaControlAuthorizationReceipt: candidateReceipt,
|
2026-08-12 04:36:45 +08:00
|
|
|
authorizationObservedAt: Date.parse('2026-08-12T04:35:00+08:00'),
|
|
|
|
|
resolveRepository: vi.fn().mockResolvedValue(boundResolution()),
|
|
|
|
|
loadDeviceIdentity: vi.fn().mockResolvedValue({
|
|
|
|
|
schema: 'guanghu.router-device/v1',
|
|
|
|
|
device_id: 'HL-DEVICE-001',
|
|
|
|
|
public_key: 'public-key',
|
|
|
|
|
}),
|
|
|
|
|
planGoal,
|
|
|
|
|
})
|
|
|
|
|
|
2026-08-12 06:10:49 +08:00
|
|
|
expect(native.invoke).toHaveBeenCalledWith(
|
|
|
|
|
'verify_persona_control_authorization',
|
|
|
|
|
{
|
|
|
|
|
receipt: candidateReceipt,
|
|
|
|
|
expectation: {
|
|
|
|
|
personaId: repositoryBinding.personaId,
|
|
|
|
|
humanResponsibilitySubject: repositoryBinding.humanResponsibilitySubject,
|
|
|
|
|
repositoryHead: repositoryBinding.gitHead,
|
|
|
|
|
modelInstanceId: 'MODEL-INSTANCE-001',
|
|
|
|
|
requestId: 'REQ-001',
|
|
|
|
|
sourceLanguageAnchor: 'current-human-utterance',
|
|
|
|
|
observedAtMilliseconds: Date.parse('2026-08-12T04:35:00+08:00'),
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
)
|
2026-08-12 04:36:45 +08:00
|
|
|
expect(planGoal).toHaveBeenCalledWith(expect.objectContaining({ personaAuthorized: true }))
|
2026-08-12 03:09:48 +08:00
|
|
|
})
|
|
|
|
|
|
2026-08-12 06:10:49 +08:00
|
|
|
it('keeps system-direct planning for a native denial', async () => {
|
|
|
|
|
const planGoal = vi.fn().mockResolvedValue(plannedGoal)
|
|
|
|
|
native.invoke.mockResolvedValue({
|
|
|
|
|
status: 'DENIED',
|
|
|
|
|
reason: 'NO_TRUSTED_SIGNER',
|
|
|
|
|
sourceCommit: 'c'.repeat(40),
|
|
|
|
|
signerId: null,
|
|
|
|
|
authorizationEnabled: false,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
await planPersonaLanguageShellGoal({
|
|
|
|
|
...input(),
|
|
|
|
|
personaControlAuthorizationReceipt: { outcome: 'VERIFIED' },
|
|
|
|
|
resolveRepository: vi.fn().mockResolvedValue(boundResolution()),
|
|
|
|
|
loadDeviceIdentity: vi.fn().mockResolvedValue({
|
|
|
|
|
schema: 'guanghu.router-device/v1',
|
|
|
|
|
device_id: 'HL-DEVICE-001',
|
|
|
|
|
public_key: 'public-key',
|
|
|
|
|
}),
|
|
|
|
|
planGoal,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
expect(planGoal).toHaveBeenCalledWith(expect.objectContaining({ personaAuthorized: false }))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
it('keeps system-direct planning when the native verifier is unavailable', async () => {
|
|
|
|
|
const planGoal = vi.fn().mockResolvedValue(plannedGoal)
|
|
|
|
|
native.invoke.mockImplementationOnce(() => { throw new Error('SOURCE_UNAVAILABLE') })
|
|
|
|
|
|
|
|
|
|
await planPersonaLanguageShellGoal({
|
|
|
|
|
...input(),
|
|
|
|
|
personaControlAuthorizationReceipt: { outcome: 'VERIFIED' },
|
|
|
|
|
resolveRepository: vi.fn().mockResolvedValue(boundResolution()),
|
|
|
|
|
loadDeviceIdentity: vi.fn().mockResolvedValue({
|
|
|
|
|
schema: 'guanghu.router-device/v1',
|
|
|
|
|
device_id: 'HL-DEVICE-001',
|
|
|
|
|
public_key: 'public-key',
|
|
|
|
|
}),
|
|
|
|
|
planGoal,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
expect(planGoal).toHaveBeenCalledWith(expect.objectContaining({ personaAuthorized: false }))
|
|
|
|
|
})
|
|
|
|
|
|
2026-08-12 03:09:48 +08:00
|
|
|
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()
|
|
|
|
|
})
|
|
|
|
|
})
|