@saadnvd1/agent-os 0.3.503 → 0.3.506

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.
Files changed (43) hide show
  1. package/README.md +67 -0
  2. package/app/api/projects/[id]/session-logs/route.ts +39 -0
  3. package/app/api/session-logs/[id]/route.ts +18 -0
  4. package/app/api/workspaces/[id]/session-logs/route.ts +35 -0
  5. package/components/Pane/DesktopTabBar.tsx +4 -1
  6. package/components/Pane/MobileTabBar.tsx +12 -1
  7. package/components/Pane/PaneViewToggle.tsx +14 -2
  8. package/components/Pane/index.tsx +28 -1
  9. package/components/PrStageBadge.tsx +15 -6
  10. package/components/Projects/ProjectSettingsDialog.tsx +11 -0
  11. package/components/SessionList/LiveLine.tsx +75 -0
  12. package/components/SessionList/RowParts.tsx +1 -7
  13. package/components/SessionList/RowShell.tsx +1 -1
  14. package/components/SessionList/RowSubtitle.tsx +4 -0
  15. package/components/SessionList/SessionRow.tsx +9 -3
  16. package/components/SessionLogs/LogDocument.tsx +29 -0
  17. package/components/SessionLogs/ProjectLogsSection.tsx +140 -0
  18. package/components/Workspaces/HqFolderForm.tsx +71 -0
  19. package/components/Workspaces/WorkspaceSettings.tsx +2 -0
  20. package/contexts/PaneContext.tsx +1 -1
  21. package/data/session-logs/index.ts +110 -0
  22. package/lib/db/migrations.ts +24 -0
  23. package/lib/done/index.ts +2 -0
  24. package/lib/orchestrator/brief.ts +1 -0
  25. package/lib/orchestrator/logs.ts +81 -0
  26. package/lib/orchestrator/serve.ts +5 -0
  27. package/lib/orchestrator/tool-names.ts +1 -0
  28. package/lib/orchestrator/tool-schemas.ts +13 -0
  29. package/lib/orchestrator/tools.ts +1 -0
  30. package/lib/session-logs/build.ts +253 -0
  31. package/lib/session-logs/folder.ts +142 -0
  32. package/lib/session-logs/gather.ts +301 -0
  33. package/lib/session-logs/hq.ts +201 -0
  34. package/lib/session-logs/index.ts +193 -0
  35. package/lib/session-logs/paths.ts +37 -0
  36. package/lib/session-logs/reexport.ts +90 -0
  37. package/lib/session-logs/store.ts +105 -0
  38. package/lib/sidebar/live-status.ts +52 -0
  39. package/lib/tasks/finish.ts +3 -0
  40. package/lib/tasks/pr-stage.ts +14 -0
  41. package/lib/tasks/session.ts +4 -0
  42. package/package.json +1 -1
  43. package/scripts/backfill-session-logs.ts +101 -0
