forge-openclaw-plugin 0.2.3 → 0.2.7

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 (117) hide show
  1. package/README.md +114 -6
  2. package/dist/assets/board-CzgvdLO8.js +6 -0
  3. package/dist/assets/board-CzgvdLO8.js.map +1 -0
  4. package/dist/assets/favicon-BCHm9dUV.ico +0 -0
  5. package/dist/assets/index-8d_oM8fL.js +27 -0
  6. package/dist/assets/index-8d_oM8fL.js.map +1 -0
  7. package/dist/assets/index-D4A_bq8m.css +1 -0
  8. package/dist/assets/motion-STUd1O46.js +10 -0
  9. package/dist/assets/motion-STUd1O46.js.map +1 -0
  10. package/dist/assets/plus-jakarta-sans-latin-ext-wght-normal-DmpS2jIq.woff2 +0 -0
  11. package/dist/assets/plus-jakarta-sans-latin-wght-normal-eXO_dkmS.woff2 +0 -0
  12. package/dist/assets/plus-jakarta-sans-vietnamese-wght-normal-qRpaaN48.woff2 +0 -0
  13. package/dist/assets/sora-latin-ext-wght-normal-CawQDOvP.woff2 +0 -0
  14. package/dist/assets/sora-latin-wght-normal-DdqRvwsR.woff2 +0 -0
  15. package/dist/assets/space-grotesk-latin-500-normal-CNSSEhBt.woff +0 -0
  16. package/dist/assets/space-grotesk-latin-500-normal-lFbtlQH6.woff2 +0 -0
  17. package/dist/assets/space-grotesk-latin-700-normal-CwsQ-cCU.woff +0 -0
  18. package/dist/assets/space-grotesk-latin-700-normal-RjhwGPKo.woff2 +0 -0
  19. package/dist/assets/space-grotesk-latin-ext-500-normal-3dgZTiw9.woff +0 -0
  20. package/dist/assets/space-grotesk-latin-ext-500-normal-DUe3BAxM.woff2 +0 -0
  21. package/dist/assets/space-grotesk-latin-ext-700-normal-BQnZhY3m.woff2 +0 -0
  22. package/dist/assets/space-grotesk-latin-ext-700-normal-HVCqSBdx.woff +0 -0
  23. package/dist/assets/space-grotesk-vietnamese-500-normal-BTqKIpxg.woff +0 -0
  24. package/dist/assets/space-grotesk-vietnamese-500-normal-BmEvtly_.woff2 +0 -0
  25. package/dist/assets/space-grotesk-vietnamese-700-normal-DMty7AZE.woff2 +0 -0
  26. package/dist/assets/space-grotesk-vietnamese-700-normal-Duxec5Rn.woff +0 -0
  27. package/dist/assets/table-CtNlETLc.js +23 -0
  28. package/dist/assets/table-CtNlETLc.js.map +1 -0
  29. package/dist/assets/ui-ThzkR_oW.js +46 -0
  30. package/dist/assets/ui-ThzkR_oW.js.map +1 -0
  31. package/dist/assets/vendor-CRS-psbw.css +1 -0
  32. package/dist/assets/vendor-DyHAI6nk.js +423 -0
  33. package/dist/assets/vendor-DyHAI6nk.js.map +1 -0
  34. package/dist/assets/viz-BJuBCz_G.js +34 -0
  35. package/dist/assets/viz-BJuBCz_G.js.map +1 -0
  36. package/dist/favicon.ico +0 -0
  37. package/dist/favicon.png +0 -0
  38. package/dist/index.html +29 -0
  39. package/dist/openclaw/api-client.d.ts +8 -0
  40. package/dist/openclaw/api-client.js +31 -4
  41. package/dist/openclaw/local-runtime.d.ts +3 -0
  42. package/dist/openclaw/local-runtime.js +135 -0
  43. package/dist/openclaw/parity.d.ts +4 -4
  44. package/dist/openclaw/parity.js +23 -33
  45. package/dist/openclaw/plugin-entry-shared.d.ts +5 -3
  46. package/dist/openclaw/plugin-entry-shared.js +52 -10
  47. package/dist/openclaw/routes.d.ts +12 -3
  48. package/dist/openclaw/routes.js +156 -924
  49. package/dist/openclaw/tools.js +242 -1100
  50. package/dist/server/app.js +2450 -0
  51. package/dist/server/db.js +313 -0
  52. package/dist/server/e2e-server.js +20 -0
  53. package/dist/server/errors.js +15 -0
  54. package/dist/server/index.js +16 -0
  55. package/dist/server/managers/base.js +17 -0
  56. package/dist/server/managers/contracts.js +47 -0
  57. package/dist/server/managers/platform/api-gateway-manager.js +11 -0
  58. package/dist/server/managers/platform/audit-manager.js +15 -0
  59. package/dist/server/managers/platform/authentication-manager.js +56 -0
  60. package/dist/server/managers/platform/authorization-manager.js +56 -0
  61. package/dist/server/managers/platform/background-job-manager.js +10 -0
  62. package/dist/server/managers/platform/configuration-manager.js +33 -0
  63. package/dist/server/managers/platform/database-manager.js +14 -0
  64. package/dist/server/managers/platform/event-bus-manager.js +7 -0
  65. package/dist/server/managers/platform/external-service-manager.js +11 -0
  66. package/dist/server/managers/platform/health-manager.js +7 -0
  67. package/dist/server/managers/platform/migration-manager.js +8 -0
  68. package/dist/server/managers/platform/search-index-manager.js +4 -0
  69. package/dist/server/managers/platform/secrets-manager.js +19 -0
  70. package/dist/server/managers/platform/session-manager.js +121 -0
  71. package/dist/server/managers/platform/storage-manager.js +16 -0
  72. package/dist/server/managers/platform/token-manager.js +37 -0
  73. package/dist/server/managers/platform/transaction-manager.js +8 -0
  74. package/dist/server/managers/platform/trusted-network.js +39 -0
  75. package/dist/server/managers/runtime.js +56 -0
  76. package/dist/server/managers/type-guards.js +4 -0
  77. package/dist/server/openapi.js +3512 -0
  78. package/dist/server/psyche-types.js +395 -0
  79. package/dist/server/repositories/activity-events.js +157 -0
  80. package/dist/server/repositories/collaboration.js +497 -0
  81. package/dist/server/repositories/comments.js +176 -0
  82. package/dist/server/repositories/deleted-entities.js +192 -0
  83. package/dist/server/repositories/domains.js +30 -0
  84. package/dist/server/repositories/event-log.js +64 -0
  85. package/dist/server/repositories/goals.js +159 -0
  86. package/dist/server/repositories/projects.js +214 -0
  87. package/dist/server/repositories/psyche.js +1356 -0
  88. package/dist/server/repositories/rewards.js +675 -0
  89. package/dist/server/repositories/settings.js +399 -0
  90. package/dist/server/repositories/tags.js +160 -0
  91. package/dist/server/repositories/task-runs.js +488 -0
  92. package/dist/server/repositories/tasks.js +413 -0
  93. package/dist/server/services/context.js +214 -0
  94. package/dist/server/services/dashboard.js +170 -0
  95. package/dist/server/services/entity-crud.js +576 -0
  96. package/dist/server/services/gamification.js +215 -0
  97. package/dist/server/services/insights.js +91 -0
  98. package/dist/server/services/projects.js +75 -0
  99. package/dist/server/services/psyche.js +63 -0
  100. package/dist/server/services/relations.js +28 -0
  101. package/dist/server/services/reviews.js +88 -0
  102. package/dist/server/services/run-recovery.js +13 -0
  103. package/dist/server/services/tagging.js +49 -0
  104. package/dist/server/services/task-run-watchdog.js +92 -0
  105. package/dist/server/services/work-time.js +176 -0
  106. package/dist/server/types.js +999 -0
  107. package/dist/server/web.js +91 -0
  108. package/openclaw.plugin.json +22 -10
  109. package/package.json +17 -4
  110. package/server/migrations/001_core.sql +333 -0
  111. package/server/migrations/002_psyche.sql +241 -0
  112. package/server/migrations/003_timer_execution.sql +18 -0
  113. package/server/migrations/004_psyche_linked_entities.sql +5 -0
  114. package/server/migrations/005_adaptive_schemas.sql +157 -0
  115. package/server/migrations/006_psyche_auth_setting.sql +4 -0
  116. package/server/migrations/007_deleted_entities.sql +16 -0
  117. package/skills/forge-openclaw/SKILL.md +189 -275
