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.
- package/README.md +133 -2
- package/dist/assets/board-_C6oMy5w.js +6 -0
- package/dist/assets/{board-8L3uX7_O.js.map → board-_C6oMy5w.js.map} +1 -1
- package/dist/assets/index-B4A6TooJ.js +63 -0
- package/dist/assets/index-B4A6TooJ.js.map +1 -0
- package/dist/assets/index-D6Xs_2mo.css +1 -0
- package/dist/assets/{motion-1GAqqi8M.js → motion-D4sZgCHd.js} +2 -2
- package/dist/assets/{motion-1GAqqi8M.js.map → motion-D4sZgCHd.js.map} +1 -1
- package/dist/assets/{table-DBGlgRjk.js → table-BWzTaky1.js} +2 -2
- package/dist/assets/{table-DBGlgRjk.js.map → table-BWzTaky1.js.map} +1 -1
- package/dist/assets/{ui-iTluWjC4.js → ui-BzK4azQb.js} +7 -7
- package/dist/assets/{ui-iTluWjC4.js.map → ui-BzK4azQb.js.map} +1 -1
- package/dist/assets/vendor-DT3pnAKJ.css +1 -0
- package/dist/assets/vendor-De38P6YR.js +729 -0
- package/dist/assets/vendor-De38P6YR.js.map +1 -0
- package/dist/assets/viz-C6hfyqzu.js +34 -0
- package/dist/assets/viz-C6hfyqzu.js.map +1 -0
- package/dist/index.html +9 -9
- package/dist/openclaw/parity.d.ts +1 -1
- package/dist/openclaw/parity.js +29 -2
- package/dist/openclaw/routes.js +207 -24
- package/dist/openclaw/tools.js +324 -35
- package/dist/server/app.js +2080 -92
- package/dist/server/db.js +3 -0
- package/dist/server/health.js +1284 -0
- package/dist/server/managers/platform/background-job-manager.js +138 -2
- package/dist/server/managers/platform/llm-manager.js +126 -0
- package/dist/server/managers/platform/openai-responses-provider.js +773 -0
- package/dist/server/managers/runtime.js +6 -1
- package/dist/server/openapi.js +718 -0
- package/dist/server/preferences-seeds.js +409 -0
- package/dist/server/preferences-types.js +368 -0
- package/dist/server/psyche-types.js +42 -18
- package/dist/server/repositories/activity-events.js +53 -4
- package/dist/server/repositories/calendar.js +89 -15
- package/dist/server/repositories/collaboration.js +8 -3
- package/dist/server/repositories/diagnostic-logs.js +243 -0
- package/dist/server/repositories/entity-ownership.js +92 -0
- package/dist/server/repositories/goals.js +7 -2
- package/dist/server/repositories/habits.js +122 -16
- package/dist/server/repositories/notes.js +119 -41
- package/dist/server/repositories/preferences.js +1765 -0
- package/dist/server/repositories/projects.js +18 -7
- package/dist/server/repositories/psyche.js +84 -27
- package/dist/server/repositories/rewards.js +112 -4
- package/dist/server/repositories/strategies.js +450 -0
- package/dist/server/repositories/tags.js +11 -6
- package/dist/server/repositories/task-runs.js +10 -2
- package/dist/server/repositories/tasks.js +99 -17
- package/dist/server/repositories/users.js +417 -0
- package/dist/server/repositories/wiki-memory.js +3366 -0
- package/dist/server/services/context.js +20 -18
- package/dist/server/services/dashboard.js +29 -6
- package/dist/server/services/entity-crud.js +21 -3
- package/dist/server/services/insights.js +9 -7
- package/dist/server/services/projects.js +2 -1
- package/dist/server/services/psyche.js +10 -9
- package/dist/server/types.js +594 -30
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/server/migrations/015_multi_user_and_strategies.sql +244 -0
- package/server/migrations/016_health_companion.sql +158 -0
- package/server/migrations/016_strategy_contracts_and_user_graph.sql +22 -0
- package/server/migrations/017_preferences.sql +131 -0
- package/server/migrations/018_preference_catalogs.sql +31 -0
- package/server/migrations/019_wiki_memory.sql +255 -0
- package/server/migrations/020_wiki_page_hierarchy.sql +11 -0
- package/server/migrations/021_hide_evidence_from_wiki_index.sql +3 -0
- package/server/migrations/022_wiki_ingest_background.sql +85 -0
- package/server/migrations/023_diagnostic_logs.sql +28 -0
- package/skills/forge-openclaw/SKILL.md +126 -34
- package/skills/forge-openclaw/entity_conversation_playbooks.md +337 -0
- package/skills/forge-openclaw/psyche_entity_playbooks.md +404 -0
- package/dist/assets/board-8L3uX7_O.js +0 -6
- package/dist/assets/index-Cj1IBH_w.js +0 -36
- package/dist/assets/index-Cj1IBH_w.js.map +0 -1
- package/dist/assets/index-DQT6EbuS.css +0 -1
- package/dist/assets/vendor-BvM2F9Dp.js +0 -503
- package/dist/assets/vendor-BvM2F9Dp.js.map +0 -1
- package/dist/assets/vendor-CRS-psbw.css +0 -1
- package/dist/assets/viz-CNeunkfu.js +0 -34
- package/dist/assets/viz-CNeunkfu.js.map +0 -1
package/dist/openclaw/tools.js
CHANGED
|
@@ -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
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
176
|
-
async execute() {
|
|
177
|
-
|
|
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 }),
|