forge-openclaw-plugin 0.2.4 → 0.2.7

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 (117) hide show
  1. package/README.md +113 -5
  2. package/dist/assets/board-CzgvdLO8.js +6 -0
  3. package/dist/assets/board-CzgvdLO8.js.map +1 -0
  4. package/dist/assets/favicon-BCHm9dUV.ico +0 -0
  5. package/dist/assets/index-8d_oM8fL.js +27 -0
  6. package/dist/assets/index-8d_oM8fL.js.map +1 -0
  7. package/dist/assets/index-D4A_bq8m.css +1 -0
  8. package/dist/assets/motion-STUd1O46.js +10 -0
  9. package/dist/assets/motion-STUd1O46.js.map +1 -0
  10. package/dist/assets/plus-jakarta-sans-latin-ext-wght-normal-DmpS2jIq.woff2 +0 -0
  11. package/dist/assets/plus-jakarta-sans-latin-wght-normal-eXO_dkmS.woff2 +0 -0
  12. package/dist/assets/plus-jakarta-sans-vietnamese-wght-normal-qRpaaN48.woff2 +0 -0
  13. package/dist/assets/sora-latin-ext-wght-normal-CawQDOvP.woff2 +0 -0
  14. package/dist/assets/sora-latin-wght-normal-DdqRvwsR.woff2 +0 -0
  15. package/dist/assets/space-grotesk-latin-500-normal-CNSSEhBt.woff +0 -0
  16. package/dist/assets/space-grotesk-latin-500-normal-lFbtlQH6.woff2 +0 -0
  17. package/dist/assets/space-grotesk-latin-700-normal-CwsQ-cCU.woff +0 -0
  18. package/dist/assets/space-grotesk-latin-700-normal-RjhwGPKo.woff2 +0 -0
  19. package/dist/assets/space-grotesk-latin-ext-500-normal-3dgZTiw9.woff +0 -0
  20. package/dist/assets/space-grotesk-latin-ext-500-normal-DUe3BAxM.woff2 +0 -0
  21. package/dist/assets/space-grotesk-latin-ext-700-normal-BQnZhY3m.woff2 +0 -0
  22. package/dist/assets/space-grotesk-latin-ext-700-normal-HVCqSBdx.woff +0 -0
  23. package/dist/assets/space-grotesk-vietnamese-500-normal-BTqKIpxg.woff +0 -0
  24. package/dist/assets/space-grotesk-vietnamese-500-normal-BmEvtly_.woff2 +0 -0
  25. package/dist/assets/space-grotesk-vietnamese-700-normal-DMty7AZE.woff2 +0 -0
  26. package/dist/assets/space-grotesk-vietnamese-700-normal-Duxec5Rn.woff +0 -0
  27. package/dist/assets/table-CtNlETLc.js +23 -0
  28. package/dist/assets/table-CtNlETLc.js.map +1 -0
  29. package/dist/assets/ui-ThzkR_oW.js +46 -0
  30. package/dist/assets/ui-ThzkR_oW.js.map +1 -0
  31. package/dist/assets/vendor-CRS-psbw.css +1 -0
  32. package/dist/assets/vendor-DyHAI6nk.js +423 -0
  33. package/dist/assets/vendor-DyHAI6nk.js.map +1 -0
  34. package/dist/assets/viz-BJuBCz_G.js +34 -0
  35. package/dist/assets/viz-BJuBCz_G.js.map +1 -0
  36. package/dist/favicon.ico +0 -0
  37. package/dist/favicon.png +0 -0
  38. package/dist/index.html +29 -0
  39. package/dist/openclaw/api-client.d.ts +8 -0
  40. package/dist/openclaw/api-client.js +31 -4
  41. package/dist/openclaw/local-runtime.d.ts +3 -0
  42. package/dist/openclaw/local-runtime.js +135 -0
  43. package/dist/openclaw/parity.d.ts +4 -4
  44. package/dist/openclaw/parity.js +23 -33
  45. package/dist/openclaw/plugin-entry-shared.d.ts +4 -2
  46. package/dist/openclaw/plugin-entry-shared.js +51 -9
  47. package/dist/openclaw/routes.d.ts +12 -3
  48. package/dist/openclaw/routes.js +156 -924
  49. package/dist/openclaw/tools.js +242 -1100
  50. package/dist/server/app.js +2450 -0
  51. package/dist/server/db.js +313 -0
  52. package/dist/server/e2e-server.js +20 -0
  53. package/dist/server/errors.js +15 -0
  54. package/dist/server/index.js +16 -0
  55. package/dist/server/managers/base.js +17 -0
  56. package/dist/server/managers/contracts.js +47 -0
  57. package/dist/server/managers/platform/api-gateway-manager.js +11 -0
  58. package/dist/server/managers/platform/audit-manager.js +15 -0
  59. package/dist/server/managers/platform/authentication-manager.js +56 -0
  60. package/dist/server/managers/platform/authorization-manager.js +56 -0
  61. package/dist/server/managers/platform/background-job-manager.js +10 -0
  62. package/dist/server/managers/platform/configuration-manager.js +33 -0
  63. package/dist/server/managers/platform/database-manager.js +14 -0
  64. package/dist/server/managers/platform/event-bus-manager.js +7 -0
  65. package/dist/server/managers/platform/external-service-manager.js +11 -0
  66. package/dist/server/managers/platform/health-manager.js +7 -0
  67. package/dist/server/managers/platform/migration-manager.js +8 -0
  68. package/dist/server/managers/platform/search-index-manager.js +4 -0
  69. package/dist/server/managers/platform/secrets-manager.js +19 -0
  70. package/dist/server/managers/platform/session-manager.js +121 -0
  71. package/dist/server/managers/platform/storage-manager.js +16 -0
  72. package/dist/server/managers/platform/token-manager.js +37 -0
  73. package/dist/server/managers/platform/transaction-manager.js +8 -0
  74. package/dist/server/managers/platform/trusted-network.js +39 -0
  75. package/dist/server/managers/runtime.js +56 -0
  76. package/dist/server/managers/type-guards.js +4 -0
  77. package/dist/server/openapi.js +3512 -0
  78. package/dist/server/psyche-types.js +395 -0
  79. package/dist/server/repositories/activity-events.js +157 -0
  80. package/dist/server/repositories/collaboration.js +497 -0
  81. package/dist/server/repositories/comments.js +176 -0
  82. package/dist/server/repositories/deleted-entities.js +192 -0
  83. package/dist/server/repositories/domains.js +30 -0
  84. package/dist/server/repositories/event-log.js +64 -0
  85. package/dist/server/repositories/goals.js +159 -0
  86. package/dist/server/repositories/projects.js +214 -0
  87. package/dist/server/repositories/psyche.js +1356 -0
  88. package/dist/server/repositories/rewards.js +675 -0
  89. package/dist/server/repositories/settings.js +399 -0
  90. package/dist/server/repositories/tags.js +160 -0
  91. package/dist/server/repositories/task-runs.js +488 -0
  92. package/dist/server/repositories/tasks.js +413 -0
  93. package/dist/server/services/context.js +214 -0
  94. package/dist/server/services/dashboard.js +170 -0
  95. package/dist/server/services/entity-crud.js +576 -0
  96. package/dist/server/services/gamification.js +215 -0
  97. package/dist/server/services/insights.js +91 -0
  98. package/dist/server/services/projects.js +75 -0
  99. package/dist/server/services/psyche.js +63 -0
  100. package/dist/server/services/relations.js +28 -0
  101. package/dist/server/services/reviews.js +88 -0
  102. package/dist/server/services/run-recovery.js +13 -0
  103. package/dist/server/services/tagging.js +49 -0
  104. package/dist/server/services/task-run-watchdog.js +92 -0
  105. package/dist/server/services/work-time.js +176 -0
  106. package/dist/server/types.js +999 -0
  107. package/dist/server/web.js +91 -0
  108. package/openclaw.plugin.json +21 -9
  109. package/package.json +17 -4
  110. package/server/migrations/001_core.sql +333 -0
  111. package/server/migrations/002_psyche.sql +241 -0
  112. package/server/migrations/003_timer_execution.sql +18 -0
  113. package/server/migrations/004_psyche_linked_entities.sql +5 -0
  114. package/server/migrations/005_adaptive_schemas.sql +157 -0
  115. package/server/migrations/006_psyche_auth_setting.sql +4 -0
  116. package/server/migrations/007_deleted_entities.sql +16 -0
  117. package/skills/forge-openclaw/SKILL.md +189 -275
