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
@@ -0,0 +1,78 @@
1
+ import { type ForgePluginConfig } from "./api-client.js";
2
+ import type { ForgePluginRegistrationApi } from "./plugin-sdk-types.js";
3
+ type ForgeGoalRecord = {
4
+ id: string;
5
+ title: string;
6
+ description?: string | null;
7
+ status?: string;
8
+ horizon?: string | null;
9
+ };
10
+ type ForgeProjectRecord = {
11
+ id: string;
12
+ title: string;
13
+ description?: string | null;
14
+ status?: string;
15
+ goalId?: string | null;
16
+ goalTitle?: string | null;
17
+ };
18
+ type ForgeTaskRecord = {
19
+ id: string;
20
+ title: string;
21
+ description?: string | null;
22
+ status?: string;
23
+ priority?: string;
24
+ dueDate?: string | null;
25
+ projectId?: string | null;
26
+ projectTitle?: string | null;
27
+ goalId?: string | null;
28
+ goalTitle?: string | null;
29
+ };
30
+ type ForgeHabitRecord = {
31
+ id: string;
32
+ title: string;
33
+ description?: string | null;
34
+ polarity?: string;
35
+ frequency?: string | null;
36
+ };
37
+ type ForgeStrategyRecord = {
38
+ id: string;
39
+ title: string;
40
+ overview?: string | null;
41
+ status?: string | null;
42
+ isLocked?: boolean;
43
+ };
44
+ type ForgeWikiPageRecord = {
45
+ id: string;
46
+ slug: string;
47
+ title: string;
48
+ kind?: string;
49
+ parentSlug?: string | null;
50
+ summary?: string | null;
51
+ contentPlain?: string | null;
52
+ };
53
+ type ForgeOperatorContext = {
54
+ generatedAt?: string;
55
+ activeProjects?: ForgeProjectRecord[];
56
+ focusTasks?: ForgeTaskRecord[];
57
+ dueHabits?: ForgeHabitRecord[];
58
+ recommendedNextTask?: ForgeTaskRecord | null;
59
+ };
60
+ type ForgeOperatorOverview = {
61
+ generatedAt?: string;
62
+ warnings?: string[];
63
+ operator?: ForgeOperatorContext | null;
64
+ };
65
+ type ForgeSessionBootstrapPayload = {
66
+ overview: ForgeOperatorOverview | null;
67
+ goals: ForgeGoalRecord[];
68
+ projects: ForgeProjectRecord[];
69
+ tasks: ForgeTaskRecord[];
70
+ habits: ForgeHabitRecord[];
71
+ strategies: ForgeStrategyRecord[];
72
+ peoplePages: ForgeWikiPageRecord[];
73
+ };
74
+ export declare function listPeopleBranchPages(pages: ForgeWikiPageRecord[]): ForgeWikiPageRecord[];
75
+ export declare function buildForgeSessionBootstrapContext(payload: ForgeSessionBootstrapPayload): string;
76
+ export declare function buildLiveForgeSessionBootstrapContext(config: ForgePluginConfig): Promise<string>;
77
+ export declare function registerForgeSessionBootstrapHook(api: ForgePluginRegistrationApi, config: ForgePluginConfig): void;
78
+ export {};
@@ -0,0 +1,240 @@
1
+ import path from "node:path";
2
+ import { isAgentBootstrapEvent } from "openclaw/plugin-sdk/hook-runtime";
3
+ import { callConfiguredForgeApi, expectForgeSuccess } from "./api-client.js";
4
+ const FORGE_SESSION_BOOTSTRAP_PATH = ".forge/generated/FORGE_SESSION_BOOTSTRAP.md";
5
+ const FORGE_SESSION_BOOTSTRAP_NAME = "forge-session-bootstrap";
6
+ function isRecord(value) {
7
+ return typeof value === "object" && value !== null;
8
+ }
9
+ function asArray(value) {
10
+ return Array.isArray(value) ? value : [];
11
+ }
12
+ function cleanInline(value) {
13
+ return (value ?? "").replace(/\s+/g, " ").trim();
14
+ }
15
+ function excerpt(value, maxLength) {
16
+ const normalized = cleanInline(value);
17
+ if (!normalized) {
18
+ return "";
19
+ }
20
+ if (normalized.length <= maxLength) {
21
+ return normalized;
22
+ }
23
+ return `${normalized.slice(0, Math.max(0, maxLength - 1)).trimEnd()}…`;
24
+ }
25
+ function formatMeta(parts) {
26
+ return parts
27
+ .map((value) => cleanInline(value))
28
+ .filter(Boolean)
29
+ .join(" | ");
30
+ }
31
+ function buildGoalIndex(goals) {
32
+ return new Map(goals.map((goal) => [goal.id, goal.title]));
33
+ }
34
+ function buildProjectIndex(projects) {
35
+ return new Map(projects.map((project) => [project.id, project.title]));
36
+ }
37
+ function formatGoal(goal) {
38
+ const meta = formatMeta([goal.status, goal.horizon]);
39
+ const summary = excerpt(goal.description, 140);
40
+ return `- ${goal.title}${meta ? ` [${meta}]` : ""}${summary ? ` — ${summary}` : ""}`;
41
+ }
42
+ function formatProject(project, goalTitles) {
43
+ const linkedGoal = cleanInline(project.goalTitle) ||
44
+ (project.goalId ? goalTitles.get(project.goalId) ?? project.goalId : "");
45
+ const meta = formatMeta([project.status, linkedGoal ? `goal: ${linkedGoal}` : ""]);
46
+ const summary = excerpt(project.description, 140);
47
+ return `- ${project.title}${meta ? ` [${meta}]` : ""}${summary ? ` — ${summary}` : ""}`;
48
+ }
49
+ function formatTask(task, projectTitles, goalTitles) {
50
+ const linkedProject = cleanInline(task.projectTitle) ||
51
+ (task.projectId ? projectTitles.get(task.projectId) ?? task.projectId : "");
52
+ const linkedGoal = cleanInline(task.goalTitle) ||
53
+ (task.goalId ? goalTitles.get(task.goalId) ?? task.goalId : "");
54
+ const meta = formatMeta([
55
+ task.status,
56
+ task.priority,
57
+ task.dueDate ? `due ${task.dueDate}` : "",
58
+ linkedProject ? `project: ${linkedProject}` : "",
59
+ !linkedProject && linkedGoal ? `goal: ${linkedGoal}` : ""
60
+ ]);
61
+ const summary = excerpt(task.description, 140);
62
+ return `- ${task.title}${meta ? ` [${meta}]` : ""}${summary ? ` — ${summary}` : ""}`;
63
+ }
64
+ function formatHabit(habit) {
65
+ const meta = formatMeta([habit.polarity, habit.frequency]);
66
+ const summary = excerpt(habit.description, 140);
67
+ return `- ${habit.title}${meta ? ` [${meta}]` : ""}${summary ? ` — ${summary}` : ""}`;
68
+ }
69
+ function formatStrategy(strategy) {
70
+ const meta = formatMeta([
71
+ strategy.status,
72
+ strategy.isLocked ? "locked" : "editable"
73
+ ]);
74
+ const summary = excerpt(strategy.overview, 140);
75
+ return `- ${strategy.title}${meta ? ` [${meta}]` : ""}${summary ? ` — ${summary}` : ""}`;
76
+ }
77
+ function hasPeopleAncestor(page, pagesBySlug) {
78
+ let parentSlug = page.parentSlug ?? null;
79
+ while (parentSlug) {
80
+ if (parentSlug === "people") {
81
+ return true;
82
+ }
83
+ parentSlug = pagesBySlug.get(parentSlug)?.parentSlug ?? null;
84
+ }
85
+ return false;
86
+ }
87
+ export function listPeopleBranchPages(pages) {
88
+ const pagesBySlug = new Map(pages.map((page) => [page.slug, page]));
89
+ return pages
90
+ .filter((page) => page.kind === "wiki")
91
+ .filter((page) => page.slug !== "people")
92
+ .filter((page) => hasPeopleAncestor(page, pagesBySlug))
93
+ .sort((left, right) => left.title.localeCompare(right.title));
94
+ }
95
+ export function buildForgeSessionBootstrapContext(payload) {
96
+ const goalTitles = buildGoalIndex(payload.goals);
97
+ const projectTitles = buildProjectIndex(payload.projects);
98
+ const overview = payload.overview?.operator ?? null;
99
+ const lines = [
100
+ "# Forge Session Bootstrap",
101
+ "",
102
+ "This block is generated from the live Forge runtime at session creation time. Treat it as a compact starting snapshot, not as the full source of truth.",
103
+ ""
104
+ ];
105
+ if (payload.overview?.generatedAt) {
106
+ lines.push(`Generated at: ${payload.overview.generatedAt}`, "");
107
+ }
108
+ if (overview) {
109
+ lines.push("## Current Forge Snapshot", "");
110
+ lines.push(`- Active projects in operator view: ${overview.activeProjects?.length ?? 0}`);
111
+ lines.push(`- Focus tasks in operator view: ${overview.focusTasks?.length ?? 0}`);
112
+ lines.push(`- Due habits in operator view: ${overview.dueHabits?.length ?? 0}`);
113
+ if (overview.recommendedNextTask) {
114
+ lines.push(`- Recommended next task: ${overview.recommendedNextTask.title}`);
115
+ }
116
+ if ((payload.overview?.warnings ?? []).length > 0) {
117
+ lines.push(`- Warnings: ${(payload.overview?.warnings ?? []).join(" | ")}`);
118
+ }
119
+ lines.push("");
120
+ }
121
+ lines.push(`## Goals (${payload.goals.length})`, "");
122
+ if (payload.goals.length === 0) {
123
+ lines.push("- None.", "");
124
+ }
125
+ else {
126
+ lines.push(...payload.goals.map((goal) => formatGoal(goal)), "");
127
+ }
128
+ lines.push(`## Projects (${payload.projects.length})`, "");
129
+ if (payload.projects.length === 0) {
130
+ lines.push("- None.", "");
131
+ }
132
+ else {
133
+ lines.push(...payload.projects.map((project) => formatProject(project, goalTitles)), "");
134
+ }
135
+ lines.push(`## Strategies (${payload.strategies.length})`, "");
136
+ if (payload.strategies.length === 0) {
137
+ lines.push("- None.", "");
138
+ }
139
+ else {
140
+ lines.push(...payload.strategies.map((strategy) => formatStrategy(strategy)), "");
141
+ }
142
+ lines.push(`## Tasks (${payload.tasks.length})`, "");
143
+ if (payload.tasks.length === 0) {
144
+ lines.push("- None.", "");
145
+ }
146
+ else {
147
+ lines.push(...payload.tasks.map((task) => formatTask(task, projectTitles, goalTitles)), "");
148
+ }
149
+ lines.push(`## Habits (${payload.habits.length})`, "");
150
+ if (payload.habits.length === 0) {
151
+ lines.push("- None.", "");
152
+ }
153
+ else {
154
+ lines.push(...payload.habits.map((habit) => formatHabit(habit)), "");
155
+ }
156
+ lines.push(`## Wiki People Pages (${payload.peoplePages.length})`, "");
157
+ if (payload.peoplePages.length === 0) {
158
+ lines.push("- None.", "");
159
+ }
160
+ else {
161
+ lines.push(...payload.peoplePages.map((page) => {
162
+ const preview = excerpt(page.contentPlain ?? page.summary, 100);
163
+ return `- ${page.title}${preview ? ` — ${preview}` : ""}`;
164
+ }), "");
165
+ }
166
+ lines.push("## Use The Forge Skill", "");
167
+ lines.push("If you need more detail about any Forge entity, use the Forge skill/tools instead of guessing.");
168
+ lines.push("Relevant Forge entities include goals, projects, tasks, habits, strategies, notes, wiki pages, sleep sessions, workout sessions, psyche records, questionnaire records, preferences records, calendar events, work blocks, task timeboxes, and user ownership context.");
169
+ lines.push("If you need more detail about any listed person or relationship page, use the Forge skill to search the wiki and open the live page.");
170
+ lines.push("");
171
+ return lines.join("\n").trim();
172
+ }
173
+ async function readForgePayload(config, pathName) {
174
+ const result = await callConfiguredForgeApi(config, {
175
+ method: "GET",
176
+ path: pathName
177
+ });
178
+ return expectForgeSuccess(result);
179
+ }
180
+ async function loadForgeSessionBootstrapPayload(config) {
181
+ const [overviewResponse, goalsResponse, projectsResponse, tasksResponse, habitsResponse, strategiesResponse, wikiPagesResponse] = await Promise.all([
182
+ readForgePayload(config, "/api/v1/operator/overview"),
183
+ readForgePayload(config, "/api/v1/goals"),
184
+ readForgePayload(config, "/api/v1/projects"),
185
+ readForgePayload(config, "/api/v1/tasks"),
186
+ readForgePayload(config, "/api/v1/habits"),
187
+ readForgePayload(config, "/api/v1/strategies"),
188
+ readForgePayload(config, "/api/v1/wiki/pages")
189
+ ]);
190
+ const wikiPages = asArray(wikiPagesResponse.pages).filter((page) => isRecord(page) &&
191
+ typeof page.slug === "string" &&
192
+ typeof page.title === "string");
193
+ return {
194
+ overview: overviewResponse && isRecord(overviewResponse) && "overview" in overviewResponse
195
+ ? (overviewResponse.overview ?? null)
196
+ : null,
197
+ goals: asArray(goalsResponse.goals),
198
+ projects: asArray(projectsResponse.projects),
199
+ tasks: asArray(tasksResponse.tasks),
200
+ habits: asArray(habitsResponse.habits),
201
+ strategies: asArray(strategiesResponse.strategies),
202
+ peoplePages: listPeopleBranchPages(wikiPages)
203
+ };
204
+ }
205
+ export async function buildLiveForgeSessionBootstrapContext(config) {
206
+ return buildForgeSessionBootstrapContext(await loadForgeSessionBootstrapPayload(config));
207
+ }
208
+ export function registerForgeSessionBootstrapHook(api, config) {
209
+ if (!api.registerHook) {
210
+ return;
211
+ }
212
+ api.registerHook("agent:bootstrap", async (event) => {
213
+ if (!isAgentBootstrapEvent(event)) {
214
+ return;
215
+ }
216
+ try {
217
+ const content = await buildLiveForgeSessionBootstrapContext(config);
218
+ if (!content.trim()) {
219
+ return;
220
+ }
221
+ const generatedPath = path.join(event.context.workspaceDir, FORGE_SESSION_BOOTSTRAP_PATH);
222
+ const existingFiles = event.context.bootstrapFiles.filter((file) => file.path !== generatedPath);
223
+ event.context.bootstrapFiles = [
224
+ ...existingFiles,
225
+ {
226
+ name: "BOOTSTRAP.md",
227
+ path: generatedPath,
228
+ content,
229
+ missing: false
230
+ }
231
+ ];
232
+ }
233
+ catch (error) {
234
+ api.logger?.warn?.(`Forge session bootstrap hook could not load live Forge context: ${error instanceof Error ? error.message : String(error)}`);
235
+ }
236
+ }, {
237
+ name: FORGE_SESSION_BOOTSTRAP_NAME,
238
+ description: "Inject a live Forge workspace summary and People wiki snapshots into agent bootstrap context."
239
+ });
240
+ }
@@ -34,6 +34,11 @@ const scopedReadSchema = Type.Object({
34
34
  const optionalString = () => Type.Optional(Type.String());
35
35
  const optionalNullableString = () => Type.Optional(Type.Union([Type.String(), Type.Null()]));
36
36
  const optionalDeleteMode = () => Type.Optional(Type.Union([Type.Literal("soft"), Type.Literal("hard")]));
37
+ const optionalBoolean = () => Type.Optional(Type.Boolean());
38
+ const optionalInteger = (minimum, maximum) => Type.Optional(Type.Integer({
39
+ ...(typeof minimum === "number" ? { minimum } : {}),
40
+ ...(typeof maximum === "number" ? { maximum } : {})
41
+ }));
37
42
  const healthLinkInputSchema = () => Type.Object({
38
43
  entityType: Type.String({ minLength: 1 }),
39
44
  entityId: Type.String({ minLength: 1 }),
@@ -465,6 +470,277 @@ export function registerForgePluginTools(api, config) {
465
470
  }));
466
471
  }
467
472
  });
