fix: require native persona authorization verification
This commit is contained in:
parent
75b6262c91
commit
584f38b478
13 changed files with 195 additions and 173 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import { describe, expect, it, vi } from 'vitest'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import type { AiModelProvider } from './aiTargets'
|
||||
import type { LanguageGoalProjection } from './languageOperatingModel'
|
||||
import type { PersonaLanguageGoalBinding } from './personaLanguageGoal'
|
||||
|
|
@ -8,6 +8,11 @@ import type {
|
|||
} from './personaRepositoryBinding'
|
||||
import { planPersonaLanguageShellGoal } from './personaLanguageShellController'
|
||||
|
||||
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 }))
|
||||
|
||||
const provider: AiModelProvider = {
|
||||
id: 'ollama-local',
|
||||
name: 'Local Ollama',
|
||||
|
|
@ -104,6 +109,8 @@ function input() {
|
|||
}
|
||||
|
||||
describe('planPersonaLanguageShellGoal', () => {
|
||||
beforeEach(() => native.invoke.mockReset())
|
||||
|
||||
it.each(['unavailable', 'unbound', 'ambiguous', 'error'] as const)(
|
||||
'fails closed at repository phase %s before device identity or cognition',
|
||||
async (phase) => {
|
||||
|
|
@ -175,30 +182,24 @@ describe('planPersonaLanguageShellGoal', () => {
|
|||
expect(planGoal).toHaveBeenCalledWith(expect.objectContaining({ personaAuthorized: false }))
|
||||
})
|
||||
|
||||
it('enables persona-primary planning only for an exact request-bound authorization receipt', async () => {
|
||||
it('enables persona-primary planning only after the native verifier accepts the exact receipt', async () => {
|
||||
const planGoal = vi.fn().mockResolvedValue(plannedGoal)
|
||||
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,
|
||||
})
|
||||
|
||||
await planPersonaLanguageShellGoal({
|
||||
...input(),
|
||||
loadPersonaControlAuthorization: vi.fn().mockResolvedValue({
|
||||
schema: 'hololake.persona-control-authorization/v2',
|
||||
outcome: 'VERIFIED',
|
||||
authorizationId: 'AUTH-001',
|
||||
verifier: 'GUANGHU_OS',
|
||||
scope: 'PERSONA_PRIMARY_LANGUAGE_PLANNING',
|
||||
personaId: repositoryBinding.personaId,
|
||||
humanResponsibilitySubject: repositoryBinding.humanResponsibilitySubject,
|
||||
repositoryHead: repositoryBinding.gitHead,
|
||||
modelInstanceId: 'MODEL-INSTANCE-001',
|
||||
requestId: 'REQ-001',
|
||||
sourceLanguageAnchor: 'current-human-utterance',
|
||||
issuedAt: '2026-08-12T04:30:00+08:00',
|
||||
validUntil: '2026-08-12T04:40:00+08:00',
|
||||
evidenceDigest: 'c'.repeat(64),
|
||||
signerId: 'GH-AIOS-AUTHORIZER-001',
|
||||
signatureAlgorithm: 'Ed25519',
|
||||
signature: 'A'.repeat(86),
|
||||
}),
|
||||
personaControlAuthorizationReceipt: candidateReceipt,
|
||||
authorizationObservedAt: Date.parse('2026-08-12T04:35:00+08:00'),
|
||||
resolveRepository: vi.fn().mockResolvedValue(boundResolution()),
|
||||
loadDeviceIdentity: vi.fn().mockResolvedValue({
|
||||
|
|
@ -209,9 +210,68 @@ describe('planPersonaLanguageShellGoal', () => {
|
|||
planGoal,
|
||||
})
|
||||
|
||||
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'),
|
||||
},
|
||||
},
|
||||
)
|
||||
expect(planGoal).toHaveBeenCalledWith(expect.objectContaining({ personaAuthorized: true }))
|
||||
})
|
||||
|
||||
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 }))
|
||||
})
|
||||
|
||||
it('returns a stable binding error and never invokes cognition for invalid device evidence', async () => {
|
||||
const planGoal = vi.fn()
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue