forge-openclaw-plugin 0.2.48 → 0.2.50

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 (36) hide show
  1. package/README.md +3 -3
  2. package/dist/assets/index-2_tuemtU.css +1 -0
  3. package/dist/assets/index-C9_gJvi6.js +91 -0
  4. package/dist/assets/index-C9_gJvi6.js.map +1 -0
  5. package/dist/index.html +2 -2
  6. package/dist/openclaw/parity.js +14 -0
  7. package/dist/openclaw/routes.js +42 -0
  8. package/dist/openclaw/session-registry.js +17 -0
  9. package/dist/openclaw/tools.js +3 -3
  10. package/dist/server/server/migrations/019_wiki_memory.sql +1 -1
  11. package/dist/server/server/migrations/052_agent_identity_tightening.sql +307 -0
  12. package/dist/server/server/migrations/053_agent_runtime_session_canonical_labels.sql +9 -0
  13. package/dist/server/server/migrations/054_sqlite_backed_wiki_memory.sql +8 -0
  14. package/dist/server/server/src/app.js +46 -14
  15. package/dist/server/server/src/db.js +0 -2
  16. package/dist/server/server/src/openapi.js +58 -3
  17. package/dist/server/server/src/repositories/agent-runtime-sessions.js +122 -16
  18. package/dist/server/server/src/repositories/model-settings.js +5 -0
  19. package/dist/server/server/src/repositories/notes.js +5 -2
  20. package/dist/server/server/src/repositories/settings.js +101 -13
  21. package/dist/server/server/src/repositories/users.js +23 -0
  22. package/dist/server/server/src/repositories/wiki-memory.js +16 -190
  23. package/dist/server/server/src/services/data-management.js +2 -9
  24. package/dist/server/server/src/types.js +13 -0
  25. package/openclaw.plugin.json +1 -1
  26. package/package.json +5 -2
  27. package/server/migrations/019_wiki_memory.sql +1 -1
  28. package/server/migrations/052_agent_identity_tightening.sql +307 -0
  29. package/server/migrations/053_agent_runtime_session_canonical_labels.sql +9 -0
  30. package/server/migrations/054_sqlite_backed_wiki_memory.sql +8 -0
  31. package/skills/forge-openclaw/SKILL.md +6 -6
  32. package/skills/forge-openclaw/entity_conversation_playbooks.md +49 -0
  33. package/skills/forge-openclaw/psyche_entity_playbooks.md +32 -0
  34. package/dist/assets/index-Bv9FWWsZ.js +0 -91
  35. package/dist/assets/index-Bv9FWWsZ.js.map +0 -1
  36. package/dist/assets/index-DtEvFzXp.css +0 -1
package/dist/index.html CHANGED
@@ -13,14 +13,14 @@
13
13
  />
14
14
  <link rel="icon" type="image/png" href="/forge/assets/favicon-BCHm9dUV.ico" />
15
15
  <link rel="alternate icon" href="/forge/assets/favicon-BCHm9dUV.ico" />
16
- <script type="module" crossorigin src="/forge/assets/index-Bv9FWWsZ.js"></script>
16
+ <script type="module" crossorigin src="/forge/assets/index-C9_gJvi6.js"></script>
17
17
  <link rel="modulepreload" crossorigin href="/forge/assets/vendor-D_NZFJze.js">
18
18
  <link rel="modulepreload" crossorigin href="/forge/assets/board-CAszQU7Y.js">
19
19
  <link rel="modulepreload" crossorigin href="/forge/assets/ui-B5MjRjKe.js">
20
20
  <link rel="modulepreload" crossorigin href="/forge/assets/motion-CU5aNClV.js">
21
21
  <link rel="modulepreload" crossorigin href="/forge/assets/table-CK0KcPYW.js">
22
22
  <link rel="stylesheet" crossorigin href="/forge/assets/vendor-DT3pnAKJ.css">
23
- <link rel="stylesheet" crossorigin href="/forge/assets/index-DtEvFzXp.css">
23
+ <link rel="stylesheet" crossorigin href="/forge/assets/index-2_tuemtU.css">
24
24
  </head>
25
25
  <body class="bg-canvas text-ink antialiased">
26
26
  <div id="root"></div>
