feat(sandbox-policy): describe enforced file families

This commit is contained in:
NI0317
2026-07-30 18:51:29 +08:00
parent d3323494ba
commit 87a4aaa32e
48 changed files with 623 additions and 140 deletions

View File

@@ -59,6 +59,8 @@ interface SdkScenario {
expectedFiles?: Readonly<Record<string, string>>
/** Assembled model-facing tool names and required argument keys. */
expectedTools?: Readonly<Record<string, readonly string[]>>
/** Stable policy-context clauses the real assembled request must include or omit. */
policyContext?: { includes: readonly string[]; excludes: readonly string[] }
}
const SCENARIOS: SdkScenario[] = [
@@ -88,6 +90,10 @@ const SCENARIOS: SdkScenario[] = [
configs: { live: persistentToolsLiveConfig, replay: persistentToolsReplayConfig },
expectedFiles: { 'note.txt': 'target:\n\tnew\n' },
expectedTools: { bash: ['command'], str_replace_editor: ['command', 'path'] },
policyContext: {
includes: ['the write and edit tools', 'terminal sessions'],
excludes: ['one-shot bash commands'],
},
},
]
@@ -117,7 +123,7 @@ async function persistedLogs(sessionsRoot: string): Promise<PersistedLog[]> {
interface LoggedRequestHeader {
type?: string
data?: { header?: { tools?: Array<{ name: string; parameters: { required?: string[] } }> } }
data?: { header?: { system?: unknown; tools?: Array<{ name: string; parameters: { required?: string[] } }> } }
}
function assembledToolRequirements(log: PersistedLog): Record<string, string[]> {
@@ -129,6 +135,15 @@ function assembledToolRequirements(log: PersistedLog): Record<string, string[]>
return Object.fromEntries(tools.map(tool => [tool.name, tool.parameters.required ?? []]))
}
function assembledSystem(log: PersistedLog): string {
const event = log.content.trimEnd().split('\n')
.map(line => JSON.parse(line) as LoggedRequestHeader)
.find(candidate => candidate.type === 'request/header')
const system = event?.data?.header?.system
if (typeof system !== 'string') throw new Error('session log has no request/header system')
return system
}
function contextOf(logs: readonly { content: string; header: Record<string, unknown> }[], cwd: string): NormalizeContext {
return {
sessionIds: logs.flatMap(log => typeof log.header.id === 'string' ? [log.header.id] : []),
@@ -359,6 +374,13 @@ describe('TypeScript SDK snapshots over the jsonrpc runtime', () => {
if (parent === undefined) throw new Error(`${scenario.name} has no parent session log`)
expect(assembledToolRequirements(parent)).toEqual(scenario.expectedTools)
}
if (scenario.policyContext !== undefined) {
const parent = ordered[0]
if (parent === undefined) throw new Error(`${scenario.name} has no parent session log`)
const system = assembledSystem(parent)
for (const clause of scenario.policyContext.includes) expect(system).toContain(clause)
for (const clause of scenario.policyContext.excludes) expect(system).not.toContain(clause)
}
if (scenario.children > 0) {
expect(notifications.some(n => n.method === 'subagent.started')).toBe(true)
expect(notifications.some(n => n.method === 'subagent.finished')).toBe(true)