forge-openclaw-plugin 0.2.25 → 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 (189) hide show
  1. package/dist/assets/{board-VmF4FAfr.js → board-ta0rUHOf.js} +3 -3
  2. package/dist/assets/{board-VmF4FAfr.js.map → board-ta0rUHOf.js.map} +1 -1
  3. package/dist/assets/index-Ro0ZF_az.css +1 -0
  4. package/dist/assets/index-ytlpSj23.js +79 -0
  5. package/dist/assets/index-ytlpSj23.js.map +1 -0
  6. package/dist/assets/{motion-DvkU14p-.js → motion-fBKPB6yw.js} +2 -2
  7. package/dist/assets/{motion-DvkU14p-.js.map → motion-fBKPB6yw.js.map} +1 -1
  8. package/dist/assets/{table-DgiPof9E.js → table-C-IGTQni.js} +2 -2
  9. package/dist/assets/{table-DgiPof9E.js.map → table-C-IGTQni.js.map} +1 -1
  10. package/dist/assets/{ui-nYfoC0Gq.js → ui-DInOpaYF.js} +2 -2
  11. package/dist/assets/{ui-nYfoC0Gq.js.map → ui-DInOpaYF.js.map} +1 -1
  12. package/dist/assets/vendor-lE3tZJcC.js +876 -0
  13. package/dist/assets/vendor-lE3tZJcC.js.map +1 -0
  14. package/dist/index.html +7 -8
  15. package/dist/openclaw/local-runtime.d.ts +3 -1
  16. package/dist/openclaw/local-runtime.js +51 -15
  17. package/dist/openclaw/plugin-entry-shared.js +24 -2
  18. package/dist/openclaw/plugin-sdk-types.d.ts +17 -0
  19. package/dist/openclaw/tools.js +0 -3
  20. package/dist/server/server/migrations/001_core.sql +411 -0
  21. package/dist/server/server/migrations/002_psyche.sql +392 -0
  22. package/dist/server/server/migrations/003_habits.sql +30 -0
  23. package/dist/server/server/migrations/004_habit_links.sql +8 -0
  24. package/dist/server/server/migrations/005_habit_psyche_links.sql +24 -0
  25. package/dist/server/server/migrations/006_work_adjustments.sql +14 -0
  26. package/dist/server/server/migrations/007_weekly_review_closures.sql +17 -0
  27. package/dist/server/server/migrations/008_calendar_execution.sql +147 -0
  28. package/dist/server/server/migrations/009_true_calendar_events.sql +195 -0
  29. package/dist/server/server/migrations/010_calendar_selection_state.sql +6 -0
  30. package/dist/server/server/migrations/011_calendar_timezone_backfill.sql +11 -0
  31. package/dist/server/server/migrations/012_work_block_ranges.sql +7 -0
  32. package/dist/server/server/migrations/013_microsoft_local_auth_settings.sql +8 -0
  33. package/dist/server/server/migrations/014_note_tags_and_ephemeral.sql +8 -0
  34. package/dist/server/server/migrations/015_multi_user_and_strategies.sql +244 -0
  35. package/dist/server/server/migrations/016_health_companion.sql +158 -0
  36. package/dist/server/server/migrations/016_strategy_contracts_and_user_graph.sql +22 -0
  37. package/dist/server/server/migrations/017_preferences.sql +131 -0
  38. package/dist/server/server/migrations/018_preference_catalogs.sql +31 -0
  39. package/dist/server/server/migrations/019_wiki_memory.sql +255 -0
  40. package/dist/server/server/migrations/020_wiki_page_hierarchy.sql +11 -0
  41. package/dist/server/server/migrations/021_hide_evidence_from_wiki_index.sql +3 -0
  42. package/dist/server/server/migrations/022_wiki_ingest_background.sql +85 -0
  43. package/dist/server/server/migrations/023_diagnostic_logs.sql +28 -0
  44. package/dist/server/server/migrations/024_questionnaires.sql +96 -0
  45. package/dist/server/server/migrations/025_ai_model_connections.sql +26 -0
  46. package/dist/server/server/migrations/026_custom_theme_settings.sql +2 -0
  47. package/dist/server/server/migrations/027_ai_processors.sql +31 -0
  48. package/dist/server/server/migrations/028_movement_domain.sql +136 -0
  49. package/dist/server/server/migrations/029_watch_micro_capture.sql +23 -0
  50. package/dist/server/server/migrations/030_surface_layouts.sql +5 -0
  51. package/dist/server/server/migrations/031_ai_processor_runtime_upgrades.sql +10 -0
  52. package/dist/server/server/migrations/032_ai_connectors.sql +44 -0
  53. package/dist/server/server/migrations/033_movement_trip_point_sync.sql +36 -0
  54. package/dist/server/server/migrations/034_movement_segment_sync.sql +49 -0
  55. package/dist/server/server/migrations/035_google_local_auth_settings.sql +2 -0
  56. package/dist/server/server/migrations/036_google_local_auth_client_secret.sql +2 -0
  57. package/dist/server/{app.js → server/src/app.js} +242 -111
  58. package/dist/server/server/src/connectors/box-registry.js +188 -0
  59. package/dist/server/{db.js → server/src/db.js} +4 -0
  60. package/dist/server/server/src/debug.js +19 -0
  61. package/dist/server/{openapi.js → server/src/openapi.js} +2 -2
  62. package/dist/server/{repositories → server/src/repositories}/ai-connectors.js +286 -23
  63. package/dist/server/{repositories → server/src/repositories}/calendar.js +1 -1
  64. package/dist/server/{repositories → server/src/repositories}/settings.js +51 -3
  65. package/dist/server/{services → server/src/services}/calendar-runtime.js +775 -58
  66. package/dist/server/server/src/services/google-calendar-oauth-config.js +176 -0
  67. package/dist/server/{types.js → server/src/types.js} +137 -19
  68. package/dist/server/{web.js → server/src/web.js} +21 -2
  69. package/dist/server/src/components/customization/utility-widgets.js +330 -0
  70. package/dist/server/src/components/workbench-boxes/health/health-boxes.js +92 -0
  71. package/dist/server/src/components/workbench-boxes/kanban/kanban-boxes.js +128 -0
  72. package/dist/server/src/components/workbench-boxes/movement/movement-boxes.js +37 -0
  73. package/dist/server/src/components/workbench-boxes/notes/notes-boxes.js +114 -0
  74. package/dist/server/src/components/workbench-boxes/projects/projects-boxes.js +57 -0
  75. package/dist/server/src/components/workbench-boxes/shared/define-workbench-box.js +4 -0
  76. package/dist/server/src/components/workbench-boxes/shared/generic-node-view.js +13 -0
  77. package/dist/server/src/components/workbench-boxes/today/today-boxes.js +63 -0
  78. package/dist/server/src/lib/api-error.js +37 -0
  79. package/dist/server/src/lib/api.js +1859 -0
  80. package/dist/server/src/lib/calendar-name-deduper.js +144 -0
  81. package/dist/server/src/lib/diagnostics.js +67 -0
  82. package/dist/server/src/lib/psyche-types.js +1 -0
  83. package/dist/server/src/lib/questionnaire-types.js +1 -0
  84. package/dist/server/src/lib/runtime-paths.js +24 -0
  85. package/dist/server/src/lib/schemas.js +234 -0
  86. package/dist/server/src/lib/snapshot-normalizer.js +374 -0
  87. package/dist/server/src/lib/theme-system.js +319 -0
  88. package/dist/server/src/lib/types.js +1 -0
  89. package/dist/server/src/lib/utils.js +22 -0
  90. package/dist/server/src/lib/workbench/boxes.js +16 -0
  91. package/dist/server/src/lib/workbench/nodes.js +15 -0
  92. package/dist/server/src/lib/workbench/registry.js +73 -0
  93. package/dist/server/src/lib/workbench/runtime.js +181 -0
  94. package/openclaw.plugin.json +1 -1
  95. package/package.json +1 -1
  96. package/server/index.js +68 -0
  97. package/server/migrations/035_google_local_auth_settings.sql +2 -0
  98. package/server/migrations/036_google_local_auth_client_secret.sql +2 -0
  99. package/skills/forge-openclaw/SKILL.md +3 -0
  100. package/skills/forge-openclaw/entity_conversation_playbooks.md +213 -24
  101. package/skills/forge-openclaw/psyche_entity_playbooks.md +82 -3
  102. package/dist/assets/index-CFCKDIMH.js +0 -67
  103. package/dist/assets/index-CFCKDIMH.js.map +0 -1
  104. package/dist/assets/index-ZPY6U1TU.css +0 -1
  105. package/dist/assets/vendor-D9PTEPSB.js +0 -824
  106. package/dist/assets/vendor-D9PTEPSB.js.map +0 -1
  107. package/dist/assets/viz-Cqb6s--o.js +0 -34
  108. package/dist/assets/viz-Cqb6s--o.js.map +0 -1
  109. package/dist/server/connectors/box-registry.js +0 -257
  110. /package/dist/server/{demo-data.js → server/src/demo-data.js} +0 -0
  111. /package/dist/server/{discovery-advertiser.js → server/src/discovery-advertiser.js} +0 -0
  112. /package/dist/server/{e2e-server.js → server/src/e2e-server.js} +0 -0
  113. /package/dist/server/{errors.js → server/src/errors.js} +0 -0
  114. /package/dist/server/{health.js → server/src/health.js} +0 -0
  115. /package/dist/server/{index.js → server/src/index.js} +0 -0
  116. /package/dist/server/{managers → server/src/managers}/base.js +0 -0
  117. /package/dist/server/{managers → server/src/managers}/contracts.js +0 -0
  118. /package/dist/server/{managers → server/src/managers}/platform/api-gateway-manager.js +0 -0
  119. /package/dist/server/{managers → server/src/managers}/platform/audit-manager.js +0 -0
  120. /package/dist/server/{managers → server/src/managers}/platform/authentication-manager.js +0 -0
  121. /package/dist/server/{managers → server/src/managers}/platform/authorization-manager.js +0 -0
  122. /package/dist/server/{managers → server/src/managers}/platform/background-job-manager.js +0 -0
  123. /package/dist/server/{managers → server/src/managers}/platform/configuration-manager.js +0 -0
  124. /package/dist/server/{managers → server/src/managers}/platform/database-manager.js +0 -0
  125. /package/dist/server/{managers → server/src/managers}/platform/event-bus-manager.js +0 -0
  126. /package/dist/server/{managers → server/src/managers}/platform/external-service-manager.js +0 -0
  127. /package/dist/server/{managers → server/src/managers}/platform/health-manager.js +0 -0
  128. /package/dist/server/{managers → server/src/managers}/platform/llm-manager.js +0 -0
  129. /package/dist/server/{managers → server/src/managers}/platform/migration-manager.js +0 -0
  130. /package/dist/server/{managers → server/src/managers}/platform/openai-responses-provider.js +0 -0
  131. /package/dist/server/{managers → server/src/managers}/platform/search-index-manager.js +0 -0
  132. /package/dist/server/{managers → server/src/managers}/platform/secrets-manager.js +0 -0
  133. /package/dist/server/{managers → server/src/managers}/platform/session-manager.js +0 -0
  134. /package/dist/server/{managers → server/src/managers}/platform/storage-manager.js +0 -0
  135. /package/dist/server/{managers → server/src/managers}/platform/token-manager.js +0 -0
  136. /package/dist/server/{managers → server/src/managers}/platform/transaction-manager.js +0 -0
  137. /package/dist/server/{managers → server/src/managers}/platform/trusted-network.js +0 -0
  138. /package/dist/server/{managers → server/src/managers}/runtime.js +0 -0
  139. /package/dist/server/{managers → server/src/managers}/type-guards.js +0 -0
  140. /package/dist/server/{movement.js → server/src/movement.js} +0 -0
  141. /package/dist/server/{preferences-seeds.js → server/src/preferences-seeds.js} +0 -0
  142. /package/dist/server/{preferences-types.js → server/src/preferences-types.js} +0 -0
  143. /package/dist/server/{psyche-types.js → server/src/psyche-types.js} +0 -0
  144. /package/dist/server/{questionnaire-flow.js → server/src/questionnaire-flow.js} +0 -0
  145. /package/dist/server/{questionnaire-seeds.js → server/src/questionnaire-seeds.js} +0 -0
  146. /package/dist/server/{questionnaire-types.js → server/src/questionnaire-types.js} +0 -0
  147. /package/dist/server/{repositories → server/src/repositories}/activity-events.js +0 -0
  148. /package/dist/server/{repositories → server/src/repositories}/ai-processors.js +0 -0
  149. /package/dist/server/{repositories → server/src/repositories}/collaboration.js +0 -0
  150. /package/dist/server/{repositories → server/src/repositories}/deleted-entities.js +0 -0
  151. /package/dist/server/{repositories → server/src/repositories}/diagnostic-logs.js +0 -0
  152. /package/dist/server/{repositories → server/src/repositories}/domains.js +0 -0
  153. /package/dist/server/{repositories → server/src/repositories}/entity-ownership.js +0 -0
  154. /package/dist/server/{repositories → server/src/repositories}/event-log.js +0 -0
  155. /package/dist/server/{repositories → server/src/repositories}/goals.js +0 -0
  156. /package/dist/server/{repositories → server/src/repositories}/habits.js +0 -0
  157. /package/dist/server/{repositories → server/src/repositories}/model-settings.js +0 -0
  158. /package/dist/server/{repositories → server/src/repositories}/notes.js +0 -0
  159. /package/dist/server/{repositories → server/src/repositories}/preferences.js +0 -0
  160. /package/dist/server/{repositories → server/src/repositories}/projects.js +0 -0
  161. /package/dist/server/{repositories → server/src/repositories}/psyche.js +0 -0
  162. /package/dist/server/{repositories → server/src/repositories}/questionnaires.js +0 -0
  163. /package/dist/server/{repositories → server/src/repositories}/rewards.js +0 -0
  164. /package/dist/server/{repositories → server/src/repositories}/strategies.js +0 -0
  165. /package/dist/server/{repositories → server/src/repositories}/surface-layouts.js +0 -0
  166. /package/dist/server/{repositories → server/src/repositories}/tags.js +0 -0
  167. /package/dist/server/{repositories → server/src/repositories}/task-runs.js +0 -0
  168. /package/dist/server/{repositories → server/src/repositories}/tasks.js +0 -0
  169. /package/dist/server/{repositories → server/src/repositories}/users.js +0 -0
  170. /package/dist/server/{repositories → server/src/repositories}/weekly-reviews.js +0 -0
  171. /package/dist/server/{repositories → server/src/repositories}/wiki-memory.js +0 -0
  172. /package/dist/server/{repositories → server/src/repositories}/work-adjustments.js +0 -0
  173. /package/dist/server/{seed-demo.js → server/src/seed-demo.js} +0 -0
  174. /package/dist/server/{services → server/src/services}/context.js +0 -0
  175. /package/dist/server/{services → server/src/services}/dashboard.js +0 -0
  176. /package/dist/server/{services → server/src/services}/entity-crud.js +0 -0
  177. /package/dist/server/{services → server/src/services}/gamification.js +0 -0
  178. /package/dist/server/{services → server/src/services}/insights.js +0 -0
  179. /package/dist/server/{services → server/src/services}/openai-codex-oauth.js +0 -0
  180. /package/dist/server/{services → server/src/services}/projects.js +0 -0
  181. /package/dist/server/{services → server/src/services}/psyche-observation-calendar.js +0 -0
  182. /package/dist/server/{services → server/src/services}/psyche.js +0 -0
  183. /package/dist/server/{services → server/src/services}/relations.js +0 -0
  184. /package/dist/server/{services → server/src/services}/reviews.js +0 -0
  185. /package/dist/server/{services → server/src/services}/run-recovery.js +0 -0
  186. /package/dist/server/{services → server/src/services}/tagging.js +0 -0
  187. /package/dist/server/{services → server/src/services}/task-run-watchdog.js +0 -0
  188. /package/dist/server/{services → server/src/services}/work-time.js +0 -0
  189. /package/dist/server/{watch-mobile.js → server/src/watch-mobile.js} +0 -0
