The CLI ao spawn command writes session metadata into project-scoped
subdirectories ({dataDir}/{projectId}-sessions/{sessionId}), while
session-manager — used by ao start and the web API — expects flat files
at {dataDir}/{sessionId}. Neither path knew about the other, so sessions
created via ao spawn were completely invisible to the dashboard: the web
API returned an empty/stale sessions array regardless of how many agents
were actively working.
Root cause: two independent write paths with no shared read path.
- packages/cli/src/commands/spawn.ts writes to getSessionDir() subdir
- packages/core/src/session-manager.ts calls listMetadata(dataDir) which
only scanned flat files at the root of dataDir
Fix: update packages/core/src/metadata.ts to handle both locations:
1. Add resolveMetadataPath(dataDir, sessionId) — checks flat path first,
then scans any {x}-sessions/ subdirectory for the session ID. Used by
all read functions so they find files regardless of which write path
created them.
2. Update listMetadata() to scan both flat files AND project subdirectories
matching the {projectId}-sessions/ naming pattern. Deduplicates by
session ID so the same session ID never appears twice.
3. Update readMetadata, readMetadataRaw, updateMetadata, deleteMetadata
to use resolveMetadataPath. updateMetadata falls back to the flat path
for genuinely new sessions that have no file yet.
Write paths are unchanged — session-manager.spawn() continues to write
flat files; ao spawn continues to write to the project subdirectory; the
agent bash script's in-flight status updates (also written to the subdir)
are now picked up correctly by the dashboard on every poll.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>