forge-openclaw-plugin 0.2.19 → 0.2.21

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 (82) hide show
  1. package/README.md +133 -2
  2. package/dist/assets/board-_C6oMy5w.js +6 -0
  3. package/dist/assets/{board-8L3uX7_O.js.map → board-_C6oMy5w.js.map} +1 -1
  4. package/dist/assets/index-B4A6TooJ.js +63 -0
  5. package/dist/assets/index-B4A6TooJ.js.map +1 -0
  6. package/dist/assets/index-D6Xs_2mo.css +1 -0
  7. package/dist/assets/{motion-1GAqqi8M.js → motion-D4sZgCHd.js} +2 -2
  8. package/dist/assets/{motion-1GAqqi8M.js.map → motion-D4sZgCHd.js.map} +1 -1
  9. package/dist/assets/{table-DBGlgRjk.js → table-BWzTaky1.js} +2 -2
  10. package/dist/assets/{table-DBGlgRjk.js.map → table-BWzTaky1.js.map} +1 -1
  11. package/dist/assets/{ui-iTluWjC4.js → ui-BzK4azQb.js} +7 -7
  12. package/dist/assets/{ui-iTluWjC4.js.map → ui-BzK4azQb.js.map} +1 -1
  13. package/dist/assets/vendor-DT3pnAKJ.css +1 -0
  14. package/dist/assets/vendor-De38P6YR.js +729 -0
  15. package/dist/assets/vendor-De38P6YR.js.map +1 -0
  16. package/dist/assets/viz-C6hfyqzu.js +34 -0
  17. package/dist/assets/viz-C6hfyqzu.js.map +1 -0
  18. package/dist/index.html +9 -9
  19. package/dist/openclaw/parity.d.ts +1 -1
  20. package/dist/openclaw/parity.js +29 -2
  21. package/dist/openclaw/routes.js +207 -24
  22. package/dist/openclaw/tools.js +324 -35
  23. package/dist/server/app.js +2080 -92
  24. package/dist/server/db.js +3 -0
  25. package/dist/server/health.js +1284 -0
  26. package/dist/server/managers/platform/background-job-manager.js +138 -2
  27. package/dist/server/managers/platform/llm-manager.js +126 -0
  28. package/dist/server/managers/platform/openai-responses-provider.js +773 -0
  29. package/dist/server/managers/runtime.js +6 -1
  30. package/dist/server/openapi.js +718 -0
  31. package/dist/server/preferences-seeds.js +409 -0
  32. package/dist/server/preferences-types.js +368 -0
  33. package/dist/server/psyche-types.js +42 -18
  34. package/dist/server/repositories/activity-events.js +53 -4
  35. package/dist/server/repositories/calendar.js +89 -15
  36. package/dist/server/repositories/collaboration.js +8 -3
  37. package/dist/server/repositories/diagnostic-logs.js +243 -0
  38. package/dist/server/repositories/entity-ownership.js +92 -0
  39. package/dist/server/repositories/goals.js +7 -2
  40. package/dist/server/repositories/habits.js +122 -16
  41. package/dist/server/repositories/notes.js +119 -41
  42. package/dist/server/repositories/preferences.js +1765 -0
  43. package/dist/server/repositories/projects.js +18 -7
  44. package/dist/server/repositories/psyche.js +84 -27
  45. package/dist/server/repositories/rewards.js +112 -4
  46. package/dist/server/repositories/strategies.js +450 -0
  47. package/dist/server/repositories/tags.js +11 -6
  48. package/dist/server/repositories/task-runs.js +10 -2
  49. package/dist/server/repositories/tasks.js +99 -17
  50. package/dist/server/repositories/users.js +417 -0
  51. package/dist/server/repositories/wiki-memory.js +3366 -0
  52. package/dist/server/services/context.js +20 -18
  53. package/dist/server/services/dashboard.js +29 -6
  54. package/dist/server/services/entity-crud.js +21 -3
  55. package/dist/server/services/insights.js +9 -7
  56. package/dist/server/services/projects.js +2 -1
  57. package/dist/server/services/psyche.js +10 -9
  58. package/dist/server/types.js +594 -30
  59. package/openclaw.plugin.json +1 -1
  60. package/package.json +1 -1
  61. package/server/migrations/015_multi_user_and_strategies.sql +244 -0
  62. package/server/migrations/016_health_companion.sql +158 -0
  63. package/server/migrations/016_strategy_contracts_and_user_graph.sql +22 -0
  64. package/server/migrations/017_preferences.sql +131 -0
  65. package/server/migrations/018_preference_catalogs.sql +31 -0
  66. package/server/migrations/019_wiki_memory.sql +255 -0
  67. package/server/migrations/020_wiki_page_hierarchy.sql +11 -0
  68. package/server/migrations/021_hide_evidence_from_wiki_index.sql +3 -0
  69. package/server/migrations/022_wiki_ingest_background.sql +85 -0
  70. package/server/migrations/023_diagnostic_logs.sql +28 -0
  71. package/skills/forge-openclaw/SKILL.md +126 -34
  72. package/skills/forge-openclaw/entity_conversation_playbooks.md +337 -0
  73. package/skills/forge-openclaw/psyche_entity_playbooks.md +404 -0
  74. package/dist/assets/board-8L3uX7_O.js +0 -6
  75. package/dist/assets/index-Cj1IBH_w.js +0 -36
  76. package/dist/assets/index-Cj1IBH_w.js.map +0 -1
  77. package/dist/assets/index-DQT6EbuS.css +0 -1
  78. package/dist/assets/vendor-BvM2F9Dp.js +0 -503
  79. package/dist/assets/vendor-BvM2F9Dp.js.map +0 -1
  80. package/dist/assets/vendor-CRS-psbw.css +0 -1
  81. package/dist/assets/viz-CNeunkfu.js +0 -34
  82. package/dist/assets/viz-CNeunkfu.js.map +0 -1