@@ -2,7 +2,7 @@ import Fastify from "fastify";
2
2
  import cors from "@fastify/cors";
3
3
  import multipart from "@fastify/multipart";
4
4
  import { CronExpressionParser } from "cron-parser";
5
- import { ZodError } from "zod";
5
+ import { z, ZodError } from "zod";
6
6
  import { configureDatabase, configureDatabaseSeeding, runInTransaction } from "./db.js";
7
7
  import { HttpError, isHttpError } from "./errors.js";
8
8
  import { listActivityEvents, listActivityEventsForTask, recordActivityEvent, removeActivityEvent } from "./repositories/activity-events.js";
@@ -13,7 +13,7 @@ import { listEventLog } from "./repositories/event-log.js";
13
13
  import { createDiagnosticMessage, DIAGNOSTIC_LOG_RETENTION_SWEEP_INTERVAL_MS, enforceDiagnosticLogRetention, listDiagnosticLogs, normalizeDiagnosticSource, recordDiagnosticLog, serializeDiagnosticError } from "./repositories/diagnostic-logs.js";
14
14
  import { createGoal, getGoalById, listGoals, updateGoal } from "./repositories/goals.js";
15
15
  import { getSurfaceLayout, resetSurfaceLayout, saveSurfaceLayout } from "./repositories/surface-layouts.js";
