osborn 0.9.24 → 0.9.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/config.js CHANGED
@@ -649,31 +649,45 @@ export async function listAllClaudeSessions(limit = 100) {
649
649
  ]);
650
650
  if (preview.messageCount < 2)
651
651
  continue;
652
- // SLUG-FIRST: prefer the slug-derived path over the content cwd.
652
+ // TWO FIELDS two distinct purposes. The dashboard uses each for
653
+ // exactly one thing:
653
654
  //
654
- // Claude Code's `--resume <id>` looks up sessions by file LOCATION
655
- // (slug folder under ~/.claude/projects/), not by the cwd field inside
656
- // the JSONL. The JSONL content cwd is immutable historical metadata —
657
- // it's where the session was ORIGINALLY recorded (e.g. sprite path
658
- // /home/sprite/workspace or /workspaces/codespaces-blank). On migration
659
- // (sprite fly), files were finalized into a new slug but their
660
- // content still records the original cwd.
655
+ // `projectPath` content cwd from JSONL.
656
+ // What the session ORIGINALLY recorded as its working directory.
657
+ // For native fly sessions that's the same as the slug-derived
658
+ // path. For imported sessions (migrated from sprite, copied from
659
+ // Codespaces, synced from a Mac) it's the source machine's cwd —
660
+ // a path that may not exist on this host. The dashboard groups
661
+ // by this so a user who imported sessions from 4 different
662
+ // places sees 4 project cards, named after where each came from.
661
663
  //
662
- // The dashboard forwards this field as `workingDirectory` to the
663
- // agent, which uses it as Claude Code's spawn cwd. If it doesn't
664
- // match the slug, the spawn lands in the wrong folder and resume
665
- // errors with "No conversation found". The slug is the source of
666
- // truth for where the file lives on disk; use it.
664
+ // `cwd` slug-derived. File LOCATION on disk.
665
+ // Forwarded to the agent as `workingDirectory` so Claude Code's
666
+ // `--resume` finds the JSONL Claude Code looks up sessions
667
+ // by slug folder, not by content cwd. Always points at the
668
+ // real on-disk location regardless of what the JSONL records.
667
669
  //
668
- // Content cwd is kept as a last-resort fallback only when slug
669
- // reversal fails (slugToPath validates with existsSync and returns
670
- // '' on ambiguous encodings like dir names containing literal '-').
670
+ // Why NOT use content cwd for routing too: imported sessions still
671
+ // live in the LOCAL slug after migration. A session whose content
672
+ // cwd is `/workspaces/codespaces-blank` was put in slug `-workspace/`
673
+ // when we synced it onto fly, so `--resume` needs cwd=/workspace to
674
+ // find it. Forwarding the content cwd would send Claude Code to a
675
+ // slug that doesn't exist on this host.
676
+ //
677
+ // Why NOT use slug-derived path for grouping too: that collapses all
678
+ // imported sessions into one "Workspace" card on the dashboard,
679
+ // losing the "this came from Codespaces, that came from sprite"
680
+ // organization the user thinks of when finding old conversations.
681
+ //
682
+ // Each field falls back to the other if its preferred source is
683
+ // empty — slugToPath returns '' for ambiguous slug encodings, and
684
+ // some old JSONL files don't carry a cwd field at all.
671
685
  const slugPath = slugToPath(c.slug);
672
686
  sessions.push({
673
687
  sessionId: c.sessionId,
674
688
  projectSlug: c.slug,
675
- projectPath: slugPath || cwd,
676
- cwd: slugPath || cwd,
689
+ projectPath: cwd || slugPath, // display / group key (original cwd)
690
+ cwd: slugPath || cwd, // resume routing (file location)
677
691
  timestamp: c.mtime,
678
692
  lastMessage: preview.lastMessage,
679
693
  messageCount: preview.messageCount,
package/dist/index.js CHANGED
@@ -164,6 +164,14 @@ function startApiServer(workingDir, port) {
164
164
  const limit = parseInt(url.searchParams.get('limit') || '100', 10);
165
165
  const sessions = await listAllClaudeSessions(limit);
166
166
  const payload = {
167
+ // The agent's working directory at launch — the BASE LAYER of all
168
+ // project organization. The dashboard groups sessions relative to
169
+ // this path: a session whose cwd === baseCwd is a "Workspace"
170
+ // session (the base); a session at `${baseCwd}/<name>` is a
171
+ // project called "<name>". Replaces the dashboard's previously-
172
+ // hardcoded base-path list — agent self-describes its base so
173
+ // the UI doesn't have to keep a sync'd copy.
174
+ baseCwd: workingDir,
167
175
  sessions: sessions.map(s => ({
168
176
  sessionId: s.sessionId,
169
177
  projectSlug: s.projectSlug,
@@ -3100,6 +3108,11 @@ async function main() {
3100
3108
  const sessions = await listAllClaudeSessions(100);
3101
3109
  await sendToFrontend({
3102
3110
  type: 'sessions_list',
3111
+ // See `/sessions` HTTP handler — baseCwd is the agent's
3112
+ // workingDir, the base layer the dashboard groups against.
3113
+ // Sent here too so the in-chat session list grouping stays
3114
+ // consistent with the dashboard's grouping.
3115
+ baseCwd: workingDir,
3103
3116
  sessions: sessions.map(s => ({
3104
3117
  sessionId: s.sessionId,
3105
3118
  projectSlug: s.projectSlug,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "osborn",
3
- "version": "0.9.24",
3
+ "version": "0.9.26",
4
4
  "description": "Voice AI coding assistant - local agent that connects to Osborn frontend",
5
5
  "type": "module",
6
6
  "bin": {