@@ -34,11 +34,13 @@ export const FORGE_SUPPORTED_PLUGIN_API_ROUTES = [
34
34
  { method: "GET", path: "/api/v1/movement/all-time", purpose: "health" },
35
35
  { method: "GET", path: "/api/v1/movement/timeline", purpose: "health" },
36
36
  { method: "GET", path: "/api/v1/movement/places", purpose: "health" },
37
+ { method: "GET", path: "/api/v1/movement/boxes/:id", purpose: "health" },
37
38
  { method: "POST", path: "/api/v1/movement/places", purpose: "health" },
38
39
  { method: "PATCH", path: "/api/v1/movement/places/:id", purpose: "health" },
39
40
  { method: "GET", path: "/api/v1/movement/trips/:id", purpose: "health" },
40
41
  { method: "POST", path: "/api/v1/movement/selection", purpose: "health" },
41
42
  { method: "GET", path: "/api/v1/movement/settings", purpose: "health" },
43
+ { method: "PATCH", path: "/api/v1/movement/settings", purpose: "health" },
42
44
  { method: "POST", path: "/api/v1/movement/user-boxes", purpose: "health" },
43
45
  {
44
46
  method: "POST",
@@ -50,18 +52,30 @@ export const FORGE_SUPPORTED_PLUGIN_API_ROUTES = [
50
52
  path: "/api/v1/movement/user-boxes/:id",
51
53
  purpose: "health"
52
54
  },
55
+ {
56
+ method: "DELETE",
57
+ path: "/api/v1/movement/user-boxes/:id",
58
+ purpose: "health"
59
+ },
53
60
  {
54
61
  method: "POST",
55
62
  path: "/api/v1/movement/automatic-boxes/:id/invalidate",
56
63
  purpose: "health"
57
64
  },
58
65
  { method: "PATCH", path: "/api/v1/movement/stays/:id", purpose: "health" },
66
+ { method: "DELETE", path: "/api/v1/movement/stays/:id", purpose: "health" },
59
67
  { method: "PATCH", path: "/api/v1/movement/trips/:id", purpose: "health" },
68
+ { method: "DELETE", path: "/api/v1/movement/trips/:id", purpose: "health" },
60
69
  {
61
70
  method: "PATCH",
62
71
  path: "/api/v1/movement/trips/:id/points/:pointId",
63
72
  purpose: "health"
64
73
  },
74
+ {
75
+ method: "DELETE",
76
+ path: "/api/v1/movement/trips/:id/points/:pointId",
77
+ purpose: "health"
78
+ },
65
79
  { method: "GET", path: "/api/v1/life-force", purpose: "health" },
66
80
  { method: "PATCH", path: "/api/v1/life-force/profile", purpose: "health" },
67
81
  {
@@ -198,6 +198,12 @@ export const FORGE_PLUGIN_ROUTE_GROUPS = [
198
198
  upstreamPath: "/api/v1/movement/places",
199
199
  target: (_match, url) => passthroughSearch("/api/v1/movement/places", url)
200
200
  },
201
+ {
202
+ method: "GET",
203
+ pattern: /^\/forge\/v1\/movement\/boxes\/([^/]+)$/,
204
+ upstreamPath: "/api/v1/movement/boxes/:id",
205
+ target: (match, url) => passthroughSearch(`/api/v1/movement/boxes/${match[1]}`, url)
206
+ },
201
207
  {
202
208
  method: "POST",
203
209
  pattern: /^\/forge\/v1\/movement\/places$/,
@@ -233,6 +239,14 @@ export const FORGE_PLUGIN_ROUTE_GROUPS = [
233
239
  upstreamPath: "/api/v1/movement/settings",
234
240
  target: (_match, url) => passthroughSearch("/api/v1/movement/settings", url)
235
241
  },
242
+ {
243
+ method: "PATCH",
244
+ pattern: /^\/forge\/v1\/movement\/settings$/,
245
+ upstreamPath: "/api/v1/movement/settings",
246
+ requestBody: "json",
247
+ requiresToken: true,
248
+ target: (_match, url) => passthroughSearch("/api/v1/movement/settings", url)
249
+ },
236
250
  {
237
251
  method: "POST",
238
252
  pattern: /^\/forge\/v1\/movement\/user-boxes$/,
@@ -257,6 +271,13 @@ export const FORGE_PLUGIN_ROUTE_GROUPS = [
257
271
  requiresToken: true,
258
272
  target: (match, url) => passthroughSearch(`/api/v1/movement/user-boxes/${match[1]}`, url)
259
273
  },
274
+ {
275
+ method: "DELETE",
276
+ pattern: /^\/forge\/v1\/movement\/user-boxes\/([^/]+)$/,
277
+ upstreamPath: "/api/v1/movement/user-boxes/:id",
278
+ requiresToken: true,
279
+ target: (match, url) => passthroughSearch(`/api/v1/movement/user-boxes/${match[1]}`, url)
280
+ },
260
281
  {
261
282
  method: "POST",
262
283
  pattern: /^\/forge\/v1\/movement\/automatic-boxes\/([^/]+)\/invalidate$/,
@@ -273,6 +294,13 @@ export const FORGE_PLUGIN_ROUTE_GROUPS = [
273
294
  requiresToken: true,
274
295
  target: (match, url) => passthroughSearch(`/api/v1/movement/stays/${match[1]}`, url)
275
296
  },
297
+ {
298
+ method: "DELETE",
299
+ pattern: /^\/forge\/v1\/movement\/stays\/([^/]+)$/,
300
+ upstreamPath: "/api/v1/movement/stays/:id",
301
+ requiresToken: true,
302
+ target: (match, url) => passthroughSearch(`/api/v1/movement/stays/${match[1]}`, url)
303
+ },
276
304
  {
277
305
  method: "PATCH",
278
306
  pattern: /^\/forge\/v1\/movement\/trips\/([^/]+)$/,
@@ -281,6 +309,13 @@ export const FORGE_PLUGIN_ROUTE_GROUPS = [
281
309
  requiresToken: true,
282
310
  target: (match, url) => passthroughSearch(`/api/v1/movement/trips/${match[1]}`, url)
283
311
  },
312
+ {
313
+ method: "DELETE",
314
+ pattern: /^\/forge\/v1\/movement\/trips\/([^/]+)$/,
315
+ upstreamPath: "/api/v1/movement/trips/:id",
316
+ requiresToken: true,
317
+ target: (match, url) => passthroughSearch(`/api/v1/movement/trips/${match[1]}`, url)
318
+ },
284
319
  {
285
320
  method: "PATCH",
286
321
  pattern: /^\/forge\/v1\/movement\/trips\/([^/]+)\/points\/([^/]+)$/,
@@ -288,6 +323,13 @@ export const FORGE_PLUGIN_ROUTE_GROUPS = [
288
323
  requestBody: "json",
289
324
  requiresToken: true,
290
325
  target: (match, url) => passthroughSearch(`/api/v1/movement/trips/${match[1]}/points/${match[2]}`, url)
326
+ },
327
+ {
328
+ method: "DELETE",
329
+ pattern: /^\/forge\/v1\/movement\/trips\/([^/]+)\/points\/([^/]+)$/,
330
+ upstreamPath: "/api/v1/movement/trips/:id/points/:pointId",
331
+ requiresToken: true,
332
+ target: (match, url) => passthroughSearch(`/api/v1/movement/trips/${match[1]}/points/${match[2]}`, url)
291
333
  }
292
334
  ]
293
335
  },
@@ -1,8 +1,22 @@
1
+ import { createHash } from "node:crypto";
1
2
  import { isAgentBootstrapEvent } from "openclaw/plugin-sdk/hook-runtime";
2
3
  import { callConfiguredForgeApi, expectForgeSuccess, resolveConfiguredForgeActorLabel } from "./api-client.js";
3
4
  const SESSION_IDS = new Map();
4
5
  const SESSION_PROVIDER = "openclaw";
5
6
  const DEFAULT_RUNTIME_AGENT_LABEL = "Forge OpenClaw";
7
+ function shortHash(value) {
8
+ return createHash("sha1").update(value).digest("hex").slice(0, 12);
9
+ }
10
+ function buildStableMachineKey(config) {
11
+ const source = JSON.stringify({
12
+ baseUrl: config.baseUrl,
13
+ dataRoot: config.dataRoot || ""
14
+ });
15
+ return `machine_${shortHash(source)}`;
16
+ }
17
+ function buildStableAgentIdentityKey(config) {
18
+ return `runtime:${SESSION_PROVIDER}:${buildStableMachineKey(config)}:default`;
19
+ }
6
20
  function isRecord(value) {
7
21
  return typeof value === "object" && value !== null;
8
22
  }
@@ -40,6 +54,9 @@ async function registerSession(config, sessionKey, metadata) {
40
54
  provider: SESSION_PROVIDER,
41
55
  agentLabel: process.env.FORGE_AGENT_LABEL?.trim() || DEFAULT_RUNTIME_AGENT_LABEL,
42
56
  agentType: SESSION_PROVIDER,
57
+ agentIdentityKey: buildStableAgentIdentityKey(config),
58
+ machineKey: buildStableMachineKey(config),
59
+ personaKey: "default",
43
60
  actorLabel,
44
61
  sessionKey,
45
62
  sessionLabel: sessionKey,
@@ -344,7 +344,7 @@ export function registerForgePluginTools(api, config) {
344
344
  api.registerTool({
345
345
  name: "forge_upsert_wiki_page",
346
346
  label: "Forge Upsert Wiki Page",
347
- description: "Create a new wiki page or update an existing one through the file-backed wiki surface.",
347
+ description: "Create a new wiki page or update an existing one through the SQLite-backed wiki surface.",
348
348
  parameters: wikiPageMutationSchema(),
349
349
  async execute(_toolCallId, params) {
350
350
  const typed = (params ?? {});
@@ -375,8 +375,8 @@ export function registerForgePluginTools(api, config) {
375
375
  });
376
376
  registerWriteTool(api, config, {
377
377
  name: "forge_sync_wiki_vault",
378
- label: "Forge Sync Wiki Vault",
379
- description: "Resync Markdown files from the local wiki vault into Forge metadata.",
378
+ label: "Forge Refresh Wiki Indexes",
379
+ description: "Rebuild SQLite wiki search, link, and metadata indexes.",
380
380
  parameters: Type.Object({
381
381
  spaceId: optionalString()
382
382
  }),
@@ -57,7 +57,7 @@ VALUES (
57
57
  'wiki_space_shared',
58
58
  'shared',
59
59
  'Shared Forge Memory',
60
- 'Shared wiki space for file-backed Forge knowledge.',
60
+ 'Shared wiki space for SQLite-backed Forge knowledge.',
61
61
  NULL,
62
62
  'shared',
63
63
  CURRENT_TIMESTAMP,
@@ -0,0 +1,307 @@
1
+ ALTER TABLE agent_identities ADD COLUMN identity_key TEXT;
2
+ ALTER TABLE agent_identities ADD COLUMN provider TEXT;
3
+ ALTER TABLE agent_identities ADD COLUMN machine_key TEXT;
4
+ ALTER TABLE agent_identities ADD COLUMN persona_key TEXT;
5
+
6
+ CREATE TABLE IF NOT EXISTS agent_identity_users (
7
+ agent_id TEXT NOT NULL REFERENCES agent_identities(id) ON DELETE CASCADE,
8
+ user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
9
+ role TEXT NOT NULL DEFAULT 'linked',
10
+ created_at TEXT NOT NULL,
11
+ updated_at TEXT NOT NULL,
12
+ PRIMARY KEY (agent_id, user_id)
13
+ );
14
+
15
+ CREATE INDEX IF NOT EXISTS idx_agent_identity_users_user
16
+ ON agent_identity_users(user_id, role);
17
+
18
+ INSERT OR IGNORE INTO users (
19
+ id, kind, handle, display_name, description, accent_color, created_at, updated_at
20
+ ) VALUES
21
+ ('user_agent_openclaw', 'bot', 'openclaw', 'OpenClaw', 'OpenClaw runtime actor linked to Forge agent identity and Kanban ownership.', '#38bdf8', datetime('now'), datetime('now')),
22
+ ('user_agent_hermes', 'bot', 'hermes', 'Hermes', 'Hermes runtime actor linked to Forge agent identity and Kanban ownership.', '#a78bfa', datetime('now'), datetime('now')),
23
+ ('user_agent_codex', 'bot', 'codex', 'Codex', 'Codex runtime actor linked to Forge agent identity and Kanban ownership.', '#22c55e', datetime('now'), datetime('now'));
24
+
25
+ UPDATE agent_tokens
26
+ SET agent_id = (
27
+ SELECT id FROM agent_identities
28
+ WHERE lower(agent_type) = 'openclaw' OR lower(label) IN ('forge openclaw', 'openclaw', 'aurel')
29
+ ORDER BY CASE WHEN lower(label) = 'forge openclaw' THEN 0 ELSE 1 END, created_at ASC
30
+ LIMIT 1
31
+ )
32
+ WHERE agent_id IN (
33
+ SELECT id FROM agent_identities
34
+ WHERE lower(agent_type) = 'openclaw' OR lower(label) IN ('forge openclaw', 'openclaw', 'aurel')
35
+ )
36
+ AND (SELECT COUNT(*) FROM agent_identities WHERE lower(agent_type) = 'openclaw' OR lower(label) IN ('forge openclaw', 'openclaw', 'aurel')) > 0;
37
+
38
+ UPDATE agent_actions
39
+ SET agent_id = (
40
+ SELECT id FROM agent_identities
41
+ WHERE lower(agent_type) = 'openclaw' OR lower(label) IN ('forge openclaw', 'openclaw', 'aurel')
42
+ ORDER BY CASE WHEN lower(label) = 'forge openclaw' THEN 0 ELSE 1 END, created_at ASC
43
+ LIMIT 1
44
+ )
45
+ WHERE agent_id IN (
46
+ SELECT id FROM agent_identities
47
+ WHERE lower(agent_type) = 'openclaw' OR lower(label) IN ('forge openclaw', 'openclaw', 'aurel')
48
+ )
49
+ AND (SELECT COUNT(*) FROM agent_identities WHERE lower(agent_type) = 'openclaw' OR lower(label) IN ('forge openclaw', 'openclaw', 'aurel')) > 0;
50
+
51
+ UPDATE approval_requests
52
+ SET requested_by_agent_id = (
53
+ SELECT id FROM agent_identities
54
+ WHERE lower(agent_type) = 'openclaw' OR lower(label) IN ('forge openclaw', 'openclaw', 'aurel')
55
+ ORDER BY CASE WHEN lower(label) = 'forge openclaw' THEN 0 ELSE 1 END, created_at ASC
56
+ LIMIT 1
57
+ )
58
+ WHERE requested_by_agent_id IN (
59
+ SELECT id FROM agent_identities
60
+ WHERE lower(agent_type) = 'openclaw' OR lower(label) IN ('forge openclaw', 'openclaw', 'aurel')
61
+ )
62
+ AND (SELECT COUNT(*) FROM agent_identities WHERE lower(agent_type) = 'openclaw' OR lower(label) IN ('forge openclaw', 'openclaw', 'aurel')) > 0;
63
+
64
+ UPDATE insights
65
+ SET origin_agent_id = (
66
+ SELECT id FROM agent_identities
67
+ WHERE lower(agent_type) = 'openclaw' OR lower(label) IN ('forge openclaw', 'openclaw', 'aurel')
68
+ ORDER BY CASE WHEN lower(label) = 'forge openclaw' THEN 0 ELSE 1 END, created_at ASC
69
+ LIMIT 1
70
+ )
71
+ WHERE origin_agent_id IN (
72
+ SELECT id FROM agent_identities
73
+ WHERE lower(agent_type) = 'openclaw' OR lower(label) IN ('forge openclaw', 'openclaw', 'aurel')
74
+ )
75
+ AND (SELECT COUNT(*) FROM agent_identities WHERE lower(agent_type) = 'openclaw' OR lower(label) IN ('forge openclaw', 'openclaw', 'aurel')) > 0;
76
+
77
+ UPDATE agent_runtime_sessions
78
+ SET agent_id = (
79
+ SELECT id FROM agent_identities
80
+ WHERE lower(agent_type) = 'openclaw' OR lower(label) IN ('forge openclaw', 'openclaw', 'aurel')
81
+ ORDER BY CASE WHEN lower(label) = 'forge openclaw' THEN 0 ELSE 1 END, created_at ASC
82
+ LIMIT 1
83
+ )
84
+ WHERE agent_id IN (
85
+ SELECT id FROM agent_identities
86
+ WHERE lower(agent_type) = 'openclaw' OR lower(label) IN ('forge openclaw', 'openclaw', 'aurel')
87
+ )
88
+ AND (SELECT COUNT(*) FROM agent_identities WHERE lower(agent_type) = 'openclaw' OR lower(label) IN ('forge openclaw', 'openclaw', 'aurel')) > 0;
89
+
90
+ DELETE FROM agent_identities
91
+ WHERE (lower(agent_type) = 'openclaw' OR lower(label) IN ('forge openclaw', 'openclaw', 'aurel'))
92
+ AND id <> (
93
+ SELECT id FROM agent_identities
94
+ WHERE lower(agent_type) = 'openclaw' OR lower(label) IN ('forge openclaw', 'openclaw', 'aurel')
95
+ ORDER BY CASE WHEN lower(label) = 'forge openclaw' THEN 0 ELSE 1 END, created_at ASC
96
+ LIMIT 1
97
+ );
98
+
99
+ UPDATE agent_identities
100
+ SET label = 'Forge OpenClaw',
101
+ agent_type = 'openclaw',
102
+ provider = 'openclaw',
103
+ identity_key = 'runtime:openclaw:legacy:default',
104
+ machine_key = 'legacy',
105
+ persona_key = 'default',
106
+ description = 'OpenClaw runtime agent with stable Forge identity and linked Kanban user.',
107
+ updated_at = datetime('now')
108
+ WHERE lower(agent_type) = 'openclaw' OR lower(label) = 'forge openclaw';
109
+
110
+ UPDATE agent_tokens
111
+ SET agent_id = (
112
+ SELECT id FROM agent_identities
113
+ WHERE lower(agent_type) = 'hermes' OR lower(label) LIKE 'forge hermes%'
114
+ ORDER BY CASE WHEN lower(label) = 'forge hermes' THEN 0 ELSE 1 END, created_at ASC
115
+ LIMIT 1
116
+ )
117
+ WHERE agent_id IN (
118
+ SELECT id FROM agent_identities
119
+ WHERE lower(agent_type) = 'hermes' OR lower(label) LIKE 'forge hermes%'
120
+ )
121
+ AND (SELECT COUNT(*) FROM agent_identities WHERE lower(agent_type) = 'hermes' OR lower(label) LIKE 'forge hermes%') > 0;
122
+
123
+ UPDATE agent_actions
124
+ SET agent_id = (
125
+ SELECT id FROM agent_identities
126
+ WHERE lower(agent_type) = 'hermes' OR lower(label) LIKE 'forge hermes%'
127
+ ORDER BY CASE WHEN lower(label) = 'forge hermes' THEN 0 ELSE 1 END, created_at ASC
128
+ LIMIT 1
129
+ )
130
+ WHERE agent_id IN (
131
+ SELECT id FROM agent_identities
132
+ WHERE lower(agent_type) = 'hermes' OR lower(label) LIKE 'forge hermes%'
133
+ )
134
+ AND (SELECT COUNT(*) FROM agent_identities WHERE lower(agent_type) = 'hermes' OR lower(label) LIKE 'forge hermes%') > 0;
135
+
136
+ UPDATE approval_requests
137
+ SET requested_by_agent_id = (
138
+ SELECT id FROM agent_identities
139
+ WHERE lower(agent_type) = 'hermes' OR lower(label) LIKE 'forge hermes%'
140
+ ORDER BY CASE WHEN lower(label) = 'forge hermes' THEN 0 ELSE 1 END, created_at ASC
141
+ LIMIT 1
142
+ )
143
+ WHERE requested_by_agent_id IN (
144
+ SELECT id FROM agent_identities
145
+ WHERE lower(agent_type) = 'hermes' OR lower(label) LIKE 'forge hermes%'
146
+ )
147
+ AND (SELECT COUNT(*) FROM agent_identities WHERE lower(agent_type) = 'hermes' OR lower(label) LIKE 'forge hermes%') > 0;
148
+
149
+ UPDATE insights
150
+ SET origin_agent_id = (
151
+ SELECT id FROM agent_identities
152
+ WHERE lower(agent_type) = 'hermes' OR lower(label) LIKE 'forge hermes%'
153
+ ORDER BY CASE WHEN lower(label) = 'forge hermes' THEN 0 ELSE 1 END, created_at ASC
154
+ LIMIT 1
155
+ )
156
+ WHERE origin_agent_id IN (
157
+ SELECT id FROM agent_identities
158
+ WHERE lower(agent_type) = 'hermes' OR lower(label) LIKE 'forge hermes%'
159
+ )
160
+ AND (SELECT COUNT(*) FROM agent_identities WHERE lower(agent_type) = 'hermes' OR lower(label) LIKE 'forge hermes%') > 0;
161
+
162
+ UPDATE agent_runtime_sessions
163
+ SET agent_id = (
164
+ SELECT id FROM agent_identities
165
+ WHERE lower(agent_type) = 'hermes' OR lower(label) LIKE 'forge hermes%'
166
+ ORDER BY CASE WHEN lower(label) = 'forge hermes' THEN 0 ELSE 1 END, created_at ASC
167
+ LIMIT 1
168
+ )
169
+ WHERE agent_id IN (
170
+ SELECT id FROM agent_identities
171
+ WHERE lower(agent_type) = 'hermes' OR lower(label) LIKE 'forge hermes%'
172
+ )
173
+ AND (SELECT COUNT(*) FROM agent_identities WHERE lower(agent_type) = 'hermes' OR lower(label) LIKE 'forge hermes%') > 0;
174
+
175
+ DELETE FROM agent_identities
176
+ WHERE (lower(agent_type) = 'hermes' OR lower(label) LIKE 'forge hermes%')
177
+ AND id <> (
178
+ SELECT id FROM agent_identities
179
+ WHERE lower(agent_type) = 'hermes' OR lower(label) LIKE 'forge hermes%'
180
+ ORDER BY CASE WHEN lower(label) = 'forge hermes' THEN 0 ELSE 1 END, created_at ASC
181
+ LIMIT 1
182
+ );
183
+
184
+ UPDATE agent_identities
185
+ SET label = 'Forge Hermes',
186
+ agent_type = 'hermes',
187
+ provider = 'hermes',
188
+ identity_key = 'runtime:hermes:legacy:default',
189
+ machine_key = 'legacy',
190
+ persona_key = 'default',
191
+ description = 'Hermes runtime agent with stable Forge identity and linked Kanban user.',
192
+ updated_at = datetime('now')
193
+ WHERE lower(agent_type) = 'hermes' OR lower(label) = 'forge hermes';
194
+
195
+ UPDATE agent_tokens
196
+ SET agent_id = (
197
+ SELECT id FROM agent_identities
198
+ WHERE lower(agent_type) = 'codex' OR lower(label) IN ('forge codex', 'codex', 'albert (codex)')
199
+ ORDER BY CASE WHEN lower(label) = 'forge codex' THEN 0 ELSE 1 END, created_at ASC
200
+ LIMIT 1
201
+ )
202
+ WHERE agent_id IN (
203
+ SELECT id FROM agent_identities
204
+ WHERE lower(agent_type) = 'codex' OR lower(label) IN ('forge codex', 'codex', 'albert (codex)')
205
+ )
206
+ AND (SELECT COUNT(*) FROM agent_identities WHERE lower(agent_type) = 'codex' OR lower(label) IN ('forge codex', 'codex', 'albert (codex)')) > 0;
207
+
208
+ UPDATE agent_actions
209
+ SET agent_id = (
210
+ SELECT id FROM agent_identities
211
+ WHERE lower(agent_type) = 'codex' OR lower(label) IN ('forge codex', 'codex', 'albert (codex)')
212
+ ORDER BY CASE WHEN lower(label) = 'forge codex' THEN 0 ELSE 1 END, created_at ASC
213
+ LIMIT 1
214
+ )
215
+ WHERE agent_id IN (
216
+ SELECT id FROM agent_identities
217
+ WHERE lower(agent_type) = 'codex' OR lower(label) IN ('forge codex', 'codex', 'albert (codex)')
218
+ )
219
+ AND (SELECT COUNT(*) FROM agent_identities WHERE lower(agent_type) = 'codex' OR lower(label) IN ('forge codex', 'codex', 'albert (codex)')) > 0;
220
+
221
+ UPDATE approval_requests
222
+ SET requested_by_agent_id = (
223
+ SELECT id FROM agent_identities
224
+ WHERE lower(agent_type) = 'codex' OR lower(label) IN ('forge codex', 'codex', 'albert (codex)')
225
+ ORDER BY CASE WHEN lower(label) = 'forge codex' THEN 0 ELSE 1 END, created_at ASC
226
+ LIMIT 1
227
+ )
228
+ WHERE requested_by_agent_id IN (
229
+ SELECT id FROM agent_identities
230
+ WHERE lower(agent_type) = 'codex' OR lower(label) IN ('forge codex', 'codex', 'albert (codex)')
231
+ )
232
+ AND (SELECT COUNT(*) FROM agent_identities WHERE lower(agent_type) = 'codex' OR lower(label) IN ('forge codex', 'codex', 'albert (codex)')) > 0;
233
+
234
+ UPDATE insights
235
+ SET origin_agent_id = (
236
+ SELECT id FROM agent_identities
237
+ WHERE lower(agent_type) = 'codex' OR lower(label) IN ('forge codex', 'codex', 'albert (codex)')
238
+ ORDER BY CASE WHEN lower(label) = 'forge codex' THEN 0 ELSE 1 END, created_at ASC
239
+ LIMIT 1
240
+ )
241
+ WHERE origin_agent_id IN (
242
+ SELECT id FROM agent_identities
243
+ WHERE lower(agent_type) = 'codex' OR lower(label) IN ('forge codex', 'codex', 'albert (codex)')
244
+ )
245
+ AND (SELECT COUNT(*) FROM agent_identities WHERE lower(agent_type) = 'codex' OR lower(label) IN ('forge codex', 'codex', 'albert (codex)')) > 0;
246
+
247
+ UPDATE agent_runtime_sessions
248
+ SET agent_id = (
249
+ SELECT id FROM agent_identities
250
+ WHERE lower(agent_type) = 'codex' OR lower(label) IN ('forge codex', 'codex', 'albert (codex)')
251
+ ORDER BY CASE WHEN lower(label) = 'forge codex' THEN 0 ELSE 1 END, created_at ASC
252
+ LIMIT 1
253
+ )
254
+ WHERE agent_id IN (
255
+ SELECT id FROM agent_identities
256
+ WHERE lower(agent_type) = 'codex' OR lower(label) IN ('forge codex', 'codex', 'albert (codex)')
257
+ )
258
+ AND (SELECT COUNT(*) FROM agent_identities WHERE lower(agent_type) = 'codex' OR lower(label) IN ('forge codex', 'codex', 'albert (codex)')) > 0;
259
+
260
+ DELETE FROM agent_identities
261
+ WHERE (lower(agent_type) = 'codex' OR lower(label) IN ('forge codex', 'codex', 'albert (codex)'))
262
+ AND id <> (
263
+ SELECT id FROM agent_identities
264
+ WHERE lower(agent_type) = 'codex' OR lower(label) IN ('forge codex', 'codex', 'albert (codex)')
265
+ ORDER BY CASE WHEN lower(label) = 'forge codex' THEN 0 ELSE 1 END, created_at ASC
266
+ LIMIT 1
267
+ );
268
+
269
+ UPDATE agent_identities
270
+ SET label = 'Forge Codex',
271
+ agent_type = 'codex',
272
+ provider = 'codex',
273
+ identity_key = 'runtime:codex:legacy:default',
274
+ machine_key = 'legacy',
275
+ persona_key = 'default',
276
+ description = 'Codex runtime agent with stable Forge identity and linked Kanban user.',
277
+ updated_at = datetime('now')
278
+ WHERE lower(agent_type) = 'codex' OR lower(label) = 'forge codex';
279
+
280
+ INSERT OR IGNORE INTO agent_identity_users (agent_id, user_id, role, created_at, updated_at)
281
+ SELECT id, 'user_agent_openclaw', 'primary', datetime('now'), datetime('now')
282
+ FROM agent_identities
283
+ WHERE provider = 'openclaw';
284
+
285
+ INSERT OR IGNORE INTO agent_identity_users (agent_id, user_id, role, created_at, updated_at)
286
+ SELECT id, 'user_agent_hermes', 'primary', datetime('now'), datetime('now')
287
+ FROM agent_identities
288
+ WHERE provider = 'hermes';
289
+
290
+ INSERT OR IGNORE INTO agent_identity_users (agent_id, user_id, role, created_at, updated_at)
291
+ SELECT id, 'user_agent_codex', 'primary', datetime('now'), datetime('now')
292
+ FROM agent_identities
293
+ WHERE provider = 'codex';
294
+
295
+ UPDATE agent_runtime_sessions
296
+ SET agent_label = (
297
+ SELECT label FROM agent_identities WHERE agent_identities.id = agent_runtime_sessions.agent_id
298
+ ),
299
+ agent_type = (
300
+ SELECT agent_type FROM agent_identities WHERE agent_identities.id = agent_runtime_sessions.agent_id
301
+ ),
302
+ updated_at = datetime('now')
303
+ WHERE agent_id IN (SELECT id FROM agent_identities WHERE provider IN ('openclaw', 'hermes', 'codex'));
304
+
305
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_agent_identities_identity_key
306
+ ON agent_identities(identity_key)
307
+ WHERE identity_key IS NOT NULL;
@@ -0,0 +1,9 @@
1
+ UPDATE agent_runtime_sessions
2
+ SET agent_label = (
3
+ SELECT label FROM agent_identities WHERE agent_identities.id = agent_runtime_sessions.agent_id
4
+ ),
5
+ agent_type = (
6
+ SELECT agent_type FROM agent_identities WHERE agent_identities.id = agent_runtime_sessions.agent_id
7
+ ),
8
+ updated_at = datetime('now')
9
+ WHERE agent_id IN (SELECT id FROM agent_identities WHERE provider IN ('openclaw', 'hermes', 'codex'));
@@ -0,0 +1,8 @@
1
+ UPDATE wiki_spaces
2
+ SET description = 'Shared wiki space for SQLite-backed Forge knowledge.'
3
+ WHERE id = 'wiki_space_shared'
4
+ AND description != 'Shared wiki space for SQLite-backed Forge knowledge.';
5
+
6
+ UPDATE notes
7
+ SET source_path = ''
8
+ WHERE source_path != '';