@saadnvd1/agent-os 0.3.592 → 0.3.595

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 (55) hide show
  1. package/app/api/bus/resurrect/route.test.ts +47 -0
  2. package/app/api/bus/resurrect/route.ts +36 -0
  3. package/app/api/sessions/[id]/pause/route.ts +5 -2
  4. package/app/api/sessions/[id]/resurrect/route.test.ts +32 -0
  5. package/app/api/sessions/[id]/resurrect/route.ts +24 -0
  6. package/app/api/sessions/ended/route.ts +19 -0
  7. package/components/Archived/ArchivedRow.tsx +11 -7
  8. package/components/Chat/PausedBanner.tsx +1 -1
  9. package/components/Providers.tsx +5 -1
  10. package/components/SessionList/LiveLine.tsx +1 -1
  11. package/components/SessionList/OrchestratorPinRow.tsx +10 -5
  12. package/components/SessionList/QuickChangeRow.tsx +1 -1
  13. package/components/SessionList/RowParts.tsx +37 -2
  14. package/components/SessionList/RowShell.tsx +6 -2
  15. package/components/SessionList/RowSubtitle.tsx +20 -12
  16. package/components/SessionList/SessionRow.tsx +28 -17
  17. package/components/SessionList/SessionRowMenu.tsx +12 -1
  18. package/components/SessionList/SessionShelves.tsx +40 -8
  19. package/components/SessionList/SidebarExtras.tsx +7 -1
  20. package/components/SessionList/index.tsx +2 -0
  21. package/components/SessionList/useDoneAction.ts +14 -0
  22. package/components/SessionList/useResurrectAction.test.ts +96 -0
  23. package/components/SessionList/useSessionTips.ts +62 -0
  24. package/components/Tasks/TaskRow.tsx +18 -0
  25. package/components/ui/HoverTip.tsx +20 -0
  26. package/data/done/index.ts +17 -0
  27. package/data/presence/index.ts +14 -7
  28. package/lib/db/migrations.ts +11 -0
  29. package/lib/db/types.ts +2 -0
  30. package/lib/done/worktree.ts +18 -2
  31. package/lib/hover-tip.ts +161 -0
  32. package/lib/orchestrator/act.ts +44 -5
  33. package/lib/orchestrator/brief.ts +2 -1
  34. package/lib/orchestrator/digest.ts +3 -2
  35. package/lib/orchestrator/serve.ts +6 -0
  36. package/lib/orchestrator/shell-guard.ts +2 -1
  37. package/lib/orchestrator/tool-names.ts +1 -0
  38. package/lib/orchestrator/tool-schemas.ts +6 -0
  39. package/lib/orchestrator/tools.ts +3 -1
  40. package/lib/resurrect/find.ts +111 -0
  41. package/lib/resurrect/index.ts +422 -0
  42. package/lib/resurrect/plan.ts +61 -0
  43. package/lib/resurrect/snapshot.ts +229 -0
  44. package/lib/security/demo.ts +1 -0
  45. package/lib/sidebar/paused.ts +8 -1
  46. package/lib/sidebar/row-actions.ts +7 -3
  47. package/lib/sidebar/row-badge.ts +40 -0
  48. package/lib/sidebar/row-details.ts +53 -0
  49. package/lib/sidebar/shelves.ts +7 -1
  50. package/lib/tasks/auto-hold.ts +17 -0
  51. package/lib/tasks/finish.ts +9 -0
  52. package/lib/tasks/pause-hold.ts +1 -1
  53. package/lib/tasks/pause.ts +76 -8
  54. package/lib/tasks/queue.ts +39 -12
  55. package/package.json +1 -1
@@ -22,6 +22,7 @@ import { getProject } from "../projects";
22
22
  import { isRemoteHost } from "../hosts";
23
23
  import { getWorkspace } from "../workspaces";
24
24
  import { isPaused } from "../orchestrator/pause";
