Policy: desktop-command rule only at command start; workspace reset restores permissions; worker state typing; test report draft
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
3 changed files +9 −4
modified
services/research-runner/src/policy.ts
+1 −1
@@ -83,7 +83,7 @@ export const DENIED_COMMAND_PATTERNS: Array<{ re: RegExp; reason: string }> = [ | |||||
| 83 | { re: /\/var\/autoscholar\/(?!runs\/)/, reason: "worker root outside the run workspace" }, | 83 | { re: /\/var\/autoscholar\/(?!runs\/)/, reason: "worker root outside the run workspace" }, | ||
| 84 | { re: /ANTHROPIC_API_KEY|WORKER_TOKEN|AUTOSCHOLAR_|\.env(\b|$)/, reason: "credential access" }, | 84 | { re: /ANTHROPIC_API_KEY|WORKER_TOKEN|AUTOSCHOLAR_|\.env(\b|$)/, reason: "credential access" }, | ||
| 85 | { | 85 | { | ||
| 86 | − re: /(^|[\s;&|(])(open|say|afplay|screencapture|caffeinate)(\s|$)/, | 86 | + re: /(^|[;&|]\s*)(open|say|afplay|screencapture|caffeinate)\s+(-|\.|\/|~|https?:)/, | ||
| 87 | reason: "desktop/session interaction", | 87 | reason: "desktop/session interaction", | ||
| 88 | }, | 88 | }, | ||
| 89 | { | 89 | { | ||
modified
services/research-runner/src/runner.ts
+6 −1
@@ -161,7 +161,12 @@ export class ResearchRunner { | |||||
| 161 | } | 161 | } | ||
| 162 | } | 162 | } | ||
| 163 | if (!resumed) { | 163 | if (!resumed) { | ||
| 164 | − if (existed) await rm(ws, { recursive: true, force: true }); | 164 | + if (existed) { | ||
| 165 | + await chmod(join(ws, "input"), 0o755).catch(() => {}); | ||||
| 166 | + for (const f of ["CLAUDE.md", "input/CLAUDE.md", "input/run.json"]) | ||||
| 167 | + await chmod(join(ws, f), 0o644).catch(() => {}); | ||||
| 168 | + await rm(ws, { recursive: true, force: true }); | ||||
| 169 | + } | ||||
| 165 | for (const d of [ | 170 | for (const d of [ | ||
| 166 | "input", | 171 | "input", | ||
| 167 | "plan", | 172 | "plan", | ||
modified
services/worker/src/main.ts
+2 −2
@@ -205,10 +205,10 @@ class Worker { | |||||
| 205 | } | 205 | } | ||
| 206 | 206 | ||||
| 207 | private async execute(job: Awaited<ReturnType<WorkerClient["lease"]>>["job"] & object) { | 207 | private async execute(job: Awaited<ReturnType<WorkerClient["lease"]>>["job"] & object) { | ||
| 208 | − const state = { | 208 | + const state: { fencingToken: number; cancel: boolean; restart?: boolean; events: RunEventInput[] } = { | ||
| 209 | fencingToken: job.fencingToken, | 209 | fencingToken: job.fencingToken, | ||
| 210 | cancel: job.cancelRequested, | 210 | cancel: job.cancelRequested, | ||
| 211 | − events: [] as RunEventInput[], | 211 | + events: [], | ||
| 212 | }; | 212 | }; | ||
| 213 | this.running.set(job.runId, state); | 213 | this.running.set(job.runId, state); | ||
| 214 | const renew = setInterval(async () => { | 214 | const renew = setInterval(async () => { | ||
| 215 | 215 | ||||