16
- import { listForgeBoxCatalog } from "./connectors/box-registry.js";
16
+ import { buildConnectorOutputCatalogEntry, listForgeBoxCatalog } from "./connectors/box-registry.js";
17
17
  import { createHabit, createHabitCheckIn, deleteHabitCheckIn, getHabitById, listHabits, updateHabit } from "./repositories/habits.js";
18
18
  import { listDomains } from "./repositories/domains.js";
19
19
  import { buildNotesSummaryByEntity, createNote, getNoteById, listNotes, updateNote } from "./repositories/notes.js";
@@ -45,12 +45,12 @@ import { getWeeklyReviewPayload } from "./services/reviews.js";
45
45
  import { finalizeWeeklyReviewClosure } from "./repositories/weekly-reviews.js";
46
46
  import { createTaskRunWatchdog } from "./services/task-run-watchdog.js";
47
47
  import { suggestTags } from "./services/tagging.js";
48
- import { CalendarConnectionConflictError, completeMicrosoftCalendarOauth, createCalendarConnection, deleteCalendarEventProjection, discoverCalendarConnection, discoverExistingCalendarConnection, getMicrosoftCalendarOauthSession, listConnectedCalendarConnections, removeCalendarConnection, pushCalendarEventUpdate, readCalendarOverview, syncCalendarConnection, startMicrosoftCalendarOauth, testMicrosoftCalendarOauthConfiguration, listCalendarProviderMetadata, updateCalendarConnectionSelection } from "./services/calendar-runtime.js";
48
+ import { CalendarConnectionConflictError, completeGoogleCalendarOauth, completeMicrosoftCalendarOauth, createCalendarConnection, deleteCalendarEventProjection, discoverCalendarConnection, discoverExistingCalendarConnection, getGoogleCalendarOauthSession, getMicrosoftCalendarOauthSession, listConnectedCalendarConnections, removeCalendarConnection, pushCalendarEventUpdate, readCalendarOverview, syncCalendarConnection, startGoogleCalendarOauth, startMicrosoftCalendarOauth, testMicrosoftCalendarOauthConfiguration, listCalendarProviderMetadata, updateCalendarConnectionSelection } from "./services/calendar-runtime.js";
49
49
  import { consumeOpenAiCodexOauthCredentials, getOpenAiCodexOauthSession, startOpenAiCodexOauthSession, submitOpenAiCodexOauthManualInput } from "./services/openai-codex-oauth.js";
50
50
  import { PSYCHE_ENTITY_TYPES, createBehaviorSchema, createBeliefEntrySchema, createBehaviorPatternSchema, createEmotionDefinitionSchema, createEventTypeSchema, createModeGuideSessionSchema, createModeProfileSchema, createPsycheValueSchema, createTriggerReportSchema, updateBehaviorSchema, updateBeliefEntrySchema, updateBehaviorPatternSchema, updateEmotionDefinitionSchema, updateEventTypeSchema, updateModeGuideSessionSchema, updateModeProfileSchema, updatePsycheValueSchema, updateTriggerReportSchema } from "./psyche-types.js";
51
51
  import { createQuestionnaireInstrumentSchema, publishQuestionnaireVersionSchema, startQuestionnaireRunSchema, updateQuestionnaireRunSchema, updateQuestionnaireVersionSchema } from "./questionnaire-types.js";
