maestro-agent 0.0.1 → 0.0.3

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 (111) hide show
  1. package/README.md +316 -2
  2. package/bin/maestro.ts +5 -0
  3. package/dist/maestro +0 -0
  4. package/dist/web/apple-touch-icon.png +0 -0
  5. package/dist/web/assets/Connections-BMA04Ycg.js +11 -0
  6. package/dist/web/assets/GanttView-DXjh0gxg.js +49 -0
  7. package/dist/web/assets/Home-Ct3Ho0Qt.js +1 -0
  8. package/dist/web/assets/HooksCrons--0kyVJcR.js +11 -0
  9. package/dist/web/assets/ProjectDetail-B_IqEpFu.js +1 -0
  10. package/dist/web/assets/Roles-D1tIQzto.js +24 -0
  11. package/dist/web/assets/Settings-yts4LUmH.js +11 -0
  12. package/dist/web/assets/Skills-DbuNLjIV.js +12 -0
  13. package/dist/web/assets/Wizard-vJol8-Y4.js +11 -0
  14. package/dist/web/assets/WorkspaceChat-DrsLs4m2.js +56 -0
  15. package/dist/web/assets/WorkspaceDashboard-B9vgrd2Z.js +6 -0
  16. package/dist/web/assets/WorkspaceNew-DoNGYHCG.js +1 -0
  17. package/dist/web/assets/WorkspaceProjects-DDp3mUse.js +6 -0
  18. package/dist/web/assets/WorkspaceSchedules-BTjmCbYG.js +1 -0
  19. package/dist/web/assets/WorkspaceTasks-mPU-bhKR.js +41 -0
  20. package/dist/web/assets/activity-CIA8bIA4.js +6 -0
  21. package/dist/web/assets/addon-fit-BlxrFPDK.js +1 -0
  22. package/dist/web/assets/arrow-right-S7ID7nDp.js +6 -0
  23. package/dist/web/assets/badge-DDTUzWIi.js +1 -0
  24. package/dist/web/assets/circle-check-B3P1qK0Z.js +6 -0
  25. package/dist/web/assets/clock-f9aYZox0.js +6 -0
  26. package/dist/web/assets/index-BRo4Du_s.js +11 -0
  27. package/dist/web/assets/index-C7kx39S9.js +196 -0
  28. package/dist/web/assets/index-D6LSdZea.css +1 -0
  29. package/dist/web/assets/plus-BHnOxbns.js +6 -0
  30. package/dist/web/assets/refresh-cw-BWX04Hg3.js +6 -0
  31. package/dist/web/assets/save-BLbb_9xz.js +6 -0
  32. package/dist/web/assets/sparkles-CDr6Dw1e.js +6 -0
  33. package/dist/web/assets/trash-2-9-ThEdey.js +6 -0
  34. package/dist/web/assets/useEventStream-DXt2Hmei.js +1 -0
  35. package/dist/web/assets/x-DVdKPXXy.js +6 -0
  36. package/dist/web/assets/xterm-DYP7pi_n.css +32 -0
  37. package/dist/web/assets/xterm-DlVFs1Kw.js +9 -0
  38. package/dist/web/favicon-512.png +0 -0
  39. package/dist/web/favicon.png +0 -0
  40. package/dist/web/index.html +15 -0
  41. package/package.json +49 -6
  42. package/src/api/agents.ts +76 -0
  43. package/src/api/audit.ts +19 -0
  44. package/src/api/autopilot.ts +73 -0
  45. package/src/api/chat.ts +801 -0
  46. package/src/api/chief.ts +84 -0
  47. package/src/api/config.ts +39 -0
  48. package/src/api/gantt.ts +72 -0
  49. package/src/api/hooks.ts +54 -0
  50. package/src/api/inbox.ts +125 -0
  51. package/src/api/lark.ts +32 -0
  52. package/src/api/memory.ts +37 -0
  53. package/src/api/ops.ts +89 -0
  54. package/src/api/projects.ts +105 -0
  55. package/src/api/roles.ts +123 -0
  56. package/src/api/runtimes.ts +62 -0
  57. package/src/api/scheduled-tasks.ts +203 -0
  58. package/src/api/sessions.ts +479 -0
  59. package/src/api/skills.ts +386 -0
  60. package/src/api/tasks.ts +457 -0
  61. package/src/api/telegram.ts +94 -0
  62. package/src/api/templates.ts +36 -0
  63. package/src/api/webhooks.ts +20 -0
  64. package/src/api/workspaces.ts +150 -0
  65. package/src/bridges/lark/index.ts +213 -0
  66. package/src/bridges/telegram/index.ts +273 -0
  67. package/src/bridges/telegram/polling.ts +185 -0
  68. package/src/chat/index.ts +86 -0
  69. package/src/chief/index.ts +461 -0
  70. package/src/core/cli.ts +333 -0
  71. package/src/core/db.ts +53 -0
  72. package/src/core/event-bus.ts +33 -0
  73. package/src/core/index.ts +6 -0
  74. package/src/core/migrations.ts +303 -0
  75. package/src/core/router.ts +69 -0
  76. package/src/core/schema.sql +232 -0
  77. package/src/core/server.ts +308 -0
  78. package/src/core/validate.ts +22 -0
  79. package/src/discovery/index.ts +194 -0
  80. package/src/gateway/adapters/telegram.ts +148 -0
  81. package/src/gateway/index.ts +31 -0
  82. package/src/gateway/manager.ts +176 -0
  83. package/src/gateway/types.ts +77 -0
  84. package/src/inbox/index.ts +500 -0
  85. package/src/ops/artifact-sync.ts +65 -0
  86. package/src/ops/autopilot.ts +338 -0
  87. package/src/ops/gc.ts +252 -0
  88. package/src/ops/index.ts +226 -0
  89. package/src/ops/project-serial.ts +52 -0
  90. package/src/ops/role-dispatch.ts +111 -0
  91. package/src/ops/runtime-scheduler.ts +447 -0
  92. package/src/ops/task-blocking.ts +65 -0
  93. package/src/ops/task-deps.ts +37 -0
  94. package/src/ops/task-workspace.ts +60 -0
  95. package/src/roles/index.ts +258 -0
  96. package/src/roles/prompt-assembler.ts +85 -0
  97. package/src/roles/workspace-role.ts +155 -0
  98. package/src/scheduler/index.ts +461 -0
  99. package/src/session/output-parser.ts +75 -0
  100. package/src/session/realtime-parser.ts +40 -0
  101. package/src/skills/builtin.ts +155 -0
  102. package/src/skills/skill-extractor.ts +452 -0
  103. package/src/skills/skill-md.ts +282 -0
  104. package/src/transport/http-api.ts +75 -0
  105. package/src/transport/index.ts +4 -0
  106. package/src/transport/local-pty.ts +119 -0
  107. package/src/transport/ssh.ts +176 -0
  108. package/src/transport/types.ts +20 -0
  109. package/src/workflows/index.ts +231 -0
  110. package/index.js +0 -1
  111. package/maestro-agent-0.0.1.tgz +0 -0
