@saadnvd1/agent-os 0.3.839 → 0.3.848
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/app/api/approvals/route.ts +5 -1
- package/app/api/health/config/route.ts +2 -0
- package/app/api/health/route.ts +4 -2
- package/app/globals.css +6 -0
- package/components/Audits/AuditsPanel.tsx +42 -35
- package/components/Chat/quote-selection.test.ts +6 -0
- package/components/Chat/quote-selection.ts +13 -10
- package/components/Devices/ConnectCard.tsx +52 -56
- package/components/Devices/DeviceGroupRow.tsx +65 -0
- package/components/Devices/DeviceRow.tsx +2 -2
- package/components/Devices/DevicesPanel.tsx +63 -49
- package/components/Devices/NetworkSection.tsx +29 -26
- package/components/Devices/TailscaleCard.tsx +9 -10
- package/components/Devices/group.test.ts +66 -0
- package/components/Devices/group.ts +67 -0
- package/components/Health/HealthPanel.tsx +5 -81
- package/components/Health/MachineChecks.tsx +20 -0
- package/components/Health/SweepRuns.tsx +46 -51
- package/components/Health/Thresholds.tsx +50 -0
- package/components/Merge/GlobalMergeSection.tsx +5 -6
- package/components/Merge/MergeApprovalToggle.tsx +20 -14
- package/components/Merge/MergeSettingsFields.tsx +0 -2
- package/components/Merge/WorktreeSweepSection.tsx +15 -34
- package/components/Models/AgentRoleCard.tsx +7 -7
- package/components/Models/ModelSelect.tsx +14 -3
- package/components/Models/ModelsSection.tsx +42 -37
- package/components/Models/ProviderCard.tsx +1 -1
- package/components/Models/RoleModelSelect.tsx +15 -3
- package/components/Models/SubagentModels.tsx +60 -45
- package/components/PhoneNotify/PhoneNotifyForm.tsx +6 -13
- package/components/Recipes/RecipesPanel.tsx +20 -9
- package/components/Recipes/RunHistory.tsx +1 -1
- package/components/Schedules/SchedulesPanel.tsx +12 -10
- package/components/SessionList/GroupShelves.tsx +8 -3
- package/components/SessionList/OrchestratorPinRow.tsx +2 -2
- package/components/SessionList/RowParts.tsx +39 -35
- package/components/SessionList/RowRename.test.ts +86 -17
- package/components/SessionList/RowShell.tsx +2 -5
- package/components/SessionList/SessionRow.tsx +0 -1
- package/components/Settings/AppWideLimits.tsx +2 -2
- package/components/Settings/{NoApprovalsSection.test.ts → ApprovalsSection.test.ts} +61 -22
- package/components/Settings/ApprovalsSection.tsx +93 -0
- package/components/Settings/ExperimentsSection.test.ts +20 -4
- package/components/Settings/ExperimentsSection.tsx +54 -34
- package/components/Settings/GeneralSection.tsx +39 -0
- package/components/Settings/ListSection.test.ts +85 -0
- package/components/Settings/ListSection.tsx +132 -0
- package/components/Settings/NotificationsSection.tsx +31 -29
- package/components/Settings/PickerSetting.tsx +5 -1
- package/components/Settings/SettingRow.tsx +91 -49
- package/components/Settings/SettingsDialog.test.ts +31 -4
- package/components/Settings/SettingsDialog.tsx +22 -17
- package/components/Settings/ValueLabel.tsx +31 -0
- package/components/Settings/settings-fields.test.ts +81 -2
- package/components/Settings/settings-fields.tsx +28 -27
- package/components/Shares/SharedLinksPanel.tsx +11 -14
- package/components/Updates/AutoUpdates.tsx +12 -8
- package/components/Updates/UpdatesSection.tsx +18 -17
- package/components/Workspaces/HqFolderForm.tsx +21 -19
- package/components/Workspaces/McpServersForm.tsx +20 -28
- package/components/Workspaces/ProjectTiersForm.tsx +25 -12
- package/components/Workspaces/RuleEditor.tsx +86 -0
- package/components/Workspaces/RulesForm.test.ts +58 -31
- package/components/Workspaces/RulesForm.tsx +129 -58
- package/components/Workspaces/WorkspaceModeForm.tsx +3 -3
- package/components/Workspaces/WorkspaceModelsForm.tsx +40 -41
- package/components/Workspaces/WorkspacePanel.tsx +73 -0
- package/components/Workspaces/WorkspacePicker.tsx +44 -0
- package/components/Workspaces/WorkspaceSettings.test.ts +153 -0
- package/components/Workspaces/WorkspaceSettings.tsx +51 -79
- package/components/Workspaces/useSettingsWorkspace.ts +47 -0
- package/components/ui/switch.tsx +1 -1
- package/content/whats-new.json +40 -0
- package/data/approvals/index.ts +2 -2
- package/data/health/index.ts +3 -0
- package/hooks/useAutoSave.ts +18 -5
- package/hooks/useSettingsUrl.ts +6 -3
- package/lib/approvals/describe.ts +64 -0
- package/lib/away/deploy.ts +34 -7
- package/lib/health/config.ts +70 -17
- package/lib/models/plan-fresh.ts +61 -0
- package/lib/models/plan-step.ts +40 -24
- package/lib/models/providers.ts +1 -1
- package/lib/orchestrator/scope.ts +10 -0
- package/lib/recipes/settle.ts +22 -0
- package/lib/settings-url.ts +14 -2
- package/lib/updates/banner.ts +2 -0
- package/lib/updates/run.ts +9 -0
- package/lib/updates/view.ts +28 -6
- package/lib/worktree-deps/paths.ts +9 -4
- package/lib/worktrees.ts +3 -0
- package/package.json +1 -1
- package/scripts/autodeploy +11 -0
- package/scripts/autodeploy.test.ts +30 -0
- package/scripts/lib/update-verify.mjs +11 -0
- package/scripts/update-run.mjs +29 -0
- package/scripts/update-run.test.ts +62 -4
- package/components/Settings/NoApprovalsSection.tsx +0 -103
- package/components/Settings/SettingToggle.tsx +0 -36
- package/components/Settings/SetupSection.tsx +0 -46
|
@@ -78,20 +78,27 @@ describe("scripts/update-run.mjs", () => {
|
|
|
78
78
|
|
|
79
79
|
// A stand-in for the restarted server: its build comes from a file the fake
|
|
80
80
|
// redeploy writes, and its startedAt can be made to change (a crash loop).
|
|
81
|
-
async function fakeServer(
|
|
81
|
+
async function fakeServer(
|
|
82
|
+
opts: { restarts?: boolean; switchAt?: number } = {}
|
|
83
|
+
) {
|
|
82
84
|
let n = 0;
|
|
85
|
+
let calls = 0;
|
|
83
86
|
const server = http.createServer((req, res) => {
|
|
84
|
-
|
|
87
|
+
let build = fs.readFileSync(path.join(dir, "build"), "utf8").trim();
|
|
85
88
|
res.setHeader("content-type", "application/json");
|
|
86
|
-
if (req.url === "/api/updates/health")
|
|
89
|
+
if (req.url === "/api/updates/health") {
|
|
90
|
+
// Something else (a deploy job) restarts it into another build.
|
|
91
|
+
const other = opts.switchAt !== undefined && calls++ >= opts.switchAt;
|
|
92
|
+
if (other) build = "other";
|
|
87
93
|
return res.end(
|
|
88
94
|
JSON.stringify({
|
|
89
95
|
ok: true,
|
|
90
96
|
build,
|
|
91
|
-
startedAt: opts.restarts ? `s${n++}` : "s",
|
|
97
|
+
startedAt: opts.restarts ? `s${n++}` : other ? "s-other" : "s",
|
|
92
98
|
migrations: { known: 3, applied: 3 },
|
|
93
99
|
})
|
|
94
100
|
);
|
|
101
|
+
}
|
|
95
102
|
res.end("{}");
|
|
96
103
|
});
|
|
97
104
|
await new Promise<void>((r) => server.listen(0, "127.0.0.1", r));
|
|
@@ -170,6 +177,57 @@ describe("scripts/update-run.mjs checks the new server", () => {
|
|
|
170
177
|
}
|
|
171
178
|
});
|
|
172
179
|
|
|
180
|
+
it("a restart into a different build is left alone: no rollback, nothing marked", async () => {
|
|
181
|
+
fs.writeFileSync(path.join(dir, "next-build"), "new");
|
|
182
|
+
fs.writeFileSync(path.join(dir, "rollback-works"), "");
|
|
183
|
+
const srv = await fakeServer({ switchAt: 2 });
|
|
184
|
+
try {
|
|
185
|
+
const s = await go(REDEPLOY, "x", verifying(srv.port, "new"));
|
|
186
|
+
expect(s).toMatchObject({ phase: "failed", outcome: "interrupted" });
|
|
187
|
+
expect(s.error).toContain("something else restarted it");
|
|
188
|
+
expect(rolled()).toBeNull();
|
|
189
|
+
} finally {
|
|
190
|
+
await srv.close();
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it("a rollback records the build it went back to as autodeploy's deployed marker", async () => {
|
|
195
|
+
fs.writeFileSync(path.join(dir, "next-build"), "stale");
|
|
196
|
+
fs.writeFileSync(path.join(dir, "rollback-works"), "");
|
|
197
|
+
const state = path.join(dir, "autodeploy");
|
|
198
|
+
fs.mkdirSync(state);
|
|
199
|
+
fs.writeFileSync(path.join(state, "deployed"), "f5054a0\n");
|
|
200
|
+
const srv = await fakeServer();
|
|
201
|
+
try {
|
|
202
|
+
const s = await go(REDEPLOY, "x", {
|
|
203
|
+
...verifying(srv.port, "new"),
|
|
204
|
+
AGENTOS_AUTODEPLOY_DIR: state,
|
|
205
|
+
});
|
|
206
|
+
expect(s.outcome).toBe("rolled_back");
|
|
207
|
+
expect(fs.readFileSync(path.join(state, "deployed"), "utf8").trim()).toBe(
|
|
208
|
+
"a"
|
|
209
|
+
);
|
|
210
|
+
} finally {
|
|
211
|
+
await srv.close();
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it("a rollback on a machine without autodeploy creates no marker", async () => {
|
|
216
|
+
fs.writeFileSync(path.join(dir, "next-build"), "stale");
|
|
217
|
+
fs.writeFileSync(path.join(dir, "rollback-works"), "");
|
|
218
|
+
const state = path.join(dir, "autodeploy");
|
|
219
|
+
const srv = await fakeServer();
|
|
220
|
+
try {
|
|
221
|
+
await go(REDEPLOY, "x", {
|
|
222
|
+
...verifying(srv.port, "new"),
|
|
223
|
+
AGENTOS_AUTODEPLOY_DIR: state,
|
|
224
|
+
});
|
|
225
|
+
expect(fs.existsSync(state)).toBe(false);
|
|
226
|
+
} finally {
|
|
227
|
+
await srv.close();
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
|
|
173
231
|
it("when the rollback doesn't come up either, it ends broken", async () => {
|
|
174
232
|
fs.writeFileSync(path.join(dir, "next-build"), "stale");
|
|
175
233
|
const srv = await fakeServer();
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { useState } from "react";
|
|
4
|
-
import { Button } from "@/components/ui/button";
|
|
5
|
-
import { SettingToggle } from "@/components/Settings/SettingToggle";
|
|
6
|
-
import { useApprovalsModeQuery, useSetApprovalsMode } from "@/data/approvals";
|
|
7
|
-
|
|
8
|
-
// What it switches off, said plainly before it can be switched on.
|
|
9
|
-
export const NO_APPROVALS_WARNING = [
|
|
10
|
-
"Every ask is approved the moment it is raised, hard lines included: public posts and messages, money, irreversible or destructive changes, credentials.",
|
|
11
|
-
"A PR stuck failing its gates, with no CI, or too big to review still merges.",
|
|
12
|
-
"Brakes stop holding new starts. Plan-only tasks still wait for approve_plan.",
|
|
13
|
-
"Every agent AgentOS starts runs with its permission prompts off, and prompts already waiting are allowed.",
|
|
14
|
-
];
|
|
15
|
-
|
|
16
|
-
export function NoApprovalsSection() {
|
|
17
|
-
const { data, isPending, isError, error } = useApprovalsModeQuery();
|
|
18
|
-
const set = useSetApprovalsMode();
|
|
19
|
-
const [confirming, setConfirming] = useState(false);
|
|
20
|
-
|
|
21
|
-
if (isPending)
|
|
22
|
-
return <div className="bg-muted/40 h-16 animate-pulse rounded-lg" />;
|
|
23
|
-
if (isError)
|
|
24
|
-
return <p className="text-destructive text-sm">{error.message}</p>;
|
|
25
|
-
|
|
26
|
-
return (
|
|
27
|
-
<div className="space-y-3">
|
|
28
|
-
<SettingToggle
|
|
29
|
-
title="No approvals"
|
|
30
|
-
detail="Shuts off every approval AgentOS would otherwise wait on, and every agent's permission prompts. Off by default."
|
|
31
|
-
checked={data.on}
|
|
32
|
-
onChange={(on) => {
|
|
33
|
-
if (on) setConfirming(true);
|
|
34
|
-
else set.mutate(false);
|
|
35
|
-
}}
|
|
36
|
-
/>
|
|
37
|
-
{confirming && !data.on && (
|
|
38
|
-
<div
|
|
39
|
-
role="alertdialog"
|
|
40
|
-
aria-label="Turn on No approvals"
|
|
41
|
-
className="rounded-lg border border-red-500/20 bg-red-500/10 p-3"
|
|
42
|
-
>
|
|
43
|
-
<p className="mb-2 text-sm font-medium text-red-400">
|
|
44
|
-
Turn on No approvals?
|
|
45
|
-
</p>
|
|
46
|
-
<ul className="text-muted-foreground mb-3 list-disc space-y-1 pl-5 text-xs">
|
|
47
|
-
{NO_APPROVALS_WARNING.map((line) => (
|
|
48
|
-
<li key={line}>{line}</li>
|
|
49
|
-
))}
|
|
50
|
-
<li>
|
|
51
|
-
Terminals already running in an agent other than Claude keep
|
|
52
|
-
asking until they are restarted. Each approval made for you is
|
|
53
|
-
logged below.
|
|
54
|
-
</li>
|
|
55
|
-
</ul>
|
|
56
|
-
<div className="flex gap-2">
|
|
57
|
-
<Button
|
|
58
|
-
size="sm"
|
|
59
|
-
variant="destructive"
|
|
60
|
-
disabled={set.isPending}
|
|
61
|
-
onClick={() =>
|
|
62
|
-
set.mutate(true, { onSuccess: () => setConfirming(false) })
|
|
63
|
-
}
|
|
64
|
-
>
|
|
65
|
-
{set.isPending ? "Turning on..." : "Turn on No approvals"}
|
|
66
|
-
</Button>
|
|
67
|
-
<Button
|
|
68
|
-
size="sm"
|
|
69
|
-
variant="ghost"
|
|
70
|
-
disabled={set.isPending}
|
|
71
|
-
onClick={() => setConfirming(false)}
|
|
72
|
-
>
|
|
73
|
-
Cancel
|
|
74
|
-
</Button>
|
|
75
|
-
</div>
|
|
76
|
-
</div>
|
|
77
|
-
)}
|
|
78
|
-
{set.error && (
|
|
79
|
-
<p className="text-destructive text-xs">{set.error.message}</p>
|
|
80
|
-
)}
|
|
81
|
-
<div>
|
|
82
|
-
<p className="mb-1 text-sm font-medium">Approved for you</p>
|
|
83
|
-
{data.audit.length === 0 ? (
|
|
84
|
-
<p className="text-muted-foreground text-xs">Nothing yet.</p>
|
|
85
|
-
) : (
|
|
86
|
-
<ul className="space-y-1">
|
|
87
|
-
{data.audit.map((row) => (
|
|
88
|
-
<li key={row.id} className="text-xs">
|
|
89
|
-
<span className="text-muted-foreground">
|
|
90
|
-
{row.at} · {row.source} ·{" "}
|
|
91
|
-
</span>
|
|
92
|
-
{row.subject}
|
|
93
|
-
{row.detail ? (
|
|
94
|
-
<span className="text-muted-foreground"> ({row.detail})</span>
|
|
95
|
-
) : null}
|
|
96
|
-
</li>
|
|
97
|
-
))}
|
|
98
|
-
</ul>
|
|
99
|
-
)}
|
|
100
|
-
</div>
|
|
101
|
-
</div>
|
|
102
|
-
);
|
|
103
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { Switch } from "@/components/ui/switch";
|
|
4
|
-
|
|
5
|
-
// One labelled switch on a Settings page; a locked one says so.
|
|
6
|
-
export function SettingToggle({
|
|
7
|
-
title,
|
|
8
|
-
detail,
|
|
9
|
-
checked,
|
|
10
|
-
locked,
|
|
11
|
-
onChange,
|
|
12
|
-
}: {
|
|
13
|
-
title: string;
|
|
14
|
-
detail: string;
|
|
15
|
-
checked: boolean;
|
|
16
|
-
locked?: boolean;
|
|
17
|
-
onChange: (on: boolean) => void;
|
|
18
|
-
}) {
|
|
19
|
-
return (
|
|
20
|
-
<label className="bg-muted/40 flex items-start gap-3 rounded-lg px-3 py-2.5">
|
|
21
|
-
<div className="min-w-0 flex-1">
|
|
22
|
-
<p className="text-sm font-medium">{title}</p>
|
|
23
|
-
<p className="text-muted-foreground text-xs">
|
|
24
|
-
{detail}
|
|
25
|
-
{locked ? " Can't be changed from here." : ""}
|
|
26
|
-
</p>
|
|
27
|
-
</div>
|
|
28
|
-
<Switch
|
|
29
|
-
checked={checked}
|
|
30
|
-
disabled={locked}
|
|
31
|
-
onCheckedChange={onChange}
|
|
32
|
-
className="mt-0.5"
|
|
33
|
-
/>
|
|
34
|
-
</label>
|
|
35
|
-
);
|
|
36
|
-
}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { Button } from "@/components/ui/button";
|
|
4
|
-
import { HealthList } from "@/components/Welcome/HealthList";
|
|
5
|
-
import { AgentList } from "@/components/Welcome/AgentList";
|
|
6
|
-
import { ReachRow } from "@/components/Reach/ReachRow";
|
|
7
|
-
import { OwnerNameRow } from "./OwnerNameRow";
|
|
8
|
-
import { settingsUiActions } from "@/stores/settingsUi";
|
|
9
|
-
import { welcomeUiActions } from "@/stores/welcomeUi";
|
|
10
|
-
|
|
11
|
-
// What this machine has for running AgentOS and agents, and the first-run setup again.
|
|
12
|
-
export function SetupSection() {
|
|
13
|
-
return (
|
|
14
|
-
<div className="flex flex-col gap-4">
|
|
15
|
-
<OwnerNameRow />
|
|
16
|
-
<div className="bg-muted/40 flex items-center gap-3 rounded-lg px-3 py-2.5">
|
|
17
|
-
<div className="min-w-0 flex-1">
|
|
18
|
-
<p className="text-sm font-medium">First-run setup</p>
|
|
19
|
-
<p className="text-muted-foreground text-xs">
|
|
20
|
-
Agents, your first project, and a first session.
|
|
21
|
-
</p>
|
|
22
|
-
</div>
|
|
23
|
-
<Button
|
|
24
|
-
size="sm"
|
|
25
|
-
variant="outline"
|
|
26
|
-
className="min-h-11 sm:min-h-8"
|
|
27
|
-
onClick={() => {
|
|
28
|
-
settingsUiActions.close();
|
|
29
|
-
welcomeUiActions.open("agents");
|
|
30
|
-
}}
|
|
31
|
-
>
|
|
32
|
-
Run setup again
|
|
33
|
-
</Button>
|
|
34
|
-
</div>
|
|
35
|
-
<div className="flex flex-col gap-2">
|
|
36
|
-
<h3 className="text-sm font-medium">Health</h3>
|
|
37
|
-
<HealthList />
|
|
38
|
-
<ReachRow />
|
|
39
|
-
</div>
|
|
40
|
-
<div className="flex flex-col gap-2">
|
|
41
|
-
<h3 className="text-sm font-medium">Agents on this machine</h3>
|
|
42
|
-
<AgentList />
|
|
43
|
-
</div>
|
|
44
|
-
</div>
|
|
45
|
-
);
|
|
46
|
-
}
|