@@ -1,5 +1,5 @@
1
1
  import { Type } from "@sinclair/typebox";
2
- import { callForgeApi, expectForgeSuccess, requireApiToken } from "./api-client.js";
2
+ import { callConfiguredForgeApi, expectForgeSuccess, requireApiToken } from "./api-client.js";
3
3
  function jsonResult(payload) {
4
4
  return {
5
5
  content: [
@@ -12,11 +12,7 @@ function jsonResult(payload) {
12
12
  };
13
13
  }
14
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,
15
+ const result = await callConfiguredForgeApi(config, {
20
16
  method: "GET",
21
17
  path
22
18
  });
@@ -24,11 +20,7 @@ async function runRead(config, path) {
24
20
  }
25
21
  async function runWrite(config, options) {
26
22
  requireApiToken(config);
27
- const result = await callForgeApi({
28
- baseUrl: config.baseUrl,
29
- apiToken: config.apiToken,
30
- actorLabel: config.actorLabel,
31
- timeoutMs: config.timeoutMs,
23
+ const result = await callConfiguredForgeApi(config, {
32
24
  method: options.method,
33
25
  path: options.path,
34
26
  body: options.body
@@ -38,20 +30,46 @@ async function runWrite(config, options) {
38
30
  const emptyObjectSchema = Type.Object({});
39
31
  const optionalString = () => Type.Optional(Type.String());
40
32
  const optionalNullableString = () => Type.Optional(Type.Union([Type.String(), Type.Null()]));
41
- const stringArray = () => Type.Array(Type.String());
42
- const optionalStringArray = () => Type.Optional(stringArray());
43
33
  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
- });
34
+ async function resolveUiEntrypoint(config) {
35
+ let webAppUrl = config.webAppUrl;
36
+ try {
37
+ const onboarding = await runRead(config, "/api/v1/agents/onboarding");
38
+ if (typeof onboarding === "object" &&
39
+ onboarding !== null &&
40
+ "onboarding" in onboarding &&
41
+ typeof onboarding.onboarding === "object" &&
42
+ onboarding.onboarding !== null &&
43
+ "webAppUrl" in onboarding.onboarding &&
44
+ typeof onboarding.onboarding.webAppUrl === "string" &&
45
+ onboarding.onboarding.webAppUrl.trim().length > 0) {
46
+ webAppUrl = onboarding.onboarding.webAppUrl;
47
+ }
48
+ }
49
+ catch {
50
+ // Fall back to the derived UI URL from config when onboarding is unavailable.
51
+ }
52
+ return {
53
+ webAppUrl,
54
+ pluginUiRoute: "/forge/v1/ui",
55
+ note: "You can continue directly in the Forge UI when a visual workflow is easier for review, Kanban, or Psyche exploration."
56
+ };
57
+ }
58
+ async function resolveCurrentWork(config) {
59
+ const payload = await runRead(config, "/api/v1/operator/context");
60
+ const context = typeof payload === "object" && payload !== null && "context" in payload && typeof payload.context === "object" && payload.context !== null
61
+ ? payload.context
62
+ : null;
63
+ const recentTaskRuns = Array.isArray(context?.recentTaskRuns) ? context.recentTaskRuns : [];
64
+ const activeTaskRuns = recentTaskRuns.filter((run) => typeof run === "object" && run !== null && "status" in run && run.status === "active");
65
+ const focusTasks = Array.isArray(context?.focusTasks) ? context.focusTasks : [];
66
+ return {
67
+ generatedAt: typeof context?.generatedAt === "string" ? context.generatedAt : new Date().toISOString(),
68
+ activeTaskRuns,
69
+ focusTasks,
70
+ recommendedNextTask: context?.recommendedNextTask ?? null,
71
+ xp: context?.xp ?? null
72
+ };
55
73
  }
56
74
  function registerReadTool(api, config, options) {
57
75
  api.registerTool({
@@ -74,726 +92,93 @@ function registerWriteTool(api, config, options) {
74
92
  const typed = params;
75
93
  return jsonResult(await runWrite(config, {
76
94
  method: options.method,
77
- path: options.path(typed),
78
- body: options.bodyless ? undefined : options.body ? options.body(typed) : typed
95
+ path: options.path,
96
+ body: options.body ? options.body(typed) : typed
79
97
  }));
80
98
  }
81
99
  });
82
100
  }
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
101
  export function registerForgePluginTools(api, config) {
157
102
  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"
103
+ name: "forge_get_operator_overview",
104
+ label: "Forge Operator Overview",
105
+ description: "Start here for most Forge work. Read the one-shot operator overview with current priorities, momentum, and onboarding guidance before searching or mutating.",
106
+ path: () => "/api/v1/operator/overview"
168
107
  });
169
108
  registerReadTool(api, config, {
170
109
  name: "forge_get_operator_context",
171
110
  label: "Forge Operator Context",
172
- description: "Read the agent-focused Forge operator context with active projects, focus tasks, board lanes, and XP state.",
111
+ 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.",
173
112
  path: () => "/api/v1/operator/context"
174
113
  });
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
114
  registerReadTool(api, config, {
182
115
  name: "forge_get_agent_onboarding",
183
116
  label: "Forge Agent Onboarding",
184
- description: "Fetch the Forge onboarding contract with recommended scopes, headers, and verification guidance.",
117
+ description: "Fetch the live Forge onboarding contract with the exact Forge tool list, batch payload rules, UI handoff rules, and verification guidance.",
185
118
  path: () => "/api/v1/agents/onboarding"
186
119
  });
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"
120
+ api.registerTool({
121
+ name: "forge_get_ui_entrypoint",
122
+ label: "Forge UI Entrypoint",
123
+ description: "Get the live Forge web UI URL and plugin redirect route. Use this only when visual review or editing is genuinely easier, not as a substitute for normal batch entity creation or updates.",
124
+ parameters: emptyObjectSchema,
125
+ async execute() {
126
+ return jsonResult(await resolveUiEntrypoint(config));
127
+ }
192
128
  });
193
129
  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"
130
+ name: "forge_get_psyche_overview",
131
+ label: "Forge Psyche Overview",
132
+ description: "Read the aggregate Psyche state across values, patterns, behaviors, beliefs, modes, and trigger reports before making Psyche recommendations or updates.",
133
+ path: () => "/api/v1/psyche/overview"
223
134
  });
224
135
  registerReadTool(api, config, {
225
136
  name: "forge_get_xp_metrics",
226
137
  label: "Forge XP Metrics",
227
- description: "Read current Forge XP metrics, reward rules, and recent reward reasons.",
138
+ description: "Read the live XP, level, streak, momentum, and reward metrics.",
228
139
  path: () => "/api/v1/metrics/xp"
229
140
  });
230
141
  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
- })
142
+ name: "forge_get_weekly_review",
143
+ label: "Forge Weekly Review",
144
+ description: "Read the current weekly review payload with wins, trends, and reward framing.",
145
+ path: () => "/api/v1/reviews/weekly"
604
146
  });
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;
147
+ api.registerTool({
148
+ name: "forge_get_current_work",
149
+ label: "Forge Current Work",
150
+ description: "Get the current live-work picture: active task runs, focus tasks, the recommended next task, and current XP state.",
151
+ parameters: emptyObjectSchema,
152
+ async execute() {
153
+ return jsonResult(await resolveCurrentWork(config));
707
154
  }
708
155
  });