473
+ registerReadTool(api, config, {
474
+ name: "forge_get_preferences_workspace",
475
+ label: "Forge Preferences Workspace",
476
+ description: "Read Forge's current preference model for one user and domain, including the summary-first landing view, next comparison pair, concept libraries, map, table, and history.",
477
+ parameters: Type.Object({
478
+ userId: optionalString(),
479
+ domain: optionalString(),
480
+ contextId: optionalString()
481
+ }),
482
+ path: (params) => withQueryParams("/api/v1/preferences/workspace", params, [
483
+ "userId",
484
+ "domain",
485
+ "contextId"
486
+ ])
487
+ });
488
+ registerWriteTool(api, config, {
489
+ name: "forge_start_preferences_game",
490
+ label: "Forge Start Preferences Game",
491
+ description: "Start the Forge comparison game for one preference domain or context and return the next pair of items to compare.",
492
+ parameters: Type.Object({
493
+ userId: Type.String({ minLength: 1 }),
494
+ domain: Type.String({ minLength: 1 }),
495
+ contextId: optionalString()
496
+ }),
497
+ method: "POST",
498
+ path: "/api/v1/preferences/game/start"
499
+ });
500
+ registerWriteTool(api, config, {
501
+ name: "forge_merge_preferences_contexts",
502
+ label: "Forge Merge Preferences Contexts",
503
+ description: "Merge one or more preference contexts into a target context.",
504
+ parameters: Type.Object({
505
+ targetContextId: Type.String({ minLength: 1 }),
506
+ sourceContextIds: Type.Array(Type.String({ minLength: 1 }))
507
+ }),
508
+ method: "POST",
509
+ path: "/api/v1/preferences/contexts/merge"
510
+ });
511
+ registerWriteTool(api, config, {
512
+ name: "forge_enqueue_preferences_item_from_entity",
513
+ label: "Forge Enqueue Preferences Item From Entity",
514
+ description: "Queue an existing Forge entity into a preference domain so it can appear in the comparison game.",
515
+ parameters: Type.Object({
516
+ userId: Type.String({ minLength: 1 }),
517
+ domain: Type.String({ minLength: 1 }),
518
+ entityType: Type.String({ minLength: 1 }),
519
+ entityId: Type.String({ minLength: 1 })
520
+ }),
521
+ method: "POST",
522
+ path: "/api/v1/preferences/items/from-entity"
523
+ });
524
+ registerWriteTool(api, config, {
525
+ name: "forge_submit_preferences_judgment",
526
+ label: "Forge Submit Preferences Judgment",
527
+ description: "Record one pairwise preference outcome such as left, right, tie, or skip.",
528
+ parameters: Type.Object({
529
+ profileId: Type.String({ minLength: 1 }),
530
+ contextId: Type.String({ minLength: 1 }),
531
+ userId: Type.String({ minLength: 1 }),
532
+ leftItemId: Type.String({ minLength: 1 }),
533
+ rightItemId: Type.String({ minLength: 1 }),
534
+ outcome: Type.String({ minLength: 1 }),
535
+ strength: Type.Optional(Type.Number({ minimum: 0.5, maximum: 2 })),
536
+ responseTimeMs: Type.Optional(Type.Union([Type.Integer({ minimum: 0 }), Type.Null()])),
537
+ source: optionalString(),
538
+ reasonTags: Type.Optional(Type.Array(Type.String()))
539
+ }),
540
+ method: "POST",
541
+ path: "/api/v1/preferences/judgments"
542
+ });
543
+ registerWriteTool(api, config, {
544
+ name: "forge_submit_preferences_signal",
545
+ label: "Forge Submit Preferences Signal",
546
+ description: "Record a direct non-pairwise preference signal such as favorite, veto, must-have, bookmark, neutral, or compare-later.",
547
+ parameters: Type.Object({
548
+ profileId: Type.String({ minLength: 1 }),
549
+ contextId: Type.String({ minLength: 1 }),
550
+ userId: Type.String({ minLength: 1 }),
551
+ itemId: Type.String({ minLength: 1 }),
552
+ signalType: Type.String({ minLength: 1 }),
553
+ strength: Type.Optional(Type.Number({ minimum: 0.5, maximum: 2 })),
554
+ source: optionalString()
555
+ }),
556
+ method: "POST",
557
+ path: "/api/v1/preferences/signals"
558
+ });
559
+ api.registerTool({
560
+ name: "forge_update_preferences_score",
561
+ label: "Forge Update Preferences Score",
562
+ description: "Override or protect the inferred state of one preference item when the user wants explicit correction.",
563
+ parameters: Type.Object({
564
+ itemId: Type.String({ minLength: 1 }),
565
+ manualStatus: optionalNullableString(),
566
+ manualScore: Type.Optional(Type.Union([Type.Number(), Type.Null()])),
567
+ confidenceLock: Type.Optional(Type.Union([Type.Number({ minimum: 0, maximum: 1 }), Type.Null()])),
568
+ bookmarked: Type.Optional(Type.Boolean()),
569
+ compareLater: Type.Optional(Type.Boolean()),
570
+ frozen: Type.Optional(Type.Boolean())
571
+ }),
572
+ async execute(_toolCallId, params) {
573
+ const typed = params;
574
+ return jsonResult(await runWrite(config, {
575
+ method: "PATCH",
576
+ path: `/api/v1/preferences/items/${typed.itemId}/score`,
577
+ body: {
578
+ manualStatus: typed.manualStatus,
579
+ manualScore: typed.manualScore,
580
+ confidenceLock: typed.confidenceLock,
581
+ bookmarked: typed.bookmarked,
582
+ compareLater: typed.compareLater,
583
+ frozen: typed.frozen
584
+ }
585
+ }));
586
+ }
587
+ });
588
+ registerReadTool(api, config, {
589
+ name: "forge_list_questionnaires",
590
+ label: "Forge List Questionnaires",
591
+ description: "List the Psyche questionnaire library across the selected user scope.",
592
+ parameters: scopedReadSchema,
593
+ path: (params) => withUserIds("/api/v1/psyche/questionnaires", params.userIds)
594
+ });
595
+ api.registerTool({
596
+ name: "forge_get_questionnaire",
597
+ label: "Forge Get Questionnaire",
598
+ description: "Read one Psyche questionnaire instrument with versions and scoring detail.",
599
+ parameters: Type.Object({
600
+ questionnaireId: Type.String({ minLength: 1 }),
601
+ userIds: Type.Optional(Type.Array(Type.String()))
602
+ }),
603
+ async execute(_toolCallId, params) {
604
+ const typed = params;
605
+ return jsonResult(await runRead(config, withQueryParams(`/api/v1/psyche/questionnaires/${typed.questionnaireId}`, typed, ["userIds"])));
606
+ }
607
+ });
608
+ api.registerTool({
609
+ name: "forge_clone_questionnaire",
610
+ label: "Forge Clone Questionnaire",
611
+ description: "Clone one Psyche questionnaire instrument into a new user-owned copy.",
612
+ parameters: Type.Object({
613
+ questionnaireId: Type.String({ minLength: 1 }),
614
+ userId: optionalNullableString()
615
+ }),
616
+ async execute(_toolCallId, params) {
617
+ const typed = params;
618
+ return jsonResult(await runWrite(config, {
619
+ method: "POST",
620
+ path: `/api/v1/psyche/questionnaires/${typed.questionnaireId}/clone`,
621
+ body: { userId: typed.userId }
622
+ }));
623
+ }
624
+ });
625
+ api.registerTool({
626
+ name: "forge_ensure_questionnaire_draft",
627
+ label: "Forge Ensure Questionnaire Draft",
628
+ description: "Create or return the editable draft version for one questionnaire instrument.",
629
+ parameters: Type.Object({
630
+ questionnaireId: Type.String({ minLength: 1 })
631
+ }),
632
+ async execute(_toolCallId, params) {
633
+ const typed = params;
634
+ return jsonResult(await runWrite(config, {
635
+ method: "POST",
636
+ path: `/api/v1/psyche/questionnaires/${typed.questionnaireId}/draft`
637
+ }));
638
+ }
639
+ });
640
+ api.registerTool({
641
+ name: "forge_publish_questionnaire_draft",
642
+ label: "Forge Publish Questionnaire Draft",
643
+ description: "Publish the current questionnaire draft as the live readable version.",
644
+ parameters: Type.Object({
645
+ questionnaireId: Type.String({ minLength: 1 }),
646
+ label: optionalString()
647
+ }),
648
+ async execute(_toolCallId, params) {
649
+ const typed = params;
650
+ return jsonResult(await runWrite(config, {
651
+ method: "POST",
652
+ path: `/api/v1/psyche/questionnaires/${typed.questionnaireId}/publish`,
653
+ body: { label: typed.label }
654
+ }));
655
+ }
656
+ });
657
+ api.registerTool({
658
+ name: "forge_start_questionnaire_run",
659
+ label: "Forge Start Questionnaire Run",
660
+ description: "Start one questionnaire answer session for a specific user.",
661
+ parameters: Type.Object({
662
+ questionnaireId: Type.String({ minLength: 1 }),
663
+ userId: Type.String({ minLength: 1 }),
664
+ author: optionalString()
665
+ }),
666
+ async execute(_toolCallId, params) {
667
+ const typed = params;
668
+ return jsonResult(await runWrite(config, {
669
+ method: "POST",
670
+ path: `/api/v1/psyche/questionnaires/${typed.questionnaireId}/runs`,
671
+ body: {
672
+ userId: typed.userId,
673
+ author: typed.author
674
+ }
675
+ }));
676
+ }
677
+ });
678
+ api.registerTool({
679
+ name: "forge_get_questionnaire_run",
680
+ label: "Forge Get Questionnaire Run",
681
+ description: "Read one questionnaire run with answers, scores, and completion state.",
682
+ parameters: Type.Object({
683
+ runId: Type.String({ minLength: 1 }),
684
+ userIds: Type.Optional(Type.Array(Type.String()))
685
+ }),
686
+ async execute(_toolCallId, params) {
687
+ const typed = params;
688
+ return jsonResult(await runRead(config, withQueryParams(`/api/v1/psyche/questionnaire-runs/${typed.runId}`, typed, ["userIds"])));
689
+ }
690
+ });
691
+ api.registerTool({
692
+ name: "forge_update_questionnaire_run",
693
+ label: "Forge Update Questionnaire Run",
694
+ description: "Patch one questionnaire run while the answers are still being filled.",
695
+ parameters: Type.Object({
696
+ runId: Type.String({ minLength: 1 }),
697
+ answers: Type.Optional(Type.Record(Type.String(), Type.Any())),
698
+ status: optionalString(),
699
+ notes: optionalString()
700
+ }),
701
+ async execute(_toolCallId, params) {
702
+ const typed = params;
703
+ return jsonResult(await runWrite(config, {
704
+ method: "PATCH",
705
+ path: `/api/v1/psyche/questionnaire-runs/${typed.runId}`,
706
+ body: {
707
+ answers: typed.answers,
708
+ status: typed.status,
709
+ notes: typed.notes
710
+ }
711
+ }));
712
+ }
713
+ });
714
+ api.registerTool({
715
+ name: "forge_complete_questionnaire_run",
716
+ label: "Forge Complete Questionnaire Run",
717
+ description: "Complete one questionnaire run and finalize its scoring pass.",
718
+ parameters: Type.Object({
719
+ runId: Type.String({ minLength: 1 })
720
+ }),
721
+ async execute(_toolCallId, params) {
722
+ const typed = params;
723
+ return jsonResult(await runWrite(config, {
724
+ method: "POST",
725
+ path: `/api/v1/psyche/questionnaire-runs/${typed.runId}/complete`
726
+ }));
727
+ }
728
+ });
729
+ registerReadTool(api, config, {
730
+ name: "forge_get_self_observation_calendar",
731
+ label: "Forge Self Observation Calendar",
732
+ description: "Read the Psyche self-observation calendar with note-backed observations, linked patterns, linked reports, and available tags.",
733
+ parameters: Type.Object({
734
+ from: optionalString(),
735
+ to: optionalString(),
736
+ userIds: Type.Optional(Type.Array(Type.String()))
737
+ }),
738
+ path: (params) => withQueryParams("/api/v1/psyche/self-observation/calendar", params, [
739
+ "from",
740
+ "to",
741
+ "userIds"
742
+ ])
743
+ });
468
744
  registerWriteTool(api, config, {
469
745
  name: "forge_search_entities",
470
746
  label: "Search Forge Entities",
@@ -491,7 +767,7 @@ export function registerForgePluginTools(api, config) {
491
767
  registerWriteTool(api, config, {
492
768
  name: "forge_create_entities",
493
769
  label: "Create Forge Entities",
494
- description: "Create one or more Forge entities through the ordered batch workflow. Pass `operations` as an array. Each operation must include `entityType` and full `data`. This is the preferred create path for planning, Psyche, and calendar records including calendar_event, work_block_template, and task_timebox.",
770
+ description: "Create one or more Forge entities through the ordered batch workflow. Pass `operations` as an array. Each operation must include `entityType` and full `data`. This is the preferred create path for planning, Psyche, calendar, preferences basic CRUD, and questionnaire_instrument records.",
495
771
  parameters: Type.Object({
496
772
  atomic: Type.Optional(Type.Boolean()),
497
773
  operations: Type.Array(Type.Object({
@@ -506,7 +782,7 @@ export function registerForgePluginTools(api, config) {
506
782
  registerWriteTool(api, config, {
507
783
  name: "forge_update_entities",
508
784
  label: "Update Forge Entities",
509
- description: "Update one or more Forge entities through the ordered batch workflow. Pass `operations` as an array. Each operation must include `entityType`, `id`, and `patch`. This is the preferred update path for calendar_event, work_block_template, and task_timebox too; Forge runs calendar sync side effects downstream.",
785
+ description: "Update one or more Forge entities through the ordered batch workflow. Pass `operations` as an array. Each operation must include `entityType`, `id`, and `patch`. This is the preferred update path for calendar_event, work_block_template, task_timebox, preferences basic CRUD entities, and questionnaire_instrument too.",
510
786
  parameters: Type.Object({
511
787
  atomic: Type.Optional(Type.Boolean()),
512
788
  operations: Type.Array(Type.Object({
@@ -522,7 +798,7 @@ export function registerForgePluginTools(api, config) {
522
798
  registerWriteTool(api, config, {
523
799
  name: "forge_delete_entities",
524
800
  label: "Delete Forge Entities",
525
- description: "Delete Forge entities in one batch request. Pass `operations` as an array with `entityType` and `id`. Delete defaults to soft mode unless hard is requested explicitly. Calendar-domain deletes still run their downstream removal logic, including remote calendar projection cleanup for calendar_event.",
801
+ description: "Delete Forge entities in one batch request. Pass `operations` as an array with `entityType` and `id`. Delete defaults to soft mode unless hard is requested explicitly. Some entities such as calendar-domain records, preference CRUD entities, and questionnaire_instrument delete immediately by design.",
526
802
  parameters: Type.Object({
527
803
  atomic: Type.Optional(Type.Boolean()),
528
804
  operations: Type.Array(Type.Object({
@@ -789,9 +1065,6 @@ export function registerForgePluginTools(api, config) {
789
1065
  ]),
790
1066
  label: Type.String({ minLength: 1 }),
791
1067
  username: optionalString(),
792
- clientId: optionalString(),
793
- clientSecret: optionalString(),
794
- refreshToken: optionalString(),
795
1068
  password: optionalString(),
796
1069
  serverUrl: optionalString(),
797
1070
  authSessionId: optionalString(),