forge-openclaw-plugin 0.2.43 → 0.2.44

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.html CHANGED
@@ -13,14 +13,14 @@
13
13
  />
14
14
  <link rel="icon" type="image/png" href="/forge/assets/favicon-BCHm9dUV.ico" />
15
15
  <link rel="alternate icon" href="/forge/assets/favicon-BCHm9dUV.ico" />
16
- <script type="module" crossorigin src="/forge/assets/index-Bqqi_RSB.js"></script>
16
+ <script type="module" crossorigin src="/forge/assets/index-s24CefIb.js"></script>
17
17
  <link rel="modulepreload" crossorigin href="/forge/assets/vendor-D_NZFJze.js">
18
18
  <link rel="modulepreload" crossorigin href="/forge/assets/board-CAszQU7Y.js">
19
19
  <link rel="modulepreload" crossorigin href="/forge/assets/ui-B5MjRjKe.js">
20
20
  <link rel="modulepreload" crossorigin href="/forge/assets/motion-CU5aNClV.js">
21
21
  <link rel="modulepreload" crossorigin href="/forge/assets/table-CK0KcPYW.js">
22
22
  <link rel="stylesheet" crossorigin href="/forge/assets/vendor-DT3pnAKJ.css">
23
- <link rel="stylesheet" crossorigin href="/forge/assets/index-IrxlatFN.css">
23
+ <link rel="stylesheet" crossorigin href="/forge/assets/index-DtEvFzXp.css">
24
24
  </head>
25
25
  <body class="bg-canvas text-ink antialiased">
26
26
  <div id="root"></div>
@@ -11,6 +11,46 @@ function jsonResult(payload) {
11
11
  details: payload
12
12
  };
13
13
  }
14
+ function normalizeText(value) {
15
+ return typeof value === "string" ? value.trim() : "";
16
+ }
17
+ function normalizeOptionalText(value) {
18
+ const text = normalizeText(value);
19
+ return text.length > 0 ? text : undefined;
20
+ }
21
+ function normalizeTaskRunStartRequest(params) {
22
+ const taskId = normalizeText(params.taskId);
23
+ if (!taskId) {
24
+ throw new Error("forge_start_task_run requires a non-empty taskId.");
25
+ }
26
+ const actor = normalizeText(params.actor);
27
+ if (!actor) {
28
+ throw new Error("forge_start_task_run requires a non-empty actor.");
29
+ }
30
+ const timerMode = params.timerMode === "planned" ? "planned" : "unlimited";
31
+ const plannedDurationSeconds = typeof params.plannedDurationSeconds === "number" &&
32
+ Number.isInteger(params.plannedDurationSeconds)
33
+ ? params.plannedDurationSeconds
34
+ : null;
35
+ if (timerMode === "planned" && plannedDurationSeconds === null) {
36
+ throw new Error("forge_start_task_run requires plannedDurationSeconds when timerMode is planned.");
37
+ }
38
+ return {
39
+ taskId,
40
+ body: {
41
+ actor,
42
+ timerMode,
43
+ plannedDurationSeconds: timerMode === "planned" ? plannedDurationSeconds : null,
44
+ overrideReason: normalizeOptionalText(params.overrideReason),
45
+ isCurrent: typeof params.isCurrent === "boolean" ? params.isCurrent : undefined,
46
+ leaseTtlSeconds: typeof params.leaseTtlSeconds === "number" &&
47
+ Number.isInteger(params.leaseTtlSeconds)
48
+ ? params.leaseTtlSeconds
49
+ : undefined,
50
+ note: normalizeOptionalText(params.note)
51
+ }
52
+ };
53
+ }
14
54
  async function runRead(config, path) {
15
55
  const result = await callConfiguredForgeApi(config, {
16
56
  method: "GET",
@@ -954,19 +994,11 @@ export function registerForgePluginTools(api, config) {
954
994
  note: Type.Optional(Type.String())
955
995
  }),
956
996
  async execute(_toolCallId, params) {
957
- const typed = params;
997
+ const typed = normalizeTaskRunStartRequest(params);
958
998
  return jsonResult(await runWrite(config, {
959
999
  method: "POST",
960
1000
  path: `/api/v1/tasks/${typed.taskId}/runs`,
961
- body: {
962
- actor: typed.actor,
963
- timerMode: typed.timerMode,
964
- plannedDurationSeconds: typed.plannedDurationSeconds,
965
- overrideReason: typed.overrideReason,
966
- isCurrent: typed.isCurrent,
967
- leaseTtlSeconds: typed.leaseTtlSeconds,
968
- note: typed.note
969
- }
1001
+ body: typed.body
970
1002
  }));
971
1003
  }
972
1004
  });