@@ -28,9 +28,17 @@ async function runWrite(config, options) {
28
28
  return expectForgeSuccess(result);
29
29
  }
30
30
  const emptyObjectSchema = Type.Object({});
31
+ const scopedReadSchema = Type.Object({
32
+ userIds: Type.Optional(Type.Array(Type.String()))
33
+ });
31
34
  const optionalString = () => Type.Optional(Type.String());
32
35
  const optionalNullableString = () => Type.Optional(Type.Union([Type.String(), Type.Null()]));
33
36
  const optionalDeleteMode = () => Type.Optional(Type.Union([Type.Literal("soft"), Type.Literal("hard")]));
37
+ const healthLinkInputSchema = () => Type.Object({
38
+ entityType: Type.String({ minLength: 1 }),
39
+ entityId: Type.String({ minLength: 1 }),
40
+ relationshipType: Type.Optional(Type.String({ minLength: 1 }))
41
+ });
34
42
  const noteInputSchema = () => Type.Object({
35
43
  contentMarkdown: Type.String({ minLength: 1 }),
36
44
  author: optionalNullableString(),
@@ -42,6 +50,24 @@ const noteInputSchema = () => Type.Object({
42
50
  anchorKey: optionalNullableString()
43
51
  })))
44
52
  });
53
+ const wikiPageMutationSchema = () => Type.Object({
54
+ pageId: optionalString(),
55
+ kind: Type.Optional(Type.Union([Type.Literal("wiki"), Type.Literal("evidence")])),
56
+ title: Type.String({ minLength: 1 }),
57
+ slug: optionalString(),
58
+ summary: optionalString(),
59
+ aliases: Type.Optional(Type.Array(Type.String())),
60
+ contentMarkdown: Type.String({ minLength: 1 }),
61
+ author: optionalNullableString(),
62
+ tags: Type.Optional(Type.Array(Type.String())),
63
+ spaceId: optionalString(),
64
+ frontmatter: Type.Optional(Type.Record(Type.String(), Type.Any())),
65
+ links: Type.Optional(Type.Array(Type.Object({
66
+ entityType: Type.String({ minLength: 1 }),
67
+ entityId: Type.String({ minLength: 1 }),
68
+ anchorKey: optionalNullableString()
69
+ })))
70
+ });
45
71
  async function resolveUiEntrypoint(config) {
46
72
  let webAppUrl = config.webAppUrl;
47
73
  try {
@@ -66,34 +92,37 @@ async function resolveUiEntrypoint(config) {
66
92
  note: "You can continue directly in the Forge UI when a visual workflow is easier for review, Kanban, or Psyche exploration."
67
93
  };
68
94
  }
69
- async function resolveCurrentWork(config) {
70
- const payload = await runRead(config, "/api/v1/operator/context");
71
- const context = typeof payload === "object" &&
72
- payload !== null &&
73
- "context" in payload &&
74
- typeof payload.context === "object" &&
75
- payload.context !== null
76
- ? payload.context
77
- : null;
78
- const recentTaskRuns = Array.isArray(context?.recentTaskRuns)
79
- ? context.recentTaskRuns
80
- : [];
81
- const activeTaskRuns = recentTaskRuns.filter((run) => typeof run === "object" &&
82
- run !== null &&
83
- "status" in run &&
84
- run.status === "active");
85
- const focusTasks = Array.isArray(context?.focusTasks)
86
- ? context.focusTasks
87
- : [];
88
- return {
89
- generatedAt: typeof context?.generatedAt === "string"
90
- ? context.generatedAt
91
- : new Date().toISOString(),
92
- activeTaskRuns,
93
- focusTasks,
94
- recommendedNextTask: context?.recommendedNextTask ?? null,
95
- xp: context?.xp ?? null
96
- };
95
+ function withUserIds(path, userIds) {
96
+ if (!userIds || userIds.length === 0) {
97
+ return path;
98
+ }
99
+ const search = new URLSearchParams();
100
+ for (const userId of userIds) {
101
+ if (userId.trim()) {
102
+ search.append("userIds", userId.trim());
103
+ }
104
+ }
105
+ return search.size > 0 ? `${path}?${search.toString()}` : path;
106
+ }
107
+ function withQueryParams(path, params, allowedKeys) {
108
+ const search = new URLSearchParams();
109
+ for (const key of allowedKeys) {
110
+ const value = params[key];
111
+ if (typeof value === "string" && value.trim()) {
112
+ search.set(key, value.trim());
113
+ }
114
+ else if (typeof value === "number" && Number.isFinite(value)) {
115
+ search.set(key, String(value));
116
+ }
117
+ else if (Array.isArray(value)) {
118
+ for (const item of value) {
119
+ if (typeof item === "string" && item.trim()) {
120
+ search.append(key, item.trim());
121
+ }
122
+ }
123
+ }
124
+ }
125
+ return search.size > 0 ? `${path}?${search.toString()}` : path;
97
126
  }
98
127
  function registerReadTool(api, config, options) {
99
128
  api.registerTool({
@@ -127,13 +156,15 @@ export function registerForgePluginTools(api, config) {
127
156
  name: "forge_get_operator_overview",
128
157
  label: "Forge Operator Overview",
129
158
  description: "Start here for most Forge work. Read the one-shot operator overview with current priorities, momentum, and onboarding guidance before searching or mutating.",
130
- path: () => "/api/v1/operator/overview"
159
+ parameters: scopedReadSchema,
160
+ path: (params) => withUserIds("/api/v1/operator/overview", params.userIds)
131
161
  });
132
162
  registerReadTool(api, config, {
133
163
  name: "forge_get_operator_context",
134
164
  label: "Forge Operator Context",
135
165
  description: "Read the current operational task board, focus queue, recent task runs, and XP state. Use this for current-work questions and work runtime decisions.",
136
- path: () => "/api/v1/operator/context"
166
+ parameters: scopedReadSchema,
167
+ path: (params) => withUserIds("/api/v1/operator/context", params.userIds)
137
168
  });
138
169
  registerReadTool(api, config, {
139
170
  name: "forge_get_agent_onboarding",
@@ -141,6 +172,12 @@ export function registerForgePluginTools(api, config) {
141
172
  description: "Fetch the live Forge onboarding contract with the exact Forge tool list, batch payload rules, UI handoff rules, and verification guidance.",
142
173
  path: () => "/api/v1/agents/onboarding"
143
174
  });
175
+ registerReadTool(api, config, {
176
+ name: "forge_get_user_directory",
177
+ label: "Forge User Directory",
178
+ description: "Read the current human and bot user directory, ownership counts, and directional relationship graph before doing multi-user planning or cross-owner edits.",
179
+ path: () => "/api/v1/users/directory"
180
+ });
144
181
  api.registerTool({
145
182
  name: "forge_get_ui_entrypoint",
146
183
  label: "Forge UI Entrypoint",
@@ -154,7 +191,8 @@ export function registerForgePluginTools(api, config) {
154
191
  name: "forge_get_psyche_overview",
155
192
  label: "Forge Psyche Overview",
156
193
  description: "Read the aggregate Psyche state across values, patterns, behaviors, beliefs, modes, and trigger reports before making Psyche recommendations or updates.",
157
- path: () => "/api/v1/psyche/overview"
194
+ parameters: scopedReadSchema,
195
+ path: (params) => withUserIds("/api/v1/psyche/overview", params.userIds)
158
196
  });
159
197
  registerReadTool(api, config, {
160
198
  name: "forge_get_xp_metrics",
@@ -166,15 +204,265 @@ export function registerForgePluginTools(api, config) {
166
204
  name: "forge_get_weekly_review",
167
205
  label: "Forge Weekly Review",
168
206
  description: "Read the current weekly review payload with wins, trends, and reward framing.",
169
- path: () => "/api/v1/reviews/weekly"
207
+ parameters: scopedReadSchema,
208
+ path: (params) => withUserIds("/api/v1/reviews/weekly", params.userIds)
209
+ });
210
+ registerReadTool(api, config, {
211
+ name: "forge_get_wiki_settings",
212
+ label: "Forge Wiki Settings",
213
+ description: "Read the current wiki spaces plus enabled LLM and embedding profiles before search, ingest, or page writes.",
214
+ path: () => "/api/v1/wiki/settings"
215
+ });
216
+ registerReadTool(api, config, {
217
+ name: "forge_list_wiki_pages",
218
+ label: "Forge List Wiki Pages",
219
+ description: "List wiki or evidence pages inside one space without search ranking.",
220
+ parameters: Type.Object({
221
+ spaceId: optionalString(),
222
+ kind: Type.Optional(Type.Union([Type.Literal("wiki"), Type.Literal("evidence")])),
223
+ limit: Type.Optional(Type.Integer({ minimum: 1, maximum: 500 }))
224
+ }),
225
+ path: (params) => withQueryParams("/api/v1/wiki/pages", params, [
226
+ "spaceId",
227
+ "kind",
228
+ "limit"
229
+ ])
230
+ });
231
+ registerReadTool(api, config, {
232
+ name: "forge_get_wiki_page",
233
+ label: "Forge Get Wiki Page",
234
+ description: "Read one wiki page with backlinks, source notes, and attached assets.",
235
+ parameters: Type.Object({
236
+ pageId: Type.String({ minLength: 1 })
237
+ }),
238
+ path: (params) => `/api/v1/wiki/pages/${encodeURIComponent(params.pageId)}`
239
+ });
240
+ registerReadTool(api, config, {
241
+ name: "forge_get_wiki_health",
242
+ label: "Forge Wiki Health",
243
+ description: "Read unresolved links, orphan pages, missing summaries, raw-source counts, and index-path state for one wiki space.",
244
+ parameters: Type.Object({
245
+ spaceId: optionalString()
246
+ }),
247
+ path: (params) => withQueryParams("/api/v1/wiki/health", params, ["spaceId"])
248
+ });
249
+ registerWriteTool(api, config, {
250
+ name: "forge_search_wiki",
251
+ label: "Forge Search Wiki",
252
+ description: "Search the wiki with text, semantic, entity, or hybrid retrieval.",
253
+ parameters: Type.Object({
254
+ spaceId: optionalString(),
255
+ kind: Type.Optional(Type.Union([Type.Literal("wiki"), Type.Literal("evidence")])),
256
+ mode: Type.Optional(Type.Union([
257
+ Type.Literal("text"),
258
+ Type.Literal("semantic"),
259
+ Type.Literal("entity"),
260
+ Type.Literal("hybrid")
261
+ ])),
262
+ query: optionalString(),
263
+ profileId: optionalString(),
264
+ linkedEntity: Type.Optional(Type.Object({
265
+ entityType: Type.String({ minLength: 1 }),
266
+ entityId: Type.String({ minLength: 1 })
267
+ })),
268
+ limit: Type.Optional(Type.Integer({ minimum: 1, maximum: 50 }))
269
+ }),
270
+ method: "POST",
271
+ path: "/api/v1/wiki/search"
272
+ });
273
+ api.registerTool({
274
+ name: "forge_upsert_wiki_page",
275
+ label: "Forge Upsert Wiki Page",
276
+ description: "Create a new wiki page or update an existing one through the file-backed wiki surface.",
277
+ parameters: wikiPageMutationSchema(),
278
+ async execute(_toolCallId, params) {
279
+ const typed = (params ?? {});
280
+ const pageId = typeof typed.pageId === "string" && typed.pageId.trim()
281
+ ? typed.pageId.trim()
282
+ : null;
283
+ const body = {
284
+ kind: typed.kind,
285
+ title: typed.title,
286
+ slug: typed.slug,
287
+ summary: typed.summary,
288
+ aliases: typed.aliases,
289
+ contentMarkdown: typed.contentMarkdown,
290
+ author: typed.author,
291
+ tags: typed.tags,
292
+ spaceId: typed.spaceId,
293
+ frontmatter: typed.frontmatter,
294
+ links: typed.links
295
+ };
296
+ return jsonResult(await runWrite(config, {
297
+ method: pageId ? "PATCH" : "POST",
298
+ path: pageId
299
+ ? `/api/v1/wiki/pages/${encodeURIComponent(pageId)}`
300
+ : "/api/v1/wiki/pages",
301
+ body
302
+ }));
303
+ }
304
+ });
305
+ registerWriteTool(api, config, {
306
+ name: "forge_sync_wiki_vault",
307
+ label: "Forge Sync Wiki Vault",
308
+ description: "Resync Markdown files from the local wiki vault into Forge metadata.",
309
+ parameters: Type.Object({
310
+ spaceId: optionalString()
311
+ }),
312
+ method: "POST",
313
+ path: "/api/v1/wiki/sync"
314
+ });
315
+ registerWriteTool(api, config, {
316
+ name: "forge_reindex_wiki_embeddings",
317
+ label: "Forge Reindex Wiki Embeddings",
318
+ description: "Recompute wiki embedding chunks for one space and optional profile.",
319
+ parameters: Type.Object({
320
+ spaceId: optionalString(),
321
+ profileId: optionalString()
322
+ }),
323
+ method: "POST",
324
+ path: "/api/v1/wiki/reindex"
325
+ });
326
+ registerWriteTool(api, config, {
327
+ name: "forge_ingest_wiki_source",
328
+ label: "Forge Ingest Wiki Source",
329
+ description: "Ingest raw text, local files, or URLs into the wiki, preserving a raw source artifact and returning page plus proposal outputs.",
330
+ parameters: Type.Object({
331
+ spaceId: optionalString(),
332
+ titleHint: optionalString(),
333
+ sourceKind: Type.Union([
334
+ Type.Literal("raw_text"),
335
+ Type.Literal("local_path"),
336
+ Type.Literal("url")
337
+ ]),
338
+ sourceText: optionalString(),
339
+ sourcePath: optionalString(),
340
+ sourceUrl: optionalString(),
341
+ mimeType: optionalString(),
342
+ llmProfileId: optionalString(),
343
+ parseStrategy: Type.Optional(Type.Union([
344
+ Type.Literal("auto"),
345
+ Type.Literal("text_only"),
346
+ Type.Literal("multimodal")
347
+ ])),
348
+ entityProposalMode: Type.Optional(Type.Union([Type.Literal("none"), Type.Literal("suggest")])),
349
+ userId: optionalNullableString(),
350
+ createAsKind: Type.Optional(Type.Union([Type.Literal("wiki"), Type.Literal("evidence")])),
351
+ linkedEntityHints: Type.Optional(Type.Array(Type.Object({
352
+ entityType: Type.String({ minLength: 1 }),
353
+ entityId: Type.String({ minLength: 1 }),
354
+ anchorKey: optionalNullableString()
355
+ })))
356
+ }),
357
+ method: "POST",
358
+ path: "/api/v1/wiki/ingest-jobs"
170
359
  });
171
360
  api.registerTool({
172
361
  name: "forge_get_current_work",
173
362
  label: "Forge Current Work",
174
363
  description: "Get the current live-work picture: active task runs, focus tasks, the recommended next task, and current XP state.",
175
- parameters: emptyObjectSchema,
176
- async execute() {
177
- return jsonResult(await resolveCurrentWork(config));
364
+ parameters: scopedReadSchema,
365
+ async execute(_toolCallId, params) {
366
+ const path = withUserIds("/api/v1/operator/context", (params ?? {}).userIds);
367
+ const payload = await runRead(config, path);
368
+ const context = typeof payload === "object" &&
369
+ payload !== null &&
370
+ "context" in payload &&
371
+ typeof payload.context === "object" &&
372
+ payload.context !== null
373
+ ? payload.context
374
+ : null;
375
+ const recentTaskRuns = Array.isArray(context?.recentTaskRuns)
376
+ ? context.recentTaskRuns
377
+ : [];
378
+ const activeTaskRuns = recentTaskRuns.filter((run) => typeof run === "object" &&
379
+ run !== null &&
380
+ "status" in run &&
381
+ run.status === "active");
382
+ const focusTasks = Array.isArray(context?.focusTasks)
383
+ ? context.focusTasks
384
+ : [];
385
+ return jsonResult({
386
+ generatedAt: typeof context?.generatedAt === "string"
387
+ ? context.generatedAt
388
+ : new Date().toISOString(),
389
+ activeTaskRuns,
390
+ focusTasks,
391
+ recommendedNextTask: context?.recommendedNextTask ?? null,
392
+ xp: context?.xp ?? null
393
+ });
394
+ }
395
+ });
396
+ registerReadTool(api, config, {
397
+ name: "forge_get_sleep_overview",
398
+ label: "Forge Sleep Overview",
399
+ description: "Read the reflective sleep surface with recent nights, sleep scores, regularity, stage averages, and linked-context counts.",
400
+ parameters: scopedReadSchema,
401
+ path: (params) => withUserIds("/api/v1/health/sleep", params.userIds)
402
+ });
403
+ registerReadTool(api, config, {
404
+ name: "forge_get_sports_overview",
405
+ label: "Forge Sports Overview",
406
+ description: "Read the sports and workout surface with training volume, workout types, effort signals, and linked workout sessions.",
407
+ parameters: scopedReadSchema,
408
+ path: (params) => withUserIds("/api/v1/health/fitness", params.userIds)
409
+ });
410
+ api.registerTool({
411
+ name: "forge_update_sleep_session",
412
+ label: "Forge Update Sleep Session",
413
+ description: "Patch one sleep session with reflective notes, tags, or linked Forge context after review.",
414
+ parameters: Type.Object({
415
+ sleepId: Type.String({ minLength: 1 }),
416
+ qualitySummary: optionalString(),
417
+ notes: optionalString(),
418
+ tags: Type.Optional(Type.Array(Type.String())),
419
+ links: Type.Optional(Type.Array(healthLinkInputSchema()))
420
+ }),
421
+ async execute(_toolCallId, params) {
422
+ const typed = params;
423
+ return jsonResult(await runWrite(config, {
424
+ method: "PATCH",
425
+ path: `/api/v1/health/sleep/${typed.sleepId}`,
426
+ body: {
427
+ qualitySummary: typed.qualitySummary,
428
+ notes: typed.notes,
429
+ tags: typed.tags,
430
+ links: typed.links
431
+ }
432
+ }));
433
+ }
434
+ });
435
+ api.registerTool({
436
+ name: "forge_update_workout_session",
437
+ label: "Forge Update Workout Session",
438
+ description: "Patch one workout session with effort, mood, meaning, tags, or linked Forge context.",
439
+ parameters: Type.Object({
440
+ workoutId: Type.String({ minLength: 1 }),
441
+ subjectiveEffort: Type.Optional(Type.Union([Type.Integer({ minimum: 1, maximum: 10 }), Type.Null()])),
442
+ moodBefore: optionalString(),
443
+ moodAfter: optionalString(),
444
+ meaningText: optionalString(),
445
+ plannedContext: optionalString(),
446
+ socialContext: optionalString(),
447
+ tags: Type.Optional(Type.Array(Type.String())),
448
+ links: Type.Optional(Type.Array(healthLinkInputSchema()))
449
+ }),
450
+ async execute(_toolCallId, params) {
451
+ const typed = params;
452
+ return jsonResult(await runWrite(config, {
453
+ method: "PATCH",
454
+ path: `/api/v1/health/workouts/${typed.workoutId}`,
455
+ body: {
456
+ subjectiveEffort: typed.subjectiveEffort,
457
+ moodBefore: typed.moodBefore,
458
+ moodAfter: typed.moodAfter,
459
+ meaningText: typed.meaningText,
460
+ plannedContext: typed.plannedContext,
461
+ socialContext: typed.socialContext,
462
+ tags: typed.tags,
463
+ links: typed.links
464
+ }
465
+ }));
178
466
  }
179
467
  });
180
468
  registerWriteTool(api, config, {
@@ -186,6 +474,7 @@ export function registerForgePluginTools(api, config) {
186
474
  entityTypes: Type.Optional(Type.Array(Type.String())),
187
475
  query: optionalString(),
188
476
  ids: Type.Optional(Type.Array(Type.String())),
477
+ userIds: Type.Optional(Type.Array(Type.String())),
189
478
  status: Type.Optional(Type.Array(Type.String())),
190
479
  linkedTo: Type.Optional(Type.Object({
191
480
  entityType: Type.String({ minLength: 1 }),