@@ -90,3 +90,17 @@ export const PR_STAGE_LABEL: Record<PrStageName, string> = {
90
90
 
91
91
  export const prStageText = (s: Pick<PrStage, "stage" | "number">) =>
92
92
  `PR #${s.number} · ${PR_STAGE_LABEL[s.stage]}`;
93
+
94
+ // The short wording for the badge itself; the full wording is the tooltip.
95
+ export const PR_STAGE_SHORT: Record<PrStageName, string> = {
96
+ merged: "Merged",
97
+ conflicts: "Conflicts",
98
+ "ci-failed": "CI failed",
99
+ "ci-running": "CI",
100
+ "ci-settling": "CI",
101
+ "review-running": "Review",
102
+ "review-blocked": "Blocked",
103
+ waiting: "Waiting on you",
104
+ armed: "Armed",
105
+ open: "PR",
106
+ };
@@ -9,6 +9,7 @@ import { getProject } from "../projects";
9
9
  import { findPRStrict } from "./gh";
10
10
  import { currentBranch, git } from "../git-exec";
11
11
  import { lookupPR } from "./pr-poll";
12
+ import { captureMergedTask } from "../session-logs";
12
13
  import { notifyStatusChanged } from "../status/hub";
13
14
  import type { TaskPR } from "./state";
14
15
 
@@ -185,6 +186,9 @@ export async function prFor(
185
186
  db.prepare(
186
187
  `UPDATE sessions SET pr_url = ?, pr_number = ?, pr_status = ? WHERE id = ?`
187
188
  ).run(pr.url, pr.number, pr.state.toLowerCase(), session.id);
189
+ // Merged on GitHub rather than through AgentOS: its log is due too.
190
+ if (pr.state === "MERGED")
191
+ void captureMergedTask(session.id, { head: pr.head });
188
192
  }
189
193
  // Only an answer just read from GitHub: a cached one may predate a
190
194
  // retarget AgentOS made itself (a restack).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saadnvd1/agent-os",
3
- "version": "0.3.503",
3
+ "version": "0.3.506",
4
4
  "description": "Self-hosted web UI for managing Claude Code sessions",
5
5
  "bin": {
6
6
  "agent-os": "scripts/agent-os"
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Writes session logs for tasks that merged before logs existed, from what
3
+ * the database still holds. Their plan files went with their worktrees, so
4
+ * each says "plan not kept". Idempotent: a task with a log is skipped.
5
+ *
6
+ * npx tsx scripts/backfill-session-logs.ts --db <path to agent-os.db> [--no-gh] [--dry] [--reexport]
7
+ *
8
+ * --db is required so a run never lands on a database by accident. Files go
9
+ * where new logs go: a project's folder, else the workspace's HQ repo (one
10
+ * commit per HQ at the end of the run), else ~/.agent-os/session-logs; run
11
+ * with HOME set elsewhere to keep a trial away from the real folder.
12
+ * --no-gh skips asking GitHub for each merge commit.
13
+ * --reexport writes the files of logs already in the database to where they
14
+ * go now, from the stored markdown (no rebuild, no model), and writes no new
15
+ * logs. Old files are left in place.
16
+ */
17
+
18
+ const args = process.argv.slice(2);
19
+ const flag = (name: string) => args.includes(name);
20
+ const dbPath = args[args.indexOf("--db") + 1];
21
+
22
+ if (!flag("--db") || !dbPath || dbPath.startsWith("--")) {
23
+ console.error(
24
+ "usage: backfill-session-logs.ts --db <path> [--no-gh] [--dry] [--reexport]"
25
+ );
26
+ process.exit(2);
27
+ }
28
+ process.env.DB_PATH = dbPath;
29
+
30
+ type Visibility = "private" | "internal" | "none";
31
+
32
+ async function main() {
33
+ if (flag("--reexport")) {
34
+ const { reexportLogs } = await import("../lib/session-logs/reexport");
35
+ const r = await reexportLogs({ dry: flag("--dry"), log: console.log });
36
+ console.log(
37
+ `${r.moved} ${flag("--dry") ? "to move" : "moved"}, ${r.unchanged} already in place, ${r.failed} failed, ${r.committed} committed`
38
+ );
39
+ return;
40
+ }
41
+ const { db } = await import("../lib/db");
42
+ const { writeLog } = await import("../lib/session-logs");
43
+ const { hasLog } = await import("../lib/session-logs/store");
44
+ const { commitLogs } = await import("../lib/session-logs/hq");
45
+ const rows = db
46
+ .prepare(
47
+ `SELECT id, name FROM sessions
48
+ WHERE task_prompt IS NOT NULL AND peer_mirror = 0
49
+ AND (task_status = 'merged' OR (pr_status = 'merged' AND task_status != 'dropped'))
50
+ ORDER BY COALESCE(merged_at, updated_at)`
51
+ )
52
+ .all() as { id: string; name: string }[];
53
+ const todo = rows.filter((r) => !hasLog(r.id));
54
+ console.log(
55
+ `${rows.length} merged tasks, ${rows.length - todo.length} already logged, ${todo.length} to write${flag("--dry") ? " (dry run)" : ""}`
56
+ );
57
+ let written = 0;
58
+ const byHq = new Map<string, { files: string[]; visibility: Visibility }>();
59
+ for (const r of todo) {
60
+ if (flag("--dry")) {
61
+ console.log(` would log ${r.name} (${r.id.slice(0, 8)})`);
62
+ continue;
63
+ }
64
+ try {
65
+ const ok = await writeLog(r.id, {
66
+ planKept: false,
67
+ noGh: flag("--no-gh"),
68
+ onFile: (file, hq) => {
69
+ const e = byHq.get(hq.dir) ?? {
70
+ files: [],
71
+ visibility: hq.visibility,
72
+ };
73
+ e.files.push(file);
74
+ byHq.set(hq.dir, e);
75
+ },
76
+ });
77
+ if (ok) written++;
78
+ console.log(
79
+ ` ${ok ? "logged" : "skipped"} ${r.name} (${r.id.slice(0, 8)})`
80
+ );
81
+ } catch (error) {
82
+ console.error(` FAILED ${r.name} (${r.id.slice(0, 8)}):`, error);
83
+ }
84
+ }
85
+ for (const [dir, { files, visibility }] of byHq)
86
+ await commitLogs(
87
+ dir,
88
+ files,
89
+ `session logs: ${files.length} backfilled`,
90
+ visibility
91
+ );
92
+ console.log(`wrote ${written} logs`);
93
+ }
94
+
95
+ main().then(
96
+ () => process.exit(0),
97
+ (error) => {
98
+ console.error(error);
99
+ process.exit(1);
100
+ }
101
+ );