forge-openclaw-plugin 0.2.24 → 0.2.26

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 (208) hide show
  1. package/README.md +13 -0
  2. package/dist/assets/{board-_C6oMy5w.js → board-ta0rUHOf.js} +3 -3
  3. package/dist/assets/{board-_C6oMy5w.js.map → board-ta0rUHOf.js.map} +1 -1
  4. package/dist/assets/index-Ro0ZF_az.css +1 -0
  5. package/dist/assets/index-ytlpSj23.js +79 -0
  6. package/dist/assets/index-ytlpSj23.js.map +1 -0
  7. package/dist/assets/{motion-D4sZgCHd.js → motion-fBKPB6yw.js} +3 -3
  8. package/dist/assets/motion-fBKPB6yw.js.map +1 -0
  9. package/dist/assets/{table-BWzTaky1.js → table-C-IGTQni.js} +2 -2
  10. package/dist/assets/{table-BWzTaky1.js.map → table-C-IGTQni.js.map} +1 -1
  11. package/dist/assets/{ui-BzK4azQb.js → ui-DInOpaYF.js} +2 -2
  12. package/dist/assets/{ui-BzK4azQb.js.map → ui-DInOpaYF.js.map} +1 -1
  13. package/dist/assets/vendor-lE3tZJcC.js +876 -0
  14. package/dist/assets/vendor-lE3tZJcC.js.map +1 -0
  15. package/dist/index.html +7 -8
  16. package/dist/openclaw/local-runtime.d.ts +3 -1
  17. package/dist/openclaw/local-runtime.js +51 -15
  18. package/dist/openclaw/parity.d.ts +1 -1
  19. package/dist/openclaw/parity.js +29 -0
  20. package/dist/openclaw/plugin-entry-shared.d.ts +1 -0
  21. package/dist/openclaw/plugin-entry-shared.js +31 -6
  22. package/dist/openclaw/plugin-sdk-types.d.ts +29 -0
  23. package/dist/openclaw/routes.js +236 -0
  24. package/dist/openclaw/session-bootstrap.d.ts +78 -0
  25. package/dist/openclaw/session-bootstrap.js +240 -0
  26. package/dist/openclaw/tools.js +279 -6
  27. package/dist/server/server/migrations/001_core.sql +411 -0
  28. package/dist/server/server/migrations/002_psyche.sql +392 -0
  29. package/dist/server/server/migrations/003_habits.sql +30 -0
  30. package/dist/server/server/migrations/004_habit_links.sql +8 -0
  31. package/dist/server/server/migrations/005_habit_psyche_links.sql +24 -0
  32. package/dist/server/server/migrations/006_work_adjustments.sql +14 -0
  33. package/dist/server/server/migrations/007_weekly_review_closures.sql +17 -0
  34. package/dist/server/server/migrations/008_calendar_execution.sql +147 -0
  35. package/dist/server/server/migrations/009_true_calendar_events.sql +195 -0
  36. package/dist/server/server/migrations/010_calendar_selection_state.sql +6 -0
  37. package/dist/server/server/migrations/011_calendar_timezone_backfill.sql +11 -0
  38. package/dist/server/server/migrations/012_work_block_ranges.sql +7 -0
  39. package/dist/server/server/migrations/013_microsoft_local_auth_settings.sql +8 -0
  40. package/dist/server/server/migrations/014_note_tags_and_ephemeral.sql +8 -0
  41. package/dist/server/server/migrations/015_multi_user_and_strategies.sql +244 -0
  42. package/dist/server/server/migrations/016_health_companion.sql +158 -0
  43. package/dist/server/server/migrations/016_strategy_contracts_and_user_graph.sql +22 -0
  44. package/dist/server/server/migrations/017_preferences.sql +131 -0
  45. package/dist/server/server/migrations/018_preference_catalogs.sql +31 -0
  46. package/dist/server/server/migrations/019_wiki_memory.sql +255 -0
  47. package/dist/server/server/migrations/020_wiki_page_hierarchy.sql +11 -0
  48. package/dist/server/server/migrations/021_hide_evidence_from_wiki_index.sql +3 -0
  49. package/dist/server/server/migrations/022_wiki_ingest_background.sql +85 -0
  50. package/dist/server/server/migrations/023_diagnostic_logs.sql +28 -0
  51. package/dist/server/server/migrations/024_questionnaires.sql +96 -0
  52. package/dist/server/server/migrations/025_ai_model_connections.sql +26 -0
  53. package/dist/server/server/migrations/026_custom_theme_settings.sql +2 -0
  54. package/dist/server/server/migrations/027_ai_processors.sql +31 -0
  55. package/dist/server/server/migrations/028_movement_domain.sql +136 -0
  56. package/dist/server/server/migrations/029_watch_micro_capture.sql +23 -0
  57. package/dist/server/server/migrations/030_surface_layouts.sql +5 -0
  58. package/dist/server/server/migrations/031_ai_processor_runtime_upgrades.sql +10 -0
  59. package/dist/server/server/migrations/032_ai_connectors.sql +44 -0
  60. package/dist/server/server/migrations/033_movement_trip_point_sync.sql +36 -0
  61. package/dist/server/server/migrations/034_movement_segment_sync.sql +49 -0
  62. package/dist/server/server/migrations/035_google_local_auth_settings.sql +2 -0
  63. package/dist/server/server/migrations/036_google_local_auth_client_secret.sql +2 -0
  64. package/dist/server/{app.js → server/src/app.js} +992 -25
  65. package/dist/server/server/src/connectors/box-registry.js +188 -0
  66. package/dist/server/{db.js → server/src/db.js} +6 -0
  67. package/dist/server/server/src/debug.js +19 -0
  68. package/dist/server/server/src/discovery-advertiser.js +114 -0
  69. package/dist/server/{health.js → server/src/health.js} +39 -11
  70. package/dist/server/{index.js → server/src/index.js} +4 -0
  71. package/dist/server/{managers → server/src/managers}/platform/llm-manager.js +40 -4
  72. package/dist/server/{managers → server/src/managers}/platform/openai-responses-provider.js +129 -19
  73. package/dist/server/server/src/movement.js +2935 -0
  74. package/dist/server/{openapi.js → server/src/openapi.js} +628 -5
  75. package/dist/server/{psyche-types.js → server/src/psyche-types.js} +15 -1
  76. package/dist/server/server/src/questionnaire-flow.js +552 -0
  77. package/dist/server/server/src/questionnaire-seeds.js +853 -0
  78. package/dist/server/server/src/questionnaire-types.js +340 -0
  79. package/dist/server/server/src/repositories/ai-connectors.js +1207 -0
  80. package/dist/server/server/src/repositories/ai-processors.js +547 -0
  81. package/dist/server/{repositories → server/src/repositories}/calendar.js +1 -1
  82. package/dist/server/{repositories → server/src/repositories}/entity-ownership.js +9 -1
  83. package/dist/server/{repositories → server/src/repositories}/habits.js +69 -5
  84. package/dist/server/server/src/repositories/model-settings.js +216 -0
  85. package/dist/server/{repositories → server/src/repositories}/notes.js +57 -15
  86. package/dist/server/{repositories → server/src/repositories}/preferences.js +124 -0
  87. package/dist/server/server/src/repositories/questionnaires.js +1338 -0
  88. package/dist/server/{repositories → server/src/repositories}/settings.js +156 -12
  89. package/dist/server/server/src/repositories/surface-layouts.js +76 -0
  90. package/dist/server/{repositories → server/src/repositories}/wiki-memory.js +5 -1
  91. package/dist/server/{services → server/src/services}/calendar-runtime.js +775 -58
  92. package/dist/server/{services → server/src/services}/entity-crud.js +81 -2
  93. package/dist/server/server/src/services/google-calendar-oauth-config.js +176 -0
  94. package/dist/server/server/src/services/openai-codex-oauth.js +153 -0
  95. package/dist/server/server/src/services/psyche-observation-calendar.js +46 -0
  96. package/dist/server/{types.js → server/src/types.js} +621 -14
  97. package/dist/server/server/src/watch-mobile.js +562 -0
  98. package/dist/server/{web.js → server/src/web.js} +30 -4
  99. package/dist/server/src/components/customization/utility-widgets.js +330 -0
  100. package/dist/server/src/components/workbench-boxes/health/health-boxes.js +92 -0
  101. package/dist/server/src/components/workbench-boxes/kanban/kanban-boxes.js +128 -0
  102. package/dist/server/src/components/workbench-boxes/movement/movement-boxes.js +37 -0
  103. package/dist/server/src/components/workbench-boxes/notes/notes-boxes.js +114 -0
  104. package/dist/server/src/components/workbench-boxes/projects/projects-boxes.js +57 -0
  105. package/dist/server/src/components/workbench-boxes/shared/define-workbench-box.js +4 -0
  106. package/dist/server/src/components/workbench-boxes/shared/generic-node-view.js +13 -0
  107. package/dist/server/src/components/workbench-boxes/today/today-boxes.js +63 -0
  108. package/dist/server/src/lib/api-error.js +37 -0
  109. package/dist/server/src/lib/api.js +1859 -0
  110. package/dist/server/src/lib/calendar-name-deduper.js +144 -0
  111. package/dist/server/src/lib/diagnostics.js +67 -0
  112. package/dist/server/src/lib/psyche-types.js +1 -0
  113. package/dist/server/src/lib/questionnaire-types.js +1 -0
  114. package/dist/server/src/lib/runtime-paths.js +24 -0
  115. package/dist/server/src/lib/schemas.js +234 -0
  116. package/dist/server/src/lib/snapshot-normalizer.js +374 -0
  117. package/dist/server/src/lib/theme-system.js +319 -0
  118. package/dist/server/src/lib/types.js +1 -0
  119. package/dist/server/src/lib/utils.js +22 -0
  120. package/dist/server/src/lib/workbench/boxes.js +16 -0
  121. package/dist/server/src/lib/workbench/nodes.js +15 -0
  122. package/dist/server/src/lib/workbench/registry.js +73 -0
  123. package/dist/server/src/lib/workbench/runtime.js +181 -0
  124. package/openclaw.plugin.json +1 -1
  125. package/package.json +6 -1
  126. package/server/index.js +68 -0
  127. package/server/migrations/024_questionnaires.sql +96 -0
  128. package/server/migrations/025_ai_model_connections.sql +26 -0
  129. package/server/migrations/026_custom_theme_settings.sql +2 -0
  130. package/server/migrations/027_ai_processors.sql +31 -0
  131. package/server/migrations/028_movement_domain.sql +136 -0
  132. package/server/migrations/029_watch_micro_capture.sql +23 -0
  133. package/server/migrations/030_surface_layouts.sql +5 -0
  134. package/server/migrations/031_ai_processor_runtime_upgrades.sql +10 -0
  135. package/server/migrations/032_ai_connectors.sql +44 -0
  136. package/server/migrations/033_movement_trip_point_sync.sql +36 -0
  137. package/server/migrations/034_movement_segment_sync.sql +49 -0
  138. package/server/migrations/035_google_local_auth_settings.sql +2 -0
  139. package/server/migrations/036_google_local_auth_client_secret.sql +2 -0
  140. package/skills/forge-openclaw/SKILL.md +15 -1
  141. package/skills/forge-openclaw/entity_conversation_playbooks.md +523 -87
  142. package/skills/forge-openclaw/psyche_entity_playbooks.md +331 -221
  143. package/dist/assets/index-DTCwBWAs.js +0 -65
  144. package/dist/assets/index-DTCwBWAs.js.map +0 -1
  145. package/dist/assets/index-DttXlAgi.css +0 -1
  146. package/dist/assets/motion-D4sZgCHd.js.map +0 -1
  147. package/dist/assets/vendor-De38P6YR.js +0 -729
  148. package/dist/assets/vendor-De38P6YR.js.map +0 -1
  149. package/dist/assets/viz-C6hfyqzu.js +0 -34
  150. package/dist/assets/viz-C6hfyqzu.js.map +0 -1
  151. package/skills/forge-openclaw/cron_jobs.md +0 -395
  152. /package/dist/server/{demo-data.js → server/src/demo-data.js} +0 -0
  153. /package/dist/server/{e2e-server.js → server/src/e2e-server.js} +0 -0
  154. /package/dist/server/{errors.js → server/src/errors.js} +0 -0
  155. /package/dist/server/{managers → server/src/managers}/base.js +0 -0
  156. /package/dist/server/{managers → server/src/managers}/contracts.js +0 -0
  157. /package/dist/server/{managers → server/src/managers}/platform/api-gateway-manager.js +0 -0
  158. /package/dist/server/{managers → server/src/managers}/platform/audit-manager.js +0 -0
  159. /package/dist/server/{managers → server/src/managers}/platform/authentication-manager.js +0 -0
  160. /package/dist/server/{managers → server/src/managers}/platform/authorization-manager.js +0 -0
  161. /package/dist/server/{managers → server/src/managers}/platform/background-job-manager.js +0 -0
  162. /package/dist/server/{managers → server/src/managers}/platform/configuration-manager.js +0 -0
  163. /package/dist/server/{managers → server/src/managers}/platform/database-manager.js +0 -0
  164. /package/dist/server/{managers → server/src/managers}/platform/event-bus-manager.js +0 -0
  165. /package/dist/server/{managers → server/src/managers}/platform/external-service-manager.js +0 -0
  166. /package/dist/server/{managers → server/src/managers}/platform/health-manager.js +0 -0
  167. /package/dist/server/{managers → server/src/managers}/platform/migration-manager.js +0 -0
  168. /package/dist/server/{managers → server/src/managers}/platform/search-index-manager.js +0 -0
  169. /package/dist/server/{managers → server/src/managers}/platform/secrets-manager.js +0 -0
  170. /package/dist/server/{managers → server/src/managers}/platform/session-manager.js +0 -0
  171. /package/dist/server/{managers → server/src/managers}/platform/storage-manager.js +0 -0
  172. /package/dist/server/{managers → server/src/managers}/platform/token-manager.js +0 -0
  173. /package/dist/server/{managers → server/src/managers}/platform/transaction-manager.js +0 -0
  174. /package/dist/server/{managers → server/src/managers}/platform/trusted-network.js +0 -0
  175. /package/dist/server/{managers → server/src/managers}/runtime.js +0 -0
  176. /package/dist/server/{managers → server/src/managers}/type-guards.js +0 -0
  177. /package/dist/server/{preferences-seeds.js → server/src/preferences-seeds.js} +0 -0
  178. /package/dist/server/{preferences-types.js → server/src/preferences-types.js} +0 -0
  179. /package/dist/server/{repositories → server/src/repositories}/activity-events.js +0 -0
  180. /package/dist/server/{repositories → server/src/repositories}/collaboration.js +0 -0
  181. /package/dist/server/{repositories → server/src/repositories}/deleted-entities.js +0 -0
  182. /package/dist/server/{repositories → server/src/repositories}/diagnostic-logs.js +0 -0
  183. /package/dist/server/{repositories → server/src/repositories}/domains.js +0 -0
  184. /package/dist/server/{repositories → server/src/repositories}/event-log.js +0 -0
  185. /package/dist/server/{repositories → server/src/repositories}/goals.js +0 -0
  186. /package/dist/server/{repositories → server/src/repositories}/projects.js +0 -0
  187. /package/dist/server/{repositories → server/src/repositories}/psyche.js +0 -0
  188. /package/dist/server/{repositories → server/src/repositories}/rewards.js +0 -0
  189. /package/dist/server/{repositories → server/src/repositories}/strategies.js +0 -0
  190. /package/dist/server/{repositories → server/src/repositories}/tags.js +0 -0
  191. /package/dist/server/{repositories → server/src/repositories}/task-runs.js +0 -0
  192. /package/dist/server/{repositories → server/src/repositories}/tasks.js +0 -0
  193. /package/dist/server/{repositories → server/src/repositories}/users.js +0 -0
  194. /package/dist/server/{repositories → server/src/repositories}/weekly-reviews.js +0 -0
  195. /package/dist/server/{repositories → server/src/repositories}/work-adjustments.js +0 -0
  196. /package/dist/server/{seed-demo.js → server/src/seed-demo.js} +0 -0
  197. /package/dist/server/{services → server/src/services}/context.js +0 -0
  198. /package/dist/server/{services → server/src/services}/dashboard.js +0 -0
  199. /package/dist/server/{services → server/src/services}/gamification.js +0 -0
  200. /package/dist/server/{services → server/src/services}/insights.js +0 -0
  201. /package/dist/server/{services → server/src/services}/projects.js +0 -0
  202. /package/dist/server/{services → server/src/services}/psyche.js +0 -0
  203. /package/dist/server/{services → server/src/services}/relations.js +0 -0
  204. /package/dist/server/{services → server/src/services}/reviews.js +0 -0
  205. /package/dist/server/{services → server/src/services}/run-recovery.js +0 -0
  206. /package/dist/server/{services → server/src/services}/tagging.js +0 -0
  207. /package/dist/server/{services → server/src/services}/task-run-watchdog.js +0 -0
  208. /package/dist/server/{services → server/src/services}/work-time.js +0 -0
