From 28127b3706b73e597d39c321526a499570195797 Mon Sep 17 00:00:00 2001 From: Ryder Sun Date: Tue, 15 Sep 2026 18:38:50 +0800 Subject: [PATCH] fix: import reported agent outputs and authorize workbuddy writes --- desktop/src/agents/agent-output-importer.cjs | 21 ++++++++++++++++++- desktop/src/agents/cli/cli-invocations.cjs | 1 + .../local-workspace-agent-invocation.cjs | 7 +++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/desktop/src/agents/agent-output-importer.cjs b/desktop/src/agents/agent-output-importer.cjs index ed25fc8..3f775fa 100644 --- a/desktop/src/agents/agent-output-importer.cjs +++ b/desktop/src/agents/agent-output-importer.cjs @@ -426,7 +426,7 @@ function importAgentOutputs(input, attachmentStore) { && !Array.isArray(input.baseline.files) ? input.baseline.files : {} - const candidates = outputFiles(safeOutputDirectory(workdirRealPath)) + const controlledCandidates = outputFiles(safeOutputDirectory(workdirRealPath)) .filter(file => !sameFileState(baselineFiles[file.name], file)) .filter(file => Math.max(file.mtimeMs, file.ctimeMs) >= startedAt - 2000) .sort((left, right) => ( @@ -434,6 +434,25 @@ function importAgentOutputs(input, attachmentStore) { || left.name.localeCompare(right.name) )) + const reportedCandidates = [] + const reportedPaths = Array.isArray(input.reportedPaths) ? input.reportedPaths : [] + for (const reportedPath of reportedPaths.slice(0, MAX_SCANNED_ENTRIES)) { + if (typeof reportedPath !== 'string' || !path.isAbsolute(reportedPath)) continue + try { + const realPath = fs.realpathSync(reportedPath) + const stat = fs.statSync(realPath) + const extension = path.extname(realPath).toLowerCase() + if (!stat.isFile() || !SUPPORTED_EXTENSIONS.has(extension) + || stat.size > 64 * 1024 * 1024 || !isInside(workdirRealPath, realPath)) continue + reportedCandidates.push({ + name: path.basename(realPath), path: realPath, size: stat.size, + mtimeMs: stat.mtimeMs, ctimeMs: stat.ctimeMs, + }) + } catch { /* reported paths are best effort */ } + } + const candidates = [...controlledCandidates, ...reportedCandidates] + .filter((file, index, all) => all.findIndex(candidate => candidate.path === file.path) === index) + const imported = [] if (input.signal?.aborted) return imported for (const file of candidates) { diff --git a/desktop/src/agents/cli/cli-invocations.cjs b/desktop/src/agents/cli/cli-invocations.cjs index 6237375..a590cc8 100644 --- a/desktop/src/agents/cli/cli-invocations.cjs +++ b/desktop/src/agents/cli/cli-invocations.cjs @@ -145,6 +145,7 @@ function invocation(kind, executable, workdir, sessionRef = '', options = {}) { '--output-format', 'stream-json', '--include-partial-messages', '--permission-mode', options.sandbox === 'workspace-write' ? 'acceptEdits' : 'plan', + ...(options.sandbox === 'workspace-write' ? ['--dangerously-skip-permissions'] : []), ...(maxTurns ? ['--max-turns', String(maxTurns)] : []), ...(sessionRef ? ['--resume', sessionRef] : []), ], diff --git a/desktop/src/workspace/local-workspace-agent-invocation.cjs b/desktop/src/workspace/local-workspace-agent-invocation.cjs index a8c7b00..2b3adab 100644 --- a/desktop/src/workspace/local-workspace-agent-invocation.cjs +++ b/desktop/src/workspace/local-workspace-agent-invocation.cjs @@ -49,6 +49,12 @@ const OUTCOME_REF_FIELDS = Object.freeze([ 'workflowOutcomeRefs', ]) +function extractReportedOutputPaths(text) { + if (typeof text !== 'string') return [] + const matches = text.match(/(?:\/(?:Users|home|tmp|private\/tmp)\/[^\s"'<>`\]\[)]+)/g) || [] + return [...new Set(matches.map(value => value.replace(/[.,;:!?]+$/, '')))] +} + function mergeOutcomeRefs(sources, options = {}) { const merged = {} const seen = new Map(OUTCOME_REF_FIELDS.map(field => [field, new Set()])) @@ -1788,6 +1794,7 @@ class LocalWorkspaceAgentInvocation { baseline: outputBaseline, startedAt, agentKind: kind, + reportedPaths: extractReportedOutputPaths(reply.text), signal: agentController.signal, })) importPromise.catch(() => {})