Add sidebar run permission preset for scheduled jobs.

Default inherits Host new-session access mode; humans can raise to workspace-write or full access. Agents never see or set the field.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
oliver 2026-09-10 22:08:39 +08:00
parent ad6e90f197
commit 8cf624ce7f
8 changed files with 140 additions and 4 deletions

View file

@ -633,6 +633,13 @@ export function createHostService(options = {}) {
agentAccess: patch.agentAccess !== undefined
? patch.agentAccess
: (patch.agent_access !== undefined ? patch.agent_access : job.agentAccess),
permissionPreset: patch.permissionPreset !== undefined
? patch.permissionPreset
: (patch.permission_preset !== undefined
? patch.permission_preset
: (patch.accessMode !== undefined
? patch.accessMode
: (patch.access_mode !== undefined ? patch.access_mode : job.permissionPreset))),
delivery: patch.delivery !== undefined
? mergeDeliveryMention(job.delivery, patch.delivery)
: job.delivery,
@ -1307,7 +1314,7 @@ export function createHostService(options = {}) {
if (code === 'ALREADY_RUNNING') {
return write(409, { ok: false, error: error.message, code, message: error.message })
}
if (code === 'INVALID_CRON' || code === 'INVALID_AT' || code === 'INVALID_SCHEDULE' || code === 'INVALID_JOB' || code === 'INVALID_TIMEZONE' || code === 'INVALID_CWD' || code === 'INVALID_DELIVERY' || code === 'IM_DELIVERY_FORBIDDEN' || code === 'IM_TARGET_FORBIDDEN' || code === 'IM_JOB_MISSING_CWD' || code === 'INVALID_WATCH' || code === 'INVALID_PROGRESS' || code === 'INVALID_REPORT' || code === 'INVALID_PERSIST_HISTORY' || code === 'INVALID_PROGRESS_PATH' || code === 'INVALID_ARCHIVE' || code === 'ARCHIVE_FAILED' || code === 'ARCHIVE_UNAVAILABLE' || code === 'INVALID_RETRIGGER') {
if (code === 'INVALID_CRON' || code === 'INVALID_AT' || code === 'INVALID_SCHEDULE' || code === 'INVALID_JOB' || code === 'INVALID_TIMEZONE' || code === 'INVALID_CWD' || code === 'INVALID_DELIVERY' || code === 'IM_DELIVERY_FORBIDDEN' || code === 'IM_TARGET_FORBIDDEN' || code === 'IM_JOB_MISSING_CWD' || code === 'INVALID_WATCH' || code === 'INVALID_PROGRESS' || code === 'INVALID_REPORT' || code === 'INVALID_PERSIST_HISTORY' || code === 'INVALID_PROGRESS_PATH' || code === 'INVALID_ARCHIVE' || code === 'ARCHIVE_FAILED' || code === 'ARCHIVE_UNAVAILABLE' || code === 'INVALID_RETRIGGER' || code === 'INVALID_PERMISSION_PRESET') {
return write(400, { ok: false, error: error.message, code, message: error.message })
}
if (code === 'PAYLOAD_TOO_LARGE') return write(413, apiError('payload_too_large', locale))
@ -1968,6 +1975,15 @@ export function makeLiveSessionPort(ctx) {
if (!agent || typeof agent.followup !== 'function') {
throw new Error('created agent has no followup')
}
try {
const wanted = typeof job?.permissionPreset === 'string' ? job.permissionPreset.trim() : ''
const permissionPresets = tryGet(ctx, 'permissionPresets')
if (wanted && permissionPresets && typeof permissionPresets.set === 'function' && agent.session) {
permissionPresets.set(agent.session, wanted)
}
} catch (error) {
console.warn?.(`[dsh-ops-cron] permission preset apply failed for job ${job?.id}: ${error instanceof Error ? error.message : error}`)
}
const message = {
id: randomUUID(),
role: 'user',