@@ -1,8 +1,11 @@
1
1
  import { createHash, randomBytes, randomUUID } from "node:crypto";
2
2
  import { getDatabase, runInTransaction } from "../db.js";
3
+ import { logForgeDebug } from "../debug.js";
3
4
  import { recordActivityEvent } from "./activity-events.js";
4
5
  import { recordEventLog } from "./event-log.js";
5
- import { createAgentTokenSchema, agentIdentitySchema, settingsPayloadSchema, updateSettingsSchema } from "../types.js";
6
+ import { resolveGoogleCalendarOauthPublicConfig } from "../services/google-calendar-oauth-config.js";
7
+ import { buildConnectionAgentIdentity, FORGE_DEFAULT_AGENT_ID, listAiModelConnections, syncForgeManagedWikiProfile } from "./model-settings.js";
8
+ import { createAgentTokenSchema, agentIdentitySchema, customThemeSchema, settingsPayloadSchema, updateSettingsSchema } from "../types.js";
6
9
  function boolFromInt(value) {
7
10
  return value === 1;
8
11
  }
@@ -24,9 +27,31 @@ function normalizeMicrosoftRedirectUri(value) {
24
27
  const trimmed = value?.trim();
25
28
  return trimmed && trimmed.length > 0 ? trimmed : defaultMicrosoftRedirectUri();
26
29
  }