@@ -0,0 +1,91 @@
1
+ import { access, readFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ const distDir = path.join(process.cwd(), "dist");
4
+ const defaultBasePath = process.env.FORGE_BASE_PATH ?? "/forge/";
5
+ const contentTypes = {
6
+ ".css": "text/css; charset=utf-8",
7
+ ".html": "text/html; charset=utf-8",
8
+ ".js": "application/javascript; charset=utf-8",
9
+ ".json": "application/json; charset=utf-8",
10
+ ".map": "application/json; charset=utf-8",
11
+ ".svg": "image/svg+xml",
12
+ ".woff": "font/woff",
13
+ ".woff2": "font/woff2"
14
+ };
15
+ function normalizeBasePath(value) {
16
+ if (!value || value === "/") {
17
+ return "/";
18
+ }
19
+ const withLeadingSlash = value.startsWith("/") ? value : `/${value}`;
20
+ return withLeadingSlash.endsWith("/") ? withLeadingSlash : `${withLeadingSlash}/`;
21
+ }
22
+ function stripBasePath(requestPath, basePath) {
23
+ const normalizedBasePath = normalizeBasePath(basePath);
24
+ if (normalizedBasePath === "/") {
25
+ return requestPath;
26
+ }
27
+ const normalizedRoot = normalizedBasePath.slice(0, -1);
28
+ if (requestPath === normalizedRoot) {
29
+ return "/";
30
+ }
31
+ if (requestPath.startsWith(normalizedBasePath)) {
32
+ const stripped = requestPath.slice(normalizedRoot.length);
33
+ return stripped.startsWith("/") ? stripped : `/${stripped}`;
34
+ }
35
+ return requestPath;
36
+ }
37
+ function resolveAsset(clientDir, requestPath) {
38
+ if (requestPath === "/") {
39
+ return path.join(clientDir, "index.html");
40
+ }
41
+ const safePath = requestPath.replace(/^\/+/, "");
42
+ return path.join(clientDir, safePath);
43
+ }
44
+ async function getClientDir() {
45
+ await access(path.join(distDir, "index.html"));
46
+ return distDir;
47
+ }
48
+ async function serveAsset(requestPath, reply) {
49
+ if (requestPath.startsWith("/api")) {
50
+ reply.code(404);
51
+ return { error: "Not found" };
52
+ }
53
+ const clientDir = await getClientDir();
54
+ const normalizedRequestPath = stripBasePath(requestPath, defaultBasePath);
55
+ const assetPath = resolveAsset(clientDir, normalizedRequestPath);
56
+ const ext = path.extname(assetPath);
57
+ try {
58
+ const payload = await readFile(assetPath);
59
+ reply.type(contentTypes[ext] ?? "application/octet-stream");
60
+ reply.header("Cache-Control", "no-store, max-age=0, must-revalidate");
61
+ if (ext === ".html") {
62
+ reply.header("Pragma", "no-cache");
63
+ }
64
+ return payload;
65
+ }
66
+ catch {
67
+ if (!path.extname(normalizedRequestPath)) {
68
+ try {
69
+ const payload = await readFile(path.join(clientDir, "index.html"));
70
+ reply.type(contentTypes[".html"]);
71
+ reply.header("Cache-Control", "no-store, max-age=0, must-revalidate");
72
+ reply.header("Pragma", "no-cache");
73
+ return payload;
74
+ }
75
+ catch {
76
+ reply.code(503);
77
+ return {
78
+ code: "frontend_not_built",
79
+ error: "Forge frontend build output is missing. Run the Vite build before serving the modern web client.",
80
+ statusCode: 503
81
+ };
82
+ }
83
+ }
84
+ reply.code(404);
85
+ return { error: "Asset not found" };
86
+ }
87
+ }
88
+ export async function registerWebRoutes(app) {
89
+ app.get("/", async (_request, reply) => serveAsset("/", reply));
90
+ app.get("/*", async (request, reply) => serveAsset(request.url, reply));
91
+ }
@@ -1,16 +1,21 @@
1
1
  {
2
2
  "id": "forge-openclaw-plugin",
3
3
  "name": "Forge",
4
- "description": "Thin OpenClaw adapter for the live Forge /api/v1 collaboration API.",
5
- "version": "0.2.4",
4
+ "description": "Curated OpenClaw adapter for the Forge collaboration API, UI entrypoint, and localhost auto-start runtime.",
5
+ "version": "0.2.7",
6
6
  "skills": [
7
7
  "./skills"
8
8
  ],
9
9
  "uiHints": {
10
- "baseUrl": {
11
- "label": "Forge Base URL",
12
- "help": "Base URL of the live Forge API bridge.",
13
- "placeholder": "http://127.0.0.1:3017"
10
+ "origin": {
11
+ "label": "Forge Origin",
12
+ "help": "Protocol and host for Forge without the port. Example: http://127.0.0.1. Localhost targets auto-start Forge.",
13
+ "placeholder": "http://127.0.0.1"
14
+ },
15
+ "port": {
16
+ "label": "Forge Port",
17
+ "help": "Forge server port. Change this if your local machine uses another port.",
18
+ "placeholder": "4317"
14
19
  },
15
20
  "apiToken": {
16
21
  "label": "Forge API Token",
@@ -33,10 +38,17 @@
33
38
  "type": "object",
34
39
  "additionalProperties": false,
35
40
  "properties": {
36
- "baseUrl": {
41
+ "origin": {
37
42
  "type": "string",
38
- "default": "http://127.0.0.1:3017",
39
- "description": "Base URL of the live Forge API bridge."
43
+ "default": "http://127.0.0.1",
44
+ "description": "Forge protocol and host without the port. Example: http://127.0.0.1. Localhost targets auto-start the bundled Forge runtime."
45
+ },
46
+ "port": {
47
+ "type": "integer",
48
+ "default": 4317,
49
+ "minimum": 1,
50
+ "maximum": 65535,
51
+ "description": "Forge server port. Override this when your local machine uses a different port."
40
52
  },
41
53
  "apiToken": {
42
54
  "type": "string",
package/package.json CHANGED
@@ -1,10 +1,19 @@
1
1
  {
2
2
  "name": "forge-openclaw-plugin",
3
- "version": "0.2.4",
4
- "description": "Thin OpenClaw adapter for the live Forge /api/v1 collaboration API.",
3
+ "version": "0.2.7",
4
+ "description": "Curated OpenClaw adapter for the Forge collaboration API, UI entrypoint, and localhost auto-start runtime.",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "private": false,
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/albertbuchard/forge.git",
11
+ "directory": "openclaw-plugin"
12
+ },
13
+ "homepage": "https://github.com/albertbuchard/forge/tree/main/openclaw-plugin#readme",
14
+ "bugs": {
15
+ "url": "https://github.com/albertbuchard/forge/issues"
16
+ },
8
17
  "publishConfig": {
9
18
  "access": "public"
10
19
  },
@@ -17,6 +26,7 @@
17
26
  ],
18
27
  "files": [
19
28
  "dist",
29
+ "server",
20
30
  "skills",
21
31
  "README.md",
22
32
  "openclaw.plugin.json"
@@ -34,9 +44,12 @@
34
44
  "openclaw": "^2026.3.22"
35
45
  },
36
46
  "dependencies": {
37
- "@sinclair/typebox": "^0.34.48"
47
+ "@fastify/cors": "^10.0.1",
48
+ "@sinclair/typebox": "^0.34.48",
49
+ "fastify": "^5.2.1",
50
+ "zod": "^3.25.67"
38
51
  },
39
52
  "scripts": {
40
- "build": "npm exec -- tsc -p tsconfig.build.json"
53
+ "build": "node ./scripts/build.mjs"
41
54
  }
42
55
  }
@@ -0,0 +1,333 @@
1
+ CREATE TABLE IF NOT EXISTS goals (
2
+ id TEXT PRIMARY KEY,
3
+ title TEXT NOT NULL,
4
+ description TEXT NOT NULL DEFAULT '',
5
+ horizon TEXT NOT NULL,
6
+ status TEXT NOT NULL,
7
+ target_points INTEGER NOT NULL,
8
+ theme_color TEXT NOT NULL,
9
+ created_at TEXT NOT NULL,
10
+ updated_at TEXT NOT NULL
11
+ );
12
+
13
+ CREATE TABLE IF NOT EXISTS tags (
14
+ id TEXT PRIMARY KEY,
15
+ name TEXT NOT NULL UNIQUE,
16
+ kind TEXT NOT NULL,
17
+ color TEXT NOT NULL,
18
+ description TEXT NOT NULL DEFAULT '',
19
+ created_at TEXT NOT NULL
20
+ );
21
+
22
+ CREATE TABLE IF NOT EXISTS goal_tags (
23
+ goal_id TEXT NOT NULL,
24
+ tag_id TEXT NOT NULL,
25
+ PRIMARY KEY (goal_id, tag_id),
26
+ FOREIGN KEY (goal_id) REFERENCES goals(id) ON DELETE CASCADE,
27
+ FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE
28
+ );
29
+
30
+ CREATE TABLE IF NOT EXISTS projects (
31
+ id TEXT PRIMARY KEY,
32
+ goal_id TEXT NOT NULL,
33
+ title TEXT NOT NULL,
34
+ description TEXT NOT NULL DEFAULT '',
35
+ status TEXT NOT NULL,
36
+ theme_color TEXT NOT NULL,
37
+ target_points INTEGER NOT NULL,
38
+ created_at TEXT NOT NULL,
39
+ updated_at TEXT NOT NULL,
40
+ FOREIGN KEY (goal_id) REFERENCES goals(id) ON DELETE CASCADE
41
+ );
42
+
43
+ CREATE TABLE IF NOT EXISTS tasks (
44
+ id TEXT PRIMARY KEY,
45
+ title TEXT NOT NULL,
46
+ description TEXT NOT NULL DEFAULT '',
47
+ status TEXT NOT NULL,
48
+ priority TEXT NOT NULL,
49
+ owner TEXT NOT NULL,
50
+ goal_id TEXT,
51
+ project_id TEXT,
52
+ due_date TEXT,
53
+ effort TEXT NOT NULL,
54
+ energy TEXT NOT NULL,
55
+ points INTEGER NOT NULL,
56
+ sort_order INTEGER NOT NULL,
57
+ completed_at TEXT,
58
+ created_at TEXT NOT NULL,
59
+ updated_at TEXT NOT NULL,
60
+ FOREIGN KEY (goal_id) REFERENCES goals(id) ON DELETE SET NULL,
61
+ FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE SET NULL
62
+ );
63
+
64
+ CREATE TABLE IF NOT EXISTS task_tags (
65
+ task_id TEXT NOT NULL,
66
+ tag_id TEXT NOT NULL,
67
+ PRIMARY KEY (task_id, tag_id),
68
+ FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE CASCADE,
69
+ FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE
70
+ );
71
+
72
+ CREATE TABLE IF NOT EXISTS activity_events (
73
+ id TEXT PRIMARY KEY,
74
+ entity_type TEXT NOT NULL,
75
+ entity_id TEXT NOT NULL,
76
+ event_type TEXT NOT NULL,
77
+ title TEXT NOT NULL,
78
+ description TEXT NOT NULL DEFAULT '',
79
+ actor TEXT,
80
+ source TEXT NOT NULL,
81
+ metadata_json TEXT NOT NULL DEFAULT '{}',
82
+ created_at TEXT NOT NULL
83
+ );
84
+
85
+ CREATE TABLE IF NOT EXISTS activity_event_corrections (
86
+ corrected_event_id TEXT PRIMARY KEY,
87
+ correcting_event_id TEXT NOT NULL,
88
+ created_at TEXT NOT NULL,
89
+ FOREIGN KEY (corrected_event_id) REFERENCES activity_events(id) ON DELETE CASCADE,
90
+ FOREIGN KEY (correcting_event_id) REFERENCES activity_events(id) ON DELETE CASCADE
91
+ );
92
+
93
+ CREATE TABLE IF NOT EXISTS task_create_idempotency (
94
+ idempotency_key TEXT PRIMARY KEY,
95
+ request_fingerprint TEXT NOT NULL,
96
+ task_id TEXT NOT NULL UNIQUE,
97
+ created_at TEXT NOT NULL,
98
+ FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE CASCADE
99
+ );
100
+
101
+ CREATE TABLE IF NOT EXISTS task_runs (
102
+ id TEXT PRIMARY KEY,
103
+ task_id TEXT NOT NULL,
104
+ actor TEXT NOT NULL,
105
+ status TEXT NOT NULL,
106
+ note TEXT NOT NULL DEFAULT '',
107
+ lease_ttl_seconds INTEGER NOT NULL,
108
+ claimed_at TEXT NOT NULL,
109
+ heartbeat_at TEXT NOT NULL,
110
+ lease_expires_at TEXT NOT NULL,
111
+ completed_at TEXT,
112
+ released_at TEXT,
113
+ timed_out_at TEXT,
114
+ updated_at TEXT NOT NULL,
115
+ FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE CASCADE
116
+ );
117
+
118
+ CREATE TABLE IF NOT EXISTS app_settings (
119
+ id INTEGER PRIMARY KEY CHECK (id = 1),
120
+ operator_name TEXT NOT NULL,
121
+ operator_email TEXT NOT NULL,
122
+ operator_title TEXT NOT NULL,
123
+ theme_preference TEXT NOT NULL,
124
+ locale_preference TEXT NOT NULL DEFAULT 'en',
125
+ goal_drift_alerts INTEGER NOT NULL DEFAULT 1,
126
+ daily_quest_reminders INTEGER NOT NULL DEFAULT 1,
127
+ achievement_celebrations INTEGER NOT NULL DEFAULT 1,
128
+ integrity_score INTEGER NOT NULL DEFAULT 98,
129
+ last_audit_at TEXT NOT NULL,
130
+ created_at TEXT NOT NULL,
131
+ updated_at TEXT NOT NULL
132
+ );
133
+
134
+ CREATE TABLE IF NOT EXISTS agent_identities (
135
+ id TEXT PRIMARY KEY,
136
+ label TEXT NOT NULL,
137
+ agent_type TEXT NOT NULL,
138
+ trust_level TEXT NOT NULL DEFAULT 'standard',
139
+ autonomy_mode TEXT NOT NULL DEFAULT 'approval_required',
140
+ approval_mode TEXT NOT NULL DEFAULT 'approval_by_default',
141
+ description TEXT NOT NULL DEFAULT '',
142
+ created_at TEXT NOT NULL,
143
+ updated_at TEXT NOT NULL
144
+ );
145
+
146
+ CREATE TABLE IF NOT EXISTS agent_tokens (
147
+ id TEXT PRIMARY KEY,
148
+ label TEXT NOT NULL,
149
+ token_hash TEXT NOT NULL,
150
+ token_prefix TEXT NOT NULL,
151
+ scopes_json TEXT NOT NULL DEFAULT '[]',
152
+ agent_id TEXT,
153
+ trust_level TEXT NOT NULL DEFAULT 'standard',
154
+ autonomy_mode TEXT NOT NULL DEFAULT 'approval_required',
155
+ approval_mode TEXT NOT NULL DEFAULT 'approval_by_default',
156
+ description TEXT NOT NULL DEFAULT '',
157
+ last_used_at TEXT,
158
+ revoked_at TEXT,
159
+ created_at TEXT NOT NULL,
160
+ updated_at TEXT NOT NULL,
161
+ FOREIGN KEY (agent_id) REFERENCES agent_identities(id) ON DELETE SET NULL
162
+ );
163
+
164
+ CREATE TABLE IF NOT EXISTS event_log (
165
+ id TEXT PRIMARY KEY,
166
+ event_kind TEXT NOT NULL,
167
+ entity_type TEXT NOT NULL,
168
+ entity_id TEXT NOT NULL,
169
+ actor TEXT,
170
+ source TEXT NOT NULL,
171
+ caused_by_event_id TEXT,
172
+ metadata_json TEXT NOT NULL DEFAULT '{}',
173
+ created_at TEXT NOT NULL,
174
+ FOREIGN KEY (caused_by_event_id) REFERENCES event_log(id) ON DELETE SET NULL
175
+ );
176
+
177
+ CREATE TABLE IF NOT EXISTS agent_actions (
178
+ id TEXT PRIMARY KEY,
179
+ agent_id TEXT,
180
+ token_id TEXT,
181
+ action_type TEXT NOT NULL,
182
+ risk_level TEXT NOT NULL,
183
+ status TEXT NOT NULL,
184
+ title TEXT NOT NULL,
185
+ summary TEXT NOT NULL DEFAULT '',
186
+ payload_json TEXT NOT NULL DEFAULT '{}',
187
+ idempotency_key TEXT,
188
+ approval_request_id TEXT,
189
+ outcome_json TEXT NOT NULL DEFAULT '{}',
190
+ created_at TEXT NOT NULL,
191
+ updated_at TEXT NOT NULL,
192
+ completed_at TEXT,
193
+ FOREIGN KEY (agent_id) REFERENCES agent_identities(id) ON DELETE SET NULL,
194
+ FOREIGN KEY (token_id) REFERENCES agent_tokens(id) ON DELETE SET NULL
195
+ );
196
+
197
+ CREATE TABLE IF NOT EXISTS approval_requests (
198
+ id TEXT PRIMARY KEY,
199
+ action_type TEXT NOT NULL,
200
+ status TEXT NOT NULL,
201
+ title TEXT NOT NULL,
202
+ summary TEXT NOT NULL DEFAULT '',
203
+ entity_type TEXT,
204
+ entity_id TEXT,
205
+ requested_by_agent_id TEXT,
206
+ requested_by_token_id TEXT,
207
+ requested_payload_json TEXT NOT NULL DEFAULT '{}',
208
+ approved_by TEXT,
209
+ approved_at TEXT,
210
+ rejected_by TEXT,
211
+ rejected_at TEXT,
212
+ resolution_note TEXT NOT NULL DEFAULT '',
213
+ created_at TEXT NOT NULL,
214
+ updated_at TEXT NOT NULL,
215
+ FOREIGN KEY (requested_by_agent_id) REFERENCES agent_identities(id) ON DELETE SET NULL,
216
+ FOREIGN KEY (requested_by_token_id) REFERENCES agent_tokens(id) ON DELETE SET NULL
217
+ );
218
+
219
+ CREATE TABLE IF NOT EXISTS insights (
220
+ id TEXT PRIMARY KEY,
221
+ origin_type TEXT NOT NULL,
222
+ origin_agent_id TEXT,
223
+ origin_label TEXT,
224
+ visibility TEXT NOT NULL DEFAULT 'visible',
225
+ status TEXT NOT NULL DEFAULT 'open',
226
+ entity_type TEXT,
227
+ entity_id TEXT,
228
+ timeframe_label TEXT,
229
+ title TEXT NOT NULL,
230
+ summary TEXT NOT NULL,
231
+ recommendation TEXT NOT NULL,
232
+ rationale TEXT NOT NULL DEFAULT '',
233
+ confidence REAL NOT NULL DEFAULT 0.5,
234
+ cta_label TEXT NOT NULL DEFAULT 'Review insight',
235
+ evidence_json TEXT NOT NULL DEFAULT '[]',
236
+ created_at TEXT NOT NULL,
237
+ updated_at TEXT NOT NULL,
238
+ FOREIGN KEY (origin_agent_id) REFERENCES agent_identities(id) ON DELETE SET NULL
239
+ );
240
+
241
+ CREATE TABLE IF NOT EXISTS insight_feedback (
242
+ id TEXT PRIMARY KEY,
243
+ insight_id TEXT NOT NULL,
244
+ actor TEXT,
245
+ feedback_type TEXT NOT NULL,
246
+ note TEXT NOT NULL DEFAULT '',
247
+ created_at TEXT NOT NULL,
248
+ FOREIGN KEY (insight_id) REFERENCES insights(id) ON DELETE CASCADE
249
+ );
250
+
251
+ CREATE TABLE IF NOT EXISTS reward_rules (
252
+ id TEXT PRIMARY KEY,
253
+ family TEXT NOT NULL,
254
+ code TEXT NOT NULL UNIQUE,
255
+ title TEXT NOT NULL,
256
+ description TEXT NOT NULL DEFAULT '',
257
+ active INTEGER NOT NULL DEFAULT 1,
258
+ config_json TEXT NOT NULL DEFAULT '{}',
259
+ created_at TEXT NOT NULL,
260
+ updated_at TEXT NOT NULL
261
+ );
262
+
263
+ CREATE TABLE IF NOT EXISTS reward_ledger (
264
+ id TEXT PRIMARY KEY,
265
+ rule_id TEXT,
266
+ event_log_id TEXT,
267
+ entity_type TEXT NOT NULL,
268
+ entity_id TEXT NOT NULL,
269
+ actor TEXT,
270
+ source TEXT NOT NULL,
271
+ delta_xp INTEGER NOT NULL,
272
+ reason_title TEXT NOT NULL,
273
+ reason_summary TEXT NOT NULL DEFAULT '',
274
+ reversible_group TEXT,
275
+ reversed_by_reward_id TEXT,
276
+ metadata_json TEXT NOT NULL DEFAULT '{}',
277
+ created_at TEXT NOT NULL,
278
+ FOREIGN KEY (rule_id) REFERENCES reward_rules(id) ON DELETE SET NULL,
279
+ FOREIGN KEY (event_log_id) REFERENCES event_log(id) ON DELETE SET NULL,
280
+ FOREIGN KEY (reversed_by_reward_id) REFERENCES reward_ledger(id) ON DELETE SET NULL
281
+ );
282
+
283
+ CREATE TABLE IF NOT EXISTS session_events (
284
+ id TEXT PRIMARY KEY,
285
+ session_id TEXT NOT NULL,
286
+ event_type TEXT NOT NULL,
287
+ actor TEXT,
288
+ source TEXT NOT NULL,
289
+ metrics_json TEXT NOT NULL DEFAULT '{}',
290
+ created_at TEXT NOT NULL
291
+ );
292
+
293
+ CREATE TABLE IF NOT EXISTS operator_sessions (
294
+ id TEXT PRIMARY KEY,
295
+ session_hash TEXT NOT NULL UNIQUE,
296
+ actor_label TEXT NOT NULL,
297
+ issued_at TEXT NOT NULL,
298
+ last_used_at TEXT NOT NULL,
299
+ expires_at TEXT NOT NULL,
300
+ revoked_at TEXT,
301
+ created_at TEXT NOT NULL,
302
+ updated_at TEXT NOT NULL
303
+ );
304
+
305
+ CREATE INDEX IF NOT EXISTS idx_tasks_status_sort ON tasks(status, sort_order);
306
+ CREATE INDEX IF NOT EXISTS idx_tasks_goal ON tasks(goal_id);
307
+ CREATE INDEX IF NOT EXISTS idx_tasks_project ON tasks(project_id, status, sort_order);
308
+ CREATE INDEX IF NOT EXISTS idx_tags_kind ON tags(kind);
309
+ CREATE INDEX IF NOT EXISTS idx_projects_goal ON projects(goal_id, created_at);
310
+ CREATE INDEX IF NOT EXISTS idx_activity_events_created_at ON activity_events(created_at DESC);
311
+ CREATE INDEX IF NOT EXISTS idx_activity_events_entity ON activity_events(entity_type, entity_id, created_at DESC);
312
+ CREATE INDEX IF NOT EXISTS idx_task_runs_task_id ON task_runs(task_id);
313
+ CREATE INDEX IF NOT EXISTS idx_task_runs_status_expiry ON task_runs(status, lease_expires_at);
314
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_task_runs_one_active_per_task
315
+ ON task_runs(task_id)
316
+ WHERE status = 'active';
317
+ CREATE INDEX IF NOT EXISTS idx_agent_tokens_active ON agent_tokens(revoked_at, created_at DESC);
318
+ CREATE INDEX IF NOT EXISTS idx_agent_tokens_agent ON agent_tokens(agent_id, created_at DESC);
319
+ CREATE INDEX IF NOT EXISTS idx_event_log_entity ON event_log(entity_type, entity_id, created_at DESC);
320
+ CREATE INDEX IF NOT EXISTS idx_event_log_kind ON event_log(event_kind, created_at DESC);
321
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_agent_actions_idempotency ON agent_actions(idempotency_key) WHERE idempotency_key IS NOT NULL;
322
+ CREATE INDEX IF NOT EXISTS idx_agent_actions_agent ON agent_actions(agent_id, created_at DESC);
323
+ CREATE INDEX IF NOT EXISTS idx_approval_requests_status ON approval_requests(status, created_at DESC);
324
+ CREATE INDEX IF NOT EXISTS idx_insights_status ON insights(status, created_at DESC);
325
+ CREATE INDEX IF NOT EXISTS idx_insights_entity ON insights(entity_type, entity_id, created_at DESC);
326
+ CREATE INDEX IF NOT EXISTS idx_insight_feedback_insight ON insight_feedback(insight_id, created_at DESC);
327
+ CREATE INDEX IF NOT EXISTS idx_reward_ledger_created ON reward_ledger(created_at DESC);
328
+ CREATE INDEX IF NOT EXISTS idx_reward_ledger_entity ON reward_ledger(entity_type, entity_id, created_at DESC);
329
+ CREATE INDEX IF NOT EXISTS idx_reward_ledger_group ON reward_ledger(reversible_group);
330
+ CREATE INDEX IF NOT EXISTS idx_session_events_session ON session_events(session_id, created_at DESC);
331
+ CREATE INDEX IF NOT EXISTS idx_session_events_type ON session_events(event_type, created_at DESC);
332
+ CREATE INDEX IF NOT EXISTS idx_operator_sessions_active
333
+ ON operator_sessions(revoked_at, expires_at, last_used_at DESC);