@saadnvd1/agent-os 0.3.782 → 0.3.790
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/hosts/[id]/agents/route.ts +6 -4
- package/app/api/machine/route.ts +5 -3
- package/app/api/whats-new/route.ts +47 -0
- package/components/Chat/ChatPanel.tsx +2 -0
- package/components/Chat/ModelPicker.tsx +82 -42
- package/components/Chat/composer/ComposerBody.tsx +3 -0
- package/components/Chat/composer/Toolbar.tsx +3 -0
- package/components/Draft/AgentModelChip.test.ts +1 -0
- package/components/Draft/AgentModelChip.tsx +36 -93
- package/components/Draft/Chip.tsx +27 -0
- package/components/Draft/DraftChips.tsx +5 -1
- package/components/Draft/DraftPanel.tsx +0 -21
- package/components/Draft/catalogBlocker.ts +19 -0
- package/components/Draft/picker/AgentModelPicker.tsx +152 -0
- package/components/Draft/picker/AgentRail.tsx +145 -0
- package/components/Draft/picker/ModelList.tsx +136 -0
- package/components/Draft/picker/ModelNote.tsx +15 -0
- package/components/Draft/picker/PickerSheet.tsx +61 -0
- package/components/Draft/picker/SameAsLast.tsx +52 -0
- package/components/Draft/picker/SearchField.tsx +48 -0
- package/components/Draft/picker/picker.test.ts +152 -0
- package/components/Draft/picker/sections.test.ts +115 -0
- package/components/Draft/picker/sections.ts +121 -0
- package/components/Draft/picker/useListKeys.ts +54 -0
- package/components/Draft/picker/useModelSearch.ts +66 -0
- package/components/SidebarFooter.tsx +5 -1
- package/components/Updates/UpdateBanner.test.ts +1 -1
- package/components/Updates/UpdateBanner.tsx +28 -14
- package/components/WhatsNew/WhatsNewButton.tsx +33 -0
- package/components/WhatsNew/WhatsNewDialog.tsx +169 -0
- package/components/WhatsNew/WhatsNewHost.tsx +17 -0
- package/components/WhatsNew/WhatsNewRows.tsx +62 -0
- package/components/WhatsNew/index.ts +2 -0
- package/components/ui/dialog.tsx +12 -1
- package/content/whats-new.json +1782 -0
- package/data/chat/useChat.ts +3 -0
- package/data/machines/index.ts +13 -4
- package/data/machines/keys.ts +2 -1
- package/data/whats-new/index.ts +44 -0
- package/hooks/useWhatsNewSeen.ts +50 -0
- package/lib/chat/drivers/copilot.ts +9 -6
- package/lib/chat/drivers/opencode.ts +1 -1
- package/lib/chat/drivers/pi.ts +1 -1
- package/lib/chat/events.ts +6 -0
- package/lib/chat/settings.ts +2 -0
- package/lib/machines/catalog.ts +5 -1
- package/lib/machines/recent.ts +31 -0
- package/lib/machines/remote.ts +28 -9
- package/lib/machines/report.ts +7 -2
- package/lib/machines/types.ts +12 -0
- package/lib/orchestrator/review-prompt.ts +2 -0
- package/lib/security/demo.ts +2 -0
- package/lib/tasks/brief.ts +1 -0
- package/lib/whats-new/ahead.ts +28 -0
- package/lib/whats-new/areas.ts +76 -0
- package/lib/whats-new/claude.ts +35 -0
- package/lib/whats-new/collect.ts +68 -0
- package/lib/whats-new/generate.ts +107 -0
- package/lib/whats-new/lint.ts +76 -0
- package/lib/whats-new/load.ts +59 -0
- package/lib/whats-new/rows.ts +79 -0
- package/lib/whats-new/schema.ts +41 -0
- package/lib/whats-new/summarize.ts +91 -0
- package/lib/whats-new/unread.ts +10 -0
- package/package.json +2 -1
- package/scripts/autodeploy +33 -10
- package/scripts/autodeploy.test.ts +92 -2
- package/scripts/publish-detached +20 -0
- package/scripts/whats-new.ts +118 -0
- package/stores/whatsNew.ts +16 -0
- package/components/Draft/draftModels.test.ts +0 -31
- package/components/Draft/draftModels.ts +0 -17
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# The publish half of scripts/autodeploy, run in its own process group under
|
|
3
|
+
# its own command line: serviceman kills the previous cron run's process group
|
|
4
|
+
# and any orphan still named like the job, so a forked copy of autodeploy
|
|
5
|
+
# would die with the next minute's run. Holds $state/publish.lock (taken by the
|
|
6
|
+
# caller) and releases it when done.
|
|
7
|
+
# scripts/publish-detached <sha> <state dir>
|
|
8
|
+
set -euo pipefail
|
|
9
|
+
head=$1
|
|
10
|
+
state=$2
|
|
11
|
+
cd "$(dirname "$0")/.."
|
|
12
|
+
log() { echo "$(date '+%Y-%m-%d %-I:%M %p') $*"; }
|
|
13
|
+
tmp=$(mktemp -d)
|
|
14
|
+
trap 'rmdir "$state/publish.lock"; rm -rf "$tmp"' EXIT
|
|
15
|
+
git archive "$head" | tar -x -C "$tmp"
|
|
16
|
+
base=$(node -p 'require("./package.json").version.split(".").slice(0,2).join(".")')
|
|
17
|
+
version="$base.$(git rev-list --count "$head")"
|
|
18
|
+
(cd "$tmp" && npm version "$version" --no-git-tag-version --allow-same-version >/dev/null)
|
|
19
|
+
log "Publishing $version"
|
|
20
|
+
"$tmp/scripts/publish" && echo "$head" > "$state/published"
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
// Adds the changelog entries for merges since the last run to
|
|
2
|
+
// content/whats-new.json. Run by scripts/autodeploy on the machine that
|
|
3
|
+
// publishes; installs only ever read the file.
|
|
4
|
+
// whats-new.ts [--since-days N] [--through-file F] [--commit] [--dry-run]
|
|
5
|
+
// --commit also commits just that file and pushes it to origin/main.
|
|
6
|
+
import { execFileSync } from "child_process";
|
|
7
|
+
import fs from "fs";
|
|
8
|
+
import path from "path";
|
|
9
|
+
import { askHaiku } from "@/lib/whats-new/claude";
|
|
10
|
+
import { generate, type PrInfo } from "@/lib/whats-new/generate";
|
|
11
|
+
import { WHATS_NEW_FILE } from "@/lib/whats-new/load";
|
|
12
|
+
import { readBlocklist } from "@/lib/whats-new/lint";
|
|
13
|
+
|
|
14
|
+
const root = process.cwd();
|
|
15
|
+
const args = process.argv.slice(2);
|
|
16
|
+
const flag = (name: string) => args.includes(name);
|
|
17
|
+
const value = (name: string) => {
|
|
18
|
+
const i = args.indexOf(name);
|
|
19
|
+
return i >= 0 ? args[i + 1] : undefined;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const run = (cmd: string, a: string[]) =>
|
|
23
|
+
execFileSync(cmd, a, { cwd: root, encoding: "utf-8", maxBuffer: 64 << 20 });
|
|
24
|
+
const git = (a: string[]) => run("git", a);
|
|
25
|
+
|
|
26
|
+
function prs(numbers: number[]): Map<number, PrInfo> {
|
|
27
|
+
const rows = JSON.parse(
|
|
28
|
+
run("gh", [
|
|
29
|
+
"pr",
|
|
30
|
+
"list",
|
|
31
|
+
"--state",
|
|
32
|
+
"merged",
|
|
33
|
+
"--limit",
|
|
34
|
+
"1000",
|
|
35
|
+
"--json",
|
|
36
|
+
"number,body,url",
|
|
37
|
+
])
|
|
38
|
+
) as { number: number; body: string; url: string }[];
|
|
39
|
+
const want = new Set(numbers);
|
|
40
|
+
return new Map(
|
|
41
|
+
rows.filter((r) => want.has(r.number)).map((r) => [r.number, r])
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function main() {
|
|
46
|
+
const throughFile = value("--through-file");
|
|
47
|
+
const commit = flag("--commit");
|
|
48
|
+
if (commit) {
|
|
49
|
+
git(["fetch", "-q", "origin", "main"]);
|
|
50
|
+
const onMain = git(["rev-parse", "--abbrev-ref", "HEAD"]).trim() === "main";
|
|
51
|
+
const level =
|
|
52
|
+
git(["rev-parse", "HEAD"]).trim() ===
|
|
53
|
+
git(["rev-parse", "origin/main"]).trim();
|
|
54
|
+
if (
|
|
55
|
+
!onMain ||
|
|
56
|
+
!level ||
|
|
57
|
+
git(["status", "--porcelain", "--untracked-files=no"]).trim()
|
|
58
|
+
) {
|
|
59
|
+
console.log("whats-new: checkout is not a clean, current main; skipped");
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
let through =
|
|
64
|
+
throughFile && fs.existsSync(throughFile)
|
|
65
|
+
? fs.readFileSync(throughFile, "utf-8").trim()
|
|
66
|
+
: null;
|
|
67
|
+
if (through) {
|
|
68
|
+
try {
|
|
69
|
+
git(["cat-file", "-e", `${through}^{commit}`]);
|
|
70
|
+
} catch {
|
|
71
|
+
through = null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const before = git(["rev-parse", "HEAD"]).trim();
|
|
75
|
+
const result = await generate(
|
|
76
|
+
root,
|
|
77
|
+
{
|
|
78
|
+
through,
|
|
79
|
+
sinceDays: Number(value("--since-days") ?? 30),
|
|
80
|
+
write: !flag("--dry-run"),
|
|
81
|
+
},
|
|
82
|
+
{ git, prs, ask: askHaiku, blocklist: readBlocklist() }
|
|
83
|
+
);
|
|
84
|
+
console.log(
|
|
85
|
+
`whats-new: ${result.added.length} added, ${result.skipped} skipped, ${result.dropped.length} dropped`
|
|
86
|
+
);
|
|
87
|
+
for (const d of result.dropped)
|
|
88
|
+
console.log(` dropped #${d.pr}: ${d.reason}`);
|
|
89
|
+
if (flag("--dry-run")) return;
|
|
90
|
+
|
|
91
|
+
if (commit && result.added.length) {
|
|
92
|
+
git(["add", WHATS_NEW_FILE]);
|
|
93
|
+
git([
|
|
94
|
+
"commit",
|
|
95
|
+
"-q",
|
|
96
|
+
"--no-verify",
|
|
97
|
+
"-m",
|
|
98
|
+
`chore(whats-new): add ${result.added.length} entries`,
|
|
99
|
+
"--",
|
|
100
|
+
WHATS_NEW_FILE,
|
|
101
|
+
]);
|
|
102
|
+
try {
|
|
103
|
+
git(["push", "-q", "origin", "HEAD:main"]);
|
|
104
|
+
} catch (e) {
|
|
105
|
+
git(["reset", "-q", "--hard", before]);
|
|
106
|
+
throw e;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (throughFile) {
|
|
110
|
+
fs.mkdirSync(path.dirname(throughFile), { recursive: true });
|
|
111
|
+
fs.writeFileSync(throughFile, `${before}\n`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
main().catch((e) => {
|
|
116
|
+
console.error(`whats-new: ${e instanceof Error ? e.message : e}`);
|
|
117
|
+
process.exit(1);
|
|
118
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { proxy } from "valtio";
|
|
2
|
+
|
|
3
|
+
// Whether the What's New dialog is open, so the footer row and the update
|
|
4
|
+
// banner can both open the one dialog. `preview` opens it showing what a
|
|
5
|
+
// waiting update would add.
|
|
6
|
+
export const whatsNewStore = proxy({ open: false, preview: false });
|
|
7
|
+
|
|
8
|
+
export const whatsNewActions = {
|
|
9
|
+
open: (preview = false) => {
|
|
10
|
+
whatsNewStore.preview = preview;
|
|
11
|
+
whatsNewStore.open = true;
|
|
12
|
+
},
|
|
13
|
+
close: () => {
|
|
14
|
+
whatsNewStore.open = false;
|
|
15
|
+
},
|
|
16
|
+
};
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { draftModels } from "./draftModels";
|
|
3
|
-
|
|
4
|
-
const qwen = { value: "ollama/qwen3", label: "Qwen 3" };
|
|
5
|
-
|
|
6
|
-
describe("draftModels", () => {
|
|
7
|
-
it("lists the default and then what OpenCode reports", () => {
|
|
8
|
-
const list = draftModels("opencode", {
|
|
9
|
-
data: [qwen],
|
|
10
|
-
isPending: false,
|
|
11
|
-
isError: false,
|
|
12
|
-
});
|
|
13
|
-
expect(list.map((m) => m.value)).toEqual(["default", "ollama/qwen3"]);
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
it("says so beside the default while loading and on failure", () => {
|
|
17
|
-
const loading = draftModels("opencode", {
|
|
18
|
-
isPending: true,
|
|
19
|
-
isError: false,
|
|
20
|
-
});
|
|
21
|
-
const failed = draftModels("opencode", { isPending: false, isError: true });
|
|
22
|
-
expect(loading).toHaveLength(1);
|
|
23
|
-
expect(loading[0].label).toContain("loading");
|
|
24
|
-
expect(failed[0].label).toContain("couldn't");
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it("keeps a fixed list as it is", () => {
|
|
28
|
-
const list = draftModels("claude", { isPending: true, isError: false });
|
|
29
|
-
expect(list.map((m) => m.value)).toContain("opus");
|
|
30
|
-
});
|
|
31
|
-
});
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import type { ChatModel } from "@/lib/chat/events";
|
|
2
|
-
import type { Draft } from "@/lib/drafts";
|
|
3
|
-
import { discoversModels, getModelOptions } from "@/lib/model-catalog";
|
|
4
|
-
|
|
5
|
-
// A fixed list, or the agent's default followed by what it reports; while it
|
|
6
|
-
// loads or if it fails, the default alone says so.
|
|
7
|
-
export function draftModels(
|
|
8
|
-
agentType: Draft["agentType"],
|
|
9
|
-
found: { data?: ChatModel[]; isPending: boolean; isError: boolean }
|
|
10
|
-
): ChatModel[] {
|
|
11
|
-
const options = getModelOptions(agentType);
|
|
12
|
-
if (!discoversModels(agentType)) return options;
|
|
13
|
-
if (found.data) return [...options, ...found.data];
|
|
14
|
-
const [first, ...rest] = options;
|
|
15
|
-
const note = found.isError ? "couldn't list models" : "loading models…";
|
|
16
|
-
return [{ ...first, label: `${first.label} (${note})` }, ...rest];
|
|
17
|
-
}
|