30
+ function logCalendarSettingsDebug(message, details) {
31
+ const serialized = Object.entries(details)
32
+ .map(([key, value]) => `${key}=${JSON.stringify(value)}`)
33
+ .join(" ");
34
+ logForgeDebug(`[forge-calendar-settings] ${message} ${serialized}`);
35
+ }
36
+ function normalizeModelConnectionId(value) {
37
+ const trimmed = value?.trim();
38
+ return trimmed && trimmed.length > 0 ? trimmed : "";
39
+ }
27
40
  function buildTokenSecret() {
28
41
  return `fg_live_${randomBytes(18).toString("hex")}`;
29
42
  }
43
+ function parseCustomThemeJson(raw) {
44
+ const trimmed = raw?.trim();
45
+ if (!trimmed) {
46
+ return null;
47
+ }
48
+ try {
49
+ return customThemeSchema.parse(JSON.parse(trimmed));
50
+ }
51
+ catch {
52
+ return null;
53
+ }
54
+ }
30
55
  function mapAgent(row) {
31
56
  return agentIdentitySchema.parse({
32
57
  id: row.id,
@@ -118,9 +143,10 @@ function readSettingsRow() {
118
143
  ensureSettingsRow();
119
144
  return getDatabase()
120
145
  .prepare(`SELECT
121
- operator_name, operator_email, operator_title, theme_preference, locale_preference,
146
+ operator_name, operator_email, operator_title, theme_preference, custom_theme_json, locale_preference,
122
147
  goal_drift_alerts, daily_quest_reminders, achievement_celebrations, max_active_tasks, time_accounting_mode,
123
- integrity_score, last_audit_at, psyche_auth_required, microsoft_client_id, microsoft_tenant_id, microsoft_redirect_uri, created_at, updated_at
148
+ integrity_score, last_audit_at, psyche_auth_required, google_client_id, google_client_secret, microsoft_client_id, microsoft_tenant_id, microsoft_redirect_uri,
149
+ forge_basic_chat_connection_id, forge_basic_chat_model, forge_wiki_connection_id, forge_wiki_model, created_at, updated_at
124
150
  FROM app_settings
125
151
  WHERE id = 1`)
126
152
  .get();
@@ -167,7 +193,23 @@ export function listAgentIdentities() {
167
193
  GROUP BY agent_identities.id
168
194
  ORDER BY agent_identities.created_at DESC`)
169
195
  .all();
170
- return rows.map(mapAgent);
196
+ const manualAgents = rows.map(mapAgent);
197
+ const modelAgents = listAiModelConnections().map(buildConnectionAgentIdentity);
198
+ const settings = readSettingsRow();
199
+ const forgeAgent = agentIdentitySchema.parse({
200
+ id: FORGE_DEFAULT_AGENT_ID,
201
+ label: "Forge Agent",
202
+ agentType: "forge_default",
203
+ trustLevel: "trusted",
204
+ autonomyMode: "approval_required",
205
+ approvalMode: "approval_by_default",
206
+ description: "Built-in Forge operator agent. Owns Forge-native task flows, prompts, and orchestration.",
207
+ tokenCount: 0,
208
+ activeTokenCount: 0,
209
+ createdAt: settings.created_at,
210
+ updatedAt: settings.updated_at
211
+ });
212
+ return [forgeAgent, ...modelAgents, ...manualAgents];
171
213
  }
172
214
  export function isPsycheAuthRequired() {
173
215
  ensureSettingsRow();
@@ -178,9 +220,27 @@ export function isPsycheAuthRequired() {
178
220
  }
179
221
  export function getSettings() {
180
222
  const row = readSettingsRow();
223
+ const connections = listAiModelConnections();
224
+ const googleConfig = resolveGoogleCalendarOauthPublicConfig(process.env, {
225
+ clientId: row.google_client_id,
226
+ clientSecret: row.google_client_secret
227
+ });
228
+ logCalendarSettingsDebug("get_settings", {
229
+ storedGoogleClientId: row.google_client_id,
230
+ storedGoogleClientSecret: row.google_client_secret.length > 0,
231
+ resolvedGoogleClientId: googleConfig.clientId,
232
+ resolvedGoogleClientSecret: googleConfig.clientSecret.length > 0,
233
+ googleIsConfigured: googleConfig.isConfigured,
234
+ googleRedirectUri: googleConfig.redirectUri
235
+ });
181
236
  const microsoftClientId = row.microsoft_client_id?.trim() ?? "";
182
237
  const microsoftTenantId = normalizeMicrosoftTenantId(row.microsoft_tenant_id);
183
238
  const microsoftRedirectUri = normalizeMicrosoftRedirectUri(row.microsoft_redirect_uri);
239
+ const basicChatConnectionId = normalizeModelConnectionId(row.forge_basic_chat_connection_id);
240
+ const wikiConnectionId = normalizeModelConnectionId(row.forge_wiki_connection_id);
241
+ const basicChatConnection = connections.find((entry) => entry.id === basicChatConnectionId) ?? null;
242
+ const wikiConnection = connections.find((entry) => entry.id === wikiConnectionId) ?? null;
243
+ const customTheme = parseCustomThemeJson(row.custom_theme_json);
184
244
  return settingsPayloadSchema.parse({
185
245
  profile: {
186
246
  operatorName: row.operator_name,
@@ -197,6 +257,7 @@ export function getSettings() {
197
257
  timeAccountingMode: row.time_accounting_mode
198
258
  },
199
259
  themePreference: row.theme_preference,
260
+ customTheme,
200
261
  localePreference: row.locale_preference,
201
262
  security: {
202
263
  integrityScore: row.integrity_score,
@@ -207,6 +268,7 @@ export function getSettings() {
207
268
  psycheAuthRequired: boolFromInt(row.psyche_auth_required)
208
269
  },
209
270
  calendarProviders: {
271
+ google: googleConfig,
210
272
  microsoft: {
211
273
  clientId: microsoftClientId,
212
274
  tenantId: microsoftTenantId,
@@ -221,15 +283,55 @@ export function getSettings() {
221
283
  : "Save the Microsoft client ID and the Forge callback redirect URI here before you try to sign in."
222
284
  }
223
285
  },
286
+ modelSettings: {
287
+ forgeAgent: {
288
+ basicChat: {
289
+ connectionId: basicChatConnection?.id ?? null,
290
+ connectionLabel: basicChatConnection?.label ?? null,
291
+ provider: basicChatConnection?.provider ?? null,
292
+ baseUrl: basicChatConnection?.baseUrl ?? null,
293
+ model: row.forge_basic_chat_model?.trim() || basicChatConnection?.model || "gpt-5.4-mini"
294
+ },
295
+ wiki: {
296
+ connectionId: wikiConnection?.id ?? null,
297
+ connectionLabel: wikiConnection?.label ?? null,
298
+ provider: wikiConnection?.provider ?? null,
299
+ baseUrl: wikiConnection?.baseUrl ?? null,
300
+ model: row.forge_wiki_model?.trim() || wikiConnection?.model || "gpt-5.4-mini"
301
+ }
302
+ },
303
+ connections,
304
+ oauth: {
305
+ openAiCodex: {
306
+ authorizeUrl: "https://auth.openai.com/oauth/authorize",
307
+ callbackUrl: "http://127.0.0.1:1455/auth/callback",
308
+ setupMessage: "Forge mirrors OpenClaw's local OpenAI Codex PKCE flow. The browser returns to localhost:1455, and Forge can also accept a pasted redirect URL when the callback cannot bind."
309
+ }
310
+ }
311
+ },
224
312
  agents: listAgentIdentities(),
225
313
  agentTokens: listAgentTokens()
226
314
  });
227
315
  }
228
- export function updateSettings(input, activity) {
316
+ export function updateSettings(input, options = {}) {
229
317
  const parsed = updateSettingsSchema.parse(input);
230
318
  return runInTransaction(() => {
231
319
  const current = getSettings();
232
320
  const now = new Date().toISOString();
321
+ const nextGoogleClientId = parsed.calendarProviders?.google?.clientId?.trim() ??
322
+ current.calendarProviders.google.storedClientId;
323
+ const nextGoogleClientSecret = parsed.calendarProviders?.google?.clientSecret?.trim() ??
324
+ current.calendarProviders.google.storedClientSecret;
325
+ logCalendarSettingsDebug("update_settings_requested", {
326
+ requestedGoogleClientId: parsed.calendarProviders?.google?.clientId ?? null,
327
+ requestedGoogleClientSecret: parsed.calendarProviders?.google?.clientSecret !== undefined
328
+ ? parsed.calendarProviders.google.clientSecret.length > 0
329
+ : null,
330
+ currentGoogleClientId: current.calendarProviders.google.storedClientId,
331
+ currentGoogleClientSecret: current.calendarProviders.google.storedClientSecret.length > 0,
332
+ nextGoogleClientId,
333
+ nextGoogleClientSecret: nextGoogleClientSecret.length > 0
334
+ });
233
335
  const next = {
234
336
  profile: {
235
337
  operatorName: parsed.profile?.operatorName ?? current.profile.operatorName,
@@ -246,9 +348,14 @@ export function updateSettings(input, activity) {
246
348
  timeAccountingMode: parsed.execution?.timeAccountingMode ?? current.execution.timeAccountingMode
247
349
  },
248
350
  themePreference: parsed.themePreference ?? current.themePreference,
351
+ customTheme: parsed.customTheme === undefined ? (current.customTheme ?? null) : parsed.customTheme,
249
352
  localePreference: parsed.localePreference ?? current.localePreference,
250
353
  psycheAuthRequired: parsed.security?.psycheAuthRequired ?? current.security.psycheAuthRequired,
251
354
  calendarProviders: {
355
+ google: resolveGoogleCalendarOauthPublicConfig(process.env, {
356
+ clientId: nextGoogleClientId,
357
+ clientSecret: nextGoogleClientSecret
358
+ }),
252
359
  microsoft: {
253
360
  clientId: parsed.calendarProviders?.microsoft?.clientId?.trim() ??
254
361
  current.calendarProviders.microsoft.clientId,
@@ -257,33 +364,70 @@ export function updateSettings(input, activity) {
257
364
  redirectUri: normalizeMicrosoftRedirectUri(parsed.calendarProviders?.microsoft?.redirectUri ??
258
365
  current.calendarProviders.microsoft.redirectUri)
259
366
  }
367
+ },
368
+ modelSettings: {
369
+ forgeAgent: {
370
+ basicChat: {
371
+ connectionId: parsed.modelSettings?.forgeAgent?.basicChat?.connectionId !==
372
+ undefined
373
+ ? normalizeModelConnectionId(parsed.modelSettings.forgeAgent.basicChat.connectionId)
374
+ : current.modelSettings.forgeAgent.basicChat.connectionId ?? "",
375
+ model: parsed.modelSettings?.forgeAgent?.basicChat?.model?.trim() ||
376
+ current.modelSettings.forgeAgent.basicChat.model
377
+ },
378
+ wiki: {
379
+ connectionId: parsed.modelSettings?.forgeAgent?.wiki?.connectionId !== undefined
380
+ ? normalizeModelConnectionId(parsed.modelSettings.forgeAgent.wiki.connectionId)
381
+ : current.modelSettings.forgeAgent.wiki.connectionId ?? "",
382
+ model: parsed.modelSettings?.forgeAgent?.wiki?.model?.trim() ||
383
+ current.modelSettings.forgeAgent.wiki.model
384
+ }
385
+ }
260
386
  }
261
387
  };
262
388
  getDatabase()
263
389
  .prepare(`UPDATE app_settings
264
- SET operator_name = ?, operator_email = ?, operator_title = ?, theme_preference = ?, locale_preference = ?,
390
+ SET operator_name = ?, operator_email = ?, operator_title = ?, theme_preference = ?, custom_theme_json = ?, locale_preference = ?,
265
391
  goal_drift_alerts = ?, daily_quest_reminders = ?, achievement_celebrations = ?, max_active_tasks = ?, time_accounting_mode = ?,
266
- psyche_auth_required = ?, microsoft_client_id = ?, microsoft_tenant_id = ?, microsoft_redirect_uri = ?, updated_at = ?
392
+ psyche_auth_required = ?, google_client_id = ?, google_client_secret = ?, microsoft_client_id = ?, microsoft_tenant_id = ?, microsoft_redirect_uri = ?,
393
+ forge_basic_chat_connection_id = ?, forge_basic_chat_model = ?, forge_wiki_connection_id = ?, forge_wiki_model = ?, updated_at = ?
267
394
  WHERE id = 1`)
268
- .run(next.profile.operatorName, next.profile.operatorEmail, next.profile.operatorTitle, next.themePreference, next.localePreference, toInt(next.notifications.goalDriftAlerts), toInt(next.notifications.dailyQuestReminders), toInt(next.notifications.achievementCelebrations), next.execution.maxActiveTasks, next.execution.timeAccountingMode, toInt(next.psycheAuthRequired), next.calendarProviders.microsoft.clientId, next.calendarProviders.microsoft.tenantId, next.calendarProviders.microsoft.redirectUri, now);
269
- if (activity) {
395
+ .run(next.profile.operatorName, next.profile.operatorEmail, next.profile.operatorTitle, next.themePreference, next.customTheme ? JSON.stringify(next.customTheme) : "", next.localePreference, toInt(next.notifications.goalDriftAlerts), toInt(next.notifications.dailyQuestReminders), toInt(next.notifications.achievementCelebrations), next.execution.maxActiveTasks, next.execution.timeAccountingMode, toInt(next.psycheAuthRequired), nextGoogleClientId, nextGoogleClientSecret, next.calendarProviders.microsoft.clientId, next.calendarProviders.microsoft.tenantId, next.calendarProviders.microsoft.redirectUri, next.modelSettings.forgeAgent.basicChat.connectionId, next.modelSettings.forgeAgent.basicChat.model, next.modelSettings.forgeAgent.wiki.connectionId, next.modelSettings.forgeAgent.wiki.model, now);
396
+ logCalendarSettingsDebug("update_settings_committed", {
397
+ persistedGoogleClientId: nextGoogleClientId,
398
+ persistedGoogleClientSecret: nextGoogleClientSecret.length > 0,
399
+ persistedMicrosoftClientId: next.calendarProviders.microsoft.clientId,
400
+ updatedAt: now
401
+ });
402
+ if (options.secrets) {
403
+ syncForgeManagedWikiProfile(options.secrets);
404
+ }
405
+ if (options.activity) {
270
406
  recordActivityEvent({
271
407
  entityType: "system",
272
408
  entityId: "app_settings",
273
409
  eventType: "settings_updated",
274
410
  title: "Forge settings updated",
275
411
  description: `Theme is now ${next.themePreference}. Language is ${next.localePreference}.`,
276
- actor: activity.actor ?? null,
277
- source: activity.source,
412
+ actor: options.activity.actor ?? null,
413
+ source: options.activity.source,
278
414
  metadata: {
279
415
  themePreference: next.themePreference,
416
+ customThemeLabel: next.customTheme?.label ?? null,
280
417
  localePreference: next.localePreference,
281
418
  goalDriftAlerts: next.notifications.goalDriftAlerts,
282
419
  dailyQuestReminders: next.notifications.dailyQuestReminders,
283
420
  maxActiveTasks: next.execution.maxActiveTasks,
284
421
  timeAccountingMode: next.execution.timeAccountingMode,
422
+ googleConfigured: next.calendarProviders.google.isConfigured,
423
+ googleAppBaseUrl: next.calendarProviders.google.appBaseUrl,
424
+ googleRedirectUri: next.calendarProviders.google.redirectUri,
285
425
  microsoftConfigured: next.calendarProviders.microsoft.clientId.trim().length > 0,
286
- microsoftTenantId: next.calendarProviders.microsoft.tenantId
426
+ microsoftTenantId: next.calendarProviders.microsoft.tenantId,
427
+ forgeBasicChatModel: next.modelSettings.forgeAgent.basicChat.model,
428
+ forgeWikiModel: next.modelSettings.forgeAgent.wiki.model,
429
+ forgeBasicChatConnectionId: next.modelSettings.forgeAgent.basicChat.connectionId || null,
430
+ forgeWikiConnectionId: next.modelSettings.forgeAgent.wiki.connectionId || null
287
431
  }
288
432
  });
289
433
  }
@@ -0,0 +1,76 @@
1
+ import { getDatabase } from "../db.js";
2
+ import { surfaceLayoutPayloadSchema } from "../types.js";
3
+ function parseLayout(row) {
4
+ const parsed = JSON.parse(row.payload_json);
5
+ const legacy = "layouts" in parsed ? parsed : null;
6
+ const legacyOrder = Array.isArray(legacy?.layouts?.lg)
7
+ ? [...legacy.layouts.lg]
8
+ .sort((left, right) => {
9
+ const leftY = typeof left.y === "number" ? left.y : 0;
10
+ const rightY = typeof right.y === "number" ? right.y : 0;
11
+ if (leftY !== rightY) {
12
+ return leftY - rightY;
13
+ }
14
+ const leftX = typeof left.x === "number" ? left.x : 0;
15
+ const rightX = typeof right.x === "number" ? right.x : 0;
16
+ return leftX - rightX;
17
+ })
18
+ .map((item) => item.i)
19
+ .filter((item) => typeof item === "string" && item.trim().length > 0)
20
+ : [];
21
+ const widgets = Object.fromEntries(Object.entries(parsed.widgets ?? {}).map(([widgetId, rawValue]) => {
22
+ const value = rawValue && typeof rawValue === "object"
23
+ ? rawValue
24
+ : {};
25
+ const legacyLg = legacy?.layouts?.lg?.find((entry) => entry.i === widgetId);
26
+ return [
27
+ widgetId,
28
+ {
29
+ hidden: value.hidden === true,
30
+ fullWidth: value.fullWidth === true ||
31
+ (typeof legacyLg?.w === "number" && legacyLg.w >= 10),
32
+ titleVisible: value.titleVisible !== false,
33
+ descriptionVisible: value.descriptionVisible !== false
34
+ }
35
+ ];
36
+ }));
37
+ return surfaceLayoutPayloadSchema.parse({
38
+ ...parsed,
39
+ surfaceId: row.surface_id,
40
+ order: Array.isArray(parsed.order) && parsed.order.length > 0
41
+ ? parsed.order
42
+ : legacyOrder,
43
+ widgets,
44
+ updatedAt: row.updated_at
45
+ });
46
+ }
47
+ export function getSurfaceLayout(surfaceId) {
48
+ const row = getDatabase()
49
+ .prepare(`SELECT surface_id, payload_json, updated_at
50
+ FROM surface_layouts
51
+ WHERE surface_id = ?`)
52
+ .get(surfaceId);
53
+ return row ? parseLayout(row) : null;
54
+ }
55
+ export function saveSurfaceLayout(surfaceId, payload) {
56
+ const now = new Date().toISOString();
57
+ const next = surfaceLayoutPayloadSchema.parse({
58
+ surfaceId,
59
+ updatedAt: now,
60
+ ...payload
61
+ });
62
+ getDatabase()
63
+ .prepare(`INSERT INTO surface_layouts (surface_id, payload_json, updated_at)
64
+ VALUES (?, ?, ?)
65
+ ON CONFLICT(surface_id) DO UPDATE SET
66
+ payload_json = excluded.payload_json,
67
+ updated_at = excluded.updated_at`)
68
+ .run(surfaceId, JSON.stringify(next), now);
69
+ return getSurfaceLayout(surfaceId);
70
+ }
71
+ export function resetSurfaceLayout(surfaceId) {
72
+ getDatabase()
73
+ .prepare(`DELETE FROM surface_layouts WHERE surface_id = ?`)
74
+ .run(surfaceId);
75
+ return null;
76
+ }
@@ -274,6 +274,7 @@ export const upsertWikiLlmProfileSchema = z.object({
274
274
  baseUrl: z.string().trim().default("https://api.openai.com/v1"),
275
275
  model: z.string().trim().min(1),
276
276
  apiKey: z.string().trim().optional(),
277
+ secretId: z.string().trim().nullable().optional(),
277
278
  systemPrompt: z.string().trim().default(""),
278
279
  reasoningEffort: wikiLlmReasoningEffortSchema.optional(),
279
280
  verbosity: wikiLlmVerbositySchema.optional(),
@@ -737,6 +738,7 @@ function getNoteStoragePath(note, space) {
737
738
  }
738
739
  function buildNoteFrontmatter(note) {
739
740
  return {
741
+ ...note.frontmatter,
740
742
  id: note.id,
741
743
  kind: note.kind,
742
744
  title: note.title,
@@ -2058,7 +2060,9 @@ export function upsertWikiLlmProfile(input, secrets) {
2058
2060
  const now = nowIso();
2059
2061
  const id = parsed.id?.trim() ||
2060
2062
  `wiki_llm_${randomUUID().replaceAll("-", "").slice(0, 10)}`;
2061
- let secretId = listWikiLlmProfiles().find((entry) => entry.id === id)?.secretId ?? null;
2063
+ let secretId = parsed.secretId ??
2064
+ listWikiLlmProfiles().find((entry) => entry.id === id)?.secretId ??
2065
+ null;
2062
2066
  if (parsed.apiKey?.trim()) {
2063
2067
  secretId =
2064
2068
  secretId ??