52
52
  import { createPreferenceCatalogItemSchema, createPreferenceCatalogSchema, createPreferenceContextSchema, createPreferenceItemSchema, enqueueEntityPreferenceItemSchema, mergePreferenceContextsSchema, preferenceWorkspaceQuerySchema, startPreferenceGameSchema, submitAbsoluteSignalSchema, submitPairwiseJudgmentSchema, updatePreferenceCatalogItemSchema, updatePreferenceCatalogSchema, updatePreferenceContextSchema, updatePreferenceItemSchema, updatePreferenceScoreSchema } from "./preferences-types.js";
53
- import { activityListQuerySchema, activitySourceSchema, createAgentActionSchema, createAgentTokenSchema, createAiConnectorSchema, createAiProcessorLinkSchema, createAiProcessorSchema, runAiConnectorSchema, writeSurfaceLayoutSchema, upsertAiModelConnectionSchema, testAiModelConnectionSchema, submitOpenAiCodexOauthManualCodeSchema, batchCreateEntitiesSchema, batchDeleteEntitiesSchema, batchRestoreEntitiesSchema, batchSearchEntitiesSchema, batchUpdateEntitiesSchema, createGoalSchema, createInsightFeedbackSchema, createInsightSchema, createStrategySchema, createUserSchema, createNoteSchema, createProjectSchema, createManualRewardGrantSchema, createCalendarEventSchema, createHabitCheckInSchema, createCalendarConnectionSchema, createDiagnosticLogSchema, discoverCalendarConnectionSchema, startMicrosoftCalendarOauthSchema, testMicrosoftCalendarOauthConfigurationSchema, createHabitSchema, createTaskTimeboxSchema, createWorkBlockTemplateSchema, createSessionEventSchema, createWorkAdjustmentSchema, createTagSchema, calendarOverviewQuerySchema, notesListQuerySchema, updateTagSchema, createTaskSchema, diagnosticLogListQuerySchema, eventsListQuerySchema, operatorLogWorkSchema, projectBoardPayloadSchema, projectListQuerySchema, entityDeleteQuerySchema, removeActivityEventSchema, resolveApprovalRequestSchema, rewardsLedgerQuerySchema, habitListQuerySchema, taskContextPayloadSchema, taskRunClaimSchema, taskRunFocusSchema, taskRunFinishSchema, taskRunHeartbeatSchema, taskRunListQuerySchema, taskListQuerySchema, tagSuggestionRequestSchema, uncompleteTaskSchema, updateSettingsSchema, updateGoalSchema, updateHabitSchema, updateInsightSchema, updateStrategySchema, updateUserSchema, updateCalendarConnectionSchema, updateCalendarEventSchema, updateNoteSchema, updateProjectSchema, updateRewardRuleSchema, updateTaskTimeboxSchema, updateTaskSchema, updateUserAccessGrantSchema, updateWorkBlockTemplateSchema, updateAiConnectorSchema, updateAiProcessorSchema, runAiProcessorSchema, workAdjustmentResultSchema, finalizeWeeklyReviewResultSchema, goalListQuerySchema, recommendTaskTimeboxesSchema, strategyListQuerySchema } from "./types.js";
53
+ import { activityListQuerySchema, activitySourceSchema, createAgentActionSchema, createAgentTokenSchema, createAiConnectorSchema, createAiProcessorLinkSchema, createAiProcessorSchema, runAiConnectorSchema, writeSurfaceLayoutSchema, upsertAiModelConnectionSchema, testAiModelConnectionSchema, submitOpenAiCodexOauthManualCodeSchema, batchCreateEntitiesSchema, batchDeleteEntitiesSchema, batchRestoreEntitiesSchema, batchSearchEntitiesSchema, batchUpdateEntitiesSchema, createGoalSchema, createInsightFeedbackSchema, createInsightSchema, createStrategySchema, createUserSchema, createNoteSchema, createProjectSchema, createManualRewardGrantSchema, createCalendarEventSchema, createHabitCheckInSchema, createCalendarConnectionSchema, createDiagnosticLogSchema, discoverCalendarConnectionSchema, startGoogleCalendarOauthSchema, startMicrosoftCalendarOauthSchema, testMicrosoftCalendarOauthConfigurationSchema, createHabitSchema, createTaskTimeboxSchema, createWorkBlockTemplateSchema, createSessionEventSchema, createWorkAdjustmentSchema, createTagSchema, calendarOverviewQuerySchema, notesListQuerySchema, updateTagSchema, createTaskSchema, diagnosticLogListQuerySchema, eventsListQuerySchema, operatorLogWorkSchema, projectBoardPayloadSchema, projectListQuerySchema, entityDeleteQuerySchema, removeActivityEventSchema, resolveApprovalRequestSchema, rewardsLedgerQuerySchema, habitListQuerySchema, taskContextPayloadSchema, taskRunClaimSchema, taskRunFocusSchema, taskRunFinishSchema, taskRunHeartbeatSchema, taskRunListQuerySchema, taskListQuerySchema, tagSuggestionRequestSchema, uncompleteTaskSchema, updateSettingsSchema, updateGoalSchema, updateHabitSchema, updateInsightSchema, updateStrategySchema, updateUserSchema, updateCalendarConnectionSchema, updateCalendarEventSchema, updateNoteSchema, updateProjectSchema, updateRewardRuleSchema, updateTaskTimeboxSchema, updateTaskSchema, updateUserAccessGrantSchema, updateWorkBlockTemplateSchema, updateAiConnectorSchema, updateAiProcessorSchema, runAiProcessorSchema, workAdjustmentResultSchema, finalizeWeeklyReviewResultSchema, goalListQuerySchema, recommendTaskTimeboxesSchema, strategyListQuerySchema } from "./types.js";
54
54
  import { buildOpenApiDocument } from "./openapi.js";
55
55
  import { registerWebRoutes } from "./web.js";
56
56
  import { createManagerRuntime } from "./managers/runtime.js";
