@saadnvd1/agent-os 0.3.570 → 0.3.572

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 (71) hide show
  1. package/README.md +1 -1
  2. package/app/api/bus/spawn/route.ts +21 -3
  3. package/app/api/client-config/route.test.ts +11 -0
  4. package/app/api/client-config/route.ts +10 -0
  5. package/app/api/load/route.test.ts +32 -0
  6. package/app/api/load/route.ts +20 -0
  7. package/app/api/sessions/[id]/pause/route.ts +97 -0
  8. package/app/api/tasks/[id]/priority/route.ts +29 -0
  9. package/app/api/tasks/route.ts +17 -2
  10. package/components/Chat/AccessPicker.tsx +10 -18
  11. package/components/Chat/ChatPanel.tsx +4 -0
  12. package/components/Chat/ModelPicker.tsx +9 -3
  13. package/components/Chat/PausedBanner.tsx +54 -0
  14. package/components/Chat/composer/ComposerBody.tsx +3 -0
  15. package/components/Chat/composer/Toolbar.tsx +3 -0
  16. package/components/Draft/DraftChips.tsx +25 -0
  17. package/components/Draft/DraftPanel.tsx +1 -0
  18. package/components/Recipes/format.ts +19 -30
  19. package/components/SessionList/QueuedRow.tsx +46 -2
  20. package/components/SessionList/RowSubtitle.tsx +21 -3
  21. package/components/SessionList/SessionRowMenu.tsx +49 -1
  22. package/components/SessionList/useShelfActions.ts +39 -2
  23. package/components/Tasks/task-state-labels.ts +16 -16
  24. package/data/chat/useChat.ts +3 -0
  25. package/data/sessions/shelf.ts +50 -0
  26. package/data/tasks/index.ts +2 -0
  27. package/data/tasks/queue.ts +38 -1
  28. package/lib/bus/index.ts +21 -2
  29. package/lib/chat/access-levels.ts +24 -0
  30. package/lib/chat/events.ts +2 -0
  31. package/lib/chat/hold.ts +11 -2
  32. package/lib/chat/settings.ts +6 -2
  33. package/lib/config/client-config-types.ts +29 -0
  34. package/lib/config/client-config.ts +28 -0
  35. package/lib/db/migrations.ts +56 -1
  36. package/lib/db/types.ts +30 -0
  37. package/lib/done/index.ts +2 -0
  38. package/lib/drafts.ts +3 -0
  39. package/lib/hosts/peer-load.ts +120 -0
  40. package/lib/load/machines.ts +124 -0
  41. package/lib/load/monitor.ts +17 -2
  42. package/lib/load/start-gate.ts +95 -0
  43. package/lib/model-catalog.ts +11 -0
  44. package/lib/orchestrator/act.ts +78 -0
  45. package/lib/orchestrator/brakes.ts +2 -0
  46. package/lib/orchestrator/brief.ts +4 -1
  47. package/lib/orchestrator/digest.ts +15 -4
  48. package/lib/orchestrator/serve.ts +18 -0
  49. package/lib/orchestrator/tool-names.ts +3 -0
  50. package/lib/orchestrator/tool-schemas.ts +33 -0
  51. package/lib/orchestrator/tools.ts +7 -1
  52. package/lib/project-config/agentos.schema.json +13 -0
  53. package/lib/project-config/schema.ts +9 -0
  54. package/lib/recipes/status-labels.ts +27 -0
  55. package/lib/sidebar/paused.ts +13 -0
  56. package/lib/sidebar/queued.ts +34 -1
  57. package/lib/sidebar/shelves.ts +21 -1
  58. package/lib/sidebar/snooze.ts +26 -0
  59. package/lib/tasks/finish.ts +7 -0
  60. package/lib/tasks/move-carry.ts +1 -0
  61. package/lib/tasks/move-flow.ts +6 -0
  62. package/lib/tasks/pause-hold.ts +70 -0
  63. package/lib/tasks/pause-policy.ts +130 -0
  64. package/lib/tasks/pause.ts +718 -0
  65. package/lib/tasks/placement.ts +219 -0
  66. package/lib/tasks/queue.ts +522 -64
  67. package/lib/tasks/schedule-types.ts +64 -0
  68. package/lib/tasks/schedule.ts +271 -0
  69. package/lib/tasks/state-labels.ts +21 -0
  70. package/package.json +1 -1
  71. package/server.ts +2 -1
