fix: close alignment review races and preserve local conversation state
This commit is contained in:
1 parent
c7231be2c5
commit
37ddb2ce24
14 files changed
+203
-45
No files matched your search
@@ -14,9 +14,10 @@ let calls=0
|
||||
const setup = (kernel:ClaudeKernel, choose='allow-always') => {
|
||||
const controller = new AbortController()
|
||||
const raw=kernel as any
|
||||
const events:any[]=[]
|
||||
raw.instance={applyFlagSettings:async()=>{},close:()=>{}}
|
||||
raw.active={settled:false,request:{runId:'r',signal:controller.signal,emit:()=>{},interactions:{approval:async (...args:any[])=>{calls++;assert.equal(args[5].toolName,'Bash');const decision={kind:'approval',optionId:choose};await args[6]?.(decision);return decision}}}}
|
||||
return { raw, controller, invoke:(extra:object={})=>raw.canUseTool('Bash',{command:'echo safe'}, {signal:controller.signal,toolUseID:'t',suggestions,...extra}) }
|
||||
raw.active={settled:false,request:{runId:'r',signal:controller.signal,emit:(event:any)=>events.push(event),interactions:{approval:async (...args:any[])=>{calls++;assert.equal(args[5].toolName,'Bash');const decision={kind:'approval',optionId:choose};await args[6]?.(decision);return decision}}}}
|
||||
return { raw, controller, events, invoke:(extra:object={})=>raw.canUseTool('Bash',{command:'echo safe'}, {signal:controller.signal,toolUseID:'t',suggestions,...extra}) }
|
||||
}
|
||||
try {
|
||||
const a=setup(create('a')); const result=await a.invoke()
|
||||
@@ -25,11 +26,23 @@ try {
|
||||
assert.equal(sessionGrantRules('a',temp).length,2);assert.equal(sessionGrantRules('b',temp).length,0)
|
||||
const once=setup(create('once'),'allow-once'); assert.equal((await once.invoke()).updatedPermissions,undefined);assert.equal(sessionGrantRules('once',temp).length,0)
|
||||
const denied=setup(create('denied'),'reject-once');assert.equal((await denied.invoke()).behavior,'deny')
|
||||
assert.deepEqual(denied.events.map(event=>event.progress.phase),['waiting-approval','thinking'],'rejected approval retires waiting status before next model event')
|
||||
for(const mode of ['ask','plan'] as const) {const old=calls;assert.equal((await setup(create(mode,mode,'full-access')).invoke()).behavior,'deny');assert.equal(calls,old)}
|
||||
const full=setup(create('full','craft','full-access'));const old=calls;assert.equal((await full.invoke()).behavior,'allow');assert.equal(calls,old)
|
||||
await full.invoke({matchedAskRule:{source:'projectSettings',toolName:'Bash',ruleContent:'echo safe:*'}});assert.equal(calls,old+1)
|
||||
const failed=setup(create('failed'));failed.raw.instance.applyFlagSettings=async()=>{throw new Error('apply failed')};assert.equal((await failed.invoke()).behavior,'deny');assert.equal(sessionGrantRules('failed',temp).length,0)
|
||||
const stale=setup(create('stale'));stale.raw.active.request.interactions.approval=async()=>{stale.controller.abort();return {kind:'approval',optionId:'allow-always'}}
|
||||
assert.equal((await stale.invoke()).behavior,'deny');assert.equal(sessionGrantRules('stale',temp).length,0)
|
||||
const skipped=setup(create('skipped-question'))
|
||||
skipped.raw.active.request.interactions.question=async()=>({kind:'question',skipped:true,answer:{questions:[]}})
|
||||
const askOptions={signal:skipped.controller.signal,toolUseID:'question-tool'}
|
||||
const questionInput={questions:[{question:'Choose?',options:[{label:'Yes',description:'Answer yes'}],multiSelect:false}]}
|
||||
assert.equal((await skipped.raw.canUseTool('AskUserQuestion',questionInput,askOptions)).behavior,'deny')
|
||||
assert.deepEqual(skipped.events.map(event=>event.progress.phase),['waiting-answer','thinking'],'skipped question retires waiting status')
|
||||
const cancelledQuestion=setup(create('cancelled-question'))
|
||||
cancelledQuestion.raw.active.request.interactions.question=async()=>{cancelledQuestion.controller.abort();return {kind:'question',skipped:true,answer:{questions:[]}}}
|
||||
assert.equal((await cancelledQuestion.raw.canUseTool('AskUserQuestion',questionInput,{...askOptions,signal:cancelledQuestion.controller.signal})).behavior,'deny')
|
||||
assert.deepEqual(cancelledQuestion.events.map(event=>event.progress.phase),['waiting-answer'],'cancelled request cannot emit stale thinking phase')
|
||||
assert.deepEqual(stale.events.map(event=>event.progress.phase),['waiting-approval'],'cancelled approval cannot emit stale tool/thinking phase')
|
||||
console.log('Claude host approval policy unit checks passed (no SDK process/application/matching verification)')
|
||||
} finally {clearSessionGrants();await rm(temp,{recursive:true,force:true})}
|
||||
Reference in new issue
Block a user