forge-openclaw-plugin 0.2.48 → 0.2.49

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/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-BAmEvOXb.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>
@@ -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,
@@ -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'));
@@ -2772,6 +2772,10 @@ const AGENT_ONBOARDING_CONVERSATION_RULES = [
2772
2772
  "When useful, help the user name, define, and connect the record in that order: offer a working label, clarify what belongs inside it, then ask about links only after the record itself feels steady.",
2773
2773
  "When the meaning is clearer than the wording, offer a tentative title or formulation yourself and invite correction instead of forcing the user to wordsmith alone.",
2774
2774
  "For direct update or review requests, the next question should usually narrow the saved object, timeframe, or route family instead of reopening the whole meaning-making arc.",
2775
+ "The opening question should help the user understand what they are actually trying to save, decide, review, or change, not make them perform the schema out loud.",
2776
+ "If the user already named the exact correction in usable language, confirm only the missing scope, timing, or route-selecting detail that still matters, then act.",
2777
+ "Once a specialized-surface lane is clear, speak in route-relevant nouns such as timeline, overlay, weekday template, published output, run detail, or node result instead of generic record language.",
2778
+ "If the next answer would not change the route, wording, timing, or write payload in a meaningful way, stop asking and act.",
2775
2779
  "Before saving, briefly summarize the working formulation in the user's own language when that would reduce ambiguity.",
2776
2780
  "Once the record is clear enough to name, stop exploring broadly and ask only for the last structural detail that still matters.",
2777
2781
  "If the record is already clear enough to save, save it instead of performing a ceremonial extra question.",
@@ -4157,6 +4161,11 @@ function buildAgentOnboardingPayload(request) {
4157
4161
  specializedDomainSurfaces: {
4158
4162
  movement: {
4159
4163
  summary: "Dedicated movement workspace API. Use these routes for stays, trips, time-in-place questions, visited places, trip detail, selection aggregates, user-defined overlays, and repair actions on already-recorded movement data.",
4164
+ routeSelectionQuestions: [
4165
+ "Is the user asking for a day, month, all-time, timeline, place, trip detail, or selected-span answer?",
4166
+ "Is this a missing-gap overlay, a saved-overlay repair, or an edit to one already-recorded stay, trip, or trip point?",
4167
+ "If the target is already known, what one time, place, or saved-object detail is still missing before acting?"
4168
+ ],
4160
4169
  readRoutes: {
4161
4170
  day: "/api/v1/movement/day",
4162
4171
  month: "/api/v1/movement/month",
@@ -4194,6 +4203,11 @@ function buildAgentOnboardingPayload(request) {
4194
4203
  },
4195
4204
  lifeForce: {
4196
4205
  summary: "Dedicated life-force API. Use it to read the current energy budget, drains, recommendations, and warnings, then patch only the parts that are meant to be user-controlled.",
4206
+ routeSelectionQuestions: [
4207
+ "Is the user trying to understand the overview, change durable profile assumptions, change a weekday curve, or log a right-now fatigue signal?",
4208
+ "Are they describing a repeatable weekly shape or a one-off current state?",
4209
+ "If the lane is already clear, what one weekday, profile field, or signal detail is still missing?"
4210
+ ],
4197
4211
  readRoutes: {
4198
4212
  overview: "/api/v1/life-force"
4199
4213
  },
@@ -4206,12 +4220,18 @@ function buildAgentOnboardingPayload(request) {
4206
4220
  "Life Force is a focused domain surface, not a batch CRUD entity type.",
4207
4221
  "Use GET /api/v1/life-force for the current overview payload with stats, drains, recommendations, and current-curve state.",
4208
4222
  "Patch the profile only for durable personal settings, update weekday templates only for the curve itself, and post fatigue signals for real-time tired or recovered observations.",
4223
+ "If the user says something like 'I always dip on Tuesdays after lunch', treat that as a weekday-template change rather than a one-off fatigue signal.",
4209
4224
  "If the user is asking what changed after a profile, template, or fatigue write, read the overview back so the effect stays visible.",
4210
4225
  "If the user already knows they want a profile change, weekday-template edit, or right-now fatigue signal, skip the broad lane question and ask only for the missing weekday, profile field, or signal detail."
4211
4226
  ]
4212
4227
  },
4213
4228
  workbench: {
4214
4229
  summary: "Dedicated graph-flow API. Use it for flow catalog reads, flow CRUD, execution, run history, published outputs, node results, and latest successful node outputs.",
4230
+ routeSelectionQuestions: [
4231
+ "Is the job flow discovery, flow editing, execution, published output, run detail, node result, latest node output, or flow chat follow-up?",
4232
+ "Does the user need a stable public contract or one execution artifact?",
4233
+ "If the flow is already known, what one run, node, or output scope detail is still missing before acting?"
4234
+ ],
4215
4235
  readRoutes: {
4216
4236
  listFlows: "/api/v1/workbench/flows",
4217
4237
  flowById: "/api/v1/workbench/flows/:id",
@@ -4237,6 +4257,7 @@ function buildAgentOnboardingPayload(request) {
4237
4257
  "Use the flow routes when the agent needs stable public input contracts, published outputs, node-level results, or reusable execution history.",
4238
4258
  "If the user is still figuring out inputs or editable structure, read flow detail or box catalog before asking them to author a payload from memory.",
4239
4259
  "Prefer the dedicated output and node-result routes over reverse-engineering raw traces.",
4260
+ "If the user only wants a published output, latest node output, or run detail, do not reopen a flow-edit intake before reading that artifact.",
4240
4261
  "If the user already named the flow and wants one output or one run, skip the broad lane question and ask only for the missing run, node, or output scope."
4241
4262
  ]
4242
4263
  }
@@ -4444,8 +4465,8 @@ function buildAgentOnboardingPayload(request) {
4444
4465
  saveSuggestionPlacement: "end_of_message",
4445
4466
  saveSuggestionTone: "gentle_optional",
4446
4467
  maxQuestionsPerTurn: 1,
4447
- psycheExplorationRule: "When a Psyche entity needs understanding first, begin with one exploratory question before any working formulation, replacement belief, suggested title, or save pitch. Keep the opening reflection to one or two short sentences, stay in plain prose instead of bullets or numbered lists, keep that first reply short, do not mention Forge search or save structure yet, avoid colons or list-shaped phrasing, prefer what/when/how over why until the experience is grounded, wait for the user's answer before offering a fuller formulation, ask permission before moving from charged exploration into naming or challenge when needed, do not widen into adjacent entities until the current one has a working sentence the user recognizes, and once the lived experience is coherent stop deepening and help the user name it cleanly. When the user is updating a Psyche record because of one fresh episode, anchor in that episode before renaming the durable formulation. If the user accepts the wording, move toward the save instead of reopening deeper exploration.",
4448
- specializedSurfaceRule: "For Movement, Life Force, and Workbench, clarify the lane first, then name the dedicated route family in plain language and do not guess at a generic CRUD path. If the truth of the current state is still uncertain, read the relevant specialized view before you mutate it. When the user already named a precise correction or review target, confirm only the route-selecting detail that is still missing. After a concrete specialized-surface correction, read the relevant specialized view back when the user is trying to understand the result rather than just store it. The canonical runtime routes stay under /api/v1/*, and the OpenClaw HTTP mirror exposes the same families under /forge/v1/movement, /forge/v1/life-force, and /forge/v1/workbench.",
4468
+ psycheExplorationRule: "When a Psyche entity needs understanding first, begin with one exploratory question before any working formulation, replacement belief, suggested title, or save pitch. Keep the opening reflection to one or two short sentences, stay in plain prose instead of bullets or numbered lists, keep that first reply short, do not mention Forge search or save structure yet, avoid colons or list-shaped phrasing, prefer what/when/how over why until the experience is grounded, wait for the user's answer before offering a fuller formulation, ask permission before moving from charged exploration into naming or challenge when needed, make the next question help the user feel more able to name the experience rather than more examined, do not widen into adjacent entities until the current one has a working sentence the user recognizes, and once the lived experience is coherent stop deepening and help the user name it cleanly. When the user is updating a Psyche record because of one fresh episode, anchor in that episode before renaming the durable formulation. If the user accepts the wording, move toward the save instead of reopening deeper exploration.",
4469
+ specializedSurfaceRule: "For Movement, Life Force, and Workbench, clarify the lane first, then name the dedicated route family in plain language and do not guess at a generic CRUD path. Use specializedDomainSurfaces.routeSelectionQuestions when they are present so the next follow-up stays route-selective instead of generic. Once the lane is clear, talk in route-relevant nouns such as timeline, overlay, weekday template, published output, run detail, or node result rather than generic record language. If the truth of the current state is still uncertain, read the relevant specialized view before you mutate it. When the user already named a precise correction or review target, confirm only the route-selecting detail that is still missing. After a concrete specialized-surface correction, read the relevant specialized view back when the user is trying to understand the result rather than just store it. The canonical runtime routes stay under /api/v1/*, and the OpenClaw HTTP mirror exposes the same families under /forge/v1/movement, /forge/v1/life-force, and /forge/v1/workbench.",
4449
4470
  reviewShortcutRule: "When the user is reviewing or correcting an existing record, narrow the saved object, timeframe, or route family first. Do not reopen the whole intake unless the user is actually redefining the record.",
4450
4471
  readModelWriteRule: "Self-observation is note-backed and should be written through observed notes with frontmatter.observedAt. Sleep and workout sessions stay on batch CRUD by default; use the reflective review helpers only when enriching one already-known record after review.",
4451
4472
  psycheOpeningQuestionRule: "Prefer a concrete opening question tied to the entity: ask when the value mattered, what happened the last time the pattern appeared, what cue or body signal came first before the behavior, what the belief starts saying about self or outcome, what feels most at risk inside the mode, what the part is trying to get the user to do or stop doing, or where the shift began in the incident. Reflect briefly before the question, choose one follow-up lane at a time, say what is becoming clearer before the next deeper question, and if several Psyche entities are visible hold the adjacent ones lightly until the main container is clear.",
@@ -2137,6 +2137,11 @@ export function buildOpenApiDocument() {
2137
2137
  "id",
2138
2138
  "label",
2139
2139
  "agentType",
2140
+ "identityKey",
2141
+ "provider",
2142
+ "machineKey",
2143
+ "personaKey",
2144
+ "linkedUsers",
2140
2145
  "trustLevel",
2141
2146
  "autonomyMode",
2142
2147
  "approvalMode",
@@ -2150,6 +2155,20 @@ export function buildOpenApiDocument() {
2150
2155
  id: { type: "string" },
2151
2156
  label: { type: "string" },
2152
2157
  agentType: { type: "string" },
2158
+ identityKey: nullable({ type: "string" }),
2159
+ provider: nullable({ type: "string", enum: ["openclaw", "hermes", "codex"] }),
2160
+ machineKey: nullable({ type: "string" }),
2161
+ personaKey: nullable({ type: "string" }),
2162
+ linkedUsers: arrayOf({
2163
+ type: "object",
2164
+ additionalProperties: false,
2165
+ required: ["userId", "role", "user"],
2166
+ properties: {
2167
+ userId: { type: "string" },
2168
+ role: { type: "string" },
2169
+ user: nullable({ $ref: "#/components/schemas/UserSummary" })
2170
+ }
2171
+ }),
2153
2172
  trustLevel: {
2154
2173
  type: "string",
2155
2174
  enum: ["standard", "trusted", "autonomous"]