709
156
  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.",
157
+ name: "forge_search_entities",
158
+ label: "Search Forge Entities",
159
+ description: "Search Forge entities before creating or updating to avoid duplicates. Pass `searches` as an array, even for one search.",
731
160
  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()
161
+ searches: Type.Array(Type.Object({
162
+ entityTypes: Type.Optional(Type.Array(Type.String())),
163
+ query: optionalString(),
164
+ ids: Type.Optional(Type.Array(Type.String())),
165
+ status: Type.Optional(Type.Array(Type.String())),
166
+ linkedTo: Type.Optional(Type.Object({
167
+ entityType: Type.String({ minLength: 1 }),
168
+ id: Type.String({ minLength: 1 })
169
+ })),
170
+ includeDeleted: Type.Optional(Type.Boolean()),
171
+ limit: Type.Optional(Type.Number({ minimum: 1, maximum: 100 })),
172
+ clientRef: optionalString()
173
+ }))
742
174
  }),
743
175
  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
176
+ path: "/api/v1/entities/search"
792
177
  });
793
178
  registerWriteTool(api, config, {
794
179
  name: "forge_create_entities",
795
180
  label: "Create Forge Entities",
796
- description: "Create multiple Forge entities in one ordered batch request.",
181
+ description: "Create one or more Forge entities through the ordered batch workflow. Pass `operations` as an array. Each operation must include `entityType` and full `data`. Batch several creates together in one call when possible.",
797
182
  parameters: Type.Object({
798
183
  atomic: Type.Optional(Type.Boolean()),
799
184
  operations: Type.Array(Type.Object({
@@ -803,12 +188,12 @@ export function registerForgePluginTools(api, config) {
803
188
  }))
804
189
  }),
805
190
  method: "POST",
806
- path: () => "/api/v1/entities/create"
191
+ path: "/api/v1/entities/create"
807
192
  });
808
193
  registerWriteTool(api, config, {
809
194
  name: "forge_update_entities",
810
195
  label: "Update Forge Entities",
811
- description: "Update multiple Forge entities in one ordered batch request.",
196
+ description: "Update one or more Forge entities through the ordered batch workflow. Pass `operations` as an array. Each operation must include `entityType`, `id`, and `patch`.",
812
197
  parameters: Type.Object({
813
198
  atomic: Type.Optional(Type.Boolean()),
814
199
  operations: Type.Array(Type.Object({
@@ -819,12 +204,12 @@ export function registerForgePluginTools(api, config) {
819
204
  }))
820
205
  }),
821
206
  method: "POST",
822
- path: () => "/api/v1/entities/update"
207
+ path: "/api/v1/entities/update"
823
208
  });
824
209
  registerWriteTool(api, config, {
825
210
  name: "forge_delete_entities",
826
211
  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.",
212
+ description: "Delete Forge entities in one batch request. Pass `operations` as an array with `entityType` and `id`. Delete defaults to soft mode unless hard is requested explicitly.",
828
213
  parameters: Type.Object({
829
214
  atomic: Type.Optional(Type.Boolean()),
830
215
  operations: Type.Array(Type.Object({
@@ -836,12 +221,12 @@ export function registerForgePluginTools(api, config) {
836
221
  }))
837
222
  }),
838
223
  method: "POST",
839
- path: () => "/api/v1/entities/delete"
224
+ path: "/api/v1/entities/delete"
840
225
  });
841
226
  registerWriteTool(api, config, {
842
227
  name: "forge_restore_entities",
843
228
  label: "Restore Forge Entities",
844
- description: "Restore soft-deleted Forge entities from the settings bin.",
229
+ description: "Restore soft-deleted Forge entities from the settings bin through the batch workflow. Pass `operations` as an array with `entityType` and `id`.",
845
230
  parameters: Type.Object({
846
231
  atomic: Type.Optional(Type.Boolean()),
847
232
  operations: Type.Array(Type.Object({
@@ -851,419 +236,176 @@ export function registerForgePluginTools(api, config) {
851
236
  }))
852
237
  }),
853
238
  method: "POST",
854
- path: () => "/api/v1/entities/restore"
239
+ path: "/api/v1/entities/restore"
855
240
  });
856
241
  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.",
242
+ name: "forge_post_insight",
243
+ label: "Forge Post Insight",
244
+ description: "Post a structured Forge insight after reading the overview. This stores an agent-authored observation or recommendation with provenance.",
888
245
  parameters: Type.Object({
889
- id: Type.String({ minLength: 1 }),
890
- note: optionalString(),
891
- actor: optionalNullableString()
246
+ entityType: optionalNullableString(),
247
+ entityId: optionalNullableString(),
248
+ timeframeLabel: optionalNullableString(),
249
+ title: Type.String({ minLength: 1 }),
250
+ summary: Type.String({ minLength: 1 }),
251
+ recommendation: Type.String({ minLength: 1 }),
252
+ rationale: optionalString(),
253
+ confidence: Type.Optional(Type.Number()),
254
+ visibility: optionalString(),
255
+ ctaLabel: optionalString()
892
256
  }),
893
257
  method: "POST",
894
- path: (params) => `/api/v1/approval-requests/${encodeURIComponent(String(params.id))}/approve`,
258
+ path: "/api/v1/insights",
895
259
  body: (params) => ({
896
- note: params.note ?? "",
897
- actor: params.actor ?? null
260
+ originType: "agent",
261
+ originAgentId: null,
262
+ originLabel: config.actorLabel || "OpenClaw",
263
+ entityType: params.entityType ?? null,
264
+ entityId: params.entityId ?? null,
265
+ timeframeLabel: params.timeframeLabel ?? null,
266
+ title: params.title,
267
+ summary: params.summary,
268
+ recommendation: params.recommendation,
269
+ rationale: typeof params.rationale === "string" ? params.rationale : "",
270
+ confidence: params.confidence,
271
+ visibility: params.visibility,
272
+ ctaLabel: typeof params.ctaLabel === "string" ? params.ctaLabel : "Review insight"
898
273
  })
899
274
  });
900
275
  registerWriteTool(api, config, {
901
- name: "forge_reject_request",
902
- label: "Reject Request",
903
- description: "Reject a pending Forge approval request.",
276
+ name: "forge_log_work",
277
+ label: "Forge Log Work",
278
+ description: "Log retroactive work or mark an existing task as completed through the operator work-log flow. Use this when the user already did the work and wants truthful evidence plus XP.",
904
279
  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({
280
+ taskId: optionalString(),
940
281
  title: optionalString(),
941
282
  description: optionalString(),
942
- active: Type.Optional(Type.Boolean()),
943
- config: Type.Optional(Type.Record(Type.String(), Type.Any()))
283
+ summary: Type.Optional(Type.String()),
284
+ goalId: optionalNullableString(),
285
+ projectId: optionalNullableString(),
286
+ owner: optionalString(),
287
+ status: optionalString(),
288
+ priority: optionalString(),
289
+ dueDate: optionalNullableString(),
290
+ effort: optionalString(),
291
+ energy: optionalString(),
292
+ points: Type.Optional(Type.Integer({ minimum: 5, maximum: 500 })),
293
+ tagIds: Type.Optional(Type.Array(Type.String()))
944
294
  }),
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
- }
295
+ method: "POST",
296
+ path: "/api/v1/operator/log-work"
951
297
  });
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.",
298
+ api.registerTool({
299
+ name: "forge_start_task_run",
300
+ label: "Forge Start Task Run",
301
+ description: "Start real live work on a task. This creates or reuses a task run and is the truthful way to start work, not just changing task status.",
956
302
  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()))
303
+ taskId: Type.String({ minLength: 1 }),
304
+ actor: Type.String({ minLength: 1 }),
305
+ timerMode: Type.Optional(Type.Union([Type.Literal("planned"), Type.Literal("unlimited")])),
306
+ plannedDurationSeconds: Type.Optional(Type.Union([Type.Integer({ minimum: 60, maximum: 86400 }), Type.Null()])),
307
+ isCurrent: Type.Optional(Type.Boolean()),
308
+ leaseTtlSeconds: Type.Optional(Type.Integer({ minimum: 1, maximum: 14400 })),
309
+ note: Type.Optional(Type.String())
963
310
  }),
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()
311
+ async execute(_toolCallId, params) {
312
+ const typed = params;
313
+ return jsonResult(await runWrite(config, {
314
+ method: "POST",
315
+ path: `/api/v1/tasks/${typed.taskId}/runs`,
316
+ body: {
317
+ actor: typed.actor,
318
+ timerMode: typed.timerMode,
319
+ plannedDurationSeconds: typed.plannedDurationSeconds,
320
+ isCurrent: typed.isCurrent,
321
+ leaseTtlSeconds: typed.leaseTtlSeconds,
322
+ note: typed.note
323
+ }
324
+ }));
1093
325
  }