25
+ import { autoHold } from "./auto-hold";
25
26
  import {
26
27
  BrakeRefused,
27
28
  braked,
@@ -77,7 +78,13 @@ import { peerReading, peerSource, refreshPeers } from "../hosts/peer-load";
77
78
  import { hostLink, linkedHostIds } from "../hosts/remote-api";
78
79
  import { hostTasks } from "./remote";
79
80
  import { projectMachines } from "../hosts/project-machines";
80
- import { claimResume, dueResumes } from "./pause";
81
+ import {
82
+ type DueResume,
83
+ claimResume,
84
+ dueResumes,
85
+ resumeHoldFor,
86
+ setPauseNote,
87
+ } from "./pause";
81
88
 
82
89
  export type QueueStatus =
83
90
  | "queued"
@@ -562,16 +569,6 @@ const sqliteMs = (v: string) =>
562
569
  export const queuedView = (id: string) =>
563
570
  listQueue().find((q) => q.id === id) ?? null;
564
571
 
565
- // Why an automatic start has to wait, or null. A start now by a person
566
- // isn't held.
567
- function autoHold(workspaceId: string | null, planOnly = false): Hold | null {
568
- if (workspaceId && isPaused(workspaceId))
569
- return { code: "orch_paused", text: "Waiting: the orchestrator is paused" };
570
- if (planOnly) return null;
571
- const window = brakesEnabled() ? windowRefusal(readUsage()) : null;
572
- return window ? { code: "window", text: `Waiting: ${window}` } : null;
573
- }
574
-
575
572
  const requestNeeds = (
576
573
  projectId: string,
577
574
  workingDirectory: string,
@@ -959,7 +956,18 @@ export async function tickOnce(now = Date.now()): Promise<"stale" | void> {
959
956
  WHERE q.status IN ('queued', 'starting') ORDER BY q.position`
960
957
  )
961
958
  .all() as QueueRowWithWorkspace[];
962
- const resumes = dueResumes();
959
+ const due = dueResumes();
960
+ // A deliberate resume starts past the slot limit; only a red machine holds
961
+ // it, and the task says so.
962
+ for (const d of due.filter((x) => x.resumeBy)) {
963
+ const s = db
964
+ .prepare(`SELECT * FROM sessions WHERE id = ?`)
965
+ .get(d.sessionId) as Session | undefined;
966
+ const why = s ? resumeHoldFor(s, d.resumeBy!) : null;
967
+ if (why) setPauseNote(d.sessionId, `held: ${why}`);
968
+ else await claimResume(d.sessionId, { reason: d.resumeBy!, force: true });
969
+ }
970
+ const resumes = due.filter((x) => !x.resumeBy);
963
971
  if (!rows.length && !resumes.length) return;
964
972
  const queued = rows.filter((r) => r.status === "queued");
965
973
  const used = usedFinishes();
@@ -1051,6 +1059,11 @@ export async function tickOnce(now = Date.now()): Promise<"stale" | void> {
1051
1059
  const plan = planStarts(candidates, groups, home, now);
1052
1060
  for (const r of queued)
1053
1061
  noteWaiting(r, withStayReason(r, plan.explain.get(r.id)));
1062
+ for (const d of resumes)
1063
+ setPauseNote(
1064
+ d.sessionId,
1065
+ resumeWaitNote(d, plan.explain.get(`resume:${d.sessionId}`))
1066
+ );
1054
1067
 
1055
1068
  for (const id of plan.start) {
1056
1069
  if (id.startsWith("resume:")) {
@@ -1067,6 +1080,20 @@ export async function tickOnce(now = Date.now()): Promise<"stale" | void> {
1067
1080
  }
1068
1081
  }
1069
1082
 
1083
+ // What a timed resume still waiting for its turn says on its task.
1084
+ function resumeWaitNote(
1085
+ d: DueResume,
1086
+ explain: { code: WaitCode | null; text: string | null } | undefined
1087
+ ): string | null {
1088
+ if (!explain) return null;
1089
+ if (explain.code && explain.code !== "slot" && explain.text)
1090
+ return `held: ${explain.text.replace(/^Waiting: /, "")}`;
1091
+ const slots = slotState(d.projectId);
1092
+ return slots.limit === null
1093
+ ? "resuming: waiting for its turn"
1094
+ : `resuming: waiting for a slot, ${slots.running} of ${slots.limit} running`;
1095
+ }
1096
+
1070
1097
  // Writes why a row waits when that changed: its hold text and code. A bare
1071
1098
  // rank isn't stored (the view computes it), so the stream stays quiet.
1072
1099
  const MACHINE_HOLDS = new Set<WaitCode>([
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saadnvd1/agent-os",
3
- "version": "0.3.592",
3
+ "version": "0.3.595",
4
4
  "description": "Self-hosted web UI for managing Claude Code sessions",
5
5
  "bin": {
6
6
  "agent-os": "scripts/agent-os"