forge-openclaw-plugin 0.2.3

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.
@@ -0,0 +1,1269 @@
1
+ import { Type } from "@sinclair/typebox";
2
+ import { callForgeApi, expectForgeSuccess, requireApiToken } from "./api-client.js";
3
+ function jsonResult(payload) {
4
+ return {
5
+ content: [
6
+ {
7
+ type: "text",
8
+ text: JSON.stringify(payload, null, 2)
9
+ }
10
+ ],
11
+ details: payload
12
+ };
13
+ }
14
+ async function runRead(config, path) {
15
+ const result = await callForgeApi({
16
+ baseUrl: config.baseUrl,
17
+ apiToken: config.apiToken,
18
+ actorLabel: config.actorLabel,
19
+ timeoutMs: config.timeoutMs,
20
+ method: "GET",
21
+ path
22
+ });
23
+ return expectForgeSuccess(result);
24
+ }
25
+ async function runWrite(config, options) {
26
+ requireApiToken(config);
27
+ const result = await callForgeApi({
28
+ baseUrl: config.baseUrl,
29
+ apiToken: config.apiToken,
30
+ actorLabel: config.actorLabel,
31
+ timeoutMs: config.timeoutMs,
32
+ method: options.method,
33
+ path: options.path,
34
+ body: options.body
35
+ });
36
+ return expectForgeSuccess(result);
37
+ }
38
+ const emptyObjectSchema = Type.Object({});
39
+ const optionalString = () => Type.Optional(Type.String());
40
+ const optionalNullableString = () => Type.Optional(Type.Union([Type.String(), Type.Null()]));
41
+ const stringArray = () => Type.Array(Type.String());
42
+ const optionalStringArray = () => Type.Optional(stringArray());
43
+ const optionalDeleteMode = () => Type.Optional(Type.Union([Type.Literal("soft"), Type.Literal("hard")]));
44
+ const taskTimerModeSchema = Type.Union([Type.Literal("planned"), Type.Literal("unlimited")]);
45
+ const timeAccountingModeSchema = Type.Union([
46
+ Type.Literal("split"),
47
+ Type.Literal("parallel"),
48
+ Type.Literal("primary_only")
49
+ ]);
50
+ function withId(properties) {
51
+ return Type.Object({
52
+ id: Type.String({ minLength: 1 }),
53
+ ...properties
54
+ });
55
+ }
56
+ function registerReadTool(api, config, options) {
57
+ api.registerTool({
58
+ name: options.name,
59
+ label: options.label,
60
+ description: options.description,
61
+ parameters: options.parameters ?? emptyObjectSchema,
62
+ async execute(_toolCallId, params) {
63
+ return jsonResult(await runRead(config, options.path((params ?? {}))));
64
+ }
65
+ });
66
+ }
67
+ function registerWriteTool(api, config, options) {
68
+ api.registerTool({
69
+ name: options.name,
70
+ label: options.label,
71
+ description: options.description,
72
+ parameters: options.parameters,
73
+ async execute(_toolCallId, params) {
74
+ const typed = params;
75
+ return jsonResult(await runWrite(config, {
76
+ method: options.method,
77
+ path: options.path(typed),
78
+ body: options.bodyless ? undefined : options.body ? options.body(typed) : typed
79
+ }));
80
+ }
81
+ });
82
+ }
83
+ function buildQuery(params) {
84
+ const search = new URLSearchParams();
85
+ for (const [key, value] of Object.entries(params)) {
86
+ if (typeof value === "string" && value.trim().length > 0) {
87
+ search.set(key, value.trim());
88
+ continue;
89
+ }
90
+ if (typeof value === "number" || typeof value === "boolean") {
91
+ search.set(key, String(value));
92
+ }
93
+ }
94
+ return search.toString();
95
+ }
96
+ function buildQueryPath(path, params) {
97
+ const suffix = buildQuery(params);
98
+ return suffix ? `${path}?${suffix}` : path;
99
+ }
100
+ function buildDeletePath(path, params) {
101
+ return buildQueryPath(path, {
102
+ mode: params.mode,
103
+ reason: params.reason
104
+ });
105
+ }
106
+ function registerPsycheCrudTools(api, config, options) {
107
+ const basePath = `/api/v1/psyche/${options.pluralName}`;
108
+ const singularLabel = options.label;
109
+ registerReadTool(api, config, {
110
+ name: `forge_list_psyche_${options.pluralName.replaceAll("-", "_")}`,
111
+ label: `Forge ${singularLabel}`,
112
+ description: `List Forge Psyche ${options.pluralName}.`,
113
+ parameters: emptyObjectSchema,
114
+ path: () => basePath
115
+ });
116
+ registerReadTool(api, config, {
117
+ name: `forge_get_psyche_${options.singularName.replaceAll("-", "_")}`,
118
+ label: `Forge ${singularLabel} Detail`,
119
+ description: `Fetch one Forge Psyche ${options.singularName}.`,
120
+ parameters: withId({}),
121
+ path: (params) => `${basePath}/${encodeURIComponent(String(params.id))}`
122
+ });
123
+ registerWriteTool(api, config, {
124
+ name: `forge_create_psyche_${options.singularName.replaceAll("-", "_")}`,
125
+ label: `Create ${singularLabel}`,
126
+ description: `Create a Forge Psyche ${options.singularName}.`,
127
+ parameters: Type.Object(options.createShape),
128
+ method: "POST",
129
+ path: () => basePath
130
+ });
131
+ registerWriteTool(api, config, {
132
+ name: `forge_update_psyche_${options.singularName.replaceAll("-", "_")}`,
133
+ label: `Update ${singularLabel}`,
134
+ description: `Update a Forge Psyche ${options.singularName}.`,
135
+ parameters: withId(options.updateShape),
136
+ method: "PATCH",
137
+ path: (params) => `${basePath}/${encodeURIComponent(String(params.id))}`,
138
+ body: (params) => {
139
+ const { id, ...body } = params;
140
+ return body;
141
+ }
142
+ });
143
+ registerWriteTool(api, config, {
144
+ name: `forge_delete_psyche_${options.singularName.replaceAll("-", "_")}`,
145
+ label: `Delete ${singularLabel}`,
146
+ description: `Delete a Forge Psyche ${options.singularName}.`,
147
+ parameters: withId({
148
+ mode: optionalDeleteMode(),
149
+ reason: optionalString()
150
+ }),
151
+ method: "DELETE",
152
+ path: (params) => buildDeletePath(`${basePath}/${encodeURIComponent(String(params.id))}`, params),
153
+ bodyless: true
154
+ });
155
+ }
156
+ export function registerForgePluginTools(api, config) {
157
+ registerReadTool(api, config, {
158
+ name: "forge_get_context",
159
+ label: "Forge Context",
160
+ description: "Read the live Forge operating context through the plugin bridge.",
161
+ path: () => "/api/v1/context"
162
+ });
163
+ registerReadTool(api, config, {
164
+ name: "forge_list_domains",
165
+ label: "Forge Domains",
166
+ description: "List Forge first-class domains, including sensitive ones such as Psyche.",
167
+ path: () => "/api/v1/domains"
168
+ });
169
+ registerReadTool(api, config, {
170
+ name: "forge_get_operator_context",
171
+ label: "Forge Operator Context",
172
+ description: "Read the agent-focused Forge operator context with active projects, focus tasks, board lanes, and XP state.",
173
+ path: () => "/api/v1/operator/context"
174
+ });
175
+ registerReadTool(api, config, {
176
+ name: "forge_get_operator_overview",
177
+ label: "Forge Operator Overview",
178
+ description: "Read the one-shot Forge operator overview with full current state, route guidance, onboarding, and optional Psyche summary.",
179
+ path: () => "/api/v1/operator/overview"
180
+ });
181
+ registerReadTool(api, config, {
182
+ name: "forge_get_agent_onboarding",
183
+ label: "Forge Agent Onboarding",
184
+ description: "Fetch the Forge onboarding contract with recommended scopes, headers, and verification guidance.",
185
+ path: () => "/api/v1/agents/onboarding"
186
+ });
187
+ registerReadTool(api, config, {
188
+ name: "forge_get_settings",
189
+ label: "Forge Settings",
190
+ description: "Read Forge operator settings, including execution policy for multitasking and timer accounting.",
191
+ path: () => "/api/v1/settings"
192
+ });
193
+ registerReadTool(api, config, {
194
+ name: "forge_get_settings_bin",
195
+ label: "Forge Deleted Items",
196
+ description: "Read Forge deleted items grouped in the settings bin.",
197
+ path: () => "/api/v1/settings/bin"
198
+ });
199
+ registerWriteTool(api, config, {
200
+ name: "forge_update_settings",
201
+ label: "Update Forge Settings",
202
+ description: "Update Forge settings such as operator profile, notifications, max active tasks, theme, locale, and time accounting mode.",
203
+ parameters: Type.Object({
204
+ profile: Type.Optional(Type.Object({
205
+ operatorName: optionalString(),
206
+ operatorEmail: optionalString(),
207
+ operatorTitle: optionalString()
208
+ })),
209
+ notifications: Type.Optional(Type.Object({
210
+ goalDriftAlerts: Type.Optional(Type.Boolean()),
211
+ dailyQuestReminders: Type.Optional(Type.Boolean()),
212
+ achievementCelebrations: Type.Optional(Type.Boolean())
213
+ })),
214
+ execution: Type.Optional(Type.Object({
215
+ maxActiveTasks: Type.Optional(Type.Number({ minimum: 1, maximum: 8 })),
216
+ timeAccountingMode: Type.Optional(timeAccountingModeSchema)
217
+ })),
218
+ themePreference: Type.Optional(Type.Union([Type.Literal("obsidian"), Type.Literal("solar"), Type.Literal("system")])),
219
+ localePreference: Type.Optional(Type.Union([Type.Literal("en"), Type.Literal("fr")]))
220
+ }),
221
+ method: "PATCH",
222
+ path: () => "/api/v1/settings"
223
+ });
224
+ registerReadTool(api, config, {
225
+ name: "forge_get_xp_metrics",
226
+ label: "Forge XP Metrics",
227
+ description: "Read current Forge XP metrics, reward rules, and recent reward reasons.",
228
+ path: () => "/api/v1/metrics/xp"
229
+ });
230
+ registerReadTool(api, config, {
231
+ name: "forge_list_reward_ledger",
232
+ label: "Forge Reward Ledger",
233
+ description: "Inspect recent Forge reward ledger events.",
234
+ parameters: Type.Object({
235
+ limit: Type.Optional(Type.Number({ minimum: 1, maximum: 200 }))
236
+ }),
237
+ path: (params) => buildQueryPath("/api/v1/rewards/ledger", params)
238
+ });
239
+ registerReadTool(api, config, {
240
+ name: "forge_list_goals",
241
+ label: "Forge Goals",
242
+ description: "List Forge life goals.",
243
+ path: () => "/api/v1/goals"
244
+ });
245
+ registerReadTool(api, config, {
246
+ name: "forge_get_goal",
247
+ label: "Forge Goal",
248
+ description: "Fetch one Forge life goal.",
249
+ parameters: withId({}),
250
+ path: (params) => `/api/v1/goals/${encodeURIComponent(String(params.id))}`
251
+ });
252
+ registerWriteTool(api, config, {
253
+ name: "forge_create_goal",
254
+ label: "Create Goal",
255
+ description: "Create a Forge life goal.",
256
+ parameters: Type.Object({
257
+ title: Type.String({ minLength: 1 }),
258
+ description: optionalString(),
259
+ horizon: optionalString(),
260
+ status: optionalString(),
261
+ targetPoints: Type.Optional(Type.Number()),
262
+ themeColor: optionalString(),
263
+ tagIds: optionalStringArray()
264
+ }),
265
+ method: "POST",
266
+ path: () => "/api/v1/goals"
267
+ });
268
+ registerWriteTool(api, config, {
269
+ name: "forge_update_goal",
270
+ label: "Update Goal",
271
+ description: "Update a Forge life goal, including renaming it.",
272
+ parameters: withId({
273
+ title: optionalString(),
274
+ description: optionalString(),
275
+ horizon: optionalString(),
276
+ status: optionalString(),
277
+ targetPoints: Type.Optional(Type.Number()),
278
+ themeColor: optionalString(),
279
+ tagIds: optionalStringArray()
280
+ }),
281
+ method: "PATCH",
282
+ path: (params) => `/api/v1/goals/${encodeURIComponent(String(params.id))}`,
283
+ body: (params) => {
284
+ const { id, ...body } = params;
285
+ return body;
286
+ }
287
+ });
288
+ registerWriteTool(api, config, {
289
+ name: "forge_delete_goal",
290
+ label: "Delete Goal",
291
+ description: "Delete a Forge life goal.",
292
+ parameters: withId({
293
+ mode: optionalDeleteMode(),
294
+ reason: optionalString()
295
+ }),
296
+ method: "DELETE",
297
+ path: (params) => buildDeletePath(`/api/v1/goals/${encodeURIComponent(String(params.id))}`, params),
298
+ bodyless: true
299
+ });
300
+ registerReadTool(api, config, {
301
+ name: "forge_list_projects",
302
+ label: "Forge Projects",
303
+ description: "List Forge projects.",
304
+ parameters: Type.Object({
305
+ goalId: optionalString(),
306
+ status: optionalString(),
307
+ limit: Type.Optional(Type.Number())
308
+ }),
309
+ path: (params) => buildQueryPath("/api/v1/projects", params)
310
+ });
311
+ registerReadTool(api, config, {
312
+ name: "forge_get_project",
313
+ label: "Forge Project",
314
+ description: "Fetch one Forge project.",
315
+ parameters: withId({}),
316
+ path: (params) => `/api/v1/projects/${encodeURIComponent(String(params.id))}`
317
+ });
318
+ registerReadTool(api, config, {
319
+ name: "forge_get_project_board",
320
+ label: "Forge Project Board",
321
+ description: "Fetch a Forge project board with tasks and evidence.",
322
+ parameters: withId({}),
323
+ path: (params) => `/api/v1/projects/${encodeURIComponent(String(params.id))}/board`
324
+ });
325
+ registerWriteTool(api, config, {
326
+ name: "forge_create_project",
327
+ label: "Create Project",
328
+ description: "Create a Forge project under a life goal.",
329
+ parameters: Type.Object({
330
+ goalId: Type.String({ minLength: 1 }),
331
+ title: Type.String({ minLength: 1 }),
332
+ description: optionalString(),
333
+ status: optionalString(),
334
+ targetPoints: Type.Optional(Type.Number()),
335
+ themeColor: optionalString()
336
+ }),
337
+ method: "POST",
338
+ path: () => "/api/v1/projects"
339
+ });
340
+ registerWriteTool(api, config, {
341
+ name: "forge_update_project",
342
+ label: "Update Project",
343
+ description: "Update a Forge project.",
344
+ parameters: withId({
345
+ goalId: optionalString(),
346
+ title: optionalString(),
347
+ description: optionalString(),
348
+ status: optionalString(),
349
+ targetPoints: Type.Optional(Type.Number()),
350
+ themeColor: optionalString()
351
+ }),
352
+ method: "PATCH",
353
+ path: (params) => `/api/v1/projects/${encodeURIComponent(String(params.id))}`,
354
+ body: (params) => {
355
+ const { id, ...body } = params;
356
+ return body;
357
+ }
358
+ });
359
+ registerWriteTool(api, config, {
360
+ name: "forge_delete_project",
361
+ label: "Delete Project",
362
+ description: "Delete a Forge project.",
363
+ parameters: withId({
364
+ mode: optionalDeleteMode(),
365
+ reason: optionalString()
366
+ }),
367
+ method: "DELETE",
368
+ path: (params) => buildDeletePath(`/api/v1/projects/${encodeURIComponent(String(params.id))}`, params),
369
+ bodyless: true
370
+ });
371
+ registerReadTool(api, config, {
372
+ name: "forge_list_tasks",
373
+ label: "Forge Tasks",
374
+ description: "List Forge tasks with optional filters.",
375
+ parameters: Type.Object({
376
+ status: optionalString(),
377
+ goalId: optionalString(),
378
+ projectId: optionalString(),
379
+ due: optionalString(),
380
+ owner: optionalString()
381
+ }),
382
+ path: (params) => buildQueryPath("/api/v1/tasks", params)
383
+ });
384
+ registerReadTool(api, config, {
385
+ name: "forge_get_task",
386
+ label: "Forge Task",
387
+ description: "Fetch one Forge task.",
388
+ parameters: withId({}),
389
+ path: (params) => `/api/v1/tasks/${encodeURIComponent(String(params.id))}`
390
+ });
391
+ registerWriteTool(api, config, {
392
+ name: "forge_create_task",
393
+ label: "Forge Create Task",
394
+ description: "Create a Forge task through the versioned API.",
395
+ parameters: Type.Object({
396
+ title: Type.String({ minLength: 1 }),
397
+ projectId: optionalNullableString(),
398
+ goalId: optionalNullableString(),
399
+ description: optionalString(),
400
+ status: optionalString(),
401
+ priority: optionalString(),
402
+ owner: optionalString(),
403
+ dueDate: optionalNullableString(),
404
+ effort: optionalString(),
405
+ energy: optionalString(),
406
+ points: Type.Optional(Type.Number()),
407
+ tagIds: optionalStringArray()
408
+ }),
409
+ method: "POST",
410
+ path: () => "/api/v1/tasks"
411
+ });
412
+ registerWriteTool(api, config, {
413
+ name: "forge_update_task",
414
+ label: "Forge Update Task",
415
+ description: "Update an existing Forge task.",
416
+ parameters: withId({
417
+ title: optionalString(),
418
+ description: optionalString(),
419
+ status: optionalString(),
420
+ priority: optionalString(),
421
+ owner: optionalString(),
422
+ goalId: optionalNullableString(),
423
+ projectId: optionalNullableString(),
424
+ dueDate: optionalNullableString(),
425
+ effort: optionalString(),
426
+ energy: optionalString(),
427
+ points: Type.Optional(Type.Number()),
428
+ tagIds: optionalStringArray()
429
+ }),
430
+ method: "PATCH",
431
+ path: (params) => `/api/v1/tasks/${encodeURIComponent(String(params.id))}`,
432
+ body: (params) => {
433
+ const { id, ...body } = params;
434
+ return body;
435
+ }
436
+ });
437
+ registerWriteTool(api, config, {
438
+ name: "forge_delete_task",
439
+ label: "Delete Task",
440
+ description: "Delete a Forge task.",
441
+ parameters: withId({
442
+ mode: optionalDeleteMode(),
443
+ reason: optionalString()
444
+ }),
445
+ method: "DELETE",
446
+ path: (params) => buildDeletePath(`/api/v1/tasks/${encodeURIComponent(String(params.id))}`, params),
447
+ bodyless: true
448
+ });
449
+ registerWriteTool(api, config, {
450
+ name: "forge_move_task",
451
+ label: "Move Task",
452
+ description: "Move a Forge task across kanban states.",
453
+ parameters: Type.Object({
454
+ id: Type.String({ minLength: 1 }),
455
+ status: Type.String({ minLength: 1 }),
456
+ sortOrder: Type.Optional(Type.Number())
457
+ }),
458
+ method: "PATCH",
459
+ path: (params) => `/api/v1/tasks/${encodeURIComponent(String(params.id))}`,
460
+ body: (params) => ({
461
+ status: params.status,
462
+ sortOrder: params.sortOrder
463
+ })
464
+ });
465
+ registerWriteTool(api, config, {
466
+ name: "forge_complete_task",
467
+ label: "Complete Task",
468
+ description: "Mark a Forge task done.",
469
+ parameters: Type.Object({
470
+ id: Type.String({ minLength: 1 })
471
+ }),
472
+ method: "PATCH",
473
+ path: (params) => `/api/v1/tasks/${encodeURIComponent(String(params.id))}`,
474
+ body: () => ({ status: "done" })
475
+ });
476
+ registerWriteTool(api, config, {
477
+ name: "forge_uncomplete_task",
478
+ label: "Reopen Task",
479
+ description: "Reopen a completed Forge task.",
480
+ parameters: Type.Object({
481
+ id: Type.String({ minLength: 1 }),
482
+ status: optionalString()
483
+ }),
484
+ method: "POST",
485
+ path: (params) => `/api/v1/tasks/${encodeURIComponent(String(params.id))}/uncomplete`,
486
+ body: (params) => ({
487
+ status: params.status ?? "focus"
488
+ })
489
+ });
490
+ registerWriteTool(api, config, {
491
+ name: "forge_log_work",
492
+ label: "Log Work",
493
+ description: "Log work that already happened by creating or updating a task and returning the XP view.",
494
+ parameters: Type.Object({
495
+ taskId: optionalString(),
496
+ title: optionalString(),
497
+ description: optionalString(),
498
+ summary: optionalString(),
499
+ goalId: optionalNullableString(),
500
+ projectId: optionalNullableString(),
501
+ owner: optionalString(),
502
+ status: optionalString(),
503
+ priority: optionalString(),
504
+ dueDate: optionalNullableString(),
505
+ effort: optionalString(),
506
+ energy: optionalString(),
507
+ points: Type.Optional(Type.Number()),
508
+ tagIds: optionalStringArray()
509
+ }),
510
+ method: "POST",
511
+ path: () => "/api/v1/operator/log-work"
512
+ });
513
+ registerWriteTool(api, config, {
514
+ name: "forge_claim_task_run",
515
+ label: "Start Task Timer",
516
+ description: "Start or renew a live Forge task timer with either a planned duration or unlimited mode.",
517
+ parameters: Type.Object({
518
+ taskId: Type.String({ minLength: 1 }),
519
+ actor: optionalString(),
520
+ leaseTtlSeconds: Type.Optional(Type.Number()),
521
+ timerMode: Type.Optional(taskTimerModeSchema),
522
+ plannedDurationSeconds: Type.Optional(Type.Union([Type.Number({ minimum: 60, maximum: 86400 }), Type.Null()])),
523
+ isCurrent: Type.Optional(Type.Boolean()),
524
+ note: optionalString()
525
+ }),
526
+ method: "POST",
527
+ path: (params) => `/api/v1/tasks/${encodeURIComponent(String(params.taskId))}/runs`,
528
+ body: (params) => ({
529
+ actor: params.actor ?? config.actorLabel,
530
+ leaseTtlSeconds: params.leaseTtlSeconds,
531
+ timerMode: params.timerMode,
532
+ plannedDurationSeconds: params.plannedDurationSeconds,
533
+ isCurrent: params.isCurrent,
534
+ note: params.note ?? ""
535
+ })
536
+ });
537
+ registerWriteTool(api, config, {
538
+ name: "forge_focus_task_run",
539
+ label: "Focus Task Timer",
540
+ description: "Mark one active Forge task timer as the current highlighted timer without stopping the others.",
541
+ parameters: Type.Object({
542
+ runId: Type.String({ minLength: 1 }),
543
+ actor: optionalString()
544
+ }),
545
+ method: "POST",
546
+ path: (params) => `/api/v1/task-runs/${encodeURIComponent(String(params.runId))}/focus`,
547
+ body: (params) => ({
548
+ actor: params.actor ?? config.actorLabel
549
+ })
550
+ });
551
+ registerWriteTool(api, config, {
552
+ name: "forge_complete_task_run",
553
+ label: "Complete Task Run",
554
+ description: "Complete a claimed task run.",
555
+ parameters: Type.Object({
556
+ runId: Type.String({ minLength: 1 }),
557
+ actor: optionalString(),
558
+ note: optionalString()
559
+ }),
560
+ method: "POST",
561
+ path: (params) => `/api/v1/task-runs/${encodeURIComponent(String(params.runId))}/complete`,
562
+ body: (params) => ({
563
+ actor: params.actor ?? config.actorLabel,
564
+ note: params.note ?? ""
565
+ })
566
+ });
567
+ registerReadTool(api, config, {
568
+ name: "forge_list_task_runs",
569
+ label: "Forge Task Runs",
570
+ description: "List task runs with optional task and status filters.",
571
+ parameters: Type.Object({
572
+ taskId: optionalString(),
573
+ status: optionalString(),
574
+ active: Type.Optional(Type.Boolean()),
575
+ limit: Type.Optional(Type.Number({ minimum: 1, maximum: 200 }))
576
+ }),
577
+ path: (params) => buildQueryPath("/api/v1/task-runs", params)
578
+ });
579
+ registerReadTool(api, config, {
580
+ name: "forge_list_active_timers",
581
+ label: "Forge Active Timers",
582
+ description: "List active Forge task timers, including multitasking state and current timer focus.",
583
+ parameters: Type.Object({
584
+ taskId: optionalString(),
585
+ limit: Type.Optional(Type.Number({ minimum: 1, maximum: 200 }))
586
+ }),
587
+ path: (params) => buildQueryPath("/api/v1/task-runs", { ...params, active: true })
588
+ });
589
+ registerWriteTool(api, config, {
590
+ name: "forge_release_task_run",
591
+ label: "Release Task Run",
592
+ description: "Release a claimed task run without marking the task complete.",
593
+ parameters: Type.Object({
594
+ runId: Type.String({ minLength: 1 }),
595
+ actor: optionalString(),
596
+ note: optionalString()
597
+ }),
598
+ method: "POST",
599
+ path: (params) => `/api/v1/task-runs/${encodeURIComponent(String(params.runId))}/release`,
600
+ body: (params) => ({
601
+ actor: params.actor ?? config.actorLabel,
602
+ note: params.note ?? ""
603
+ })
604
+ });
605
+ registerReadTool(api, config, {
606
+ name: "forge_list_comments",
607
+ label: "Forge Comments",
608
+ description: "List comments for a Forge entity or report anchor.",
609
+ parameters: Type.Object({
610
+ entityType: Type.String({ minLength: 1 }),
611
+ entityId: Type.String({ minLength: 1 }),
612
+ anchorId: optionalString()
613
+ }),
614
+ path: (params) => buildQueryPath("/api/v1/comments", {
615
+ entityType: params.entityType,
616
+ entityId: params.entityId,
617
+ anchorId: params.anchorId
618
+ })
619
+ });
620
+ registerWriteTool(api, config, {
621
+ name: "forge_add_comment",
622
+ label: "Forge Add Comment",
623
+ description: "Add a user or agent comment to a Forge entity or report anchor.",
624
+ parameters: Type.Object({
625
+ entityType: Type.String({ minLength: 1 }),
626
+ entityId: Type.String({ minLength: 1 }),
627
+ body: Type.String({ minLength: 1 }),
628
+ anchorId: optionalString()
629
+ }),
630
+ method: "POST",
631
+ path: () => "/api/v1/comments"
632
+ });
633
+ registerWriteTool(api, config, {
634
+ name: "forge_update_comment",
635
+ label: "Forge Update Comment",
636
+ description: "Update an existing Forge comment.",
637
+ parameters: Type.Object({
638
+ id: Type.String({ minLength: 1 }),
639
+ body: Type.String({ minLength: 1 })
640
+ }),
641
+ method: "PATCH",
642
+ path: (params) => `/api/v1/comments/${encodeURIComponent(String(params.id))}`,
643
+ body: (params) => ({
644
+ body: params.body
645
+ })
646
+ });
647
+ registerReadTool(api, config, {
648
+ name: "forge_get_comment",
649
+ label: "Forge Comment",
650
+ description: "Fetch one Forge comment.",
651
+ parameters: withId({}),
652
+ path: (params) => `/api/v1/comments/${encodeURIComponent(String(params.id))}`
653
+ });
654
+ registerWriteTool(api, config, {
655
+ name: "forge_delete_comment",
656
+ label: "Delete Comment",
657
+ description: "Delete an existing Forge comment.",
658
+ parameters: withId({
659
+ mode: optionalDeleteMode(),
660
+ reason: optionalString()
661
+ }),
662
+ method: "DELETE",
663
+ path: (params) => buildDeletePath(`/api/v1/comments/${encodeURIComponent(String(params.id))}`, params),
664
+ bodyless: true
665
+ });
666
+ registerReadTool(api, config, {
667
+ name: "forge_list_tags",
668
+ label: "Forge Tags",
669
+ description: "List Forge tags across value, category, and execution kinds.",
670
+ path: () => "/api/v1/tags"
671
+ });
672
+ registerReadTool(api, config, {
673
+ name: "forge_get_tag",
674
+ label: "Forge Tag",
675
+ description: "Fetch one Forge tag.",
676
+ parameters: withId({}),
677
+ path: (params) => `/api/v1/tags/${encodeURIComponent(String(params.id))}`
678
+ });
679
+ registerWriteTool(api, config, {
680
+ name: "forge_create_tag",
681
+ label: "Create Tag",
682
+ description: "Create a Forge tag.",
683
+ parameters: Type.Object({
684
+ name: Type.String({ minLength: 1 }),
685
+ kind: optionalString(),
686
+ color: optionalString(),
687
+ description: optionalString()
688
+ }),
689
+ method: "POST",
690
+ path: () => "/api/v1/tags"
691
+ });
692
+ registerWriteTool(api, config, {
693
+ name: "forge_update_tag",
694
+ label: "Update Tag",
695
+ description: "Update a Forge tag.",
696
+ parameters: withId({
697
+ name: optionalString(),
698
+ kind: optionalString(),
699
+ color: optionalString(),
700
+ description: optionalString()
701
+ }),
702
+ method: "PATCH",
703
+ path: (params) => `/api/v1/tags/${encodeURIComponent(String(params.id))}`,
704
+ body: (params) => {
705
+ const { id, ...body } = params;
706
+ return body;
707
+ }
708
+ });
709
+ registerWriteTool(api, config, {
710
+ name: "forge_delete_tag",
711
+ label: "Delete Tag",
712
+ description: "Delete a Forge tag.",
713
+ parameters: withId({
714
+ mode: optionalDeleteMode(),
715
+ reason: optionalString()
716
+ }),
717
+ method: "DELETE",
718
+ path: (params) => buildDeletePath(`/api/v1/tags/${encodeURIComponent(String(params.id))}`, params),
719
+ bodyless: true
720
+ });
721
+ registerReadTool(api, config, {
722
+ name: "forge_list_insights",
723
+ label: "Forge Insights",
724
+ description: "List structured Forge insights.",
725
+ path: () => "/api/v1/insights"
726
+ });
727
+ registerWriteTool(api, config, {
728
+ name: "forge_post_insight",
729
+ label: "Forge Post Insight",
730
+ description: "Post a structured Forge insight after reading the one-shot overview. This tool stamps the insight as agent-originated automatically.",
731
+ parameters: Type.Object({
732
+ entityType: optionalNullableString(),
733
+ entityId: optionalNullableString(),
734
+ timeframeLabel: optionalNullableString(),
735
+ title: Type.String({ minLength: 1 }),
736
+ summary: Type.String({ minLength: 1 }),
737
+ recommendation: Type.String({ minLength: 1 }),
738
+ rationale: optionalString(),
739
+ confidence: Type.Optional(Type.Number()),
740
+ visibility: optionalString(),
741
+ ctaLabel: optionalString()
742
+ }),
743
+ method: "POST",
744
+ path: () => "/api/v1/insights",
745
+ body: (params) => ({
746
+ originType: "agent",
747
+ originAgentId: null,
748
+ originLabel: config.actorLabel || "OpenClaw",
749
+ entityType: params.entityType ?? null,
750
+ entityId: params.entityId ?? null,
751
+ timeframeLabel: params.timeframeLabel ?? null,
752
+ title: params.title,
753
+ summary: params.summary,
754
+ recommendation: params.recommendation,
755
+ rationale: params.rationale ?? "",
756
+ confidence: params.confidence,
757
+ visibility: params.visibility,
758
+ ctaLabel: params.ctaLabel ?? "Review insight"
759
+ })
760
+ });
761
+ registerWriteTool(api, config, {
762
+ name: "forge_update_insight",
763
+ label: "Forge Update Insight",
764
+ description: "Update a structured Forge insight.",
765
+ parameters: withId({
766
+ title: optionalString(),
767
+ summary: optionalString(),
768
+ recommendation: optionalString(),
769
+ rationale: optionalString(),
770
+ confidence: Type.Optional(Type.Number()),
771
+ visibility: optionalString(),
772
+ status: optionalString()
773
+ }),
774
+ method: "PATCH",
775
+ path: (params) => `/api/v1/insights/${encodeURIComponent(String(params.id))}`,
776
+ body: (params) => {
777
+ const { id, ...body } = params;
778
+ return body;
779
+ }
780
+ });
781
+ registerWriteTool(api, config, {
782
+ name: "forge_delete_insight",
783
+ label: "Delete Insight",
784
+ description: "Delete a structured Forge insight. Default is soft delete unless mode=hard is set explicitly.",
785
+ parameters: withId({
786
+ mode: optionalDeleteMode(),
787
+ reason: optionalString()
788
+ }),
789
+ method: "DELETE",
790
+ path: (params) => buildDeletePath(`/api/v1/insights/${encodeURIComponent(String(params.id))}`, params),
791
+ bodyless: true
792
+ });
793
+ registerWriteTool(api, config, {
794
+ name: "forge_create_entities",
795
+ label: "Create Forge Entities",
796
+ description: "Create multiple Forge entities in one ordered batch request.",
797
+ parameters: Type.Object({
798
+ atomic: Type.Optional(Type.Boolean()),
799
+ operations: Type.Array(Type.Object({
800
+ entityType: Type.String({ minLength: 1 }),
801
+ data: Type.Record(Type.String(), Type.Any()),
802
+ clientRef: optionalString()
803
+ }))
804
+ }),
805
+ method: "POST",
806
+ path: () => "/api/v1/entities/create"
807
+ });
808
+ registerWriteTool(api, config, {
809
+ name: "forge_update_entities",
810
+ label: "Update Forge Entities",
811
+ description: "Update multiple Forge entities in one ordered batch request.",
812
+ parameters: Type.Object({
813
+ atomic: Type.Optional(Type.Boolean()),
814
+ operations: Type.Array(Type.Object({
815
+ entityType: Type.String({ minLength: 1 }),
816
+ id: Type.String({ minLength: 1 }),
817
+ patch: Type.Record(Type.String(), Type.Any()),
818
+ clientRef: optionalString()
819
+ }))
820
+ }),
821
+ method: "POST",
822
+ path: () => "/api/v1/entities/update"
823
+ });
824
+ registerWriteTool(api, config, {
825
+ name: "forge_delete_entities",
826
+ label: "Delete Forge Entities",
827
+ description: "Delete multiple Forge entities in one ordered batch request. Delete defaults to soft mode unless hard is requested explicitly.",
828
+ parameters: Type.Object({
829
+ atomic: Type.Optional(Type.Boolean()),
830
+ operations: Type.Array(Type.Object({
831
+ entityType: Type.String({ minLength: 1 }),
832
+ id: Type.String({ minLength: 1 }),
833
+ mode: optionalDeleteMode(),
834
+ reason: optionalString(),
835
+ clientRef: optionalString()
836
+ }))
837
+ }),
838
+ method: "POST",
839
+ path: () => "/api/v1/entities/delete"
840
+ });
841
+ registerWriteTool(api, config, {
842
+ name: "forge_restore_entities",
843
+ label: "Restore Forge Entities",
844
+ description: "Restore soft-deleted Forge entities from the settings bin.",
845
+ parameters: Type.Object({
846
+ atomic: Type.Optional(Type.Boolean()),
847
+ operations: Type.Array(Type.Object({
848
+ entityType: Type.String({ minLength: 1 }),
849
+ id: Type.String({ minLength: 1 }),
850
+ clientRef: optionalString()
851
+ }))
852
+ }),
853
+ method: "POST",
854
+ path: () => "/api/v1/entities/restore"
855
+ });
856
+ registerWriteTool(api, config, {
857
+ name: "forge_search_entities",
858
+ label: "Search Forge Entities",
859
+ description: "Search multiple Forge entity groups in one call, including optional deleted-item visibility.",
860
+ parameters: Type.Object({
861
+ searches: Type.Array(Type.Object({
862
+ entityTypes: Type.Optional(Type.Array(Type.String())),
863
+ query: optionalString(),
864
+ ids: Type.Optional(Type.Array(Type.String())),
865
+ status: Type.Optional(Type.Array(Type.String())),
866
+ linkedTo: Type.Optional(Type.Object({
867
+ entityType: Type.String({ minLength: 1 }),
868
+ id: Type.String({ minLength: 1 })
869
+ })),
870
+ includeDeleted: Type.Optional(Type.Boolean()),
871
+ limit: Type.Optional(Type.Number({ minimum: 1, maximum: 100 })),
872
+ clientRef: optionalString()
873
+ }))
874
+ }),
875
+ method: "POST",
876
+ path: () => "/api/v1/entities/search"
877
+ });
878
+ registerReadTool(api, config, {
879
+ name: "forge_list_approval_requests",
880
+ label: "Forge Approval Requests",
881
+ description: "Inspect Forge approval requests.",
882
+ path: () => "/api/v1/approval-requests"
883
+ });
884
+ registerWriteTool(api, config, {
885
+ name: "forge_approve_request",
886
+ label: "Approve Request",
887
+ description: "Approve a pending Forge approval request.",
888
+ parameters: Type.Object({
889
+ id: Type.String({ minLength: 1 }),
890
+ note: optionalString(),
891
+ actor: optionalNullableString()
892
+ }),
893
+ method: "POST",
894
+ path: (params) => `/api/v1/approval-requests/${encodeURIComponent(String(params.id))}/approve`,
895
+ body: (params) => ({
896
+ note: params.note ?? "",
897
+ actor: params.actor ?? null
898
+ })
899
+ });
900
+ registerWriteTool(api, config, {
901
+ name: "forge_reject_request",
902
+ label: "Reject Request",
903
+ description: "Reject a pending Forge approval request.",
904
+ parameters: Type.Object({
905
+ id: Type.String({ minLength: 1 }),
906
+ note: optionalString(),
907
+ actor: optionalNullableString()
908
+ }),
909
+ method: "POST",
910
+ path: (params) => `/api/v1/approval-requests/${encodeURIComponent(String(params.id))}/reject`,
911
+ body: (params) => ({
912
+ note: params.note ?? "",
913
+ actor: params.actor ?? null
914
+ })
915
+ });
916
+ registerReadTool(api, config, {
917
+ name: "forge_list_agents",
918
+ label: "Forge Agents",
919
+ description: "List registered Forge agents and their trust posture.",
920
+ path: () => "/api/v1/agents"
921
+ });
922
+ registerReadTool(api, config, {
923
+ name: "forge_list_agent_actions",
924
+ label: "Forge Agent Actions",
925
+ description: "Inspect actions associated with a Forge agent.",
926
+ parameters: withId({}),
927
+ path: (params) => `/api/v1/agents/${encodeURIComponent(String(params.id))}/actions`
928
+ });
929
+ registerReadTool(api, config, {
930
+ name: "forge_list_reward_rules",
931
+ label: "Reward Rules",
932
+ description: "List Forge reward rules.",
933
+ path: () => "/api/v1/rewards/rules"
934
+ });
935
+ registerWriteTool(api, config, {
936
+ name: "forge_update_reward_rule",
937
+ label: "Update Reward Rule",
938
+ description: "Update a Forge reward rule configuration.",
939
+ parameters: withId({
940
+ title: optionalString(),
941
+ description: optionalString(),
942
+ active: Type.Optional(Type.Boolean()),
943
+ config: Type.Optional(Type.Record(Type.String(), Type.Any()))
944
+ }),
945
+ method: "PATCH",
946
+ path: (params) => `/api/v1/rewards/rules/${encodeURIComponent(String(params.id))}`,
947
+ body: (params) => {
948
+ const { id, ...body } = params;
949
+ return body;
950
+ }
951
+ });
952
+ registerWriteTool(api, config, {
953
+ name: "forge_award_xp_bonus",
954
+ label: "Award XP Bonus",
955
+ description: "Create a manual, explainable XP bonus in the Forge reward ledger.",
956
+ parameters: Type.Object({
957
+ entityType: Type.String({ minLength: 1 }),
958
+ entityId: Type.String({ minLength: 1 }),
959
+ deltaXp: Type.Number(),
960
+ reasonTitle: Type.String({ minLength: 1 }),
961
+ reasonSummary: optionalString(),
962
+ metadata: Type.Optional(Type.Record(Type.String(), Type.Any()))
963
+ }),
964
+ method: "POST",
965
+ path: () => "/api/v1/rewards/bonus"
966
+ });
967
+ registerReadTool(api, config, {
968
+ name: "forge_get_psyche_overview",
969
+ label: "Forge Psyche Overview",
970
+ description: "Read the current Psyche operating picture.",
971
+ path: () => "/api/v1/psyche/overview"
972
+ });
973
+ registerPsycheCrudTools(api, config, {
974
+ pluralName: "values",
975
+ singularName: "value",
976
+ label: "Psyche Values",
977
+ createShape: {
978
+ title: Type.String({ minLength: 1 }),
979
+ description: optionalString(),
980
+ valuedDirection: optionalString(),
981
+ whyItMatters: optionalString(),
982
+ linkedGoalIds: optionalStringArray(),
983
+ linkedProjectIds: optionalStringArray(),
984
+ linkedTaskIds: optionalStringArray(),
985
+ committedActions: Type.Optional(Type.Array(Type.String()))
986
+ },
987
+ updateShape: {
988
+ title: optionalString(),
989
+ description: optionalString(),
990
+ valuedDirection: optionalString(),
991
+ whyItMatters: optionalString(),
992
+ linkedGoalIds: optionalStringArray(),
993
+ linkedProjectIds: optionalStringArray(),
994
+ linkedTaskIds: optionalStringArray(),
995
+ committedActions: Type.Optional(Type.Array(Type.String()))
996
+ }
997
+ });
998
+ registerPsycheCrudTools(api, config, {
999
+ pluralName: "patterns",
1000
+ singularName: "pattern",
1001
+ label: "Psyche Patterns",
1002
+ createShape: {
1003
+ title: Type.String({ minLength: 1 }),
1004
+ description: optionalString(),
1005
+ targetBehavior: optionalString(),
1006
+ cueContexts: Type.Optional(Type.Array(Type.String())),
1007
+ shortTermPayoff: optionalString(),
1008
+ longTermCost: optionalString(),
1009
+ preferredResponse: optionalString(),
1010
+ linkedValueIds: optionalStringArray(),
1011
+ linkedSchemaLabels: Type.Optional(Type.Array(Type.String())),
1012
+ linkedModeLabels: Type.Optional(Type.Array(Type.String()))
1013
+ },
1014
+ updateShape: {
1015
+ title: optionalString(),
1016
+ description: optionalString(),
1017
+ targetBehavior: optionalString(),
1018
+ cueContexts: Type.Optional(Type.Array(Type.String())),
1019
+ shortTermPayoff: optionalString(),
1020
+ longTermCost: optionalString(),
1021
+ preferredResponse: optionalString(),
1022
+ linkedValueIds: optionalStringArray(),
1023
+ linkedSchemaLabels: Type.Optional(Type.Array(Type.String())),
1024
+ linkedModeLabels: Type.Optional(Type.Array(Type.String()))
1025
+ }
1026
+ });
1027
+ registerPsycheCrudTools(api, config, {
1028
+ pluralName: "behaviors",
1029
+ singularName: "behavior",
1030
+ label: "Psyche Behaviors",
1031
+ createShape: {
1032
+ kind: Type.String({ minLength: 1 }),
1033
+ title: Type.String({ minLength: 1 }),
1034
+ description: optionalString(),
1035
+ commonCues: Type.Optional(Type.Array(Type.String())),
1036
+ urgeStory: optionalString(),
1037
+ shortTermPayoff: optionalString(),
1038
+ longTermCost: optionalString(),
1039
+ replacementMove: optionalString(),
1040
+ repairPlan: optionalString(),
1041
+ linkedPatternIds: optionalStringArray(),
1042
+ linkedValueIds: optionalStringArray(),
1043
+ linkedSchemaIds: optionalStringArray(),
1044
+ linkedModeIds: optionalStringArray()
1045
+ },
1046
+ updateShape: {
1047
+ kind: optionalString(),
1048
+ title: optionalString(),
1049
+ description: optionalString(),
1050
+ commonCues: Type.Optional(Type.Array(Type.String())),
1051
+ urgeStory: optionalString(),
1052
+ shortTermPayoff: optionalString(),
1053
+ longTermCost: optionalString(),
1054
+ replacementMove: optionalString(),
1055
+ repairPlan: optionalString(),
1056
+ linkedPatternIds: optionalStringArray(),
1057
+ linkedValueIds: optionalStringArray(),
1058
+ linkedSchemaIds: optionalStringArray(),
1059
+ linkedModeIds: optionalStringArray()
1060
+ }
1061
+ });
1062
+ registerPsycheCrudTools(api, config, {
1063
+ pluralName: "beliefs",
1064
+ singularName: "belief",
1065
+ label: "Psyche Beliefs",
1066
+ createShape: {
1067
+ statement: Type.String({ minLength: 1 }),
1068
+ schemaId: optionalNullableString(),
1069
+ beliefType: optionalString(),
1070
+ originNote: optionalString(),
1071
+ confidence: Type.Optional(Type.Number()),
1072
+ evidenceFor: Type.Optional(Type.Array(Type.String())),
1073
+ evidenceAgainst: Type.Optional(Type.Array(Type.String())),
1074
+ flexibleAlternative: optionalString(),
1075
+ linkedValueIds: optionalStringArray(),
1076
+ linkedBehaviorIds: optionalStringArray(),
1077
+ linkedModeIds: optionalStringArray(),
1078
+ linkedReportIds: optionalStringArray()
1079
+ },
1080
+ updateShape: {
1081
+ statement: optionalString(),
1082
+ schemaId: optionalNullableString(),
1083
+ beliefType: optionalString(),
1084
+ originNote: optionalString(),
1085
+ confidence: Type.Optional(Type.Number()),
1086
+ evidenceFor: Type.Optional(Type.Array(Type.String())),
1087
+ evidenceAgainst: Type.Optional(Type.Array(Type.String())),
1088
+ flexibleAlternative: optionalString(),
1089
+ linkedValueIds: optionalStringArray(),
1090
+ linkedBehaviorIds: optionalStringArray(),
1091
+ linkedModeIds: optionalStringArray(),
1092
+ linkedReportIds: optionalStringArray()
1093
+ }
1094
+ });
1095
+ registerReadTool(api, config, {
1096
+ name: "forge_list_psyche_schema_catalog",
1097
+ label: "Psyche Schema Catalog",
1098
+ description: "List the fixed schema-therapy catalog used by Forge Psyche.",
1099
+ path: () => "/api/v1/psyche/schema-catalog"
1100
+ });
1101
+ registerReadTool(api, config, {
1102
+ name: "forge_list_psyche_reports",
1103
+ label: "Psyche Reports",
1104
+ description: "List Forge Psyche trigger reports.",
1105
+ path: () => "/api/v1/psyche/reports"
1106
+ });
1107
+ registerReadTool(api, config, {
1108
+ name: "forge_get_psyche_report",
1109
+ label: "Forge Psyche Report",
1110
+ description: "Fetch a detailed Psyche trigger report.",
1111
+ parameters: withId({}),
1112
+ path: (params) => `/api/v1/psyche/reports/${encodeURIComponent(String(params.id))}`
1113
+ });
1114
+ registerWriteTool(api, config, {
1115
+ name: "forge_create_psyche_report",
1116
+ label: "Create Psyche Report",
1117
+ description: "Create a Forge Psyche trigger report.",
1118
+ parameters: Type.Object({
1119
+ title: Type.String({ minLength: 1 }),
1120
+ status: optionalString(),
1121
+ eventTypeId: optionalNullableString(),
1122
+ customEventType: optionalString(),
1123
+ eventSituation: optionalString(),
1124
+ occurredAt: optionalNullableString(),
1125
+ emotions: Type.Optional(Type.Array(Type.Any())),
1126
+ thoughts: Type.Optional(Type.Array(Type.Any())),
1127
+ behaviors: Type.Optional(Type.Array(Type.Any())),
1128
+ consequences: Type.Optional(Type.Any()),
1129
+ linkedPatternIds: optionalStringArray(),
1130
+ linkedValueIds: optionalStringArray(),
1131
+ linkedGoalIds: optionalStringArray(),
1132
+ linkedProjectIds: optionalStringArray(),
1133
+ linkedTaskIds: optionalStringArray(),
1134
+ linkedBehaviorIds: optionalStringArray(),
1135
+ linkedBeliefIds: optionalStringArray(),
1136
+ linkedModeIds: optionalStringArray(),
1137
+ modeOverlays: Type.Optional(Type.Array(Type.String())),
1138
+ schemaLinks: Type.Optional(Type.Array(Type.String())),
1139
+ modeTimeline: Type.Optional(Type.Array(Type.Any())),
1140
+ nextMoves: Type.Optional(Type.Array(Type.String()))
1141
+ }),
1142
+ method: "POST",
1143
+ path: () => "/api/v1/psyche/reports"
1144
+ });
1145
+ registerWriteTool(api, config, {
1146
+ name: "forge_update_psyche_report",
1147
+ label: "Forge Update Psyche Report",
1148
+ description: "Update a Psyche trigger report through the versioned API.",
1149
+ parameters: withId({
1150
+ title: optionalString(),
1151
+ status: optionalString(),
1152
+ eventTypeId: optionalNullableString(),
1153
+ customEventType: optionalString(),
1154
+ eventSituation: optionalString(),
1155
+ occurredAt: optionalNullableString(),
1156
+ emotions: Type.Optional(Type.Array(Type.Any())),
1157
+ thoughts: Type.Optional(Type.Array(Type.Any())),
1158
+ behaviors: Type.Optional(Type.Array(Type.Any())),
1159
+ consequences: Type.Optional(Type.Any()),
1160
+ linkedPatternIds: optionalStringArray(),
1161
+ linkedValueIds: optionalStringArray(),
1162
+ linkedGoalIds: optionalStringArray(),
1163
+ linkedProjectIds: optionalStringArray(),
1164
+ linkedTaskIds: optionalStringArray(),
1165
+ linkedBehaviorIds: optionalStringArray(),
1166
+ linkedBeliefIds: optionalStringArray(),
1167
+ linkedModeIds: optionalStringArray(),
1168
+ modeOverlays: Type.Optional(Type.Array(Type.String())),
1169
+ schemaLinks: Type.Optional(Type.Array(Type.String())),
1170
+ modeTimeline: Type.Optional(Type.Array(Type.Any())),
1171
+ nextMoves: Type.Optional(Type.Array(Type.String()))
1172
+ }),
1173
+ method: "PATCH",
1174
+ path: (params) => `/api/v1/psyche/reports/${encodeURIComponent(String(params.id))}`,
1175
+ body: (params) => {
1176
+ const { id, ...body } = params;
1177
+ return body;
1178
+ }
1179
+ });
1180
+ registerWriteTool(api, config, {
1181
+ name: "forge_delete_psyche_report",
1182
+ label: "Delete Psyche Report",
1183
+ description: "Delete a Forge Psyche trigger report.",
1184
+ parameters: withId({}),
1185
+ method: "DELETE",
1186
+ path: (params) => `/api/v1/psyche/reports/${encodeURIComponent(String(params.id))}`,
1187
+ bodyless: true
1188
+ });
1189
+ registerPsycheCrudTools(api, config, {
1190
+ pluralName: "modes",
1191
+ singularName: "mode",
1192
+ label: "Psyche Modes",
1193
+ createShape: {
1194
+ family: Type.String({ minLength: 1 }),
1195
+ archetype: optionalString(),
1196
+ title: Type.String({ minLength: 1 }),
1197
+ persona: optionalString(),
1198
+ imagery: optionalString(),
1199
+ symbolicForm: optionalString(),
1200
+ facialExpression: optionalString(),
1201
+ fear: optionalString(),
1202
+ burden: optionalString(),
1203
+ protectiveJob: optionalString(),
1204
+ originContext: optionalString(),
1205
+ firstAppearanceAt: optionalNullableString(),
1206
+ linkedPatternIds: optionalStringArray(),
1207
+ linkedBehaviorIds: optionalStringArray(),
1208
+ linkedValueIds: optionalStringArray()
1209
+ },
1210
+ updateShape: {
1211
+ family: optionalString(),
1212
+ archetype: optionalString(),
1213
+ title: optionalString(),
1214
+ persona: optionalString(),
1215
+ imagery: optionalString(),
1216
+ symbolicForm: optionalString(),
1217
+ facialExpression: optionalString(),
1218
+ fear: optionalString(),
1219
+ burden: optionalString(),
1220
+ protectiveJob: optionalString(),
1221
+ originContext: optionalString(),
1222
+ firstAppearanceAt: optionalNullableString(),
1223
+ linkedPatternIds: optionalStringArray(),
1224
+ linkedBehaviorIds: optionalStringArray(),
1225
+ linkedValueIds: optionalStringArray()
1226
+ }
1227
+ });
1228
+ registerPsycheCrudTools(api, config, {
1229
+ pluralName: "event-types",
1230
+ singularName: "event_type",
1231
+ label: "Psyche Event Types",
1232
+ createShape: {
1233
+ label: Type.String({ minLength: 1 }),
1234
+ description: optionalString()
1235
+ },
1236
+ updateShape: {
1237
+ label: optionalString(),
1238
+ description: optionalString()
1239
+ }
1240
+ });
1241
+ registerPsycheCrudTools(api, config, {
1242
+ pluralName: "emotions",
1243
+ singularName: "emotion",
1244
+ label: "Psyche Emotions",
1245
+ createShape: {
1246
+ label: Type.String({ minLength: 1 }),
1247
+ description: optionalString(),
1248
+ category: optionalString()
1249
+ },
1250
+ updateShape: {
1251
+ label: optionalString(),
1252
+ description: optionalString(),
1253
+ category: optionalString()
1254
+ }
1255
+ });
1256
+ registerPsycheCrudTools(api, config, {
1257
+ pluralName: "mode-guides",
1258
+ singularName: "mode_guide",
1259
+ label: "Psyche Mode Guides",
1260
+ createShape: {
1261
+ summary: Type.String({ minLength: 1 }),
1262
+ answers: Type.Array(Type.Any())
1263
+ },
1264
+ updateShape: {
1265
+ summary: optionalString(),
1266
+ answers: Type.Optional(Type.Array(Type.Any()))
1267
+ }
1268
+ });
1269
+ }