fix(headless): preserve explicit policy flags in custom profiles
This commit is contained in:
1 parent
9db5b1e36a
commit
3ca9c5e623
8 files changed
+24
-15
No files matched your search
@@ -1494,7 +1494,7 @@ export interface Config {
|
||||
sessionId?: string
|
||||
/** Whether stdout carries the machine-readable event stream instead of final text. */
|
||||
json?: boolean
|
||||
/** Permission preset applied before the task; omission keeps the session's current policy. */
|
||||
/** Permission preset applied before the task; an explicit CLI flag takes precedence. Omission keeps current policy. */
|
||||
permissionMode?: string
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1496,7 +1496,7 @@ export interface Config {
|
||||
sessionId?: string
|
||||
/** Whether stdout carries the machine-readable event stream instead of final text. */
|
||||
json?: boolean
|
||||
/** Permission preset applied before the task; omission keeps the session's current policy. */
|
||||
/** Permission preset applied before the task; an explicit CLI flag takes precedence. Omission keeps current policy. */
|
||||
permissionMode?: string
|
||||
}
|
||||
```
|
||||
|
||||
@@ -27,8 +27,8 @@
|
||||
en: dbaa2fac63910a4c
|
||||
zh: 4fd5c17815ca444b
|
||||
/deepseek-ai-dsh-headless/use-this-package/supervising-implementation-tasks:
|
||||
en: 71d556ea725df5ce
|
||||
zh: e397938f6c39bc99
|
||||
en: 894505df28f1b821
|
||||
zh: 846414cf09e4956a
|
||||
/deepseek-ai-dsh-headless/use-this-package/machine-readable-output:
|
||||
en: df1ffa7f21b89136
|
||||
zh: 52bf016b3c2bc0f9
|
||||
|
||||
@@ -85,7 +85,7 @@ dsh headless --permission-mode workspace-write --add-dir "$git_dir" --json - < t
|
||||
|
||||
`--add-dir` accepts repeatable physical absolute directory paths in the execution environment. The shared Git directory permits staging, committing, and repository hooks without granting the parent checkout's source files. It grants all metadata in that directory, including shared refs, hooks, and configuration. Later host Git commands can execute modified hooks or configuration outside the sandbox; use this grant for trusted tasks and review metadata changes before host Git operations. Additional grants apply to this invocation and must be supplied again when resuming. The shipped profile passes them to `sandbox-policy`; the runner checks the original parsed CLI grants directly. An overlay that replaces the policy row must include `ctx.headlessStartup.additionalWritableRoots`, or the runner rejects the request before model work. The [local sandbox provider](../../sandbox/sandbox-local/README.md) defines platform support.
|
||||
|
||||
Read the opening `session.sessionId`, review the diff, and send revision instructions with `--session-id` from the same worktree. Repeat the directory grants. `--permission-mode` selects an existing permission preset through the permission service and overrides the session's saved selection before the task runs; omission preserves normal defaults and saved policy. Neither option edits saved profiles or user settings.
|
||||
Read the opening `session.sessionId`, review the diff, and send revision instructions with `--session-id` from the same worktree. Repeat the directory grants. `--permission-mode` selects an existing permission preset through the permission service and overrides runner configuration and the session's saved selection before the task runs; omission preserves normal defaults and saved policy. Neither option edits saved profiles or user settings.
|
||||
|
||||
Ask the worker to report its working directory, base and resulting commit, changed files, exact gate commands, exit codes, log paths, and blocked or unrun checks. Review that evidence and trust completed gate reports. Run a check again only when a revision or new finding invalidates its evidence. Bound investigation in the task, and keep publication with the supervisor. A gate blocked by host IPC, external caches, or nested sandboxing remains unrun until the supervisor authorizes and runs it in a suitable environment.
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ dsh headless --permission-mode workspace-write --add-dir "$git_dir" --json - < t
|
||||
|
||||
`--add-dir` 接受可重复的执行环境物理绝对目录路径。共享 Git 目录允许暂存、提交和执行仓库钩子,而不授予父检出目录源文件的写权限。它授权该目录中的所有元数据,包括共享引用、钩子和配置。后续主机 Git 命令可能在沙箱之外执行修改后的钩子或配置;仅对可信任务使用此授权,并在运行主机 Git 操作前审查元数据变更。额外授权仅适用于本次调用,恢复会话时必须再次提供。随附配置档将其传给 `sandbox-policy`;runner 直接检查原始解析出的 CLI 授权。替换策略行的 overlay 必须包含 `ctx.headlessStartup.additionalWritableRoots`,否则 runner 会在模型工作前拒绝请求。[本地沙箱提供方](../../sandbox/sandbox-local/README.zh.md)定义各平台的支持情况。
|
||||
|
||||
读取开头的 `session.sessionId`,审查 diff,然后在同一 worktree 中用 `--session-id` 发送修改指令,并再次提供目录授权。`--permission-mode` 通过权限服务选择已有权限预设,并在任务运行前覆盖会话保存的选择;省略时保留正常默认值和已保存策略。两个选项都不会修改已保存的配置档或用户设置。
|
||||
读取开头的 `session.sessionId`,审查 diff,然后在同一 worktree 中用 `--session-id` 发送修改指令,并再次提供目录授权。`--permission-mode` 通过权限服务选择已有权限预设,并在任务运行前覆盖 runner 配置和会话保存的选择;省略时保留正常默认值和已保存策略。两个选项都不会修改已保存的配置档或用户设置。
|
||||
|
||||
要求 worker 报告工作目录、基线与结果提交、修改文件、精确的检查命令、退出码、日志路径,以及受阻或未运行的检查。审查这些证据并信任已完成检查的报告。仅当修改或新发现使证据失效时再次运行检查。在任务中限制调查范围,并由监督者负责发布。因主机 IPC、外部缓存或嵌套沙箱而受阻的检查,在监督者授权并于合适环境中执行前仍属于未运行。
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ export interface Config {
|
||||
sessionId?: string
|
||||
/** Whether stdout carries the machine-readable event stream instead of final text. */
|
||||
json?: boolean
|
||||
/** Permission preset applied before the task; omission keeps the session's current policy. */
|
||||
/** Permission preset applied before the task; an explicit CLI flag takes precedence. Omission keeps current policy. */
|
||||
permissionMode?: string
|
||||
}
|
||||
|
||||
@@ -331,12 +331,14 @@ async function run(ctx: Context, config: Config, io: HeadlessIo): Promise<void>
|
||||
throw new Error('headless-runner: sessionId must not be blank')
|
||||
}
|
||||
|
||||
const startup = ctx.get('headlessStartup')
|
||||
const permissionMode = startup?.permissionMode ?? config.permissionMode
|
||||
const permissions = ctx.get('permissionPresets')
|
||||
if (config.permissionMode !== undefined) {
|
||||
if (permissionMode !== undefined) {
|
||||
if (permissions === undefined) throw new Error('headless --permission-mode requires the permissionPresets service')
|
||||
permissions.resolve(config.permissionMode)
|
||||
permissions.resolve(permissionMode)
|
||||
}
|
||||
const requestedRoots = ctx.get('headlessStartup')?.additionalWritableRoots
|
||||
const requestedRoots = startup?.additionalWritableRoots
|
||||
if (requestedRoots !== undefined && requestedRoots.length > 0) {
|
||||
const policy = ctx.get('sandboxPolicy')
|
||||
if (policy === undefined || requestedRoots.some(root => !policy.additionalWritableRoots?.includes(root))) {
|
||||
@@ -379,7 +381,7 @@ async function run(ctx: Context, config: Config, io: HeadlessIo): Promise<void>
|
||||
// re-read the log the runner holds before submitting the task.
|
||||
assertAdoptable(agent.session.header, liveEvents(agent.session), sessionId, cwd)
|
||||
}
|
||||
if (config.permissionMode !== undefined && permissions !== undefined) permissions.set(agent.session, config.permissionMode)
|
||||
if (permissionMode !== undefined && permissions !== undefined) permissions.set(agent.session, permissionMode)
|
||||
const firstSeq = agent.session.seq
|
||||
const projection = config.json === true ? projectJsonRun(ctx, agent, io.stdout, { cwd }) : undefined
|
||||
const stopReasoning = projection === undefined ? streamReasoning(ctx, agent, io.stderr) : undefined
|
||||
|
||||
@@ -49,6 +49,7 @@ interface BenchOptions {
|
||||
sessionId?: string
|
||||
json?: boolean
|
||||
permissionMode?: string
|
||||
startupPermissionMode?: string
|
||||
additionalWritableRoots?: string[]
|
||||
observe?: () => Promise<ObservationStub>
|
||||
/** Leave the query service unmounted to exercise the fail-loud path. */
|
||||
@@ -131,10 +132,11 @@ async function bench(script: Script, options: BenchOptions = {}): Promise<{
|
||||
run(): Promise<{ code: number; out: string; err: string; order: string[] }>
|
||||
}> {
|
||||
const ctx = new Context()
|
||||
if (options.additionalWritableRoots !== undefined) {
|
||||
if (options.additionalWritableRoots !== undefined || options.startupPermissionMode !== undefined) {
|
||||
ctx.provide('headlessStartup', {
|
||||
task: options.task, sessionId: options.sessionId, json: options.json ?? false,
|
||||
additionalWritableRoots: options.additionalWritableRoots,
|
||||
permissionMode: options.startupPermissionMode,
|
||||
})
|
||||
}
|
||||
provideWorkingDirectoryFixture(ctx)
|
||||
@@ -275,12 +277,14 @@ describe('headless runner', () => {
|
||||
} finally { await test.ctx.fiber.dispose() }
|
||||
})
|
||||
|
||||
it('applies an explicit preset before submitting the task', async () => {
|
||||
it.each([false, true])('applies the explicit CLI preset ahead of runner config (overridden=%s)', async (overridden) => {
|
||||
let selected: string | undefined
|
||||
const test = await bench({ afterPrompt(session, message) {
|
||||
expect(selected).toBe('workspace-write')
|
||||
appendTurn(session, 1, message, 'done', true)
|
||||
} }, { permissionMode: 'workspace-write' })
|
||||
} }, overridden
|
||||
? { permissionMode: 'danger-full-access', startupPermissionMode: 'workspace-write' }
|
||||
: { permissionMode: 'workspace-write' })
|
||||
test.ctx.provide('permissionPresets', { resolve() {}, set(_session: Session, name: string) { selected = name } } as never)
|
||||
try { expect(await test.run()).toMatchObject({ code: 0 }) }
|
||||
finally { await test.ctx.fiber.dispose() }
|
||||
|
||||
@@ -886,7 +886,10 @@ async function verifyProviderCwdResume(
|
||||
})
|
||||
const output = records(resumed.stdout)
|
||||
expect(output[0]).toEqual({ type: 'session', sessionId: primary?.header.id, cwd: providerCwd })
|
||||
expect(output.at(-1)).toEqual({ type: 'final', text: finalTextFromSession(primary!.content) })
|
||||
expect(output.at(-1)).toEqual({
|
||||
type: 'final', sessionId: primary?.header.id,
|
||||
reason: { kind: 'completed' }, text: finalTextFromSession(primary!.content),
|
||||
})
|
||||
const continued = await persistedSessions(cwd)
|
||||
expect(continued).toHaveLength(1)
|
||||
expect(continued[0]?.header).toEqual(primary?.header)
|
||||
|
||||
Reference in new issue
Block a user