@@ -0,0 +1,130 @@
1
+ /**
2
+ * The rules of a timed pause, as pure functions: what can't be paused, who
3
+ * may undo whose pause, and the one prompt a session gets when it resumes.
4
+ */
5
+
6
+ import type { PauseBy } from "./schedule-types";
7
+
8
+ export interface PauseFacts {
9
+ // A task (it holds a slot) as opposed to a plain chat or terminal session.
10
+ isTask: boolean;
11
+ isOrchestrator: boolean;
12
+ isQuickChange: boolean;
13
+ // Runs on, or is a mirror of, a linked machine.
14
+ isRemote: boolean;
15
+ taskStatus: string | null;
16
+ pauseState: string | null;
17
+ // Moving, or its stack is landing.
18
+ held: boolean;
19
+ // Between `gh pr merge` and the database knowing it, or tidying after.
20
+ merging: boolean;
21
+ settingUp: boolean;
22
+ // Waiting on a person.
23
+ chatWaiting: boolean;
24
+ terminalWaiting: boolean;
25
+ openAsk: boolean;
26
+ openQuestion: boolean;
27
+ escalated: boolean;
28
+ planAwaiting: boolean;
29
+ // A terminal whose agent can be started again from its saved conversation
30
+ // (Claude with a conversation id). A chat always can.
31
+ resumable: boolean;
32
+ }
33
+
34
+ // Why it can't be paused now, or null.
35
+ export function pauseRefusal(f: PauseFacts): string | null {
36
+ if (f.isOrchestrator)
37
+ return "An orchestrator is paused with its workspace's Pause, not on its own";
38
+ if (f.isQuickChange)
39
+ return "A quick change runs no agent, so there is nothing to pause";
40
+ if (f.isRemote) return "It runs on another machine: pause it from there";
41
+ if (!f.resumable)
42
+ return "Its agent can't be started again from where it stopped, so it can't be paused";
43
+ if (f.isTask && f.taskStatus && f.taskStatus !== "running")
44
+ return `It's ${f.taskStatus}, not running`;
45
+ if (f.pauseState === "paused") return "It's already paused";
46
+ if (f.merging) return "It's merging";
47
+ if (f.held) return "It's moving to another machine or its stack is landing";
48
+ if (f.settingUp) return "It's still setting up; pause it once it has started";
49
+ if (
50
+ f.chatWaiting ||
51
+ f.terminalWaiting ||
52
+ f.openAsk ||
53
+ f.openQuestion ||
54
+ f.escalated ||
55
+ f.planAwaiting
56
+ )
57
+ return "It's waiting on you";
58
+ return null;
59
+ }
60
+
61
+ const RANK: Record<PauseBy, number> = {
62
+ scheduler: 1,
63
+ orchestrator: 2,
64
+ person: 3,
65
+ };
66
+
67
+ // A pause is lifted by its own kind or a higher one: a person above the
68
+ // orchestrator above the scheduler.
69
+ export function mayResume(actor: PauseBy, pausedBy: PauseBy): boolean {
70
+ return RANK[actor] >= RANK[pausedBy];
71
+ }
72
+
73
+ export function resumeRefusal(
74
+ actor: PauseBy,
75
+ pausedBy: PauseBy,
76
+ untilLabel: string | null
77
+ ): string | null {
78
+ if (mayResume(actor, pausedBy)) return null;
79
+ const who = pausedBy === "person" ? "a person" : "the orchestrator";
80
+ return `Paused by ${who}${untilLabel ? ` until ${untilLabel}` : ""}; only ${pausedBy === "person" ? "they" : "the orchestrator or a person"} can resume it`;
81
+ }
82
+
83
+ export interface HeldMessage {
84
+ from: string;
85
+ text: string;
86
+ }
87
+
88
+ const BY_TEXT: Record<PauseBy, string> = {
89
+ person: "by the user",
90
+ orchestrator: "by the orchestrator",
91
+ scheduler: "by the scheduler because the machine was loaded",
92
+ };
93
+
94
+ export function durationText(fromMs: number, untilMs: number | null): string {
95
+ if (untilMs === null) return "until resumed";
96
+ const minutes = Math.max(1, Math.round((untilMs - fromMs) / 60_000));
97
+ if (minutes < 90) return `for ${minutes} minute${minutes === 1 ? "" : "s"}`;
98
+ const hours = Math.round(minutes / 60);
99
+ if (hours < 48) return `for ${hours} hour${hours === 1 ? "" : "s"}`;
100
+ const days = Math.round(hours / 24);
101
+ return `for ${days} days`;
102
+ }
103
+
104
+ // The prompt a resumed session gets, with everything held while it was
105
+ // paused folded in.
106
+ export function continuePrompt(input: {
107
+ pausedAt: string;
108
+ by: PauseBy;
109
+ duration: string;
110
+ interrupted: boolean;
111
+ held: HeldMessage[];
112
+ }): string {
113
+ const lines = [
114
+ `You were paused at ${input.pausedAt} (${BY_TEXT[input.by]}, ${input.duration}) and are resumed now.${
115
+ input.interrupted
116
+ ? " Your last step was interrupted; check whether it finished before redoing it."
117
+ : ""
118
+ }`,
119
+ ];
120
+ if (input.held.length) {
121
+ lines.push(
122
+ `While you were paused, ${input.held.length} message${input.held.length === 1 ? "" : "s"} arrived:`,
123
+ ...input.held.map((m, i) => `${i + 1}. From ${m.from}: ${m.text}`)
124
+ );
125
+ }
126
+ lines.push(
127
+ "Check the worktree, the branch against its PR and CI before you continue, then carry on with the task."
128
+ );
129
+ return lines.join("\n");
130
+ }