fix(athena): resolve task output files to project directory instead of process.cwd()
task-output-writer wrote to process.cwd()/.sisyphus/task-outputs/ while council_finalize read from ctx.directory/.sisyphus/task-outputs/. When these differ, all output files appear missing. Fix by passing directory to writeTaskOutput from BackgroundManager, and adding basePath to council_read. Also removes process.chdir() from tests that was masking the bug.
This commit is contained in:
@@ -75,16 +75,13 @@ function createMockManager(tasks: Record<string, Partial<BackgroundTask>>): Back
|
||||
}
|
||||
|
||||
let tmpDir: string
|
||||
let originalCwd: string
|
||||
|
||||
beforeEach(async () => {
|
||||
tmpDir = await mkdtemp(join(tmpdir(), "council-flow-"))
|
||||
await mkdir(join(tmpDir, ".sisyphus", "task-outputs"), { recursive: true })
|
||||
originalCwd = process.cwd()
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
process.chdir(originalCwd)
|
||||
await rm(tmpDir, { recursive: true, force: true })
|
||||
})
|
||||
|
||||
@@ -142,8 +139,7 @@ describe("council archive integration flow", () => {
|
||||
expect(archiveContent).toBe(agents[i].response)
|
||||
}
|
||||
|
||||
process.chdir(tmpDir)
|
||||
const readTool = createCouncilRead()
|
||||
const readTool = createCouncilRead(tmpDir)
|
||||
|
||||
for (let i = 0; i < agents.length; i++) {
|
||||
const taskOutputPath = join(".sisyphus", "task-outputs", `${agents[i].id}.md`)
|
||||
@@ -183,8 +179,7 @@ describe("council archive integration flow", () => {
|
||||
const archiveContent = await readFile(join(tmpDir, member.archive_file!), "utf-8")
|
||||
expect(archiveContent).toBe("Analysis still in progress...")
|
||||
|
||||
process.chdir(tmpDir)
|
||||
const readTool = createCouncilRead()
|
||||
const readTool = createCouncilRead(tmpDir)
|
||||
const taskOutputPath = join(".sisyphus", "task-outputs", `${taskId}.md`)
|
||||
const readResult = await readTool.execute({ file_path: taskOutputPath }, toolContext)
|
||||
const parsed = JSON.parse(readResult)
|
||||
@@ -284,8 +279,7 @@ describe("council archive integration flow", () => {
|
||||
const fullContent = await readFile(join(tmpDir, member.archive_file!), "utf-8")
|
||||
expect(fullContent).toHaveLength(9000)
|
||||
|
||||
process.chdir(tmpDir)
|
||||
const readTool = createCouncilRead()
|
||||
const readTool = createCouncilRead(tmpDir)
|
||||
const taskOutputPath = join(".sisyphus", "task-outputs", `${taskId}.md`)
|
||||
const readResult = await readTool.execute({ file_path: taskOutputPath }, toolContext)
|
||||
const parsed = JSON.parse(readResult)
|
||||
|
||||
@@ -32,10 +32,9 @@ describe("createCouncilRead", () => {
|
||||
const archivePath = join(sisyphusDir, "member-1.txt")
|
||||
await writeFile(archivePath, "Some preamble\n<COUNCIL_MEMBER_RESPONSE>Full analysis here</COUNCIL_MEMBER_RESPONSE>")
|
||||
|
||||
const tool = createCouncilRead()
|
||||
const tool = createCouncilRead(tempDir)
|
||||
const relativePath = `.sisyphus/member-1.txt`
|
||||
|
||||
process.chdir(tempDir)
|
||||
const result = await tool.execute({ file_path: relativePath }, toolContext)
|
||||
const parsed = JSON.parse(result)
|
||||
|
||||
@@ -50,10 +49,9 @@ describe("createCouncilRead", () => {
|
||||
const archivePath = join(sisyphusDir, "member-2.txt")
|
||||
await writeFile(archivePath, "<COUNCIL_MEMBER_RESPONSE>Partial analysis still writing...")
|
||||
|
||||
const tool = createCouncilRead()
|
||||
const tool = createCouncilRead(tempDir)
|
||||
const relativePath = `.sisyphus/member-2.txt`
|
||||
|
||||
process.chdir(tempDir)
|
||||
const result = await tool.execute({ file_path: relativePath }, toolContext)
|
||||
const parsed = JSON.parse(result)
|
||||
|
||||
@@ -67,10 +65,9 @@ describe("createCouncilRead", () => {
|
||||
const archivePath = join(sisyphusDir, "member-3.txt")
|
||||
await writeFile(archivePath, "Just some plain text without any tags.")
|
||||
|
||||
const tool = createCouncilRead()
|
||||
const tool = createCouncilRead(tempDir)
|
||||
const relativePath = `.sisyphus/member-3.txt`
|
||||
|
||||
process.chdir(tempDir)
|
||||
const result = await tool.execute({ file_path: relativePath }, toolContext)
|
||||
const parsed = JSON.parse(result)
|
||||
|
||||
@@ -80,7 +77,7 @@ describe("createCouncilRead", () => {
|
||||
|
||||
describe("#given a path outside .sisyphus/", () => {
|
||||
it("#then returns Access denied error", async () => {
|
||||
const tool = createCouncilRead()
|
||||
const tool = createCouncilRead(tempDir)
|
||||
const result = await tool.execute({ file_path: "/etc/passwd" }, toolContext)
|
||||
const parsed = JSON.parse(result)
|
||||
|
||||
@@ -90,10 +87,9 @@ describe("createCouncilRead", () => {
|
||||
|
||||
describe("#given a missing file within .sisyphus/", () => {
|
||||
it("#then returns has_response false with File not found error", async () => {
|
||||
const tool = createCouncilRead()
|
||||
const tool = createCouncilRead(tempDir)
|
||||
const relativePath = `.sisyphus/nonexistent-file.txt`
|
||||
|
||||
process.chdir(tempDir)
|
||||
const result = await tool.execute({ file_path: relativePath }, toolContext)
|
||||
const parsed = JSON.parse(result)
|
||||
|
||||
@@ -105,7 +101,7 @@ describe("createCouncilRead", () => {
|
||||
|
||||
describe("#given a path traversal attempt", () => {
|
||||
it("#then returns Access denied error", async () => {
|
||||
const tool = createCouncilRead()
|
||||
const tool = createCouncilRead(tempDir)
|
||||
const result = await tool.execute({ file_path: "../../../etc/passwd" }, toolContext)
|
||||
const parsed = JSON.parse(result)
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { tool, type ToolDefinition } from "@opencode-ai/plugin"
|
||||
import { readFile } from "node:fs/promises"
|
||||
import { join } from "node:path"
|
||||
import { extractCouncilResponse } from "./council-response-extractor"
|
||||
|
||||
export function createCouncilRead(): ToolDefinition {
|
||||
export function createCouncilRead(basePath?: string): ToolDefinition {
|
||||
return tool({
|
||||
description:
|
||||
"Read a council archive file and extract the council member response. Use this to access full results for truncated members or for follow-up/cross-check analysis.",
|
||||
@@ -15,7 +16,9 @@ export function createCouncilRead(): ToolDefinition {
|
||||
}
|
||||
|
||||
try {
|
||||
const content = await readFile(args.file_path, "utf-8")
|
||||
const base = basePath ?? process.cwd()
|
||||
const absPath = join(base, args.file_path)
|
||||
const content = await readFile(absPath, "utf-8")
|
||||
const extraction = extractCouncilResponse(content)
|
||||
return JSON.stringify(extraction, null, 2)
|
||||
} catch {
|
||||
|
||||
Reference in New Issue
Block a user