1094
326
  });
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.",
327
+ api.registerTool({
328
+ name: "forge_heartbeat_task_run",
329
+ label: "Forge Heartbeat Task Run",
330
+ description: "Refresh the lease on an active task run while work is continuing.",
1118
331
  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()))
332
+ taskRunId: Type.String({ minLength: 1 }),
333
+ actor: optionalString(),
334
+ leaseTtlSeconds: Type.Optional(Type.Integer({ minimum: 1, maximum: 14400 })),
335
+ note: Type.Optional(Type.String())
1172
336
  }),
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()
337
+ async execute(_toolCallId, params) {
338
+ const typed = params;
339
+ return jsonResult(await runWrite(config, {
340
+ method: "POST",
341
+ path: `/api/v1/task-runs/${typed.taskRunId}/heartbeat`,
342
+ body: {
343
+ actor: typed.actor,
344
+ leaseTtlSeconds: typed.leaseTtlSeconds,
345
+ note: typed.note
346
+ }
347
+ }));
1226
348
  }
1227
349
  });
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()
350
+ api.registerTool({
351
+ name: "forge_focus_task_run",
352
+ label: "Forge Focus Task Run",
353
+ description: "Mark an active task run as the current focused run when several runs exist.",
354
+ parameters: Type.Object({
355
+ taskRunId: Type.String({ minLength: 1 }),
356
+ actor: optionalString()
357
+ }),
358
+ async execute(_toolCallId, params) {
359
+ const typed = params;
360
+ return jsonResult(await runWrite(config, {
361
+ method: "POST",
362
+ path: `/api/v1/task-runs/${typed.taskRunId}/focus`,
363
+ body: {
364
+ actor: typed.actor
365
+ }
366
+ }));
1239
367
  }
1240
368
  });
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()
369
+ api.registerTool({
370
+ name: "forge_complete_task_run",
371
+ label: "Forge Complete Task Run",
372
+ description: "Finish an active task run as completed work and let Forge award the appropriate completion rewards.",
373
+ parameters: Type.Object({
374
+ taskRunId: Type.String({ minLength: 1 }),
375
+ actor: optionalString(),
376
+ note: Type.Optional(Type.String())
377
+ }),
378
+ async execute(_toolCallId, params) {
379
+ const typed = params;
380
+ return jsonResult(await runWrite(config, {
381
+ method: "POST",
382
+ path: `/api/v1/task-runs/${typed.taskRunId}/complete`,
383
+ body: {
384
+ actor: typed.actor,
385
+ note: typed.note
386
+ }
387
+ }));
1254
388
  }
1255
389
  });
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()))
390
+ api.registerTool({
391
+ name: "forge_release_task_run",
392
+ label: "Forge Release Task Run",
393
+ description: "Stop an active task run without completing it. Use this to truthfully stop current work.",
394
+ parameters: Type.Object({
395
+ taskRunId: Type.String({ minLength: 1 }),
396
+ actor: optionalString(),
397
+ note: Type.Optional(Type.String())
398
+ }),
399
+ async execute(_toolCallId, params) {
400
+ const typed = params;
401
+ return jsonResult(await runWrite(config, {
402
+ method: "POST",
403
+ path: `/api/v1/task-runs/${typed.taskRunId}/release`,
404
+ body: {
405
+ actor: typed.actor,
406
+ note: typed.note
407
+ }
408
+ }));
1267
409
  }
1268
410
  });
1269
411
  }