@@ -0,0 +1,303 @@
1
+ import type { Database } from "bun:sqlite";
2
+ import { existsSync } from "fs";
3
+ import { join } from "path";
4
+
5
+ export interface Migration {
6
+ version: number;
7
+ up: string;
8
+ }
9
+
10
+ export const migrations: Migration[] = [
11
+ { version: 1, up: "CREATE TABLE IF NOT EXISTS config (key TEXT PRIMARY KEY, value TEXT, updated_at INTEGER)" },
12
+ { version: 2, up: "ALTER TABLE task ADD COLUMN acceptance_criteria TEXT DEFAULT ''" },
13
+ {
14
+ version: 3,
15
+ up: `CREATE TABLE IF NOT EXISTS chat_message (
16
+ id TEXT PRIMARY KEY,
17
+ workspace_id TEXT NOT NULL REFERENCES workspace(id),
18
+ sender_type TEXT NOT NULL CHECK(sender_type IN ('human','agent')),
19
+ sender_id TEXT NOT NULL,
20
+ content TEXT NOT NULL,
21
+ mentions_json TEXT DEFAULT '[]',
22
+ reply_to_id TEXT,
23
+ idempotency_key TEXT,
24
+ seq INTEGER NOT NULL DEFAULT 0,
25
+ created_at INTEGER NOT NULL
26
+ )`,
27
+ },
28
+ {
29
+ version: 4,
30
+ up: "CREATE INDEX IF NOT EXISTS idx_chat_workspace_seq ON chat_message(workspace_id, seq)",
31
+ },
32
+ {
33
+ version: 5,
34
+ up: "CREATE UNIQUE INDEX IF NOT EXISTS idx_chat_idempotency ON chat_message(idempotency_key) WHERE idempotency_key IS NOT NULL",
35
+ },
36
+ {
37
+ version: 6,
38
+ up: `CREATE TABLE IF NOT EXISTS chat_delivery (
39
+ id TEXT PRIMARY KEY,
40
+ message_id TEXT NOT NULL REFERENCES chat_message(id),
41
+ target_agent_id TEXT NOT NULL,
42
+ status TEXT NOT NULL DEFAULT 'pending' CHECK(status IN ('pending','delivered','acked')),
43
+ delivered_at INTEGER,
44
+ acked_at INTEGER,
45
+ UNIQUE(message_id, target_agent_id)
46
+ )`,
47
+ },
48
+ {
49
+ version: 7,
50
+ up: "CREATE INDEX IF NOT EXISTS idx_delivery_agent_status ON chat_delivery(target_agent_id, status)",
51
+ },
52
+ {
53
+ version: 8,
54
+ up: "SELECT 1", // primary_agent_id now in initial schema
55
+ },
56
+ {
57
+ version: 9,
58
+ up: `INSERT OR IGNORE INTO chat_message (id, workspace_id, sender_type, sender_id, content, mentions_json, seq, created_at)
59
+ SELECT
60
+ im.id,
61
+ COALESCE((SELECT w.id FROM workspace w LIMIT 1), 'legacy'),
62
+ CASE WHEN im.from_actor = 'user' THEN 'human' ELSE 'agent' END,
63
+ im.from_actor,
64
+ COALESCE(im.body, im.subject, ''),
65
+ '[]',
66
+ ROW_NUMBER() OVER (ORDER BY im.created_at),
67
+ im.created_at
68
+ FROM inbox_message im`,
69
+ },
70
+ {
71
+ version: 10,
72
+ up: `CREATE TABLE IF NOT EXISTS chat_conversation (
73
+ workspace_id TEXT PRIMARY KEY REFERENCES workspace(id),
74
+ messages_json TEXT NOT NULL DEFAULT '[]',
75
+ model TEXT NOT NULL DEFAULT 'claude-sonnet-4-20250514',
76
+ system_prompt TEXT,
77
+ updated_at INTEGER NOT NULL
78
+ )`,
79
+ },
80
+ { version: 11, up: "ALTER TABLE task ADD COLUMN start_date INTEGER" },
81
+ { version: 12, up: "ALTER TABLE task ADD COLUMN end_date INTEGER" },
82
+ { version: 13, up: "ALTER TABLE task ADD COLUMN progress INTEGER DEFAULT 0" },
83
+ {
84
+ version: 14,
85
+ up: `CREATE TABLE IF NOT EXISTS skill (
86
+ id TEXT PRIMARY KEY,
87
+ name TEXT NOT NULL UNIQUE,
88
+ description TEXT,
89
+ source TEXT NOT NULL DEFAULT 'local' CHECK(source IN ('local','url','zip')),
90
+ source_url TEXT,
91
+ platforms_json TEXT DEFAULT '[]',
92
+ enabled INTEGER DEFAULT 1,
93
+ config_json TEXT DEFAULT '{}',
94
+ installed_at INTEGER NOT NULL,
95
+ updated_at INTEGER NOT NULL
96
+ )`,
97
+ },
98
+ {
99
+ version: 15,
100
+ up: `CREATE TABLE IF NOT EXISTS workflow_definition (
101
+ id TEXT PRIMARY KEY,
102
+ scope TEXT NOT NULL DEFAULT 'global' CHECK(scope IN ('global','workspace','project')),
103
+ scope_id TEXT,
104
+ name TEXT NOT NULL,
105
+ definition_json TEXT NOT NULL,
106
+ created_at INTEGER NOT NULL,
107
+ updated_at INTEGER NOT NULL,
108
+ UNIQUE(scope, scope_id, name)
109
+ )`,
110
+ },
111
+ {
112
+ version: 16,
113
+ up: "SELECT 1", // workflow_id now in initial schema; normalizeCanonicalSchema adds it for older DBs
114
+ },
115
+ {
116
+ version: 17,
117
+ up: "CREATE INDEX IF NOT EXISTS idx_workflow_scope ON workflow_definition(scope, scope_id)",
118
+ },
119
+ {
120
+ version: 18,
121
+ up: "SELECT 1", // workdir now in initial schema; normalizeCanonicalSchema adds it for older DBs
122
+ },
123
+ {
124
+ version: 19,
125
+ up: `CREATE TABLE IF NOT EXISTS scheduled_task (
126
+ id TEXT PRIMARY KEY,
127
+ workspace_id TEXT NOT NULL REFERENCES workspace(id),
128
+ project_id TEXT NOT NULL REFERENCES project(id),
129
+ title TEXT NOT NULL,
130
+ description TEXT,
131
+ priority INTEGER DEFAULT 0,
132
+ schedule_type TEXT NOT NULL CHECK(schedule_type IN ('cron','once')),
133
+ cron_expr TEXT,
134
+ run_at INTEGER,
135
+ assignee_role_id TEXT REFERENCES role(id),
136
+ assignee_role_name TEXT,
137
+ enabled INTEGER DEFAULT 1,
138
+ last_fired_at INTEGER,
139
+ next_fire_at INTEGER,
140
+ last_task_id TEXT,
141
+ created_by TEXT DEFAULT 'user',
142
+ created_at INTEGER NOT NULL,
143
+ updated_at INTEGER NOT NULL
144
+ )`,
145
+ },
146
+ {
147
+ version: 20,
148
+ up: "CREATE INDEX IF NOT EXISTS idx_scheduled_task_workspace ON scheduled_task(workspace_id)",
149
+ },
150
+ {
151
+ version: 21,
152
+ up: "CREATE INDEX IF NOT EXISTS idx_scheduled_task_next_fire ON scheduled_task(enabled, next_fire_at)",
153
+ },
154
+ {
155
+ version: 22,
156
+ up: "SELECT 1", // archived_at now in initial schema; normalizeCanonicalSchema adds it for older DBs
157
+ },
158
+ {
159
+ version: 23,
160
+ up: "SELECT 1", // runtime version now in initial schema; normalizeCanonicalSchema adds it for older DBs
161
+ },
162
+ {
163
+ version: 24,
164
+ up: "SELECT 1", // assignee_role_id now in initial schema
165
+ },
166
+ ];
167
+
168
+ export function runMigrations(db: Database, hubDir?: string): void {
169
+ // Auto-backup before migrations if hubDir provided
170
+ if (hubDir && migrations.length > 0) {
171
+ const current = getCurrentVersion(db);
172
+ const pending = migrations.filter(m => m.version > current);
173
+ if (pending.length > 0) {
174
+ try {
175
+ const backupDir = join(hubDir, "backups");
176
+ const { mkdirSync, copyFileSync } = require("fs");
177
+ mkdirSync(backupDir, { recursive: true });
178
+ const dbPath = join(hubDir, "maestro.db");
179
+ if (existsSync(dbPath)) {
180
+ const backupPath = join(backupDir, `pre-migration-${Date.now()}.db`);
181
+ copyFileSync(dbPath, backupPath);
182
+ console.log(` \x1b[32m✓\x1b[0m Auto-backup: ${backupPath}`);
183
+ }
184
+ } catch {}
185
+ }
186
+ }
187
+
188
+ db.exec(`
189
+ CREATE TABLE IF NOT EXISTS schema_version (
190
+ version INTEGER PRIMARY KEY,
191
+ applied_at INTEGER NOT NULL
192
+ )
193
+ `);
194
+
195
+ const current = getCurrentVersion(db);
196
+
197
+ for (const m of migrations) {
198
+ if (m.version <= current) continue;
199
+ db.exec(m.up);
200
+ db.run("INSERT INTO schema_version (version, applied_at) VALUES (?, ?)", [m.version, Date.now()]);
201
+ }
202
+
203
+ normalizeCanonicalSchema(db);
204
+ }
205
+
206
+ function getCurrentVersion(db: Database): number {
207
+ try {
208
+ return (db.query("SELECT MAX(version) as v FROM schema_version").get() as any)?.v ?? -1;
209
+ } catch {
210
+ return -1;
211
+ }
212
+ }
213
+
214
+ function normalizeCanonicalSchema(db: Database): void {
215
+ const roleColumns = tableColumns(db, "role");
216
+ if (
217
+ roleColumns.has("required_capabilities_json") &&
218
+ !roleColumns.has("capabilities_json")
219
+ ) {
220
+ db.exec("ALTER TABLE role RENAME COLUMN required_capabilities_json TO capabilities_json");
221
+ }
222
+
223
+ const projectColumns = tableColumns(db, "project");
224
+ if (projectColumns.size > 0 && !projectColumns.has("workflow_id")) {
225
+ db.exec("ALTER TABLE project ADD COLUMN workflow_id TEXT");
226
+ }
227
+ if (projectColumns.size > 0 && !projectColumns.has("workdir")) {
228
+ db.exec("ALTER TABLE project ADD COLUMN workdir TEXT");
229
+ }
230
+
231
+ const taskColumns = tableColumns(db, "task");
232
+ if (taskColumns.size > 0 && !taskColumns.has("archived_at")) {
233
+ db.exec("ALTER TABLE task ADD COLUMN archived_at INTEGER");
234
+ }
235
+
236
+ const runtimeColumns = tableColumns(db, "agent_runtime");
237
+ if (runtimeColumns.size > 0 && !runtimeColumns.has("version")) {
238
+ db.exec("ALTER TABLE agent_runtime ADD COLUMN version TEXT");
239
+ }
240
+
241
+ db.exec(`
242
+ CREATE TABLE IF NOT EXISTS workflow_definition (
243
+ id TEXT PRIMARY KEY,
244
+ scope TEXT NOT NULL DEFAULT 'global' CHECK(scope IN ('global','workspace','project')),
245
+ scope_id TEXT,
246
+ name TEXT NOT NULL,
247
+ definition_json TEXT NOT NULL,
248
+ created_at INTEGER NOT NULL,
249
+ updated_at INTEGER NOT NULL,
250
+ UNIQUE(scope, scope_id, name)
251
+ )
252
+ `);
253
+ db.exec("CREATE INDEX IF NOT EXISTS idx_workflow_scope ON workflow_definition(scope, scope_id)");
254
+
255
+ db.exec(`
256
+ CREATE TABLE IF NOT EXISTS scheduled_task (
257
+ id TEXT PRIMARY KEY,
258
+ workspace_id TEXT NOT NULL REFERENCES workspace(id),
259
+ project_id TEXT NOT NULL REFERENCES project(id),
260
+ title TEXT NOT NULL,
261
+ description TEXT,
262
+ priority INTEGER DEFAULT 0,
263
+ schedule_type TEXT NOT NULL CHECK(schedule_type IN ('cron','once')),
264
+ cron_expr TEXT,
265
+ run_at INTEGER,
266
+ assignee_role_id TEXT REFERENCES role(id),
267
+ assignee_role_name TEXT,
268
+ enabled INTEGER DEFAULT 1,
269
+ last_fired_at INTEGER,
270
+ next_fire_at INTEGER,
271
+ last_task_id TEXT,
272
+ created_by TEXT DEFAULT 'user',
273
+ created_at INTEGER NOT NULL,
274
+ updated_at INTEGER NOT NULL
275
+ )
276
+ `);
277
+ db.exec("CREATE INDEX IF NOT EXISTS idx_scheduled_task_workspace ON scheduled_task(workspace_id)");
278
+ db.exec("CREATE INDEX IF NOT EXISTS idx_scheduled_task_next_fire ON scheduled_task(enabled, next_fire_at)");
279
+
280
+ const taskCols2 = tableColumns(db, "task");
281
+ if (taskCols2.size > 0 && !taskCols2.has("work_dir")) {
282
+ db.exec("ALTER TABLE task ADD COLUMN work_dir TEXT");
283
+ }
284
+ if (taskCols2.size > 0 && !taskCols2.has("assignee_role_id")) {
285
+ db.exec("ALTER TABLE task ADD COLUMN assignee_role_id TEXT REFERENCES role(id)");
286
+ }
287
+
288
+ db.exec(`
289
+ CREATE TABLE IF NOT EXISTS telegram_binding (
290
+ chat_id TEXT PRIMARY KEY,
291
+ workspace_id TEXT NOT NULL REFERENCES workspace(id),
292
+ bound_at INTEGER NOT NULL
293
+ )
294
+ `);
295
+ }
296
+
297
+ function tableColumns(db: Database, table: string): Set<string> {
298
+ try {
299
+ return new Set((db.query(`PRAGMA table_info(${table})`).all() as any[]).map((row) => row.name));
300
+ } catch {
301
+ return new Set();
302
+ }
303
+ }
@@ -0,0 +1,69 @@
1
+ import { ValidationError } from "./validate";
2
+
3
+ type Handler = (req: Request, params: Record<string, string>) => Response | Promise<Response>;
4
+
5
+ interface Route {
6
+ method: string;
7
+ pattern: URLPattern;
8
+ handler: Handler;
9
+ }
10
+
11
+ export class Router {
12
+ private routes: Route[] = [];
13
+
14
+ get(path: string, handler: Handler) { this.add("GET", path, handler); }
15
+ post(path: string, handler: Handler) { this.add("POST", path, handler); }
16
+ put(path: string, handler: Handler) { this.add("PUT", path, handler); }
17
+ patch(path: string, handler: Handler) { this.add("PATCH", path, handler); }
18
+ delete(path: string, handler: Handler) { this.add("DELETE", path, handler); }
19
+
20
+ private add(method: string, path: string, handler: Handler) {
21
+ this.routes.push({
22
+ method,
23
+ pattern: new URLPattern({ pathname: path }),
24
+ handler,
25
+ });
26
+ }
27
+
28
+ async handle(req: Request): Promise<Response> {
29
+ const method = req.method;
30
+ for (const route of this.routes) {
31
+ if (route.method !== method) continue;
32
+ const match = route.pattern.exec(req.url);
33
+ if (match) {
34
+ const params = match.pathname.groups as Record<string, string>;
35
+ try {
36
+ return await route.handler(req, params);
37
+ } catch (err: any) {
38
+ if (err.name === "ValidationError") {
39
+ return json({ error: err.message }, 400);
40
+ }
41
+ console.error(`[API Error] ${method} ${req.url}:`, err);
42
+ return json({ error: "Internal server error" }, 500);
43
+ }
44
+ }
45
+ }
46
+ return json({ error: "Not found" }, 404);
47
+ }
48
+ }
49
+
50
+ export function json(data: unknown, status = 200): Response {
51
+ return new Response(JSON.stringify(data), {
52
+ status,
53
+ headers: { "Content-Type": "application/json" },
54
+ });
55
+ }
56
+
57
+ export async function body<T = any>(req: Request): Promise<T> {
58
+ const text = await req.text();
59
+ if (!text) throw new ValidationError("Request body is empty");
60
+ try {
61
+ return JSON.parse(text) as T;
62
+ } catch {
63
+ throw new ValidationError("Invalid JSON in request body");
64
+ }
65
+ }
66
+
67
+ export function getIdempotencyKey(req: Request): string | null {
68
+ return req.headers.get("Idempotency-Key") || req.headers.get("idempotency-key") || null;
69
+ }
@@ -0,0 +1,232 @@
1
+ -- MAESTRO v0.1 — 完整 DDL (16 张表 + 索引)
2
+ PRAGMA journal_mode=WAL;
3
+
4
+ CREATE TABLE IF NOT EXISTS workspace (
5
+ id TEXT PRIMARY KEY,
6
+ name TEXT NOT NULL,
7
+ goal TEXT,
8
+ status TEXT NOT NULL DEFAULT 'active',
9
+ primary_agent_id TEXT,
10
+ created_at INTEGER NOT NULL,
11
+ updated_at INTEGER NOT NULL
12
+ );
13
+
14
+ CREATE TABLE IF NOT EXISTS project (
15
+ id TEXT PRIMARY KEY,
16
+ workspace_id TEXT NOT NULL REFERENCES workspace(id),
17
+ workflow_id TEXT,
18
+ workdir TEXT,
19
+ name TEXT NOT NULL,
20
+ charter_template TEXT,
21
+ status TEXT NOT NULL DEFAULT 'active',
22
+ rationale TEXT,
23
+ created_by TEXT,
24
+ created_at INTEGER NOT NULL,
25
+ updated_at INTEGER NOT NULL
26
+ );
27
+
28
+ CREATE TABLE IF NOT EXISTS workflow_definition (
29
+ id TEXT PRIMARY KEY,
30
+ scope TEXT NOT NULL DEFAULT 'global' CHECK(scope IN ('global','workspace','project')),
31
+ scope_id TEXT,
32
+ name TEXT NOT NULL,
33
+ definition_json TEXT NOT NULL,
34
+ created_at INTEGER NOT NULL,
35
+ updated_at INTEGER NOT NULL,
36
+ UNIQUE(scope, scope_id, name)
37
+ );
38
+
39
+ CREATE TABLE IF NOT EXISTS role (
40
+ id TEXT PRIMARY KEY,
41
+ workspace_id TEXT REFERENCES workspace(id),
42
+ project_id TEXT REFERENCES project(id),
43
+ name TEXT NOT NULL,
44
+ role_md_path TEXT,
45
+ version TEXT,
46
+ capabilities_json TEXT DEFAULT '[]',
47
+ preferred_runtimes_json TEXT DEFAULT '[]',
48
+ headcount INTEGER DEFAULT 1,
49
+ created_at INTEGER NOT NULL,
50
+ updated_at INTEGER NOT NULL
51
+ );
52
+
53
+ CREATE TABLE IF NOT EXISTS agent_runtime (
54
+ id TEXT PRIMARY KEY,
55
+ type TEXT NOT NULL,
56
+ transport TEXT NOT NULL,
57
+ cmd TEXT,
58
+ target TEXT,
59
+ version TEXT,
60
+ capacity INTEGER DEFAULT -1,
61
+ oversubscription_factor REAL DEFAULT 1.0,
62
+ status TEXT NOT NULL DEFAULT 'online',
63
+ capabilities_json TEXT DEFAULT '[]',
64
+ created_at INTEGER NOT NULL
65
+ );
66
+
67
+ CREATE TABLE IF NOT EXISTS agent (
68
+ id TEXT PRIMARY KEY,
69
+ role_id TEXT REFERENCES role(id),
70
+ runtime_id TEXT REFERENCES agent_runtime(id),
71
+ name TEXT NOT NULL,
72
+ workdir TEXT,
73
+ status TEXT NOT NULL DEFAULT 'idle',
74
+ metrics_json TEXT DEFAULT '{}',
75
+ created_at INTEGER NOT NULL,
76
+ last_active_at INTEGER NOT NULL
77
+ );
78
+
79
+ CREATE TABLE IF NOT EXISTS session (
80
+ id TEXT PRIMARY KEY,
81
+ agent_id TEXT NOT NULL REFERENCES agent(id),
82
+ task_id TEXT,
83
+ pty_pid INTEGER,
84
+ transcript_path TEXT,
85
+ status TEXT NOT NULL DEFAULT 'running',
86
+ started_at INTEGER NOT NULL,
87
+ ended_at INTEGER
88
+ );
89
+
90
+ CREATE TABLE IF NOT EXISTS task (
91
+ id TEXT PRIMARY KEY,
92
+ project_id TEXT NOT NULL REFERENCES project(id),
93
+ parent_task_id TEXT,
94
+ title TEXT NOT NULL,
95
+ description TEXT,
96
+ status TEXT NOT NULL DEFAULT 'open',
97
+ required_capabilities_json TEXT DEFAULT '[]',
98
+ assignee_agent_id TEXT,
99
+ assignee_role_id TEXT REFERENCES role(id),
100
+ priority INTEGER DEFAULT 0,
101
+ lineage_depth INTEGER DEFAULT 0,
102
+ done_when_json TEXT,
103
+ created_by TEXT,
104
+ archived_at INTEGER,
105
+ claim_token TEXT,
106
+ work_dir TEXT,
107
+ created_at INTEGER NOT NULL,
108
+ updated_at INTEGER NOT NULL
109
+ );
110
+
111
+ CREATE TABLE IF NOT EXISTS task_thread_item (
112
+ id TEXT PRIMARY KEY,
113
+ task_id TEXT NOT NULL REFERENCES task(id),
114
+ kind TEXT NOT NULL,
115
+ author TEXT NOT NULL,
116
+ content TEXT,
117
+ ref_id TEXT,
118
+ created_at INTEGER NOT NULL
119
+ );
120
+
121
+ CREATE TABLE IF NOT EXISTS artifact (
122
+ id TEXT PRIMARY KEY,
123
+ task_id TEXT NOT NULL REFERENCES task(id),
124
+ kind TEXT NOT NULL,
125
+ path TEXT,
126
+ url TEXT,
127
+ meta_json TEXT DEFAULT '{}',
128
+ created_at INTEGER NOT NULL
129
+ );
130
+
131
+ CREATE TABLE IF NOT EXISTS task_dependency (
132
+ task_id TEXT NOT NULL REFERENCES task(id),
133
+ depends_on TEXT NOT NULL REFERENCES task(id),
134
+ PRIMARY KEY(task_id, depends_on)
135
+ );
136
+
137
+ CREATE TABLE IF NOT EXISTS hook_binding (
138
+ id TEXT PRIMARY KEY,
139
+ scope TEXT NOT NULL,
140
+ scope_id TEXT NOT NULL,
141
+ event TEXT NOT NULL,
142
+ when_expr TEXT,
143
+ action TEXT NOT NULL,
144
+ enabled INTEGER DEFAULT 1,
145
+ created_at INTEGER NOT NULL
146
+ );
147
+
148
+ CREATE TABLE IF NOT EXISTS cron_binding (
149
+ id TEXT PRIMARY KEY,
150
+ scope TEXT NOT NULL,
151
+ scope_id TEXT NOT NULL,
152
+ cron_expr TEXT NOT NULL,
153
+ action TEXT NOT NULL,
154
+ enabled INTEGER DEFAULT 1,
155
+ last_fired_at INTEGER,
156
+ created_at INTEGER NOT NULL
157
+ );
158
+
159
+ CREATE TABLE IF NOT EXISTS scheduled_task (
160
+ id TEXT PRIMARY KEY,
161
+ workspace_id TEXT NOT NULL REFERENCES workspace(id),
162
+ project_id TEXT NOT NULL REFERENCES project(id),
163
+ title TEXT NOT NULL,
164
+ description TEXT,
165
+ priority INTEGER DEFAULT 0,
166
+ schedule_type TEXT NOT NULL CHECK(schedule_type IN ('cron','once')),
167
+ cron_expr TEXT,
168
+ run_at INTEGER,
169
+ assignee_role_id TEXT REFERENCES role(id),
170
+ assignee_role_name TEXT,
171
+ enabled INTEGER DEFAULT 1,
172
+ last_fired_at INTEGER,
173
+ next_fire_at INTEGER,
174
+ last_task_id TEXT,
175
+ created_by TEXT DEFAULT 'user',
176
+ created_at INTEGER NOT NULL,
177
+ updated_at INTEGER NOT NULL
178
+ );
179
+
180
+ CREATE TABLE IF NOT EXISTS event_log (
181
+ id TEXT PRIMARY KEY,
182
+ type TEXT NOT NULL,
183
+ payload_json TEXT,
184
+ created_at INTEGER NOT NULL
185
+ );
186
+
187
+ CREATE TABLE IF NOT EXISTS memory (
188
+ id TEXT PRIMARY KEY,
189
+ scope TEXT NOT NULL,
190
+ scope_id TEXT NOT NULL,
191
+ key TEXT NOT NULL,
192
+ value TEXT,
193
+ updated_at INTEGER NOT NULL,
194
+ UNIQUE(scope, scope_id, key)
195
+ );
196
+
197
+ CREATE TABLE IF NOT EXISTS inbox_message (
198
+ id TEXT PRIMARY KEY,
199
+ kind TEXT NOT NULL,
200
+ from_actor TEXT NOT NULL,
201
+ to_actor TEXT NOT NULL,
202
+ subject TEXT,
203
+ body TEXT,
204
+ ref_json TEXT,
205
+ status TEXT NOT NULL DEFAULT 'unread',
206
+ created_at INTEGER NOT NULL,
207
+ read_at INTEGER
208
+ );
209
+
210
+ CREATE TABLE IF NOT EXISTS audit_log (
211
+ id TEXT PRIMARY KEY,
212
+ actor TEXT NOT NULL,
213
+ action TEXT NOT NULL,
214
+ target TEXT,
215
+ payload_json TEXT,
216
+ created_at INTEGER NOT NULL
217
+ );
218
+
219
+ -- 索引
220
+ CREATE INDEX IF NOT EXISTS idx_task_project ON task(project_id);
221
+ CREATE INDEX IF NOT EXISTS idx_task_status ON task(status);
222
+ CREATE INDEX IF NOT EXISTS idx_task_assignee ON task(assignee_agent_id);
223
+ CREATE INDEX IF NOT EXISTS idx_workflow_scope ON workflow_definition(scope, scope_id);
224
+ CREATE INDEX IF NOT EXISTS idx_agent_status ON agent(status);
225
+ CREATE INDEX IF NOT EXISTS idx_agent_role ON agent(role_id);
226
+ CREATE INDEX IF NOT EXISTS idx_session_agent ON session(agent_id);
227
+ CREATE INDEX IF NOT EXISTS idx_event_type ON event_log(type);
228
+ CREATE INDEX IF NOT EXISTS idx_event_time ON event_log(created_at);
229
+ CREATE INDEX IF NOT EXISTS idx_inbox_to ON inbox_message(to_actor, status);
230
+ CREATE INDEX IF NOT EXISTS idx_audit_time ON audit_log(created_at);
231
+ CREATE INDEX IF NOT EXISTS idx_scheduled_task_workspace ON scheduled_task(workspace_id);
232
+ CREATE INDEX IF NOT EXISTS idx_scheduled_task_next_fire ON scheduled_task(enabled, next_fire_at);