hive-am 1.0.0 → 1.2.0
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/CHANGELOG.md +114 -1
- package/README.md +34 -4
- package/package.json +13 -2
- package/server/dist/api.js +42 -677
- package/server/dist/connections/addressing.js +87 -0
- package/server/dist/connections/channel-skill.js +62 -0
- package/server/dist/connections/fake.js +1 -0
- package/server/dist/connections/files.js +45 -1
- package/server/dist/connections/format.js +9 -1
- package/server/dist/connections/manager.js +60 -3
- package/server/dist/connections/outbound.js +2 -1
- package/server/dist/connections/prompt.js +12 -3
- package/server/dist/connections/rate.js +20 -0
- package/server/dist/connections/router.js +55 -22
- package/server/dist/connections/secrets.js +52 -0
- package/server/dist/connections/store.js +6 -43
- package/server/dist/connections/telegram.js +105 -14
- package/server/dist/cron.js +138 -0
- package/server/dist/db/json.js +16 -0
- package/server/dist/db/migrations.js +199 -0
- package/server/dist/db.js +98 -196
- package/server/dist/errors.js +11 -0
- package/server/dist/git/browse.js +99 -0
- package/server/dist/git/ops.js +136 -19
- package/server/dist/git/repo.js +2 -1
- package/server/dist/history/claude.js +33 -20
- package/server/dist/history/index.js +17 -9
- package/server/dist/history/kiro.js +17 -12
- package/server/dist/history/lines.js +12 -0
- package/server/dist/history/opencode.js +18 -1
- package/server/dist/http/body.js +46 -0
- package/server/dist/http/errors.js +21 -0
- package/server/dist/http/guards.js +44 -0
- package/server/dist/http/router.js +24 -0
- package/server/dist/http/routes/agents.js +103 -0
- package/server/dist/http/routes/channel.js +16 -0
- package/server/dist/http/routes/colonies.js +9 -0
- package/server/dist/http/routes/connections.js +36 -0
- package/server/dist/http/routes/fs.js +22 -0
- package/server/dist/http/routes/git.js +90 -0
- package/server/dist/http/routes/meta.js +11 -0
- package/server/dist/http/routes/notebook.js +34 -0
- package/server/dist/http/routes/orchestration.js +32 -0
- package/server/dist/http/routes/resources.js +68 -0
- package/server/dist/http/routes/schedules.js +40 -0
- package/server/dist/http/routes/skills.js +47 -0
- package/server/dist/http/routes/terminals.js +18 -0
- package/server/dist/http/routes/types.js +36 -0
- package/server/dist/http/validate.js +65 -0
- package/server/dist/index.js +74 -4
- package/server/dist/instructions.js +23 -11
- package/server/dist/objects/agent.js +49 -0
- package/server/dist/objects/database.js +451 -0
- package/server/dist/objects/db-history.js +79 -0
- package/server/dist/objects/docker-info.js +75 -0
- package/server/dist/objects/docker.js +285 -0
- package/server/dist/objects/driver-props.js +196 -0
- package/server/dist/objects/exec.js +16 -0
- package/server/dist/objects/http-history.js +44 -0
- package/server/dist/objects/http-script.js +644 -0
- package/server/dist/objects/http.js +735 -0
- package/server/dist/objects/index.js +445 -0
- package/server/dist/objects/model.js +250 -0
- package/server/dist/objects/runner.js +211 -0
- package/server/dist/objects/stats.js +106 -0
- package/server/dist/objects/store.js +115 -0
- package/server/dist/objects/supervisor.js +111 -0
- package/server/dist/objects/templates.js +67 -0
- package/server/dist/providers/claude.js +5 -13
- package/server/dist/providers/index.js +7 -1
- package/server/dist/providers/kiro.js +4 -5
- package/server/dist/providers/opencode-server.js +164 -0
- package/server/dist/providers/opencode.js +60 -37
- package/server/dist/providers/preamble.js +2 -0
- package/server/dist/providers/spawn.js +31 -3
- package/server/dist/runtime.js +10 -7
- package/server/dist/schedules.js +222 -0
- package/server/dist/seed.js +15 -11
- package/server/dist/services/agents.js +90 -0
- package/server/dist/services/colonies.js +38 -0
- package/server/dist/services/connections.js +76 -0
- package/server/dist/services/notebook.js +22 -0
- package/server/dist/services/providers.js +27 -0
- package/server/dist/services/schedules.js +18 -0
- package/server/dist/sessions-summary.js +51 -0
- package/server/dist/skills/defaults.js +120 -0
- package/server/dist/skills/notebook.js +3 -8
- package/server/dist/terminals.js +177 -0
- package/server/dist/wake.js +149 -0
- package/server/dist/watch.js +201 -0
- package/server/mcp/dispatch.mjs +169 -1
- package/web/.next/BUILD_ID +1 -1
- package/web/.next/app-build-manifest.json +145 -85
- package/web/.next/app-path-routes-manifest.json +5 -2
- package/web/.next/build-manifest.json +14 -14
- package/web/.next/prerender-manifest.json +85 -37
- package/web/.next/react-loadable-manifest.json +26 -1
- package/web/.next/required-server-files.json +13 -1
- package/web/.next/routes-manifest.json +32 -0
- package/web/.next/server/app/_not-found/page.js +2 -2
- package/web/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/_not-found.html +1 -1
- package/web/.next/server/app/_not-found.rsc +12 -11
- package/web/.next/server/app/agents/[id]/page.js +4 -4
- package/web/.next/server/app/agents/[id]/page.js.nft.json +1 -1
- package/web/.next/server/app/agents/[id]/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/agents/page.js +2 -2
- package/web/.next/server/app/agents/page.js.nft.json +1 -1
- package/web/.next/server/app/agents/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/agents.html +1 -1
- package/web/.next/server/app/agents.rsc +14 -13
- package/web/.next/server/app/connections/page.js +2 -2
- package/web/.next/server/app/connections/page.js.nft.json +1 -1
- package/web/.next/server/app/connections/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/connections.html +1 -1
- package/web/.next/server/app/connections.rsc +14 -13
- package/web/.next/server/app/index.html +1 -1
- package/web/.next/server/app/index.rsc +14 -13
- package/web/.next/server/app/page.js +2 -2
- package/web/.next/server/app/page.js.nft.json +1 -1
- package/web/.next/server/app/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/relations/page.js +2 -2
- package/web/.next/server/app/relations/page.js.nft.json +1 -1
- package/web/.next/server/app/relations/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/relations.html +1 -1
- package/web/.next/server/app/relations.rsc +14 -13
- package/web/.next/server/app/resources/[id]/page.js +2 -0
- package/web/.next/server/app/resources/[id]/page.js.nft.json +1 -0
- package/web/.next/server/app/resources/[id]/page_client-reference-manifest.js +1 -0
- package/web/.next/server/app/resources/page.js +2 -0
- package/web/.next/server/app/resources/page.js.nft.json +1 -0
- package/web/.next/server/app/resources/page_client-reference-manifest.js +1 -0
- package/web/.next/server/app/resources.html +1 -0
- package/web/.next/server/app/resources.meta +15 -0
- package/web/.next/server/app/resources.rsc +25 -0
- package/web/.next/server/app/schedules/page.js +2 -0
- package/web/.next/server/app/schedules/page.js.nft.json +1 -0
- package/web/.next/server/app/schedules/page_client-reference-manifest.js +1 -0
- package/web/.next/server/app/schedules.html +1 -0
- package/web/.next/server/app/schedules.meta +15 -0
- package/web/.next/server/app/schedules.rsc +25 -0
- package/web/.next/server/app/sessions/page.js +2 -2
- package/web/.next/server/app/sessions/page.js.nft.json +1 -1
- package/web/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/sessions.html +1 -1
- package/web/.next/server/app/sessions.rsc +14 -13
- package/web/.next/server/app/skills/page.js +2 -2
- package/web/.next/server/app/skills/page.js.nft.json +1 -1
- package/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/skills.html +1 -1
- package/web/.next/server/app/skills.rsc +14 -13
- package/web/.next/server/app/types/page.js +2 -2
- package/web/.next/server/app/types/page.js.nft.json +1 -1
- package/web/.next/server/app/types/page_client-reference-manifest.js +1 -1
- package/web/.next/server/app/types.html +1 -1
- package/web/.next/server/app/types.rsc +14 -13
- package/web/.next/server/app-paths-manifest.json +5 -2
- package/web/.next/server/chunks/143.js +1 -1
- package/web/.next/server/chunks/274.js +1 -0
- package/web/.next/server/chunks/306.js +1 -0
- package/web/.next/server/chunks/423.js +1 -0
- package/web/.next/server/chunks/505.js +6 -0
- package/web/.next/server/chunks/555.js +1 -0
- package/web/.next/server/chunks/612.js +1 -0
- package/web/.next/server/chunks/638.js +1 -0
- package/web/.next/server/chunks/692.js +18 -0
- package/web/.next/server/chunks/716.js +2 -0
- package/web/.next/server/chunks/790.js +11 -0
- package/web/.next/server/chunks/802.js +1 -0
- package/web/.next/server/chunks/890.js +1 -1
- package/web/.next/server/chunks/919.js +1 -0
- package/web/.next/server/chunks/950.js +6 -0
- package/web/.next/server/middleware-build-manifest.js +1 -1
- package/web/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/web/.next/server/next-font-manifest.js +1 -1
- package/web/.next/server/next-font-manifest.json +1 -1
- package/web/.next/server/pages/404.html +1 -1
- package/web/.next/server/pages/500.html +1 -1
- package/web/.next/server/server-reference-manifest.json +1 -1
- package/web/.next/static/chunks/0937d497-114a2a3de515565e.js +1 -0
- package/web/.next/static/chunks/176-aeaf907a6ea5627d.js +1 -0
- package/web/.next/static/chunks/291-efae53ea5f7ee92d.js +1 -0
- package/web/.next/static/chunks/2b9ff8ee-969e65cc354b2150.js +1 -0
- package/web/.next/static/chunks/30-9adcbe92b15a6815.js +1 -0
- package/web/.next/static/chunks/316-26da9cc233ba7557.js +1 -0
- package/web/.next/static/chunks/350-56599dc3bf06066d.js +1 -0
- package/web/.next/static/chunks/486-13e08542f3a1cbac.js +1 -0
- package/web/.next/static/chunks/491-6b4e6201f5110dff.js +1 -0
- package/web/.next/static/chunks/517-ccaf8cc5c733cbd8.js +1 -0
- package/web/.next/static/chunks/545.bfbd92b7632c09de.js +1 -0
- package/web/.next/static/chunks/{805-d946ebedb7e85f73.js → 562-7c2e33d838e869bf.js} +1 -1
- package/web/.next/static/chunks/581-3ad5c4343dad3f50.js +1 -0
- package/web/.next/static/chunks/586-f7017d585a84738d.js +1 -0
- package/web/.next/static/chunks/657.b4c39e676c3e20dc.js +1 -0
- package/web/.next/static/chunks/701.6abcaf5e60463eef.js +6 -0
- package/web/.next/static/chunks/705-a16e1fe09be1ac7f.js +1 -0
- package/web/.next/static/chunks/706-fa2be0f10ffebe6a.js +1 -0
- package/web/.next/static/chunks/837-33c7480524f48577.js +1 -0
- package/web/.next/static/chunks/839-23c5508622fae942.js +1 -0
- package/web/.next/static/chunks/844-fe55292e0701f436.js +1 -0
- package/web/.next/static/chunks/89-b194fcffe5120c44.js +1 -0
- package/web/.next/static/chunks/97.594543c2196de524.js +1 -0
- package/web/.next/static/chunks/app/_not-found/page-238a7b80bda6b4ae.js +1 -0
- package/web/.next/static/chunks/app/agents/[id]/page-4af6c246c4ca7a97.js +1 -0
- package/web/.next/static/chunks/app/agents/page-f4c4c80f955f3967.js +1 -0
- package/web/.next/static/chunks/app/connections/page-0936beaf02d863c6.js +1 -0
- package/web/.next/static/chunks/app/layout-87ebb70bd9cb8443.js +1 -0
- package/web/.next/static/chunks/app/page-a7f292117b7d76bf.js +1 -0
- package/web/.next/static/chunks/app/relations/page-fba25393c8688714.js +1 -0
- package/web/.next/static/chunks/app/resources/[id]/page-d110d89bb8b5fca7.js +1 -0
- package/web/.next/static/chunks/app/resources/page-bbd0b450d3f3131d.js +1 -0
- package/web/.next/static/chunks/app/schedules/page-a1124397fc760413.js +1 -0
- package/web/.next/static/chunks/app/sessions/page-0e322811b0cde724.js +1 -0
- package/web/.next/static/chunks/app/skills/page-22ea0ef89d9f7a13.js +1 -0
- package/web/.next/static/chunks/app/types/page-18c4e1db2724d41d.js +1 -0
- package/web/.next/static/chunks/fcafe1ac.366e4950d3b0d4b7.js +18 -0
- package/web/.next/static/chunks/framework-bbac72adbbe2d328.js +1 -0
- package/web/.next/static/chunks/main-app-6086068e5fb5eacd.js +1 -0
- package/web/.next/static/chunks/main-c15f3fee674e0303.js +1 -0
- package/web/.next/static/chunks/pages/_app-d1670a2c6ce392fc.js +1 -0
- package/web/.next/static/chunks/pages/_error-95aac014d202fa7a.js +1 -0
- package/web/.next/static/chunks/webpack-ef515e41a83a16ac.js +1 -0
- package/web/.next/static/css/945795da867f5250.css +1 -0
- package/web/.next/static/css/dd4e32c285f75776.css +1 -0
- package/web/.next/static/p4S3i9kvO359GW91DQiHu/_buildManifest.js +1 -0
- package/web/.next/trace +29 -23
- package/web/.next/types/app/resources/[id]/page.ts +84 -0
- package/web/.next/types/app/resources/page.ts +84 -0
- package/web/.next/types/app/schedules/page.ts +84 -0
- package/web/.next/types/routes.d.ts +7 -2
- package/web/.next/types/validator.ts +27 -0
- package/web/next.config.mjs +4 -0
- package/web/.next/server/chunks/149.js +0 -11
- package/web/.next/server/chunks/247.js +0 -1
- package/web/.next/server/chunks/851.js +0 -1
- package/web/.next/server/chunks/932.js +0 -2
- package/web/.next/static/84BpRSahg2BTwk3mX6AG8/_buildManifest.js +0 -1
- package/web/.next/static/chunks/0937d497-44abb0f9355ace92.js +0 -1
- package/web/.next/static/chunks/2b9ff8ee-c3b61ac1e608c57d.js +0 -1
- package/web/.next/static/chunks/351-65da8c1a6e25f93b.js +0 -1
- package/web/.next/static/chunks/463-6ae35cdefdc59169.js +0 -1
- package/web/.next/static/chunks/493-92266798be872b8f.js +0 -1
- package/web/.next/static/chunks/517-eb26c72f6059aa26.js +0 -1
- package/web/.next/static/chunks/657.f1e03b7b65eba1d9.js +0 -1
- package/web/.next/static/chunks/687-3a4205f51be38602.js +0 -1
- package/web/.next/static/chunks/837-fd7623601dc78ed8.js +0 -1
- package/web/.next/static/chunks/850-318f32484989dd59.js +0 -1
- package/web/.next/static/chunks/89-ace2e1aa63babfdf.js +0 -1
- package/web/.next/static/chunks/951-e250b541b67da7ce.js +0 -1
- package/web/.next/static/chunks/app/_not-found/page-bbd54d7b9a55d7ae.js +0 -1
- package/web/.next/static/chunks/app/agents/[id]/page-5eaf094fec246f1b.js +0 -1
- package/web/.next/static/chunks/app/agents/page-e9b17f1c3439ef98.js +0 -1
- package/web/.next/static/chunks/app/connections/page-c6afd90abd82d47a.js +0 -1
- package/web/.next/static/chunks/app/layout-c415d5a847cd94b3.js +0 -1
- package/web/.next/static/chunks/app/page-6fc87fc5a128a204.js +0 -1
- package/web/.next/static/chunks/app/relations/page-0355a93a8eb67989.js +0 -1
- package/web/.next/static/chunks/app/sessions/page-beae9a74a07d69e0.js +0 -1
- package/web/.next/static/chunks/app/skills/page-4f839ac913e26d32.js +0 -1
- package/web/.next/static/chunks/app/types/page-4f6b99af3232f861.js +0 -1
- package/web/.next/static/chunks/framework-0e68884e76d9a8d3.js +0 -1
- package/web/.next/static/chunks/main-app-d6f73ccd41f325c5.js +0 -1
- package/web/.next/static/chunks/main-cd22509db15764e2.js +0 -1
- package/web/.next/static/chunks/pages/_app-0f75d689c738a6fc.js +0 -1
- package/web/.next/static/chunks/pages/_error-d481f6d49fb4455d.js +0 -1
- package/web/.next/static/chunks/webpack-d40de72e52f65f79.js +0 -1
- package/web/.next/static/css/abe1a6216491f944.css +0 -1
- /package/web/.next/static/{84BpRSahg2BTwk3mX6AG8 → p4S3i9kvO359GW91DQiHu}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
const columns = (db, table) => db.prepare(`PRAGMA table_info(${table})`).all().map((c) => c.name);
|
|
2
|
+
/** ALTER TABLE ... ADD COLUMN, skipped when the column is already there (`def` is the column name followed by its type). */
|
|
3
|
+
export function addColumn(db, table, def) {
|
|
4
|
+
const name = def.split(/\s+/)[0];
|
|
5
|
+
if (columns(db, table).includes(name))
|
|
6
|
+
return false;
|
|
7
|
+
db.exec(`ALTER TABLE ${table} ADD COLUMN ${def}`);
|
|
8
|
+
return true;
|
|
9
|
+
}
|
|
10
|
+
export const MIGRATIONS = [
|
|
11
|
+
{
|
|
12
|
+
version: 1, name: 'baseline',
|
|
13
|
+
up(db) {
|
|
14
|
+
db.exec(`
|
|
15
|
+
CREATE TABLE IF NOT EXISTS skills (
|
|
16
|
+
id TEXT PRIMARY KEY, name TEXT NOT NULL UNIQUE, description TEXT NOT NULL DEFAULT '',
|
|
17
|
+
content TEXT NOT NULL DEFAULT '', created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL
|
|
18
|
+
);
|
|
19
|
+
CREATE TABLE IF NOT EXISTS agent_types (
|
|
20
|
+
id TEXT PRIMARY KEY, name TEXT NOT NULL UNIQUE, description TEXT NOT NULL DEFAULT '',
|
|
21
|
+
role TEXT NOT NULL, provider TEXT NOT NULL, model TEXT NOT NULL DEFAULT '',
|
|
22
|
+
system_prompt TEXT NOT NULL DEFAULT '', permission TEXT NOT NULL DEFAULT 'acceptEdits',
|
|
23
|
+
color TEXT NOT NULL DEFAULT '', created_at INTEGER NOT NULL
|
|
24
|
+
);
|
|
25
|
+
CREATE TABLE IF NOT EXISTS type_skills (
|
|
26
|
+
type_id TEXT NOT NULL REFERENCES agent_types(id) ON DELETE CASCADE,
|
|
27
|
+
skill_id TEXT NOT NULL REFERENCES skills(id) ON DELETE CASCADE,
|
|
28
|
+
PRIMARY KEY (type_id, skill_id)
|
|
29
|
+
);
|
|
30
|
+
CREATE TABLE IF NOT EXISTS agents (
|
|
31
|
+
id TEXT PRIMARY KEY, name TEXT NOT NULL UNIQUE, description TEXT NOT NULL DEFAULT '',
|
|
32
|
+
role TEXT NOT NULL, type_id TEXT REFERENCES agent_types(id) ON DELETE SET NULL,
|
|
33
|
+
provider TEXT NOT NULL, model TEXT NOT NULL DEFAULT '', system_prompt TEXT NOT NULL DEFAULT '',
|
|
34
|
+
permission TEXT NOT NULL DEFAULT 'acceptEdits', cwd TEXT NOT NULL,
|
|
35
|
+
session_id TEXT, status TEXT NOT NULL DEFAULT 'idle',
|
|
36
|
+
created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL
|
|
37
|
+
);
|
|
38
|
+
CREATE TABLE IF NOT EXISTS agent_skills (
|
|
39
|
+
agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
|
|
40
|
+
skill_id TEXT NOT NULL REFERENCES skills(id) ON DELETE CASCADE,
|
|
41
|
+
PRIMARY KEY (agent_id, skill_id)
|
|
42
|
+
);
|
|
43
|
+
CREATE TABLE IF NOT EXISTS assignments (
|
|
44
|
+
orchestrator_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
|
|
45
|
+
worker_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
|
|
46
|
+
PRIMARY KEY (orchestrator_id, worker_id)
|
|
47
|
+
);
|
|
48
|
+
CREATE TABLE IF NOT EXISTS colonies (
|
|
49
|
+
id TEXT PRIMARY KEY, name TEXT NOT NULL UNIQUE, color TEXT NOT NULL DEFAULT '', cwd TEXT NOT NULL DEFAULT '',
|
|
50
|
+
permission TEXT NOT NULL DEFAULT 'acceptEdits', system_prompt TEXT NOT NULL DEFAULT '',
|
|
51
|
+
inherit TEXT NOT NULL DEFAULT '{}', created_at INTEGER NOT NULL
|
|
52
|
+
);
|
|
53
|
+
CREATE TABLE IF NOT EXISTS colony_skills (
|
|
54
|
+
colony_id TEXT NOT NULL REFERENCES colonies(id) ON DELETE CASCADE,
|
|
55
|
+
skill_id TEXT NOT NULL REFERENCES skills(id) ON DELETE CASCADE,
|
|
56
|
+
PRIMARY KEY (colony_id, skill_id)
|
|
57
|
+
);
|
|
58
|
+
-- Pointer log of every native session an agent has ever used (for the Sessions view).
|
|
59
|
+
CREATE TABLE IF NOT EXISTS agent_sessions (
|
|
60
|
+
agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
|
|
61
|
+
provider TEXT NOT NULL, session_id TEXT NOT NULL, cwd TEXT NOT NULL,
|
|
62
|
+
first_seen INTEGER NOT NULL, last_seen INTEGER NOT NULL,
|
|
63
|
+
PRIMARY KEY (agent_id, session_id)
|
|
64
|
+
);
|
|
65
|
+
-- Dispatch log: who asked whom to do what. Results live in the worker's own session.
|
|
66
|
+
CREATE TABLE IF NOT EXISTS dispatches (
|
|
67
|
+
id TEXT PRIMARY KEY, from_id TEXT NOT NULL, to_id TEXT NOT NULL, task TEXT NOT NULL,
|
|
68
|
+
status TEXT NOT NULL, result TEXT, created_at INTEGER NOT NULL, finished_at INTEGER
|
|
69
|
+
);
|
|
70
|
+
CREATE TABLE IF NOT EXISTS seeded_skills (slug TEXT PRIMARY KEY);
|
|
71
|
+
|
|
72
|
+
-- Channels (Telegram...). Known threads: where to send a reply and when it was last active.
|
|
73
|
+
CREATE TABLE IF NOT EXISTS connections (
|
|
74
|
+
id TEXT PRIMARY KEY, kind TEXT NOT NULL, name TEXT NOT NULL UNIQUE,
|
|
75
|
+
agent_id TEXT REFERENCES agents(id) ON DELETE SET NULL,
|
|
76
|
+
config TEXT NOT NULL DEFAULT '{}', allowed TEXT NOT NULL DEFAULT '[]',
|
|
77
|
+
enabled INTEGER NOT NULL DEFAULT 1, created_at INTEGER NOT NULL
|
|
78
|
+
);
|
|
79
|
+
CREATE TABLE IF NOT EXISTS threads (
|
|
80
|
+
id TEXT PRIMARY KEY, connection_id TEXT NOT NULL REFERENCES connections(id) ON DELETE CASCADE,
|
|
81
|
+
external_key TEXT NOT NULL, title TEXT NOT NULL DEFAULT '', target TEXT NOT NULL, last_user TEXT,
|
|
82
|
+
created_at INTEGER NOT NULL, last_activity INTEGER NOT NULL,
|
|
83
|
+
UNIQUE (connection_id, external_key)
|
|
84
|
+
);
|
|
85
|
+
CREATE TABLE IF NOT EXISTS thread_messages (
|
|
86
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT, thread_id TEXT NOT NULL REFERENCES threads(id) ON DELETE CASCADE,
|
|
87
|
+
direction TEXT NOT NULL, external_id TEXT NOT NULL, user_id TEXT, user_name TEXT,
|
|
88
|
+
text TEXT NOT NULL, ts INTEGER NOT NULL,
|
|
89
|
+
UNIQUE (thread_id, direction, external_id)
|
|
90
|
+
);
|
|
91
|
+
CREATE TABLE IF NOT EXISTS connection_cursor (
|
|
92
|
+
connection_id TEXT PRIMARY KEY REFERENCES connections(id) ON DELETE CASCADE, value TEXT NOT NULL
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
-- Wake-ups, schedules and process watches (what an agent asks hive-am to do later).
|
|
96
|
+
DROP TABLE IF EXISTS wakeups;
|
|
97
|
+
CREATE TABLE IF NOT EXISTS agent_wakeups (
|
|
98
|
+
id TEXT PRIMARY KEY, agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
|
|
99
|
+
connection_id TEXT, thread_id TEXT REFERENCES threads(id) ON DELETE CASCADE,
|
|
100
|
+
due_at INTEGER NOT NULL, note TEXT NOT NULL, created_at INTEGER NOT NULL
|
|
101
|
+
);
|
|
102
|
+
CREATE TABLE IF NOT EXISTS agent_schedules (
|
|
103
|
+
id TEXT PRIMARY KEY, agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
|
|
104
|
+
connection_id TEXT, thread_id TEXT REFERENCES threads(id) ON DELETE CASCADE,
|
|
105
|
+
kind TEXT NOT NULL, expr TEXT NOT NULL, tz TEXT NOT NULL, note TEXT NOT NULL,
|
|
106
|
+
enabled INTEGER NOT NULL DEFAULT 1, next_due INTEGER, created_at INTEGER NOT NULL,
|
|
107
|
+
last_fired_at INTEGER, fire_count INTEGER NOT NULL DEFAULT 0, last_status TEXT, last_detail TEXT
|
|
108
|
+
);
|
|
109
|
+
CREATE TABLE IF NOT EXISTS schedule_runs (
|
|
110
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT, schedule_id TEXT NOT NULL REFERENCES agent_schedules(id) ON DELETE CASCADE,
|
|
111
|
+
fired_at INTEGER NOT NULL, status TEXT NOT NULL, detail TEXT, duration_ms INTEGER
|
|
112
|
+
);
|
|
113
|
+
CREATE INDEX IF NOT EXISTS idx_schedule_runs ON schedule_runs (schedule_id, id);
|
|
114
|
+
CREATE TABLE IF NOT EXISTS agent_watches (
|
|
115
|
+
id TEXT PRIMARY KEY, agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
|
|
116
|
+
connection_id TEXT, thread_id TEXT REFERENCES threads(id) ON DELETE CASCADE,
|
|
117
|
+
pid INTEGER NOT NULL, pid_stamp TEXT, file TEXT, log TEXT, note TEXT NOT NULL,
|
|
118
|
+
started_at INTEGER NOT NULL, deadline INTEGER NOT NULL, req_id TEXT, req_name TEXT
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
-- The agent's own notebook, and the resources of a colony.
|
|
122
|
+
CREATE TABLE IF NOT EXISTS agent_notebooks (
|
|
123
|
+
agent_id TEXT PRIMARY KEY REFERENCES agents(id) ON DELETE CASCADE,
|
|
124
|
+
content TEXT NOT NULL DEFAULT '', version INTEGER NOT NULL DEFAULT 0, seen INTEGER NOT NULL DEFAULT 0,
|
|
125
|
+
updated_at INTEGER NOT NULL DEFAULT 0, updated_by TEXT NOT NULL DEFAULT ''
|
|
126
|
+
);
|
|
127
|
+
CREATE TABLE IF NOT EXISTS colony_objects (
|
|
128
|
+
id TEXT PRIMARY KEY, colony_id TEXT REFERENCES colonies(id) ON DELETE SET NULL,
|
|
129
|
+
kind TEXT NOT NULL, name TEXT NOT NULL UNIQUE COLLATE NOCASE, config TEXT NOT NULL,
|
|
130
|
+
created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL
|
|
131
|
+
);
|
|
132
|
+
-- The agent chosen to look after a resource (one per resource; the table keeps its first version's shape).
|
|
133
|
+
CREATE TABLE IF NOT EXISTS object_agents (
|
|
134
|
+
object_id TEXT NOT NULL REFERENCES colony_objects(id) ON DELETE CASCADE,
|
|
135
|
+
agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
|
|
136
|
+
PRIMARY KEY (object_id, agent_id)
|
|
137
|
+
);`);
|
|
138
|
+
// Columns added by later builds: databases created before them get them here.
|
|
139
|
+
addColumn(db, 'agents', 'colony_id TEXT REFERENCES colonies(id) ON DELETE SET NULL');
|
|
140
|
+
addColumn(db, 'agents', "overrides TEXT NOT NULL DEFAULT '[]'");
|
|
141
|
+
addColumn(db, 'agents', 'session_cwd TEXT');
|
|
142
|
+
addColumn(db, 'agents', 'instr_hash TEXT');
|
|
143
|
+
addColumn(db, 'agent_sessions', "kind TEXT NOT NULL DEFAULT 'direct'");
|
|
144
|
+
addColumn(db, 'agent_sessions', 'from_id TEXT');
|
|
145
|
+
addColumn(db, 'agent_sessions', 'task TEXT');
|
|
146
|
+
addColumn(db, 'dispatches', 'session_id TEXT');
|
|
147
|
+
addColumn(db, 'agent_wakeups', 'req_id TEXT');
|
|
148
|
+
addColumn(db, 'agent_wakeups', 'req_name TEXT');
|
|
149
|
+
addColumn(db, 'agent_schedules', 'req_id TEXT');
|
|
150
|
+
addColumn(db, 'agent_schedules', 'req_name TEXT');
|
|
151
|
+
addColumn(db, 'threads', 'muted INTEGER NOT NULL DEFAULT 0');
|
|
152
|
+
addColumn(db, 'threads', 'seen_id INTEGER NOT NULL DEFAULT 0');
|
|
153
|
+
addColumn(db, 'thread_messages', 'files TEXT');
|
|
154
|
+
// Skills briefly had a "default/personal" split; all skills are equal now.
|
|
155
|
+
for (const col of ['kind', 'slug']) {
|
|
156
|
+
if (columns(db, 'skills').includes(col)) {
|
|
157
|
+
try {
|
|
158
|
+
db.exec(`ALTER TABLE skills DROP COLUMN ${col}`);
|
|
159
|
+
}
|
|
160
|
+
catch { /* an old SQLite that cannot drop it: the column is harmless */ }
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
// How a skill reaches the agent: `always` puts all its text in the instructions; `on_demand` lists name + description and the agent reads the text with `skill_read`.
|
|
164
|
+
if (addColumn(db, 'skills', "load TEXT NOT NULL DEFAULT 'always'")) {
|
|
165
|
+
// The skills that came with hive-am are long, situational guides (the notebook one must always be in view).
|
|
166
|
+
db.exec("UPDATE skills SET load='on_demand' WHERE id LIKE 'default-%' AND id<>'default-notebook'");
|
|
167
|
+
}
|
|
168
|
+
// How each assignment loads the skill (Skill.load is only the suggestion offered when assigning).
|
|
169
|
+
for (const table of ['type_skills', 'colony_skills', 'agent_skills']) {
|
|
170
|
+
if (!addColumn(db, table, "load TEXT NOT NULL DEFAULT 'always'"))
|
|
171
|
+
continue;
|
|
172
|
+
db.exec(`UPDATE ${table} SET load=COALESCE((SELECT load FROM skills WHERE skills.id=${table}.skill_id), 'always')`);
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
version: 2, name: 'renames',
|
|
178
|
+
up(db) {
|
|
179
|
+
// The first name of a cluster was `boss`.
|
|
180
|
+
db.exec("UPDATE colony_objects SET kind='cluster' WHERE kind='boss'");
|
|
181
|
+
// The section "Objects" became "Resources": the skill that came with hive-am follows, unless the person already renamed it.
|
|
182
|
+
db.exec("UPDATE skills SET name='Colony resources' WHERE id='default-objects' AND name='Colony objects' AND NOT EXISTS (SELECT 1 FROM skills WHERE lower(name)='colony resources')");
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
];
|
|
186
|
+
export const SCHEMA_VERSION = MIGRATIONS[MIGRATIONS.length - 1].version;
|
|
187
|
+
/** Brings a database up to the current schema. Returns the versions it applied (empty when it was already current). */
|
|
188
|
+
export function migrate(db, migrations = MIGRATIONS) {
|
|
189
|
+
const applied = [];
|
|
190
|
+
let current = db.pragma('user_version', { simple: true });
|
|
191
|
+
for (const m of migrations) {
|
|
192
|
+
if (m.version <= current)
|
|
193
|
+
continue;
|
|
194
|
+
db.transaction(() => { m.up(db); db.pragma(`user_version = ${m.version}`); })();
|
|
195
|
+
current = m.version;
|
|
196
|
+
applied.push(m.version);
|
|
197
|
+
}
|
|
198
|
+
return applied;
|
|
199
|
+
}
|
package/server/dist/db.js
CHANGED
|
@@ -3,118 +3,28 @@ import { mkdirSync } from 'node:fs';
|
|
|
3
3
|
import { homedir } from 'node:os';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
5
|
import { randomUUID } from 'node:crypto';
|
|
6
|
+
import { migrate } from './db/migrations.js';
|
|
7
|
+
import { parseJson } from './db/json.js';
|
|
6
8
|
export const DATA_DIR = process.env.HIVE_AM_HOME ?? join(homedir(), '.hive-am');
|
|
7
9
|
mkdirSync(DATA_DIR, { recursive: true });
|
|
8
10
|
export const db = new Database(join(DATA_DIR, 'hive-am.db'));
|
|
9
11
|
db.pragma('journal_mode = WAL');
|
|
10
12
|
db.pragma('foreign_keys = ON');
|
|
11
|
-
db.
|
|
12
|
-
|
|
13
|
-
id TEXT PRIMARY KEY, name TEXT NOT NULL UNIQUE, description TEXT NOT NULL DEFAULT '',
|
|
14
|
-
content TEXT NOT NULL DEFAULT '', created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL
|
|
15
|
-
);
|
|
16
|
-
CREATE TABLE IF NOT EXISTS agent_types (
|
|
17
|
-
id TEXT PRIMARY KEY, name TEXT NOT NULL UNIQUE, description TEXT NOT NULL DEFAULT '',
|
|
18
|
-
role TEXT NOT NULL, provider TEXT NOT NULL, model TEXT NOT NULL DEFAULT '',
|
|
19
|
-
system_prompt TEXT NOT NULL DEFAULT '', permission TEXT NOT NULL DEFAULT 'acceptEdits',
|
|
20
|
-
color TEXT NOT NULL DEFAULT '', created_at INTEGER NOT NULL
|
|
21
|
-
);
|
|
22
|
-
CREATE TABLE IF NOT EXISTS type_skills (
|
|
23
|
-
type_id TEXT NOT NULL REFERENCES agent_types(id) ON DELETE CASCADE,
|
|
24
|
-
skill_id TEXT NOT NULL REFERENCES skills(id) ON DELETE CASCADE,
|
|
25
|
-
PRIMARY KEY (type_id, skill_id)
|
|
26
|
-
);
|
|
27
|
-
CREATE TABLE IF NOT EXISTS agents (
|
|
28
|
-
id TEXT PRIMARY KEY, name TEXT NOT NULL UNIQUE, description TEXT NOT NULL DEFAULT '',
|
|
29
|
-
role TEXT NOT NULL, type_id TEXT REFERENCES agent_types(id) ON DELETE SET NULL,
|
|
30
|
-
provider TEXT NOT NULL, model TEXT NOT NULL DEFAULT '', system_prompt TEXT NOT NULL DEFAULT '',
|
|
31
|
-
permission TEXT NOT NULL DEFAULT 'acceptEdits', cwd TEXT NOT NULL,
|
|
32
|
-
session_id TEXT, status TEXT NOT NULL DEFAULT 'idle',
|
|
33
|
-
created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL
|
|
34
|
-
);
|
|
35
|
-
CREATE TABLE IF NOT EXISTS agent_skills (
|
|
36
|
-
agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
|
|
37
|
-
skill_id TEXT NOT NULL REFERENCES skills(id) ON DELETE CASCADE,
|
|
38
|
-
PRIMARY KEY (agent_id, skill_id)
|
|
39
|
-
);
|
|
40
|
-
CREATE TABLE IF NOT EXISTS assignments (
|
|
41
|
-
orchestrator_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
|
|
42
|
-
worker_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
|
|
43
|
-
PRIMARY KEY (orchestrator_id, worker_id)
|
|
44
|
-
);
|
|
45
|
-
CREATE TABLE IF NOT EXISTS colonies (
|
|
46
|
-
id TEXT PRIMARY KEY, name TEXT NOT NULL UNIQUE, color TEXT NOT NULL DEFAULT '', cwd TEXT NOT NULL DEFAULT '',
|
|
47
|
-
permission TEXT NOT NULL DEFAULT 'acceptEdits', system_prompt TEXT NOT NULL DEFAULT '',
|
|
48
|
-
inherit TEXT NOT NULL DEFAULT '{}', created_at INTEGER NOT NULL
|
|
49
|
-
);
|
|
50
|
-
CREATE TABLE IF NOT EXISTS colony_skills (
|
|
51
|
-
colony_id TEXT NOT NULL REFERENCES colonies(id) ON DELETE CASCADE,
|
|
52
|
-
skill_id TEXT NOT NULL REFERENCES skills(id) ON DELETE CASCADE,
|
|
53
|
-
PRIMARY KEY (colony_id, skill_id)
|
|
54
|
-
);
|
|
55
|
-
-- Pointer log of every native session an agent has ever used (for the Sessions view).
|
|
56
|
-
CREATE TABLE IF NOT EXISTS agent_sessions (
|
|
57
|
-
agent_id TEXT NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
|
|
58
|
-
provider TEXT NOT NULL, session_id TEXT NOT NULL, cwd TEXT NOT NULL,
|
|
59
|
-
first_seen INTEGER NOT NULL, last_seen INTEGER NOT NULL,
|
|
60
|
-
PRIMARY KEY (agent_id, session_id)
|
|
61
|
-
);
|
|
62
|
-
-- Dispatch log: who asked whom to do what. Results live in the worker's own session.
|
|
63
|
-
CREATE TABLE IF NOT EXISTS dispatches (
|
|
64
|
-
id TEXT PRIMARY KEY, from_id TEXT NOT NULL, to_id TEXT NOT NULL, task TEXT NOT NULL,
|
|
65
|
-
status TEXT NOT NULL, result TEXT, created_at INTEGER NOT NULL, finished_at INTEGER
|
|
66
|
-
);
|
|
67
|
-
`);
|
|
68
|
-
// Lightweight migrations for databases created before colonies existed.
|
|
69
|
-
const agentCols = db.prepare('PRAGMA table_info(agents)').all().map((c) => c.name);
|
|
70
|
-
if (!agentCols.includes('colony_id'))
|
|
71
|
-
db.exec('ALTER TABLE agents ADD COLUMN colony_id TEXT REFERENCES colonies(id) ON DELETE SET NULL');
|
|
72
|
-
if (!agentCols.includes('overrides'))
|
|
73
|
-
db.exec("ALTER TABLE agents ADD COLUMN overrides TEXT NOT NULL DEFAULT '[]'");
|
|
74
|
-
if (!agentCols.includes('session_cwd'))
|
|
75
|
-
db.exec('ALTER TABLE agents ADD COLUMN session_cwd TEXT');
|
|
76
|
-
if (!agentCols.includes('instr_hash'))
|
|
77
|
-
db.exec('ALTER TABLE agents ADD COLUMN instr_hash TEXT');
|
|
78
|
-
// Skills briefly had a "default/personal" split; all skills are equal now.
|
|
79
|
-
for (const col of ['kind', 'slug']) {
|
|
80
|
-
try {
|
|
81
|
-
db.exec(`ALTER TABLE skills DROP COLUMN ${col}`);
|
|
82
|
-
}
|
|
83
|
-
catch { /* already gone */ }
|
|
84
|
-
}
|
|
85
|
-
db.exec('CREATE TABLE IF NOT EXISTS seeded_skills (slug TEXT PRIMARY KEY)');
|
|
86
|
-
// How a skill reaches the agent: `always` puts all its text in the instructions; `on_demand` lists name + description and the agent reads the text with `skill_read` when a task needs it.
|
|
87
|
-
if (!db.prepare('PRAGMA table_info(skills)').all().some((c) => c.name === 'load')) {
|
|
88
|
-
db.exec("ALTER TABLE skills ADD COLUMN load TEXT NOT NULL DEFAULT 'always'");
|
|
89
|
-
// The skills that came with hive-am are long, situational guides (the notebook one must always be in view).
|
|
90
|
-
db.exec("UPDATE skills SET load='on_demand' WHERE id LIKE 'default-%' AND id<>'default-notebook'");
|
|
91
|
-
}
|
|
92
|
-
// How each assignment loads the skill (see Skill.load, which is only the suggestion offered when assigning).
|
|
93
|
-
for (const [table] of [['type_skills'], ['colony_skills'], ['agent_skills']]) {
|
|
94
|
-
if (db.prepare(`PRAGMA table_info(${table})`).all().some((c) => c.name === 'load'))
|
|
95
|
-
continue;
|
|
96
|
-
db.exec(`ALTER TABLE ${table} ADD COLUMN load TEXT NOT NULL DEFAULT 'always'`);
|
|
97
|
-
db.exec(`UPDATE ${table} SET load=COALESCE((SELECT load FROM skills WHERE skills.id=${table}.skill_id), 'always')`);
|
|
98
|
-
}
|
|
99
|
-
const sessCols = db.prepare('PRAGMA table_info(agent_sessions)').all().map((c) => c.name);
|
|
100
|
-
if (!sessCols.includes('kind'))
|
|
101
|
-
db.exec("ALTER TABLE agent_sessions ADD COLUMN kind TEXT NOT NULL DEFAULT 'direct'");
|
|
102
|
-
if (!sessCols.includes('from_id'))
|
|
103
|
-
db.exec('ALTER TABLE agent_sessions ADD COLUMN from_id TEXT');
|
|
104
|
-
if (!sessCols.includes('task'))
|
|
105
|
-
db.exec('ALTER TABLE agent_sessions ADD COLUMN task TEXT');
|
|
106
|
-
const dispCols = db.prepare('PRAGMA table_info(dispatches)').all().map((c) => c.name);
|
|
107
|
-
if (!dispCols.includes('session_id'))
|
|
108
|
-
db.exec('ALTER TABLE dispatches ADD COLUMN session_id TEXT');
|
|
13
|
+
// Every table and column is created by the numbered migrations (db/migrations.ts); modules that own a table import `db` from here, so it is ready by then.
|
|
14
|
+
migrate(db);
|
|
109
15
|
const now = () => Date.now();
|
|
110
16
|
const uid = () => randomUUID();
|
|
17
|
+
// Statements are prepared once and reused (the SQL text is the key; the few dynamic ones only vary by table name).
|
|
18
|
+
const prepared = new Map();
|
|
19
|
+
const q = (sql) => { let st = prepared.get(sql); if (!st)
|
|
20
|
+
prepared.set(sql, (st = db.prepare(sql))); return st; };
|
|
111
21
|
// ---- skills ----
|
|
112
22
|
export const skills = {
|
|
113
|
-
list: () =>
|
|
114
|
-
get: (id) =>
|
|
23
|
+
list: () => q('SELECT * FROM skills ORDER BY name').all(),
|
|
24
|
+
get: (id) => q('SELECT * FROM skills WHERE id=?').get(id),
|
|
115
25
|
create(p) {
|
|
116
26
|
const id = uid(), t = now();
|
|
117
|
-
|
|
27
|
+
q('INSERT INTO skills (id,name,description,content,created_at,updated_at,load) VALUES (?,?,?,?,?,?,?)').run(id, p.name, p.description ?? '', p.content ?? '', t, t, p.load ?? ((p.content ?? '').length > 2000 ? 'on_demand' : 'always'));
|
|
118
28
|
return skills.get(id);
|
|
119
29
|
},
|
|
120
30
|
update(id, p) {
|
|
@@ -122,36 +32,38 @@ export const skills = {
|
|
|
122
32
|
if (!cur)
|
|
123
33
|
return;
|
|
124
34
|
const n = { ...cur, ...p };
|
|
125
|
-
|
|
35
|
+
q('UPDATE skills SET name=?, description=?, content=?, load=?, updated_at=? WHERE id=?').run(n.name, n.description, n.content, n.load, now(), id);
|
|
126
36
|
return skills.get(id);
|
|
127
37
|
},
|
|
128
|
-
remove: (id) =>
|
|
38
|
+
remove: (id) => q('DELETE FROM skills WHERE id=?').run(id).changes > 0,
|
|
129
39
|
/**
|
|
130
40
|
* Adds the skills that come with hive-am, once each: they are ordinary skills afterwards (edit them, delete them). A
|
|
131
41
|
* skill that was seeded before is never added back, and one the user already has under that name is left alone.
|
|
132
42
|
*/
|
|
133
43
|
seedDefaults(defs) {
|
|
134
44
|
const t = now();
|
|
135
|
-
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
45
|
+
db.transaction(() => {
|
|
46
|
+
for (const d of defs) {
|
|
47
|
+
const id = `default-${d.slug}`;
|
|
48
|
+
if (q('SELECT 1 FROM seeded_skills WHERE slug=?').get(d.slug))
|
|
49
|
+
continue;
|
|
50
|
+
q('INSERT OR IGNORE INTO seeded_skills (slug) VALUES (?)').run(d.slug);
|
|
51
|
+
if (q('SELECT 1 FROM skills WHERE id=?').get(id))
|
|
52
|
+
continue; // from an earlier build: keep it as it is
|
|
53
|
+
const taken = !!q('SELECT 1 FROM skills WHERE lower(name)=lower(?)').get(d.name);
|
|
54
|
+
q('INSERT INTO skills (id,name,description,content,created_at,updated_at,load) VALUES (?,?,?,?,?,?,?)').run(id, taken ? `${d.name} (hive-am)` : d.name, d.description, d.content, t, t, d.load ?? 'always');
|
|
55
|
+
}
|
|
56
|
+
})();
|
|
145
57
|
},
|
|
146
58
|
/** Agents/types using each skill, for the library view. */
|
|
147
59
|
usage() {
|
|
148
60
|
const out = {};
|
|
149
61
|
for (const s of skills.list())
|
|
150
62
|
out[s.id] = { agents: 0, types: 0 };
|
|
151
|
-
for (const r of
|
|
63
|
+
for (const r of q('SELECT skill_id, COUNT(*) c FROM agent_skills GROUP BY skill_id').all())
|
|
152
64
|
if (out[r.skill_id])
|
|
153
65
|
out[r.skill_id].agents = r.c;
|
|
154
|
-
for (const r of
|
|
66
|
+
for (const r of q('SELECT skill_id, COUNT(*) c FROM type_skills GROUP BY skill_id').all())
|
|
155
67
|
if (out[r.skill_id])
|
|
156
68
|
out[r.skill_id].types = r.c;
|
|
157
69
|
return out;
|
|
@@ -166,10 +78,11 @@ const JOINS = {
|
|
|
166
78
|
/** How each skill assigned to this type/colony/agent is loaded. */
|
|
167
79
|
function loadsOf(kind, id) {
|
|
168
80
|
const j = JOINS[kind], out = {};
|
|
169
|
-
for (const r of
|
|
81
|
+
for (const r of q(`SELECT skill_id, load FROM ${j.table} WHERE ${j.owner}=?`).all(id))
|
|
170
82
|
out[r.skill_id] = r.load === 'on_demand' ? 'on_demand' : 'always';
|
|
171
83
|
return out;
|
|
172
84
|
}
|
|
85
|
+
const skillIdsOf = (kind, id) => { const j = JOINS[kind]; return q(`SELECT skill_id FROM ${j.table} WHERE ${j.owner}=?`).all(id).map((x) => x.skill_id); };
|
|
173
86
|
/**
|
|
174
87
|
* Replaces the skills of a type/colony/agent. A skill keeps the way it was loaded before; a new one takes what the caller
|
|
175
88
|
* says, or else the skill's own suggestion.
|
|
@@ -177,98 +90,87 @@ function loadsOf(kind, id) {
|
|
|
177
90
|
function assignSkills(kind, id, ids, loads) {
|
|
178
91
|
const j = JOINS[kind], prev = loadsOf(kind, id);
|
|
179
92
|
db.transaction(() => {
|
|
180
|
-
|
|
93
|
+
q(`DELETE FROM ${j.table} WHERE ${j.owner}=?`).run(id);
|
|
181
94
|
for (const s of ids) {
|
|
182
95
|
const load = loads?.[s] ?? prev[s] ?? skills.get(s)?.load ?? 'always';
|
|
183
|
-
|
|
96
|
+
q(`INSERT OR IGNORE INTO ${j.table} (${j.owner}, skill_id, load) VALUES (?,?,?)`).run(id, s, load === 'on_demand' ? 'on_demand' : 'always');
|
|
184
97
|
}
|
|
185
98
|
})();
|
|
186
99
|
}
|
|
187
100
|
// ---- agent types ----
|
|
188
|
-
function
|
|
189
|
-
|
|
190
|
-
return { ...r, skill_ids, skill_loads: loadsOf('type', r.id) };
|
|
101
|
+
function typeOf(r) {
|
|
102
|
+
return { ...r, skill_ids: skillIdsOf('type', r.id), skill_loads: loadsOf('type', r.id) };
|
|
191
103
|
}
|
|
192
104
|
export const types = {
|
|
193
|
-
list: () =>
|
|
194
|
-
get(id) { const r =
|
|
195
|
-
create(p) {
|
|
105
|
+
list: () => q('SELECT * FROM agent_types ORDER BY role DESC, name').all().map(typeOf),
|
|
106
|
+
get(id) { const r = q('SELECT * FROM agent_types WHERE id=?').get(id); return r ? typeOf(r) : undefined; },
|
|
107
|
+
create: db.transaction((p) => {
|
|
196
108
|
const id = uid();
|
|
197
|
-
|
|
109
|
+
q('INSERT INTO agent_types (id,name,description,role,provider,model,system_prompt,permission,color,created_at) VALUES (?,?,?,?,?,?,?,?,?,?)').run(id, p.name, p.description ?? '', p.role, p.provider, p.model ?? '', p.system_prompt ?? '', (p.permission ?? 'acceptEdits'), p.color ?? '', now());
|
|
198
110
|
types.setSkills(id, p.skill_ids ?? [], p.skill_loads);
|
|
199
111
|
return types.get(id);
|
|
200
|
-
},
|
|
201
|
-
update(id, p) {
|
|
112
|
+
}),
|
|
113
|
+
update: db.transaction((id, p) => {
|
|
202
114
|
const cur = types.get(id);
|
|
203
115
|
if (!cur)
|
|
204
116
|
return;
|
|
205
117
|
const n = { ...cur, ...p };
|
|
206
|
-
|
|
118
|
+
q('UPDATE agent_types SET name=?, description=?, role=?, provider=?, model=?, system_prompt=?, permission=?, color=? WHERE id=?')
|
|
207
119
|
.run(n.name, n.description, n.role, n.provider, n.model, n.system_prompt, n.permission, n.color, id);
|
|
208
120
|
if (p.skill_ids || p.skill_loads)
|
|
209
121
|
types.setSkills(id, p.skill_ids ?? cur.skill_ids, p.skill_loads);
|
|
210
122
|
return types.get(id);
|
|
211
|
-
},
|
|
123
|
+
}),
|
|
212
124
|
setSkills: (id, ids, loads) => assignSkills('type', id, ids, loads),
|
|
213
|
-
remove: (id) =>
|
|
125
|
+
remove: (id) => q('DELETE FROM agent_types WHERE id=?').run(id).changes > 0,
|
|
214
126
|
};
|
|
215
127
|
// ---- colonies ----
|
|
216
128
|
const DEFAULT_INHERIT = { cwd: true, permission: true, skills: true, prompt: true };
|
|
217
|
-
function
|
|
218
|
-
const
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
try {
|
|
222
|
-
inherit = { ...DEFAULT_INHERIT, ...JSON.parse(r.inherit) };
|
|
223
|
-
}
|
|
224
|
-
catch { /* keep defaults */ }
|
|
225
|
-
return { ...r, inherit, skill_ids, skill_loads: loadsOf('colony', r.id), agent_ids };
|
|
129
|
+
function colonyOf(r) {
|
|
130
|
+
const agent_ids = q('SELECT id FROM agents WHERE colony_id=? ORDER BY name').all(r.id).map((x) => x.id);
|
|
131
|
+
const inherit = { ...DEFAULT_INHERIT, ...parseJson(r.inherit, {}, `colony ${r.id} inherit`) };
|
|
132
|
+
return { ...r, inherit, skill_ids: skillIdsOf('colony', r.id), skill_loads: loadsOf('colony', r.id), agent_ids };
|
|
226
133
|
}
|
|
227
134
|
export const colonies = {
|
|
228
|
-
list: () =>
|
|
229
|
-
get(id) { const r =
|
|
230
|
-
create(p) {
|
|
135
|
+
list: () => q('SELECT * FROM colonies ORDER BY name').all().map(colonyOf),
|
|
136
|
+
get(id) { const r = q('SELECT * FROM colonies WHERE id=?').get(id); return r ? colonyOf(r) : undefined; },
|
|
137
|
+
create: db.transaction((p) => {
|
|
231
138
|
const id = uid();
|
|
232
|
-
|
|
139
|
+
q('INSERT INTO colonies (id,name,color,cwd,permission,system_prompt,inherit,created_at) VALUES (?,?,?,?,?,?,?,?)').run(id, p.name, p.color ?? '', p.cwd ?? '', (p.permission ?? 'acceptEdits'), p.system_prompt ?? '', JSON.stringify({ ...DEFAULT_INHERIT, ...(p.inherit ?? {}) }), now());
|
|
233
140
|
colonies.setSkills(id, p.skill_ids ?? [], p.skill_loads);
|
|
234
141
|
if (p.agent_ids)
|
|
235
142
|
colonies.setMembers(id, p.agent_ids);
|
|
236
143
|
return colonies.get(id);
|
|
237
|
-
},
|
|
238
|
-
update(id, p) {
|
|
144
|
+
}),
|
|
145
|
+
update: db.transaction((id, p) => {
|
|
239
146
|
const cur = colonies.get(id);
|
|
240
147
|
if (!cur)
|
|
241
148
|
return;
|
|
242
149
|
const n = { ...cur, ...p, inherit: { ...cur.inherit, ...(p.inherit ?? {}) } };
|
|
243
|
-
|
|
150
|
+
q('UPDATE colonies SET name=?, color=?, cwd=?, permission=?, system_prompt=?, inherit=? WHERE id=?')
|
|
244
151
|
.run(n.name, n.color, n.cwd, n.permission, n.system_prompt, JSON.stringify(n.inherit), id);
|
|
245
152
|
if (p.skill_ids || p.skill_loads)
|
|
246
153
|
colonies.setSkills(id, p.skill_ids ?? cur.skill_ids, p.skill_loads);
|
|
247
154
|
if (p.agent_ids)
|
|
248
155
|
colonies.setMembers(id, p.agent_ids);
|
|
249
156
|
return colonies.get(id);
|
|
250
|
-
},
|
|
157
|
+
}),
|
|
251
158
|
setSkills: (id, ids, loads) => assignSkills('colony', id, ids, loads),
|
|
252
159
|
/** Replaces membership; an agent belongs to at most one colony. */
|
|
253
|
-
setMembers(id, agentIds) {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
remove: (id) => db.prepare('DELETE FROM colonies WHERE id=?').run(id).changes > 0,
|
|
160
|
+
setMembers: db.transaction((id, agentIds) => {
|
|
161
|
+
q('UPDATE agents SET colony_id=NULL WHERE colony_id=?').run(id);
|
|
162
|
+
for (const a of agentIds)
|
|
163
|
+
q('UPDATE agents SET colony_id=? WHERE id=?').run(id, a);
|
|
164
|
+
}),
|
|
165
|
+
/** Members stay (their colony becomes none through the foreign key); the colony's own skill assignments go with it. */
|
|
166
|
+
remove: (id) => q('DELETE FROM colonies WHERE id=?').run(id).changes > 0,
|
|
261
167
|
};
|
|
262
168
|
// ---- agents ----
|
|
263
169
|
const FIELDS = ['cwd', 'permission', 'skills', 'prompt'];
|
|
264
|
-
function
|
|
265
|
-
const skill_ids =
|
|
266
|
-
const worker_ids =
|
|
267
|
-
|
|
268
|
-
try {
|
|
269
|
-
overrides = JSON.parse(r.overrides ?? '[]').filter((f) => FIELDS.includes(f));
|
|
270
|
-
}
|
|
271
|
-
catch { /* none */ }
|
|
170
|
+
function agentOf(r) {
|
|
171
|
+
const skill_ids = skillIdsOf('agent', r.id);
|
|
172
|
+
const worker_ids = q('SELECT worker_id FROM assignments WHERE orchestrator_id=?').all(r.id).map((x) => x.worker_id);
|
|
173
|
+
const overrides = parseJson(r.overrides, [], `agent ${r.id} overrides`).filter((f) => FIELDS.includes(f));
|
|
272
174
|
const col = r.colony_id ? colonies.get(r.colony_id) : undefined;
|
|
273
175
|
const follows = (f) => !!col && col.inherit[f] && !overrides.includes(f);
|
|
274
176
|
const inherited = FIELDS.filter(follows);
|
|
@@ -289,69 +191,69 @@ export function resolved(a) {
|
|
|
289
191
|
return { ...a, cwd: a.effective.cwd, permission: a.effective.permission, system_prompt: a.effective.system_prompt, skill_ids: a.effective.skill_ids, skill_loads: a.effective.skill_loads };
|
|
290
192
|
}
|
|
291
193
|
export const agents = {
|
|
292
|
-
list: () =>
|
|
293
|
-
get(id) { const r =
|
|
294
|
-
byName(name) { const r =
|
|
295
|
-
|
|
194
|
+
list: () => q('SELECT * FROM agents ORDER BY role DESC, name').all().map(agentOf),
|
|
195
|
+
get(id) { const r = q('SELECT * FROM agents WHERE id=?').get(id); return r ? agentOf(r) : undefined; },
|
|
196
|
+
byName(name) { const r = q('SELECT * FROM agents WHERE lower(name)=lower(?)').get(name); return r ? agentOf(r) : undefined; },
|
|
197
|
+
/** The agent, its skills and its workers are written together: either all of it exists afterwards or none. */
|
|
198
|
+
create: db.transaction((p) => {
|
|
296
199
|
const id = uid(), t = now();
|
|
297
|
-
|
|
200
|
+
q('INSERT INTO agents (id,name,description,role,type_id,provider,model,system_prompt,permission,cwd,session_id,status,created_at,updated_at,colony_id,overrides) VALUES (?,?,?,?,?,?,?,?,?,?,NULL,?,?,?,?,?)').run(id, p.name, p.description ?? '', p.role, p.type_id ?? null, p.provider, p.model ?? '', p.system_prompt ?? '', (p.permission ?? 'acceptEdits'), p.cwd ?? '', 'idle', t, t, p.colony_id ?? null, JSON.stringify(p.overrides ?? []));
|
|
298
201
|
agents.setSkills(id, p.skill_ids ?? [], p.skill_loads);
|
|
299
202
|
agents.setWorkers(id, p.worker_ids ?? []);
|
|
300
203
|
return agents.get(id);
|
|
301
|
-
},
|
|
302
|
-
update(id, p) {
|
|
204
|
+
}),
|
|
205
|
+
update: db.transaction((id, p) => {
|
|
303
206
|
const cur = agents.get(id);
|
|
304
207
|
if (!cur)
|
|
305
208
|
return;
|
|
306
209
|
const n = { ...cur, ...p };
|
|
307
|
-
|
|
210
|
+
q('UPDATE agents SET name=?, description=?, role=?, type_id=?, provider=?, model=?, system_prompt=?, permission=?, cwd=?, colony_id=?, overrides=?, updated_at=? WHERE id=?')
|
|
308
211
|
.run(n.name, n.description, n.role, n.type_id, n.provider, n.model, n.system_prompt, n.permission, n.cwd, n.colony_id ?? null, JSON.stringify(n.overrides ?? []), now(), id);
|
|
309
212
|
if (p.skill_ids || p.skill_loads)
|
|
310
213
|
agents.setSkills(id, p.skill_ids ?? cur.skill_ids, p.skill_loads);
|
|
311
214
|
if (p.worker_ids)
|
|
312
215
|
agents.setWorkers(id, p.worker_ids);
|
|
313
216
|
return agents.get(id);
|
|
314
|
-
},
|
|
315
|
-
setInstrHash: (id, hash) =>
|
|
316
|
-
setStatus: (id, status) =>
|
|
317
|
-
setSession(a, sessionId) {
|
|
318
|
-
|
|
217
|
+
}),
|
|
218
|
+
setInstrHash: (id, hash) => q('UPDATE agents SET instr_hash=? WHERE id=?').run(hash, id),
|
|
219
|
+
setStatus: (id, status) => q('UPDATE agents SET status=?, updated_at=? WHERE id=?').run(status, now(), id),
|
|
220
|
+
setSession: db.transaction((a, sessionId) => {
|
|
221
|
+
q('UPDATE agents SET session_id=?, session_cwd=?, instr_hash=NULL, updated_at=? WHERE id=?').run(sessionId, sessionId ? a.effective.cwd : null, now(), a.id);
|
|
319
222
|
if (sessionId) {
|
|
320
|
-
|
|
223
|
+
q(`INSERT INTO agent_sessions (agent_id, provider, session_id, cwd, first_seen, last_seen) VALUES (?,?,?,?,?,?)
|
|
321
224
|
ON CONFLICT(agent_id, session_id) DO UPDATE SET last_seen=excluded.last_seen`)
|
|
322
225
|
.run(a.id, a.provider, sessionId, a.effective.cwd, now(), now());
|
|
323
226
|
}
|
|
324
|
-
},
|
|
227
|
+
}),
|
|
325
228
|
/** Logs a native session without making it the agent's current conversation (used for delegations). */
|
|
326
229
|
recordSession(a, sessionId, kind, fromId, task) {
|
|
327
|
-
|
|
230
|
+
q(`INSERT INTO agent_sessions (agent_id, provider, session_id, cwd, first_seen, last_seen, kind, from_id, task) VALUES (?,?,?,?,?,?,?,?,?)
|
|
328
231
|
ON CONFLICT(agent_id, session_id) DO UPDATE SET last_seen=excluded.last_seen`)
|
|
329
232
|
.run(a.id, a.provider, sessionId, a.effective.cwd, now(), now(), kind, fromId ?? null, task ? task.slice(0, 500) : null);
|
|
330
233
|
},
|
|
331
234
|
setSkills: (id, ids, loads) => assignSkills('agent', id, ids, loads),
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
allSessions: () => db.prepare(`SELECT s.*, a.name agent_name, a.role agent_role, f.name from_name FROM agent_sessions s JOIN agents a ON a.id=s.agent_id LEFT JOIN agents f ON f.id=s.from_id ORDER BY s.last_seen DESC`).all(),
|
|
235
|
+
/** Replaces the workers an orchestrator may dispatch to (never itself). */
|
|
236
|
+
setWorkers: db.transaction((id, ids) => {
|
|
237
|
+
q('DELETE FROM assignments WHERE orchestrator_id=?').run(id);
|
|
238
|
+
for (const w of ids)
|
|
239
|
+
if (w !== id)
|
|
240
|
+
q('INSERT OR IGNORE INTO assignments (orchestrator_id, worker_id) VALUES (?,?)').run(id, w);
|
|
241
|
+
}),
|
|
242
|
+
remove: (id) => q('DELETE FROM agents WHERE id=?').run(id).changes > 0,
|
|
243
|
+
sessions: (id) => q('SELECT s.*, f.name from_name FROM agent_sessions s LEFT JOIN agents f ON f.id=s.from_id WHERE s.agent_id=? ORDER BY s.last_seen DESC').all(id),
|
|
244
|
+
allSessions: () => q(`SELECT s.*, a.name agent_name, a.role agent_role, f.name from_name FROM agent_sessions s JOIN agents a ON a.id=s.agent_id LEFT JOIN agents f ON f.id=s.from_id ORDER BY s.last_seen DESC`).all(),
|
|
343
245
|
};
|
|
344
246
|
// ---- dispatch log ----
|
|
345
247
|
export const dispatches = {
|
|
346
248
|
start(from, to, task) {
|
|
347
249
|
const id = uid();
|
|
348
|
-
|
|
250
|
+
q('INSERT INTO dispatches (id, from_id, to_id, task, status, result, created_at, finished_at) VALUES (?,?,?,?,?,NULL,?,NULL)').run(id, from, to, task, 'running', now());
|
|
349
251
|
return id;
|
|
350
252
|
},
|
|
351
|
-
setSession: (id, sessionId) =>
|
|
352
|
-
finish: (id, status, result) =>
|
|
353
|
-
recent: (limit = 50) =>
|
|
253
|
+
setSession: (id, sessionId) => q('UPDATE dispatches SET session_id=? WHERE id=?').run(sessionId, id),
|
|
254
|
+
finish: (id, status, result) => q('UPDATE dispatches SET status=?, result=?, finished_at=? WHERE id=?').run(status, result.slice(0, 20000), now(), id),
|
|
255
|
+
recent: (limit = 50) => q('SELECT * FROM dispatches ORDER BY created_at DESC LIMIT ?').all(limit),
|
|
354
256
|
};
|
|
355
257
|
// Agents left 'running' by a crash/blackout are simply idle again: their session is intact on disk.
|
|
356
|
-
|
|
357
|
-
|
|
258
|
+
q("UPDATE agents SET status='idle' WHERE status='running'").run();
|
|
259
|
+
q("UPDATE dispatches SET status='interrupted' WHERE status='running'").run();
|