@@ -2569,10 +2569,10 @@ const AGENT_ONBOARDING_TOOL_INPUT_CATALOG = [
2569
2569
  toolName: "forge_connect_calendar_provider",
2570
2570
  summary: "Create a Forge calendar connection for Google, Apple, Exchange Online, or custom CalDAV.",
2571
2571
  whenToUse: "Use only when the operator explicitly wants Forge connected to an external calendar provider.",
2572
- inputShape: '{ provider: "google"|"apple"|"caldav"|"microsoft", label: string, username?: string, clientId?: string, clientSecret?: string, refreshToken?: string, password?: string, serverUrl?: string, authSessionId?: string, selectedCalendarUrls: string[], forgeCalendarUrl?: string, createForgeCalendar?: boolean }',
2572
+ inputShape: '{ provider: "google"|"apple"|"caldav"|"microsoft", label: string, username?: string, password?: string, serverUrl?: string, authSessionId?: string, selectedCalendarUrls: string[], forgeCalendarUrl?: string, createForgeCalendar?: boolean }',
2573
2573
  requiredFields: ["provider", "label", "provider-specific credentials"],
2574
2574
  notes: [
2575
- "Google uses OAuth client credentials plus a refresh token.",
2575
+ "Google now uses an interactive localhost Authorization Code + PKCE flow. The user signs in interactively on the same machine running Forge, Forge exchanges the authorization code on the backend, and forge_connect_calendar_provider should only be used after a completed Google authSessionId exists.",
2576
2576
  "Apple starts from https://caldav.icloud.com and autodiscovers the principal plus calendars after authentication.",
2577
2577
  "Exchange Online uses Microsoft Graph. In the current Forge implementation it is read-only: Forge mirrors the selected calendars but does not publish work blocks or timeboxes back to Microsoft.",
2578
2578
  "In the current self-hosted local runtime, Exchange Online now uses an interactive Microsoft public-client sign-in flow with PKCE after the operator has saved the Microsoft client ID, tenant, and redirect URI in Settings -> Calendar. Non-interactive callers should treat Microsoft connection setup as a Settings-owned operator action unless a completed authSessionId already exists.",
@@ -3072,8 +3072,8 @@ function buildAgentOnboardingPayload(request) {
3072
3072
  saveSuggestionPlacement: "end_of_message",
3073
3073
  saveSuggestionTone: "gentle_optional",
3074
3074
  maxQuestionsPerTurn: 1,
3075
- psycheExplorationRule: "When a Psyche entity needs understanding first, begin with one exploratory question before any working formulation, replacement belief, suggested title, or save pitch. Keep the opening reflection to one or two short sentences, stay in plain prose instead of bullets or numbered lists, keep that first reply short, do not mention Forge search or save structure yet, avoid colons or list-shaped phrasing, and wait for the user's answer before offering a fuller formulation.",
3076
- psycheOpeningQuestionRule: "Prefer a concrete opening question tied to the entity: ask when the value mattered, what happened the last time the pattern appeared, what felt threatened before the behavior, what the feared outcome is inside the belief, what the mode is protecting, what the part says to do, or where the shift began in the incident.",
3075
+ psycheExplorationRule: "When a Psyche entity needs understanding first, begin with one exploratory question before any working formulation, replacement belief, suggested title, or save pitch. Keep the opening reflection to one or two short sentences, stay in plain prose instead of bullets or numbered lists, keep that first reply short, do not mention Forge search or save structure yet, avoid colons or list-shaped phrasing, prefer what/when/how over why until the experience is grounded, and wait for the user's answer before offering a fuller formulation.",
3076
+ psycheOpeningQuestionRule: "Prefer a concrete opening question tied to the entity: ask when the value mattered, what happened the last time the pattern appeared, what cue or body signal came first before the behavior, what the belief starts saying about self or outcome, what feels most at risk inside the mode, what the part is trying to get the user to do or stop doing, or where the shift began in the incident. Reflect briefly before the question and stay in one follow-up lane at a time.",
3077
3077
  duplicateCheckRoute: "/api/v1/entities/search",
3078
3078
  uiSuggestionRule: "offer_visual_ui_when_review_or_editing_would_be_easier",
3079
3079
  browserFallbackRule: "Do not open the Forge UI or a browser just to create or update normal entities when the batch entity tools can do the job.",
@@ -5711,6 +5711,37 @@ export async function buildServer(options = {}) {
5711
5711
  providers: listCalendarProviderMetadata(),
5712
5712
  connections: listConnectedCalendarConnections()
5713
5713
  }));
5714
+ app.post("/api/v1/calendar/oauth/google/start", async (request) => {
5715
+ requireScopedAccess(request.headers, ["write"], {
5716
+ route: "/api/v1/calendar/oauth/google/start"
5717
+ });
5718
+ return await startGoogleCalendarOauth(startGoogleCalendarOauthSchema.parse(request.body ?? {}), {
5719
+ browserOrigin: typeof request.body?.browserOrigin ===
5720
+ "string"
5721
+ ? request.body.browserOrigin
5722
+ : null,
5723
+ openerOrigin: typeof request.headers.origin === "string"
5724
+ ? request.headers.origin
5725
+ : typeof request.headers.referer === "string"
5726
+ ? request.headers.referer
5727
+ : null,
5728
+ requestBaseOrigin: getRequestOrigin(request)
5729
+ });
5730
+ });
5731
+ app.get("/api/v1/calendar/oauth/google/session/:id", async (request, reply) => {
5732
+ requireScopedAccess(request.headers, ["write"], { route: "/api/v1/calendar/oauth/google/session/:id" });
5733
+ try {
5734
+ return getGoogleCalendarOauthSession(request.params.id);
5735
+ }
5736
+ catch (error) {
5737
+ if (error instanceof Error &&
5738
+ error.message.startsWith("Unknown Google calendar auth session")) {
5739
+ reply.code(404);
5740
+ return { error: "Google calendar auth session not found" };
5741
+ }
5742
+ throw error;
5743
+ }
5744
+ });
5714
5745
  app.post("/api/v1/calendar/oauth/microsoft/start", async (request) => {
5715
5746
  requireScopedAccess(request.headers, ["write"], {
5716
5747
  route: "/api/v1/calendar/oauth/microsoft/start"
@@ -5783,6 +5814,54 @@ export async function buildServer(options = {}) {
5783
5814
  setTimeout(() => window.close(), 180);
5784
5815
  </script>
5785
5816
  </body>
5817
+ </html>`;
5818
+ reply.type("text/html; charset=utf-8");
5819
+ return body;
5820
+ });
5821
+ app.get("/api/v1/calendar/oauth/google/callback", async (request, reply) => {
5822
+ const query = request.query;
5823
+ const result = await completeGoogleCalendarOauth({
5824
+ state: query.state ?? null,
5825
+ code: query.code ?? null,
5826
+ error: query.error ?? null,
5827
+ errorDescription: query.error_description ?? null
5828
+ });
5829
+ const session = result.session;
5830
+ const escapedOrigin = JSON.stringify(result.openerOrigin || "*");
5831
+ const escapedMessage = JSON.stringify({
5832
+ type: "forge:google-calendar-auth",
5833
+ sessionId: session.sessionId,
5834
+ status: session.status
5835
+ });
5836
+ const body = `<!doctype html>
5837
+ <html lang="en">
5838
+ <head>
5839
+ <meta charset="utf-8" />
5840
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
5841
+ <title>Forge Google sign-in</title>
5842
+ <style>
5843
+ body{margin:0;font-family:ui-sans-serif,system-ui,sans-serif;background:#0b1320;color:#f8fafc;display:grid;place-items:center;min-height:100vh}
5844
+ main{max-width:30rem;padding:2rem;border:1px solid rgba(255,255,255,.08);border-radius:24px;background:linear-gradient(180deg,rgba(18,28,38,.98),rgba(11,17,28,.98))}
5845
+ h1{margin:0 0 .75rem;font-size:1.15rem}
5846
+ p{margin:0;color:rgba(248,250,252,.72);line-height:1.6}
5847
+ </style>
5848
+ </head>
5849
+ <body>
5850
+ <main>
5851
+ <h1>${session.status === "authorized" ? "Google account connected" : "Google sign-in needs attention"}</h1>
5852
+ <p>${session.status === "authorized" ? "Forge received your Google account and sent the result back to the calendar setup flow. You can close this window." : (session.error ?? "Forge could not complete Google sign-in. You can close this window and try again from Settings.")}</p>
5853
+ </main>
5854
+ <script>
5855
+ const message = ${escapedMessage};
5856
+ const targetOrigin = ${escapedOrigin};
5857
+ try {
5858
+ if (window.opener && !window.opener.closed) {
5859
+ window.opener.postMessage(message, targetOrigin);
5860
+ }
5861
+ } catch {}
5862
+ setTimeout(() => window.close(), 180);
5863
+ </script>
5864
+ </body>
5786
5865
  </html>`;
5787
5866
  reply.type("text/html; charset=utf-8");
5788
5867
  return body;
@@ -6959,132 +7038,184 @@ export async function buildServer(options = {}) {
6959
7038
  secrets: managers.secrets
6960
7039
  }, { trigger: "route" });
6961
7040
  });
6962
- app.get("/api/v1/ai-connectors/catalog/boxes", async (request) => {
6963
- requireScopedAccess(request.headers, ["read"], {
6964
- route: "/api/v1/ai-connectors/catalog/boxes"
7041
+ const registerFlowApiRoutes = (basePath, noun, options) => {
7042
+ const collectionKey = options?.collectionKey ?? "connectors";
7043
+ const singularKey = options?.singularKey ?? "connector";
7044
+ const catalogPath = options?.catalogPath ?? `${basePath}/catalog/boxes`;
7045
+ app.get(catalogPath, async (request) => {
7046
+ requireScopedAccess(request.headers, ["read"], {
7047
+ route: catalogPath
7048
+ });
7049
+ return {
7050
+ boxes: [
7051
+ ...listForgeBoxCatalog(),
7052
+ ...listAiConnectors().flatMap((connector) => connector.publishedOutputs.map((output) => buildConnectorOutputCatalogEntry({
7053
+ connectorId: connector.id,
7054
+ title: connector.title,
7055
+ outputId: output.id
7056
+ })))
7057
+ ]
7058
+ };
6965
7059
  });
6966
- return {
6967
- boxes: listForgeBoxCatalog()
6968
- };
6969
- });
6970
- app.get("/api/v1/ai-connectors", async (request) => {
6971
- requireScopedAccess(request.headers, ["read"], {
6972
- route: "/api/v1/ai-connectors"
7060
+ app.get(basePath, async (request) => {
7061
+ requireScopedAccess(request.headers, ["read"], {
7062
+ route: basePath
7063
+ });
7064
+ return {
7065
+ [collectionKey]: listAiConnectors()
7066
+ };
6973
7067
  });
6974
- return {
6975
- connectors: listAiConnectors()
6976
- };
6977
- });
6978
- app.post("/api/v1/ai-connectors", async (request, reply) => {
6979
- requireScopedAccess(request.headers, ["write"], {
6980
- route: "/api/v1/ai-connectors"
7068
+ app.post(basePath, async (request, reply) => {
7069
+ requireScopedAccess(request.headers, ["write"], {
7070
+ route: basePath
7071
+ });
7072
+ const connector = createAiConnector(createAiConnectorSchema.parse(request.body ?? {}));
7073
+ reply.code(201);
7074
+ return { [singularKey]: connector };
6981
7075
  });
6982
- const connector = createAiConnector(createAiConnectorSchema.parse(request.body ?? {}));
6983
- reply.code(201);
6984
- return { connector };
6985
- });
6986
- app.get("/api/v1/ai-connectors/:id", async (request, reply) => {
6987
- requireScopedAccess(request.headers, ["read"], {
6988
- route: "/api/v1/ai-connectors/:id"
7076
+ app.get(`${basePath}/:id`, async (request, reply) => {
7077
+ requireScopedAccess(request.headers, ["read"], {
7078
+ route: `${basePath}/:id`
7079
+ });
7080
+ const connector = getAiConnectorById(request.params.id);
7081
+ if (!connector) {
7082
+ reply.code(404);
7083
+ return { error: `${noun} not found` };
7084
+ }
7085
+ return {
7086
+ [singularKey]: connector,
7087
+ runs: listAiConnectorRuns(connector.id),
7088
+ conversation: getAiConnectorConversationForConnector(connector.id)
7089
+ };
6989
7090
  });
6990
- const connector = getAiConnectorById(request.params.id);
6991
- if (!connector) {
6992
- reply.code(404);
6993
- return { error: "AI connector not found" };
6994
- }
6995
- return {
6996
- connector,
6997
- runs: listAiConnectorRuns(connector.id),
6998
- conversation: getAiConnectorConversationForConnector(connector.id)
6999
- };
7000
- });
7001
- app.patch("/api/v1/ai-connectors/:id", async (request, reply) => {
7002
- requireScopedAccess(request.headers, ["write"], {
7003
- route: "/api/v1/ai-connectors/:id"
7091
+ app.patch(`${basePath}/:id`, async (request, reply) => {
7092
+ requireScopedAccess(request.headers, ["write"], {
7093
+ route: `${basePath}/:id`
7094
+ });
7095
+ const connector = updateAiConnector(request.params.id, updateAiConnectorSchema.parse(request.body ?? {}));
7096
+ if (!connector) {
7097
+ reply.code(404);
7098
+ return { error: `${noun} not found` };
7099
+ }
7100
+ return { [singularKey]: connector };
7004
7101
  });
7005
- const connector = updateAiConnector(request.params.id, updateAiConnectorSchema.parse(request.body ?? {}));
7006
- if (!connector) {
7007
- reply.code(404);
7008
- return { error: "AI connector not found" };
7009
- }
7010
- return { connector };
7011
- });
7012
- app.delete("/api/v1/ai-connectors/:id", async (request, reply) => {
7013
- requireScopedAccess(request.headers, ["write"], {
7014
- route: "/api/v1/ai-connectors/:id"
7102
+ app.delete(`${basePath}/:id`, async (request, reply) => {
7103
+ requireScopedAccess(request.headers, ["write"], {
7104
+ route: `${basePath}/:id`
7105
+ });
7106
+ const connector = deleteAiConnector(request.params.id);
7107
+ if (!connector) {
7108
+ reply.code(404);
7109
+ return { error: `${noun} not found` };
7110
+ }
7111
+ return { [singularKey]: connector };
7015
7112
  });
7016
- const connector = deleteAiConnector(request.params.id);
7017
- if (!connector) {
7018
- reply.code(404);
7019
- return { error: "AI connector not found" };
7020
- }
7021
- return { connector };
7022
- });
7023
- app.post("/api/v1/ai-connectors/:id/run", async (request, reply) => {
7024
- requireScopedAccess(request.headers, ["write"], {
7025
- route: "/api/v1/ai-connectors/:id/run"
7113
+ app.post(`${basePath}/:id/run`, async (request, reply) => {
7114
+ requireScopedAccess(request.headers, ["write"], {
7115
+ route: `${basePath}/:id/run`
7116
+ });
7117
+ const connector = getAiConnectorById(request.params.id);
7118
+ if (!connector) {
7119
+ reply.code(404);
7120
+ return { error: `${noun} not found` };
7121
+ }
7122
+ const execution = await runAiConnector(connector.id, runAiConnectorSchema.parse(request.body ?? {}), {
7123
+ llm: managers.llm,
7124
+ secrets: managers.secrets
7125
+ }, "run");
7126
+ return {
7127
+ [singularKey]: execution.connector,
7128
+ run: execution.run,
7129
+ conversation: execution.conversation
7130
+ };
7026
7131
  });
7027
- const connector = getAiConnectorById(request.params.id);
7028
- if (!connector) {
7029
- reply.code(404);
7030
- return { error: "AI connector not found" };
7031
- }
7032
- return await runAiConnector(connector.id, runAiConnectorSchema.parse(request.body ?? {}), {
7033
- llm: managers.llm,
7034
- secrets: managers.secrets
7035
- }, "run");
7132
+ app.post(`${basePath}/:id/chat`, async (request, reply) => {
7133
+ requireScopedAccess(request.headers, ["write"], {
7134
+ route: `${basePath}/:id/chat`
7135
+ });
7136
+ const connector = getAiConnectorById(request.params.id);
7137
+ if (!connector) {
7138
+ reply.code(404);
7139
+ return { error: `${noun} not found` };
7140
+ }
7141
+ const execution = await runAiConnector(connector.id, runAiConnectorSchema.parse(request.body ?? {}), {
7142
+ llm: managers.llm,
7143
+ secrets: managers.secrets
7144
+ }, "chat");
7145
+ return {
7146
+ [singularKey]: execution.connector,
7147
+ run: execution.run,
7148
+ conversation: execution.conversation
7149
+ };
7150
+ });
7151
+ app.get(`${basePath}/:id/output`, async (request, reply) => {
7152
+ requireScopedAccess(request.headers, ["read"], {
7153
+ route: `${basePath}/:id/output`
7154
+ });
7155
+ const connector = getAiConnectorById(request.params.id);
7156
+ if (!connector) {
7157
+ reply.code(404);
7158
+ return { error: `${noun} not found` };
7159
+ }
7160
+ return {
7161
+ [singularKey]: connector,
7162
+ output: connector.lastRun?.result ?? null
7163
+ };
7164
+ });
7165
+ app.get(`${basePath}/:id/runs`, async (request, reply) => {
7166
+ requireScopedAccess(request.headers, ["read"], {
7167
+ route: `${basePath}/:id/runs`
7168
+ });
7169
+ const connector = getAiConnectorById(request.params.id);
7170
+ if (!connector) {
7171
+ reply.code(404);
7172
+ return { error: `${noun} not found` };
7173
+ }
7174
+ return {
7175
+ runs: listAiConnectorRuns(connector.id)
7176
+ };
7177
+ });
7178
+ };
7179
+ registerFlowApiRoutes("/api/v1/workbench/flows", "Workbench flow", {
7180
+ collectionKey: "flows",
7181
+ singularKey: "flow",
7182
+ catalogPath: "/api/v1/workbench/catalog/boxes"
7036
7183
  });
7037
- app.post("/api/v1/ai-connectors/:id/chat", async (request, reply) => {
7184
+ app.post("/api/v1/workbench/run", async (request, reply) => {
7038
7185
  requireScopedAccess(request.headers, ["write"], {
7039
- route: "/api/v1/ai-connectors/:id/chat"
7186
+ route: "/api/v1/workbench/run"
7040
7187
  });
7041
- const connector = getAiConnectorById(request.params.id);
7042
- if (!connector) {
7188
+ const payload = runAiConnectorSchema
7189
+ .extend({
7190
+ flowId: z.string().trim().min(1)
7191
+ })
7192
+ .parse(request.body ?? {});
7193
+ const flow = getAiConnectorById(payload.flowId);
7194
+ if (!flow) {
7043
7195
  reply.code(404);
7044
- return { error: "AI connector not found" };
7196
+ return { error: "Workbench flow not found" };
7045
7197
  }
7046
- return await runAiConnector(connector.id, runAiConnectorSchema.parse(request.body ?? {}), {
7198
+ const { flowId, ...runInput } = payload;
7199
+ const execution = await runAiConnector(flow.id, runInput, {
7047
7200
  llm: managers.llm,
7048
7201
  secrets: managers.secrets
7049
- }, "chat");
7050
- });
7051
- app.get("/api/v1/ai-connectors/:id/output", async (request, reply) => {
7052
- requireScopedAccess(request.headers, ["read"], {
7053
- route: "/api/v1/ai-connectors/:id/output"
7054
- });
7055
- const connector = getAiConnectorById(request.params.id);
7056
- if (!connector) {
7057
- reply.code(404);
7058
- return { error: "AI connector not found" };
7059
- }
7060
- return {
7061
- connector,
7062
- output: connector.lastRun?.result ?? null
7063
- };
7064
- });
7065
- app.get("/api/v1/ai-connectors/:id/runs", async (request, reply) => {
7066
- requireScopedAccess(request.headers, ["read"], {
7067
- route: "/api/v1/ai-connectors/:id/runs"
7068
- });
7069
- const connector = getAiConnectorById(request.params.id);
7070
- if (!connector) {
7071
- reply.code(404);
7072
- return { error: "AI connector not found" };
7073
- }
7202
+ }, "run");
7074
7203
  return {
7075
- runs: listAiConnectorRuns(connector.id)
7204
+ flow: execution.connector,
7205
+ run: execution.run,
7206
+ conversation: execution.conversation
7076
7207
  };
7077
7208
  });
7078
- app.get("/api/v1/ai-connectors/by-slug/:slug", async (request, reply) => {
7209
+ app.get("/api/v1/workbench/flows/by-slug/:slug", async (request, reply) => {
7079
7210
  requireScopedAccess(request.headers, ["read"], {
7080
- route: "/api/v1/ai-connectors/by-slug/:slug"
7211
+ route: "/api/v1/workbench/flows/by-slug/:slug"
7081
7212
  });
7082
7213
  const connector = getAiConnectorBySlug(request.params.slug);
7083
7214
  if (!connector) {
7084
7215
  reply.code(404);
7085
- return { error: "AI connector not found" };
7216
+ return { error: "Workbench flow not found" };
7086
7217
  }
7087
- return { connector };
7218
+ return { flow: connector };
7088
7219
  });
7089
7220
  app.post("/api/v1/settings/tokens", async (request, reply) => {
7090
7221
  const auth = requireOperatorSession(request.headers, { route: "/api/v1/settings/tokens" });
@@ -0,0 +1,188 @@
1
+ import { getDatabase } from "../db.js";
2
+ import { getFitnessViewData, getSleepViewData } from "../health.js";
3
+ import { listMovementPlaces } from "../movement.js";
4
+ import { createNote, listNotes } from "../repositories/notes.js";
5
+ import { updateTask } from "../repositories/tasks.js";
6
+ import { searchEntities } from "../services/entity-crud.js";
7
+ import { executeCommonWorkbenchTool, mapWorkbenchTools } from "../../../src/lib/workbench/runtime.js";
8
+ import { getWorkbenchNodeCatalog, getWorkbenchNodeDefinition } from "../../../src/lib/workbench/registry.js";
9
+ function createSnapshotForConnectorOutput(boxId) {
10
+ const outputId = boxId.replace(/^connector-output:/, "");
11
+ const rows = getDatabase()
12
+ .prepare(`SELECT id, title, published_outputs_json, last_run_json FROM ai_connectors ORDER BY updated_at DESC`)
13
+ .all();
14
+ for (const row of rows) {
15
+ const outputs = JSON.parse(row.published_outputs_json || "[]");
16
+ const output = outputs.find((entry) => entry.id === outputId);
17
+ if (!output) {
18
+ continue;
19
+ }
20
+ const lastRun = row.last_run_json
21
+ ? JSON.parse(row.last_run_json)
22
+ : null;
23
+ const published = lastRun?.result?.outputs?.[outputId] ?? null;
24
+ return {
25
+ boxId,
26
+ label: output.label,
27
+ capturedAt: new Date().toISOString(),
28
+ contentText: published?.text ?? `${row.title}\nNo published output has been generated yet.`,
29
+ contentJson: published?.json ?? null,
30
+ tools: []
31
+ };
32
+ }
33
+ return null;
34
+ }
35
+ function createRuntimeContext(input) {
36
+ const services = {
37
+ entities: {
38
+ search: searchEntities
39
+ },
40
+ notes: {
41
+ create: createNote,
42
+ list: listNotes
43
+ },
44
+ movement: {
45
+ listPlaces: listMovementPlaces
46
+ },
47
+ health: {
48
+ getSleepViewData: getSleepViewData,
49
+ getFitnessViewData: getFitnessViewData
50
+ },
51
+ tasks: {
52
+ update: ((taskId, patch) => updateTask(taskId, patch, { source: "agent", actor: "Workbench" }))
53
+ }
54
+ };
55
+ return {
56
+ actor: input?.actor ?? { userIds: null, source: "api" },
57
+ services,
58
+ routeParams: input?.routeParams,
59
+ filters: input?.filters,
60
+ now: new Date().toISOString()
61
+ };
62
+ }
63
+ function toCatalogEntry(definition) {
64
+ if (!definition) {
65
+ return null;
66
+ }
67
+ const toPortDefinition = (port) => ({
68
+ key: port.key,
69
+ label: port.label,
70
+ kind: port.kind,
71
+ required: port.required ?? false,
72
+ expandableKeys: "expandableKeys" in port ? (port.expandableKeys ?? []) : []
73
+ });
74
+ return {
75
+ id: definition.id,
76
+ boxId: definition.id,
77
+ surfaceId: definition.surfaceId,
78
+ routePath: definition.routePath,
79
+ title: definition.title,
80
+ label: definition.title,
81
+ icon: definition.icon ?? null,
82
+ description: definition.description,
83
+ category: definition.category,
84
+ tags: definition.tags,
85
+ capabilityModes: [
86
+ "content",
87
+ ...(definition.tools.length > 0 ? ["tool"] : [])
88
+ ],
89
+ inputs: definition.inputs.map(toPortDefinition),
90
+ params: definition.params.map(toPortDefinition),
91
+ output: definition.output.map(toPortDefinition),
92
+ tools: definition.tools,
93
+ outputs: definition.output.map(toPortDefinition),
94
+ toolAdapters: definition.tools,
95
+ snapshotResolverKey: undefined
96
+ };
97
+ }
98
+ export function listForgeBoxCatalog() {
99
+ return getWorkbenchNodeCatalog();
100
+ }
101
+ export function getForgeBoxCatalogEntry(boxId) {
102
+ return toCatalogEntry(getWorkbenchNodeDefinition(boxId));
103
+ }
104
+ export function buildConnectorOutputCatalogEntry(input) {
105
+ return {
106
+ id: `connector-output:${input.outputId}`,
107
+ boxId: `connector-output:${input.outputId}`,
108
+ surfaceId: null,
109
+ routePath: `/workbench/${input.connectorId}`,
110
+ title: `${input.title} output`,
111
+ label: `${input.title} output`,
112
+ icon: null,
113
+ description: "Published Workbench output.",
114
+ category: "Workbench outputs",
115
+ tags: ["workbench", "output"],
116
+ capabilityModes: ["content"],
117
+ inputs: [],
118
+ params: [],
119
+ output: [
120
+ {
121
+ key: "primary",
122
+ label: "Published output",
123
+ kind: "content",
124
+ required: false,
125
+ expandableKeys: []
126
+ }
127
+ ],
128
+ tools: [],
129
+ outputs: [
130
+ {
131
+ key: "primary",
132
+ label: "Published output",
133
+ kind: "content",
134
+ required: false,
135
+ expandableKeys: []
136
+ }
137
+ ],
138
+ toolAdapters: []
139
+ };
140
+ }
141
+ export function resolveForgeBoxSnapshot(boxId, contextInput, executionInput) {
142
+ if (boxId.startsWith("connector-output:")) {
143
+ return (createSnapshotForConnectorOutput(boxId) ?? {
144
+ boxId,
145
+ label: boxId,
146
+ capturedAt: new Date().toISOString(),
147
+ contentText: "This connector output has not been generated yet.",
148
+ contentJson: null,
149
+ tools: []
150
+ });
151
+ }
152
+ const definition = getWorkbenchNodeDefinition(boxId);
153
+ if (!definition) {
154
+ return {
155
+ boxId,
156
+ label: boxId,
157
+ capturedAt: new Date().toISOString(),
158
+ contentText: "This Workbench node is not registered.",
159
+ contentJson: null,
160
+ tools: []
161
+ };
162
+ }
163
+ const execution = definition.execute({
164
+ nodeId: boxId,
165
+ definition,
166
+ inputs: executionInput?.inputs ?? {},
167
+ params: executionInput?.params ?? {},
168
+ context: createRuntimeContext(contextInput)
169
+ });
170
+ if (execution instanceof Promise) {
171
+ throw new Error("Workbench box execution must be synchronous for snapshot resolution.");
172
+ }
173
+ return {
174
+ boxId: definition.id,
175
+ label: definition.title,
176
+ capturedAt: new Date().toISOString(),
177
+ contentText: execution.primaryText,
178
+ contentJson: execution.payload,
179
+ tools: mapWorkbenchTools(definition.tools)
180
+ };
181
+ }
182
+ export function executeForgeBoxTool(boxId, toolKey, args, contextInput) {
183
+ const definition = getWorkbenchNodeDefinition(boxId);
184
+ if (!definition) {
185
+ throw new Error(`Unknown Forge box: ${boxId}`);
186
+ }
187
+ return executeCommonWorkbenchTool(createRuntimeContext(contextInput), definition, toolKey, args);
188
+ }