-
Notifications
You must be signed in to change notification settings - Fork 46
🤖 feat: persist sub-agent reports and transcripts after cleanup #2065
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 24e2fd75bf
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review Addressed the transcript archival/retrieval issues:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0fd1efa97e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review Gated the subagent transcript fetch behind the dialog open state to avoid unnecessary I/O for closed dialogs. |
|
Codex Review: Didn't find any major issues. Chef's kiss. ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
- Make transcript dialog layout scroll correctly - Pass parent workspaceId through transcript rendering to enable nested drill-down - Make Storybook play test robust to multiple 'View transcript' buttons
- Wait for transcript load before asserting message content - Assert on stable user message text
Use a matcher function that inspects element.textContent to avoid failures when MarkdownRenderer splits text across elements.
Assert transcript rendering via data-message-block instead of brittle text matching.
The app can render multiple View transcript buttons across stories; scope the click to the specific taskId used in this story.
|
@codex review Follow-ups since last round:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f8fd8efe6b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
f8fd8ef to
a86cf2c
Compare
|
@codex review Addressed the two threads:
|
Summary
Persist completed sub-agent task artifacts (reports + transcripts) into the parent session directory so they remain retrievable after workspace cleanup/restart. Adds a minimal UI affordance to view archived transcripts directly from completed
tasktool calls.Background
Today, sub-agent workspaces are cleaned up after
agent_report, which deletes~/.mux/sessions/<taskId>. Completed reports were only retrievable via an in-memory TTL cache, and transcripts were not viewable once the workspace was removed.Implementation
subagent-reports.json+ per-task payload).TaskServiceand task tools to load completed reports from disk (restart-safe) and to validate descendant scope via persisted ancestry.subagent-transcripts.json+chat.jsonl/partial.json).workspace.getSubagentTranscriptORPC endpoint and a small transcript dialog + “View transcript” button inTaskToolCall.Validation
make static-checkRisks
📋 Implementation Plan
Persist sub-agent reports + transcripts after cleanup
Context / Why
Sub-agent workspaces (created via the
tasktool) are currently deleted after the sub-agent callsagent_report(git worktree +~/.mux/sessions/<taskId>). The report payload is delivered back to the parent, but any latertask_await/UI lookups rely on an in-memory 1-hour cache (completedReportsByTaskId) insideTaskService.Goals:
subagent-patches/).Non-goals:
task_apply_git_patchflow stays explicit).Evidence (repo facts)
TaskServicecaches completed reports for 1 hour:COMPLETED_REPORT_CACHE_TTL_MS = 60 * 60 * 1000andcompletedReportsByTaskIdis the fallback when a task workspace is removed. (src/node/services/taskService.ts)waitForAgentReport()rejects if the task workspace is missing from config unless the in-memory cache hits. (src/node/services/taskService.ts)cleanupReportedLeafTask()→workspaceService.remove(workspaceId, true). (src/node/services/taskService.ts)fsPromises.rm(config.getSessionDir(workspaceId), { recursive: true, force: true }). (src/node/services/workspaceService.ts)subagent-patches.json+subagent-patches/<childTaskId>/series.mbox. (src/node/services/subagentGitPatchArtifacts.ts)chat.jsonlunder the session dir and is parsed line-by-line, skipping malformed JSON. (src/node/services/historyService.ts)src/browser/components/tools/TaskToolCall.tsx)Plan
1) Add a persistent “subagent artifacts” store in the parent session dir
Create a new node service that mirrors the patch artifact pattern (atomic JSON + per-task subdirectory):
New files (names can be tweaked, but keep them consistent and explicit):
src/node/services/subagentReportArtifacts.tssrc/node/services/subagentTranscriptArtifacts.tsOn-disk layout (inside parent session dir):
subagent-reports.jsonsubagent-reports/<childTaskId>/report.md(orreport.json)subagent-transcripts.jsonsubagent-transcripts/<childTaskId>/chat.jsonlsubagent-transcripts/<childTaskId>/partial.json(optional, only if exists)Metadata schemas (TypeScript interfaces + defensive versioning):
SubagentReportArtifact:childTaskId,parentWorkspaceIdcreatedAtMs,updatedAtMstitle?,reportMarkdownancestorWorkspaceIds: string[](used for scope checks after cleanup)SubagentTranscriptArtifact:childTaskId,parentWorkspaceIdcreatedAtMs,updatedAtMschatPath(relative to parent session dir preferred),partialPath?messageCount?(optional; can be computed lazily)Implementation notes:
read*ArtifactsFile()returns empty{ version, artifactsByChildTaskId: {} }on ENOENT.write-file-atomic.workspaceFileLocks.withLock(workspaceId, …).Net new LoC (product code): ~200–300
2) Persist reports at
agent_reporttime (and stop relying on TTL)Update
src/node/services/taskService.ts:In
finalizeAgentTaskReport():parentWorkspaceId(and before cleanup), write the report artifact into the parent’s session dir.ancestorWorkspaceIdsalongside the report so scope checks can be preserved after the child workspace is deleted.Shape:
Replace the “1-hour TTL cache as source of truth” with “disk as source of truth”:
completedReportsByTaskIdonly as a small in-memory LRU (optional) or remove it entirely.waitForAgentReport():config.getSessionDir(options.requestingWorkspaceId)readSubagentReportArtifact(workspaceSessionDir, taskId){ reportMarkdown, title }.Update descendant-scope checks to consult the persisted report artifact:
filterDescendantAgentTaskIds()/isDescendantAgentTask()should, on “task not in config”, attempt:config.getSessionDir(ancestorWorkspaceId)artifact.ancestorWorkspaceIds.includes(ancestorWorkspaceId)This removes the need for time-based expiry; the report stays available as long as the parent session exists.
Net new/changed LoC (product code): ~150–250
3) Persist transcripts when the child workspace is removed (copy chat.jsonl before deletion)
Update
src/node/services/workspaceService.tsinsideremove():After
metadataResult.successand after the timing/usage rollups (already present), but before:If
metadata.parentWorkspaceIdis set:~/.mux/sessions/<workspaceId>/chat.jsonl→~/.mux/sessions/<parentId>/subagent-transcripts/<workspaceId>/chat.jsonlpartial.jsonsimilarly (if present)SubagentTranscriptArtifactentry for<workspaceId>.Defensive behavior:
status: "missing"(or omit paths) rather than throwing.Roll-up for nested tasks (recommended)
If a sub-agent itself spawned sub-agents, its session dir may contain
subagent-*.json+ directories for its children. Before deleting that session dir, roll those artifacts up into the parent session dir:subagent-patches.jsoninto parent’ssubagent-patches.json(and copy directories).subagent-reports.jsoninto parent’ssubagent-reports.json.subagent-transcripts.jsoninto parent’ssubagent-transcripts.json.This ensures artifacts survive even if intermediate task workspaces are also cleaned up.
Why roll-up matters
cleanupReportedLeafTask()can delete a reported parent task after its descendants finish. If that parent task had stored artifacts for its own children (grandchildren from the original root’s perspective), those artifacts would otherwise be deleted with the parent’s session directory.Net new/changed LoC (product code): ~200–350 (includes roll-up logic)
4) UI: “View transcript” for completed tasks (even after cleanup)
Because the task workspace no longer exists in config, the UI cannot open it. Add an explicit archived transcript viewer.
4a) Backend API / IPC
Add a new workspace/task API endpoint (ORPC) such as:
workspace.getSubagentTranscript({ taskId: string })→{ success: true, messages: MuxMessage[], title?: string }Backend behavior:
taskService.isDescendantAgentTask(requester, taskId).subagent-reports.json/subagent-transcripts.jsonin the requester’s session dir and validate ancestor chain.chat.jsonlpath (and mergepartial.jsonif present).4b) Frontend dialog
Add a dialog component that:
MuxMessage[]via the new API.StreamingMessageAggregatorinstance to computeDisplayedMessage[].MessageRenderer.Suggested location:
src/browser/components/tools/SubagentTranscriptDialog.tsx(invoked from task tool UIs).4c) Add affordances in task tool renderers
Update
src/browser/components/tools/TaskToolCall.tsx:TaskToolCallandTaskAwaitResult, add a small button next to the taskId when:result.status === "completed"and a transcript artifact exists (or optimistically show and handle errors).Net new/changed LoC (product code): ~250–450
Approaches & LoC estimates
Approach A (recommended): Full persistence + UI transcript viewer
Estimated net LoC: ~800–1,350 (product code)
Approach B (smaller): Persistence only (no UI yet)
waitForAgentReport()to read from diskEstimated net LoC: ~400–700 (product code)
Acceptance criteria
task_awaiteven after:Generated with
mux• Model:openai:gpt-5.2• Thinking:xhigh• Cost: $12.78