@@ -1980,6 +1980,29 @@ function buildPreferredMutationPath(entityType) {
1980
1980
  return "Read-only surface.";
1981
1981
  }
1982
1982
  }
1983
+ function buildPreferredMutationTool(entityType) {
1984
+ if (entityType in AGENT_ONBOARDING_BATCH_ROUTE_BASES) {
1985
+ return "forge_create_entities | forge_update_entities | forge_delete_entities | forge_search_entities";
1986
+ }
1987
+ switch (entityType) {
1988
+ case "wiki_page":
1989
+ return "forge_upsert_wiki_page";
1990
+ case "calendar_connection":
1991
+ return "forge_connect_calendar_provider | forge_sync_calendar_connection";
1992
+ case "task_run":
1993
+ return "forge_start_task_run | forge_heartbeat_task_run | forge_focus_task_run | forge_complete_task_run | forge_release_task_run";
1994
+ case "questionnaire_run":
1995
+ return "forge_start_questionnaire_run | forge_update_questionnaire_run | forge_complete_questionnaire_run";
1996
+ case "preference_judgment":
1997
+ return "forge_submit_preferences_judgment";
1998
+ case "preference_signal":
1999
+ return "forge_submit_preferences_signal";
2000
+ case "work_adjustment":
2001
+ return "forge_adjust_work_minutes";
2002
+ default:
2003
+ return null;
2004
+ }
2005
+ }
1983
2006
  function buildPreferredReadPath(entityType) {
1984
2007
  if (entityType in AGENT_ONBOARDING_BATCH_ROUTE_BASES) {
1985
2008
  return AGENT_ONBOARDING_BATCH_ROUTE_BASES[entityType];
@@ -2024,11 +2047,10 @@ function enrichOnboardingEntityGuide(entry) {
2024
2047
  : null,
2025
2048
  preferredMutationPath: buildPreferredMutationPath(entry.entityType),
2026
2049
  preferredReadPath: buildPreferredReadPath(entry.entityType),
2027
- preferredMutationTool: classification === "batch_crud_entity"
2028
- ? "forge_create_entities | forge_update_entities | forge_delete_entities | forge_search_entities"
2029
- : classification === "specialized_domain_surface"
2050
+ preferredMutationTool: buildPreferredMutationTool(entry.entityType) ??
2051
+ (classification === "specialized_domain_surface"
2030
2052
  ? "Follow forge_get_agent_onboarding.entityRouteModel.specializedDomainSurfaces for the dedicated route family."
2031
- : null
2053
+ : null)
2032
2054
  };
2033
2055
  }
2034
2056
  const AGENT_ONBOARDING_ENTITY_CATALOG = [
@@ -2758,7 +2780,8 @@ const AGENT_ONBOARDING_CONVERSATION_RULES = [
2758
2780
  "For specialized surfaces, ask what would make the answer or change useful before you ask route-shaped details such as provider, weekday, flow id, run id, or trip id.",
2759
2781
  "When the user already named a precise correction or review target, do not widen back out into a meta lane question. Confirm only the missing route-selecting detail and then act.",
2760
2782
  "Once the route family is clear, say it plainly enough that another agent could follow the same path without guessing.",
2761
- "For Movement specifically, treat missing-data corrections as user-defined overlay boxes unless the user is editing an already-recorded stay or trip. When the user already gave a clear instruction like 'that missing block was home', act after only the last ambiguity is resolved."
2783
+ "For Movement specifically, treat missing-data corrections as user-defined overlay boxes unless the user is editing an already-recorded stay or trip. When the user already gave a clear instruction like 'that missing block was home', act after only the last ambiguity is resolved.",
2784
+ "For meaning-bearing updates, especially in Psyche, briefly say what feels newly true before you ask for the one structural detail that still changes the save."
2762
2785
  ];
2763
2786
  const AGENT_ONBOARDING_ENTITY_CONVERSATION_PLAYBOOKS = [
2764
2787
  {
@@ -2917,7 +2940,8 @@ const AGENT_ONBOARDING_ENTITY_CONVERSATION_PLAYBOOKS = [
2917
2940
  "Confirm the task.",
2918
2941
  "Confirm the actor only if it is not already obvious.",
2919
2942
  "Ask whether the run should be planned or unlimited only if that changes the action.",
2920
- "Start the run instead of turning it into a longer intake."
2943
+ "Start the run instead of turning it into a longer intake.",
2944
+ "Use the dedicated task-run tool for start, heartbeat, focus, complete, and release work instead of bouncing to the UI or a generic web route."
2921
2945
  ]
2922
2946
  },
2923
2947
  {
@@ -3071,6 +3095,7 @@ const AGENT_ONBOARDING_ENTITY_CONVERSATION_PLAYBOOKS = [
3071
3095
  "Ask whether the focus is a stay, a trip, a place, a timeline window, or a selected span.",
3072
3096
  "Ask for the time window, place, or movement item that makes the question concrete.",
3073
3097
  "Ask what they are trying to notice, preserve, or answer through that movement context.",
3098
+ "Choose the dedicated day, month, all-time, timeline, places, trip-detail, or selection route once the question shape is clear.",
3074
3099
  "Skip the meta lane question when the user already named the exact correction or review target and only one ambiguity remains.",
3075
3100
  "If the request is filling a missing-data gap, use a user-defined movement box rather than a raw stay or trip patch.",
3076
3101
  "If the request is repairing already-saved movement data, use the repair route that matches the saved object instead of treating it like a missing span.",
@@ -3104,6 +3129,7 @@ const AGENT_ONBOARDING_ENTITY_CONVERSATION_PLAYBOOKS = [
3104
3129
  "Ask whether they need the flow contract, a run result, a published output, or a node result.",
3105
3130
  "Ask what the user is trying to learn, repair, or publish through that flow.",
3106
3131
  "If the user already named the flow and action clearly, skip the meta lane question and ask only for the missing run, node, or output scope.",
3132
+ "If the user wants a stable public input contract or published output, prefer those dedicated reads instead of detouring through run history first.",
3107
3133
  "If the user is still shaping a payload or edit, prefer flow detail or box catalog reads before asking for structured inputs.",
3108
3134
  "Prefer flow detail or published-output reads for stable contracts, and use run or node-result routes only when the user is asking about execution history or debugging.",
3109
3135
  "Route to the dedicated workbench route family once the execution lane is clear."
@@ -3853,6 +3879,61 @@ const AGENT_ONBOARDING_TOOL_INPUT_CATALOG = [
3853
3879
  example: '{"taskRunId":"run_123","actor":"aurel","note":"Stopping for now; blocked on feedback","closeoutNote":{"contentMarkdown":"Blocked on feedback from design before I can continue."}}'
3854
3880
  }
3855
3881
  ];
3882
+ const VALIDATION_ROUTE_TOOL_MAP = {
3883
+ "POST /api/v1/entities/search": "forge_search_entities",
3884
+ "POST /api/v1/entities/create": "forge_create_entities",
3885
+ "POST /api/v1/entities/update": "forge_update_entities",
3886
+ "POST /api/v1/entities/delete": "forge_delete_entities",
3887
+ "POST /api/v1/entities/restore": "forge_restore_entities",
3888
+ "POST /api/v1/tasks/:id/runs": "forge_start_task_run",
3889
+ "POST /api/v1/task-runs/:id/heartbeat": "forge_heartbeat_task_run",
3890
+ "POST /api/v1/task-runs/:id/focus": "forge_focus_task_run",
3891
+ "POST /api/v1/task-runs/:id/complete": "forge_complete_task_run",
3892
+ "POST /api/v1/task-runs/:id/release": "forge_release_task_run",
3893
+ "POST /api/tasks/:id/runs": "forge_start_task_run",
3894
+ "POST /api/task-runs/:id/heartbeat": "forge_heartbeat_task_run",
3895
+ "POST /api/task-runs/:id/focus": "forge_focus_task_run",
3896
+ "POST /api/task-runs/:id/complete": "forge_complete_task_run",
3897
+ "POST /api/task-runs/:id/release": "forge_release_task_run"
3898
+ };
3899
+ function formatValidationSummary(issues) {
3900
+ return issues
3901
+ .slice(0, 3)
3902
+ .map((issue) => `${issue.path}: ${issue.message}`)
3903
+ .join("; ");
3904
+ }
3905
+ function buildValidationHelp(method, routeUrl, issues) {
3906
+ const route = routeUrl || "unknown_route";
3907
+ const toolName = VALIDATION_ROUTE_TOOL_MAP[`${method.toUpperCase()} ${route}`];
3908
+ const toolInput = toolName
3909
+ ? AGENT_ONBOARDING_TOOL_INPUT_CATALOG.find((entry) => entry.toolName === toolName)
3910
+ : undefined;
3911
+ return {
3912
+ route,
3913
+ validationSummary: formatValidationSummary(issues),
3914
+ ...(toolInput
3915
+ ? {
3916
+ expectedShape: {
3917
+ toolName: toolInput.toolName,
3918
+ inputShape: toolInput.inputShape,
3919
+ requiredFields: [...toolInput.requiredFields],
3920
+ example: toolInput.example,
3921
+ notes: [...toolInput.notes]
3922
+ }
3923
+ }
3924
+ : {
3925
+ expectedShape: {
3926
+ inputShape: "See details[] for the rejected fields on this route.",
3927
+ requiredFields: [],
3928
+ example: null,
3929
+ notes: [
3930
+ "Retry with only the documented top-level keys for this route.",
3931
+ "Omit optional fields instead of sending null unless the route contract explicitly allows null."
3932
+ ]
3933
+ }
3934
+ })
3935
+ };
3936
+ }
3856
3937
  function buildAgentOnboardingPayload(request) {
3857
3938
  const origin = getRequestOrigin(request);
3858
3939
  return {
@@ -5075,6 +5156,10 @@ export async function buildServer(options = {}) {
5075
5156
  });
5076
5157
  app.setErrorHandler((error, request, reply) => {
5077
5158
  const validationIssues = error instanceof ZodError ? formatValidationIssues(error) : undefined;
5159
+ const routeUrl = request.routeOptions.url || request.url;
5160
+ const validationHelp = validationIssues
5161
+ ? buildValidationHelp(request.method, routeUrl, validationIssues)
5162
+ : undefined;
5078
5163
  const statusCode = isHttpError(error)
5079
5164
  ? error.statusCode
5080
5165
  : isManagerError(error)
@@ -5082,7 +5167,6 @@ export async function buildServer(options = {}) {
5082
5167
  : error instanceof ZodError
5083
5168
  ? 400
5084
5169
  : 500;
5085
- const routeUrl = request.routeOptions.url || request.url;
5086
5170
  if (!shouldSkipAutomaticDiagnosticRoute(routeUrl)) {
5087
5171
  try {
5088
5172
  recordDiagnosticLog({
@@ -5123,10 +5207,11 @@ export async function buildServer(options = {}) {
5123
5207
  ? "invalid_request"
5124
5208
  : "internal_error",
5125
5209
  error: validationIssues
5126
- ? "Request validation failed"
5210
+ ? `Request validation failed for ${request.method.toUpperCase()} ${routeUrl}. ${validationHelp?.validationSummary ?? ""}`.trim()
5127
5211
  : getErrorMessage(error),
5128
5212
  statusCode,
5129
5213
  ...(validationIssues ? { details: validationIssues } : {}),
5214
+ ...(validationHelp ?? {}),
5130
5215
  ...(isHttpError(error) && error.details ? error.details : {}),
5131
5216
  ...(isManagerError(error) && error.details ? error.details : {})
5132
5217
  });
@@ -2780,10 +2780,9 @@ function updateMovementBoxOverrideState(id, input) {
2780
2780
  overridden_user_box_ids_json = ?,
2781
2781
  override_ranges_json = ?,
2782
2782
  is_overridden = ?,
2783
- is_fully_hidden = ?,
2784
- updated_at = ?
2783
+ is_fully_hidden = ?
2785
2784
  WHERE id = ?`)
2786
- .run(input.overrideCount, JSON.stringify(input.overriddenAutomaticBoxIds), input.trueStartedAt, input.trueEndedAt, input.overriddenStartedAt, input.overriddenEndedAt, input.overriddenByBoxId, JSON.stringify(input.overriddenUserBoxIds), JSON.stringify(input.overrideRanges), input.isOverridden ? 1 : 0, input.isFullyHidden ? 1 : 0, nowIso(), id);
2785
+ .run(input.overrideCount, JSON.stringify(input.overriddenAutomaticBoxIds), input.trueStartedAt, input.trueEndedAt, input.overriddenStartedAt, input.overriddenEndedAt, input.overriddenByBoxId, JSON.stringify(input.overriddenUserBoxIds), JSON.stringify(input.overrideRanges), input.isOverridden ? 1 : 0, input.isFullyHidden ? 1 : 0, id);
2787
2786
  }
2788
2787
  function recomputeMovementBoxOverrideState(userId) {
2789
2788
  const rows = listMovementBoxRows({ userIds: [userId] });
@@ -333,6 +333,18 @@ export function buildOpenApiDocument() {
333
333
  message: { type: "string" }
334
334
  }
335
335
  };
336
+ const validationExpectedShape = {
337
+ type: "object",
338
+ additionalProperties: true,
339
+ required: ["inputShape", "requiredFields", "notes"],
340
+ properties: {
341
+ toolName: { type: "string" },
342
+ inputShape: { type: "string" },
343
+ requiredFields: arrayOf({ type: "string" }),
344
+ example: { type: ["string", "null"] },
345
+ notes: arrayOf({ type: "string" })
346
+ }
347
+ };
336
348
  const errorResponse = {
337
349
  type: "object",
338
350
  additionalProperties: true,
@@ -341,7 +353,12 @@ export function buildOpenApiDocument() {
341
353
  code: { type: "string" },
342
354
  error: { type: "string" },
343
355
  statusCode: { type: "integer" },
344
- details: arrayOf({ $ref: "#/components/schemas/ValidationIssue" })
356
+ route: { type: "string" },
357
+ validationSummary: { type: "string" },
358
+ details: arrayOf({ $ref: "#/components/schemas/ValidationIssue" }),
359
+ expectedShape: {
360
+ $ref: "#/components/schemas/ValidationExpectedShape"
361
+ }
345
362
  }
346
363
  };
347
364
  const tag = {
@@ -4098,6 +4115,7 @@ export function buildOpenApiDocument() {
4098
4115
  components: {
4099
4116
  schemas: {
4100
4117
  ValidationIssue: validationIssue,
4118
+ ValidationExpectedShape: validationExpectedShape,
4101
4119
  ErrorResponse: errorResponse,
4102
4120
  Tag: tag,
4103
4121
  Goal: goal,
@@ -2,7 +2,7 @@
2
2
  "id": "forge-openclaw-plugin",
3
3
  "name": "Forge",
4
4
  "description": "Curated OpenClaw adapter for the Forge collaboration API, UI entrypoint, and localhost auto-start runtime.",
5
- "version": "0.2.43",
5
+ "version": "0.2.44",
6
6
  "skills": [
7
7
  "./skills"
8
8
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "forge-openclaw-plugin",
3
- "version": "0.2.43",
3
+ "version": "0.2.44",
4
4
  "description": "Curated OpenClaw adapter for the Forge collaboration API, UI entrypoint, and localhost auto-start runtime.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -61,6 +61,9 @@ Forge correctly, and gather only the structure that still matters.
61
61
  and then act.
62
62
  - Once the route family is clear, say it plainly enough that another agent could follow
63
63
  the same path without guessing.
64
+ - For meaning-bearing updates, especially in Psyche-adjacent work, briefly say what
65
+ feels newly true before you ask for the one structural detail that still changes the
66
+ save.
64
67
  - Do not read schema fields out loud unless the user explicitly wants a checklist.
65
68
  - One focused question is the default. Ask two only when both questions serve the same
66
69
  job and the user is steady enough for it.
@@ -780,6 +783,7 @@ Arc:
780
783
  2. Confirm the actor only if it is not already obvious.
781
784
  3. Ask whether the run should be planned or unlimited only if that changes the action.
782
785
  4. Start the run instead of turning it into intake.
786
+ 5. Use the dedicated task-run tool for start, heartbeat, focus, complete, and release work. Do not bounce to the Forge UI, a browser session, or a generic web route for those actions unless the user explicitly wants the visual surface.
783
787
 
784
788
  Ready to start when:
785
789
 
@@ -1011,9 +1015,11 @@ Arc:
1011
1015
  3. Ask whether the focus is a stay, a trip, a place, a timeline window, or a selected span.
1012
1016
  4. Ask for the time window, place, or movement item that makes the question concrete.
1013
1017
  5. Ask what they are trying to notice, preserve, or answer through that movement context.
1014
- 6. Skip the meta lane question when the user already named the exact correction or
1018
+ 6. Choose the dedicated day, month, all-time, timeline, places, trip-detail, or
1019
+ selection route once the question shape is clear.
1020
+ 7. Skip the meta lane question when the user already named the exact correction or
1015
1021
  review target and only one ambiguity remains.
1016
- 7. Route to the dedicated movement read or write path once the surface is clear.
1022
+ 8. Route to the dedicated movement read or write path once the surface is clear.
1017
1023
 
1018
1024
  Direct action rules:
1019
1025
 
@@ -1101,7 +1107,9 @@ Arc:
1101
1107
  when the lane is still blurred.
1102
1108
  6. If the user already named the life-force lane clearly, skip the meta lane question
1103
1109
  and ask only for the specific weekday, profile field, or signal that still matters.
1104
- 7. Route to the dedicated life-force path once the lane is clear.
1110
+ 7. If the user wants to see what changed after a write, read the overview back instead
1111
+ of leaving the result implicit.
1112
+ 8. Route to the dedicated life-force path once the lane is clear.
1105
1113
 
1106
1114
  Helpful follow-up lanes:
1107
1115
 
@@ -1156,7 +1164,9 @@ Arc:
1156
1164
  4. Ask whether they need the flow contract, a run result, a published output, or a node result.
1157
1165
  5. If the user already named the flow and action clearly, skip the meta lane
1158
1166
  question and ask only for the missing run, node, or output scope.
1159
- 6. Route to the dedicated workbench route family once the execution lane is clear.
1167
+ 6. If the user wants a stable public input contract or published output, prefer those
1168
+ dedicated reads instead of detouring through run history first.
1169
+ 7. Route to the dedicated workbench route family once the execution lane is clear.
1160
1170
 
1161
1171
  Helpful follow-up lanes:
1162
1172