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,216 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { getDatabase } from "../db.js";
3
+ import { aiModelConnectionSchema, upsertAiModelConnectionSchema } from "../types.js";
4
+ import { deleteEncryptedSecret, readEncryptedSecret, storeEncryptedSecret } from "./calendar.js";
5
+ import { upsertWikiLlmProfile } from "./wiki-memory.js";
6
+ export const DEFAULT_OPENAI_BASE_URL = "https://api.openai.com/v1";
7
+ export const DEFAULT_OPENAI_CODEX_BASE_URL = "https://chatgpt.com/backend-api";
8
+ export const FORGE_MANAGED_WIKI_PROFILE_ID = "wiki_llm_forge_managed";
9
+ export const FORGE_DEFAULT_AGENT_ID = "agt_forge_default";
10
+ function parseMetadata(value) {
11
+ try {
12
+ const parsed = JSON.parse(value);
13
+ return parsed && typeof parsed === "object" ? parsed : {};
14
+ }
15
+ catch {
16
+ return {};
17
+ }
18
+ }
19
+ function defaultAuthMode(provider) {
20
+ return provider === "openai-codex" ? "oauth" : "api_key";
21
+ }
22
+ export function defaultBaseUrlForProvider(provider) {
23
+ if (provider === "openai-codex") {
24
+ return DEFAULT_OPENAI_CODEX_BASE_URL;
25
+ }
26
+ if (provider === "openai-compatible") {
27
+ return "http://127.0.0.1:11434/v1";
28
+ }
29
+ return DEFAULT_OPENAI_BASE_URL;
30
+ }
31
+ function buildConnectionAgentId(connectionId) {
32
+ return `agt_model_${connectionId.replace(/[^a-zA-Z0-9]+/g, "_")}`;
33
+ }
34
+ export function buildConnectionAgentIdentity(connection) {
35
+ const detail = connection.provider === "openai-codex"
36
+ ? "Chat agent backed by OpenAI Codex OAuth."
37
+ : connection.provider === "openai-compatible"
38
+ ? "Chat agent backed by a local or OpenAI-compatible endpoint."
39
+ : "Chat agent backed by the OpenAI API.";
40
+ return {
41
+ id: connection.agentId,
42
+ label: connection.agentLabel,
43
+ agentType: connection.provider,
44
+ trustLevel: "trusted",
45
+ autonomyMode: "approval_required",
46
+ approvalMode: "approval_by_default",
47
+ description: detail,
48
+ tokenCount: 0,
49
+ activeTokenCount: 0,
50
+ createdAt: connection.createdAt,
51
+ updatedAt: connection.updatedAt
52
+ };
53
+ }
54
+ function mapConnection(row) {
55
+ const hasStoredCredential = Boolean(row.secret_id) && Boolean(readEncryptedSecret(row.secret_id));
56
+ return aiModelConnectionSchema.parse({
57
+ id: row.id,
58
+ label: row.label,
59
+ provider: row.provider,
60
+ authMode: row.auth_mode,
61
+ baseUrl: row.base_url,
62
+ model: row.model,
63
+ accountLabel: row.account_label,
64
+ enabled: row.enabled === 1,
65
+ status: hasStoredCredential ? "connected" : "needs_attention",
66
+ hasStoredCredential,
67
+ usesOAuth: row.auth_mode === "oauth",
68
+ supportsCustomBaseUrl: row.provider !== "openai-codex",
69
+ agentId: buildConnectionAgentId(row.id),
70
+ agentLabel: `${row.label} agent`,
71
+ createdAt: row.created_at,
72
+ updatedAt: row.updated_at
73
+ });
74
+ }
75
+ export function listAiModelConnections() {
76
+ const rows = getDatabase()
77
+ .prepare(`SELECT id, label, provider, auth_mode, base_url, model, account_label, secret_id, enabled, metadata_json, created_at, updated_at
78
+ FROM ai_model_connections
79
+ ORDER BY created_at DESC`)
80
+ .all();
81
+ return rows.map(mapConnection);
82
+ }
83
+ export function getAiModelConnectionById(connectionId) {
84
+ const row = getDatabase()
85
+ .prepare(`SELECT id, label, provider, auth_mode, base_url, model, account_label, secret_id, enabled, metadata_json, created_at, updated_at
86
+ FROM ai_model_connections
87
+ WHERE id = ?`)
88
+ .get(connectionId);
89
+ return row ? mapConnection(row) : null;
90
+ }
91
+ export function readModelConnectionCredential(connectionId, secrets) {
92
+ const row = getDatabase()
93
+ .prepare(`SELECT secret_id
94
+ FROM ai_model_connections
95
+ WHERE id = ?`)
96
+ .get(connectionId);
97
+ if (!row?.secret_id) {
98
+ return null;
99
+ }
100
+ const cipherText = readEncryptedSecret(row.secret_id);
101
+ if (!cipherText) {
102
+ return null;
103
+ }
104
+ return secrets.openJson(cipherText);
105
+ }
106
+ export function upsertAiModelConnection(input, secrets, options = {}) {
107
+ const parsed = upsertAiModelConnectionSchema.parse(input);
108
+ const existing = parsed.id?.trim()
109
+ ? getDatabase()
110
+ .prepare(`SELECT id, label, provider, auth_mode, base_url, model, account_label, secret_id, enabled, metadata_json, created_at, updated_at
111
+ FROM ai_model_connections
112
+ WHERE id = ?`)
113
+ .get(parsed.id.trim())
114
+ : undefined;
115
+ const id = existing?.id ??
116
+ `mdl_${randomUUID().replaceAll("-", "").slice(0, 10)}`;
117
+ const now = new Date().toISOString();
118
+ const provider = parsed.provider;
119
+ const authMode = parsed.authMode ?? defaultAuthMode(provider);
120
+ const baseUrl = parsed.baseUrl?.trim() ||
121
+ existing?.base_url ||
122
+ defaultBaseUrlForProvider(provider);
123
+ let secretId = existing?.secret_id ?? null;
124
+ let accountLabel = existing?.account_label ?? null;
125
+ if (parsed.apiKey?.trim()) {
126
+ secretId =
127
+ secretId ?? `mdl_secret_${randomUUID().replaceAll("-", "").slice(0, 10)}`;
128
+ storeEncryptedSecret(secretId, secrets.sealJson({
129
+ kind: "api_key",
130
+ provider,
131
+ apiKey: parsed.apiKey.trim()
132
+ }), `${parsed.label} AI connection`);
133
+ }
134
+ else if (options.oauthCredential) {
135
+ secretId =
136
+ secretId ?? `mdl_secret_${randomUUID().replaceAll("-", "").slice(0, 10)}`;
137
+ accountLabel = options.oauthCredential.accountId;
138
+ storeEncryptedSecret(secretId, secrets.sealJson(options.oauthCredential), `${parsed.label} AI OAuth connection`);
139
+ }
140
+ getDatabase()
141
+ .prepare(`INSERT INTO ai_model_connections (
142
+ id, label, provider, auth_mode, base_url, model, account_label, secret_id, enabled, metadata_json, created_at, updated_at
143
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
144
+ ON CONFLICT(id) DO UPDATE SET
145
+ label = excluded.label,
146
+ provider = excluded.provider,
147
+ auth_mode = excluded.auth_mode,
148
+ base_url = excluded.base_url,
149
+ model = excluded.model,
150
+ account_label = excluded.account_label,
151
+ secret_id = excluded.secret_id,
152
+ enabled = excluded.enabled,
153
+ metadata_json = excluded.metadata_json,
154
+ updated_at = excluded.updated_at`)
155
+ .run(id, parsed.label, provider, authMode, baseUrl, parsed.model.trim(), accountLabel, secretId, parsed.enabled ? 1 : 0, JSON.stringify(parseMetadata(existing?.metadata_json ?? "{}")), existing?.created_at ?? now, now);
156
+ return getAiModelConnectionById(id);
157
+ }
158
+ export function deleteAiModelConnection(connectionId, secrets) {
159
+ const row = getDatabase()
160
+ .prepare(`SELECT id, secret_id
161
+ FROM ai_model_connections
162
+ WHERE id = ?`)
163
+ .get(connectionId);
164
+ if (!row) {
165
+ return null;
166
+ }
167
+ getDatabase()
168
+ .prepare(`DELETE FROM ai_model_connections WHERE id = ?`)
169
+ .run(connectionId);
170
+ if (row.secret_id) {
171
+ deleteEncryptedSecret(row.secret_id);
172
+ }
173
+ getDatabase()
174
+ .prepare(`UPDATE app_settings
175
+ SET forge_basic_chat_connection_id = CASE WHEN forge_basic_chat_connection_id = ? THEN '' ELSE forge_basic_chat_connection_id END,
176
+ forge_wiki_connection_id = CASE WHEN forge_wiki_connection_id = ? THEN '' ELSE forge_wiki_connection_id END,
177
+ updated_at = ?
178
+ WHERE id = 1`)
179
+ .run(connectionId, connectionId, new Date().toISOString());
180
+ syncForgeManagedWikiProfile(secrets);
181
+ return row.id;
182
+ }
183
+ export function syncForgeManagedWikiProfile(secrets) {
184
+ const settings = getDatabase()
185
+ .prepare(`SELECT forge_wiki_connection_id, forge_wiki_model
186
+ FROM app_settings
187
+ WHERE id = 1`)
188
+ .get();
189
+ const connectionId = settings?.forge_wiki_connection_id?.trim() ?? "";
190
+ const fallbackModel = settings?.forge_wiki_model?.trim() || "gpt-5.4-mini";
191
+ const connection = connectionId ? getAiModelConnectionById(connectionId) : null;
192
+ const row = connectionId
193
+ ? getDatabase()
194
+ .prepare(`SELECT secret_id
195
+ FROM ai_model_connections
196
+ WHERE id = ?`)
197
+ .get(connectionId)
198
+ : undefined;
199
+ upsertWikiLlmProfile({
200
+ id: FORGE_MANAGED_WIKI_PROFILE_ID,
201
+ label: "Forge wiki ingest",
202
+ provider: connection?.provider === "openai-compatible"
203
+ ? "openai-compatible"
204
+ : connection?.provider === "openai-codex"
205
+ ? "openai-codex"
206
+ : "openai-responses",
207
+ baseUrl: connection?.baseUrl ?? DEFAULT_OPENAI_BASE_URL,
208
+ model: connection?.model ?? fallbackModel,
209
+ secretId: row?.secret_id ?? null,
210
+ enabled: true,
211
+ metadata: {
212
+ managedBySettings: true,
213
+ connectionId: connection?.id ?? null
214
+ }
215
+ }, secrets);
216
+ }
@@ -80,6 +80,22 @@ function noteMatchesTextTerm(note, term) {
80
80
  }
81
81
  return note.tags.some((tag) => tag.toLowerCase().includes(normalized));
82
82
  }
83
+ function filterNotesByOwnerIds(notes, userIds) {
84
+ if (!userIds || userIds.length === 0) {
85
+ return notes;
86
+ }
87
+ const allowed = new Set(userIds);
88
+ return notes.filter((note) => note.userId !== null && allowed.has(note.userId));
89
+ }
90
+ export function resolveNoteObservedAt(note) {
91
+ const observedAt = typeof note.frontmatter.observedAt === "string"
92
+ ? note.frontmatter.observedAt.trim()
93
+ : "";
94
+ if (observedAt.length > 0 && !Number.isNaN(Date.parse(observedAt))) {
95
+ return new Date(observedAt).toISOString();
96
+ }
97
+ return note.createdAt;
98
+ }
83
99
  function cleanupExpiredNotes() {
84
100
  const expiredRows = getDatabase()
85
101
  .prepare(`SELECT id
@@ -198,6 +214,16 @@ function listAllNoteRows() {
198
214
  ORDER BY created_at DESC`)
199
215
  .all();
200
216
  }
217
+ function listActiveNotes() {
218
+ const rows = listAllNoteRows();
219
+ const linksByNoteId = new Map();
220
+ for (const link of listLinkRowsForNotes(rows.map((row) => row.id))) {
221
+ const current = linksByNoteId.get(link.note_id) ?? [];
222
+ current.push(link);
223
+ linksByNoteId.set(link.note_id, current);
224
+ }
225
+ return filterDeletedEntities("note", rows.map((row) => mapNote(row, linksByNoteId.get(row.id) ?? [])));
226
+ }
201
227
  function findMatchingNoteIds(query) {
202
228
  const ftsQuery = buildFtsQuery(query);
203
229
  if (!ftsQuery) {
@@ -313,23 +339,17 @@ export function listNotes(query = {}) {
313
339
  ...(parsed.query ? [parsed.query] : []),
314
340
  ...parsed.textTerms
315
341
  ]);
316
- const rows = listAllNoteRows();
317
- const linksByNoteId = new Map();
318
- for (const link of listLinkRowsForNotes(rows.map((row) => row.id))) {
319
- const current = linksByNoteId.get(link.note_id) ?? [];
320
- current.push(link);
321
- linksByNoteId.set(link.note_id, current);
322
- }
323
- return filterDeletedEntities("note", rows
324
- .filter((row) => parsed.kind ? row.kind === parsed.kind : true)
325
- .filter((row) => parsed.spaceId ? row.space_id === parsed.spaceId : true)
326
- .filter((row) => parsed.slug ? row.slug.toLowerCase() === parsed.slug.toLowerCase() : true)
327
- .filter((row) => parsed.author
328
- ? (row.author ?? "")
342
+ return filterNotesByOwnerIds(listActiveNotes()
343
+ .filter((note) => parsed.kind ? note.kind === parsed.kind : true)
344
+ .filter((note) => parsed.spaceId ? note.spaceId === parsed.spaceId : true)
345
+ .filter((note) => parsed.slug
346
+ ? note.slug.toLowerCase() === parsed.slug.toLowerCase()
347
+ : true)
348
+ .filter((note) => parsed.author
349
+ ? (note.author ?? "")
329
350
  .toLowerCase()
330
351
  .includes(parsed.author.toLowerCase())
331
352
  : true)
332
- .map((row) => mapNote(row, linksByNoteId.get(row.id) ?? []))
333
353
  .filter((note) => {
334
354
  if (!matchingIds) {
335
355
  return true;
@@ -361,7 +381,29 @@ export function listNotes(query = {}) {
361
381
  }
362
382
  return true;
363
383
  })
364
- .slice(0, parsed.limit ?? 100));
384
+ .slice(0, parsed.limit ?? 100), parsed.userIds);
385
+ }
386
+ export function listNotesByObservedAtRange({ from, to, userIds, limit = 400 }) {
387
+ cleanupExpiredNotes();
388
+ const fromMs = Date.parse(from);
389
+ const toMs = Date.parse(to);
390
+ if (Number.isNaN(fromMs) || Number.isNaN(toMs)) {
391
+ return [];
392
+ }
393
+ return filterNotesByOwnerIds(listActiveNotes(), userIds)
394
+ .map((note) => ({
395
+ note,
396
+ observedAt: resolveNoteObservedAt(note)
397
+ }))
398
+ .filter(({ observedAt }) => {
399
+ const observedAtMs = Date.parse(observedAt);
400
+ return (!Number.isNaN(observedAtMs) &&
401
+ observedAtMs >= fromMs &&
402
+ observedAtMs < toMs);
403
+ })
404
+ .sort((left, right) => left.observedAt.localeCompare(right.observedAt))
405
+ .slice(0, limit)
406
+ .map(({ note }) => note);
365
407
  }
366
408
  export function createNote(input, context) {
367
409
  cleanupExpiredNotes();
@@ -444,6 +444,53 @@ function listStoredScores(contextId) {
444
444
  WHERE context_id = ?`)
445
445
  .all(contextId);
446
446
  }
447
+ export function listPreferenceContexts() {
448
+ return getDatabase()
449
+ .prepare(`SELECT id, profile_id, name, description, share_mode, active, is_default, decay_days, created_at, updated_at
450
+ FROM preference_contexts
451
+ ORDER BY created_at ASC`)
452
+ .all().map(mapContext);
453
+ }
454
+ export function getPreferenceContextById(contextId) {
455
+ return readContext(contextId) ?? undefined;
456
+ }
457
+ export function listPreferenceItems() {
458
+ return getDatabase()
459
+ .prepare(`SELECT id, profile_id, label, description, tags_json, feature_weights_json, source_entity_type, source_entity_id, metadata_json, created_at, updated_at
460
+ FROM preference_items
461
+ ORDER BY created_at ASC`)
462
+ .all().map(mapItem);
463
+ }
464
+ export function getPreferenceItemById(itemId) {
465
+ return getItemById(itemId) ?? undefined;
466
+ }
467
+ export function listPreferenceCatalogs() {
468
+ return getDatabase()
469
+ .prepare(`SELECT id, profile_id, domain, slug, title, description, source, archived, created_at, updated_at
470
+ FROM preference_catalogs
471
+ WHERE archived = 0
472
+ ORDER BY created_at ASC`)
473
+ .all()
474
+ .filter((row) => row.archived === 0)
475
+ .map((row) => readCatalog(row.id))
476
+ .filter((catalog) => catalog !== null);
477
+ }
478
+ export function getPreferenceCatalogById(catalogId) {
479
+ return readCatalog(catalogId) ?? undefined;
480
+ }
481
+ export function listPreferenceCatalogItems() {
482
+ return getDatabase()
483
+ .prepare(`SELECT id, catalog_id, label, description, tags_json, feature_weights_json, position, archived, created_at, updated_at
484
+ FROM preference_catalog_items
485
+ WHERE archived = 0
486
+ ORDER BY catalog_id ASC, position ASC, created_at ASC`)
487
+ .all()
488
+ .filter((row) => row.archived === 0)
489
+ .map(mapCatalogItem);
490
+ }
491
+ export function getPreferenceCatalogItemById(catalogItemId) {
492
+ return readCatalogItem(catalogItemId) ?? undefined;
493
+ }
447
494
  function listStoredDimensions(contextId) {
448
495
  return getDatabase()
449
496
  .prepare(`SELECT id, profile_id, context_id, dimension_id, leaning, confidence, movement, context_sensitivity, evidence_count, updated_at
@@ -1535,6 +1582,55 @@ export function updatePreferenceContext(contextId, patch) {
1535
1582
  }
1536
1583
  return updated;
1537
1584
  }
1585
+ export function deletePreferenceContext(contextId) {
1586
+ const current = readContext(contextId);
1587
+ if (!current) {
1588
+ throw new HttpError(404, "preferences_context_not_found", `Preference context ${contextId} was not found.`);
1589
+ }
1590
+ const remainingContexts = listContexts(current.profileId).filter((entry) => entry.id !== contextId);
1591
+ if (remainingContexts.length === 0) {
1592
+ throw new HttpError(400, "preferences_context_last_remaining", "A preference profile must keep at least one context.");
1593
+ }
1594
+ const replacementDefault = remainingContexts.find((entry) => entry.isDefault) ?? remainingContexts[0];
1595
+ const timestamp = nowIso();
1596
+ runInTransaction(() => {
1597
+ getDatabase()
1598
+ .prepare(`DELETE FROM pairwise_judgments WHERE context_id = ?`)
1599
+ .run(contextId);
1600
+ getDatabase()
1601
+ .prepare(`DELETE FROM absolute_signals WHERE context_id = ?`)
1602
+ .run(contextId);
1603
+ getDatabase()
1604
+ .prepare(`DELETE FROM preference_item_scores WHERE context_id = ?`)
1605
+ .run(contextId);
1606
+ getDatabase()
1607
+ .prepare(`DELETE FROM preference_dimension_summaries WHERE context_id = ?`)
1608
+ .run(contextId);
1609
+ getDatabase()
1610
+ .prepare(`DELETE FROM preference_snapshots WHERE context_id = ?`)
1611
+ .run(contextId);
1612
+ getDatabase()
1613
+ .prepare(`DELETE FROM preference_contexts WHERE id = ?`)
1614
+ .run(contextId);
1615
+ if (current.isDefault) {
1616
+ getDatabase()
1617
+ .prepare(`UPDATE preference_contexts
1618
+ SET is_default = CASE WHEN id = ? THEN 1 ELSE 0 END, updated_at = ?
1619
+ WHERE profile_id = ?`)
1620
+ .run(replacementDefault.id, timestamp, current.profileId);
1621
+ getDatabase()
1622
+ .prepare(`UPDATE preference_profiles
1623
+ SET default_context_id = ?, updated_at = ?
1624
+ WHERE id = ?`)
1625
+ .run(replacementDefault.id, timestamp, current.profileId);
1626
+ }
1627
+ });
1628
+ const profile = readProfileById(current.profileId);
1629
+ if (profile) {
1630
+ recomputeContext(profile, replacementDefault);
1631
+ }
1632
+ return current;
1633
+ }
1538
1634
  export function mergePreferenceContexts(input) {
1539
1635
  const parsed = mergePreferenceContextsSchema.parse(input);
1540
1636
  const source = readContext(parsed.sourceContextId);
@@ -1661,6 +1757,34 @@ export function updatePreferenceItem(itemId, patch) {
1661
1757
  }
1662
1758
  return updated;
1663
1759
  }
1760
+ export function deletePreferenceItem(itemId) {
1761
+ const current = getItemById(itemId);
1762
+ if (!current) {
1763
+ throw new HttpError(404, "preferences_item_not_found", `Preference item ${itemId} was not found.`);
1764
+ }
1765
+ runInTransaction(() => {
1766
+ getDatabase()
1767
+ .prepare(`DELETE FROM pairwise_judgments
1768
+ WHERE left_item_id = ? OR right_item_id = ?`)
1769
+ .run(itemId, itemId);
1770
+ getDatabase()
1771
+ .prepare(`DELETE FROM absolute_signals WHERE item_id = ?`)
1772
+ .run(itemId);
1773
+ getDatabase()
1774
+ .prepare(`DELETE FROM preference_item_scores WHERE item_id = ?`)
1775
+ .run(itemId);
1776
+ getDatabase()
1777
+ .prepare(`DELETE FROM preference_items WHERE id = ?`)
1778
+ .run(itemId);
1779
+ });
1780
+ const profile = readProfileById(current.profileId);
1781
+ if (profile) {
1782
+ for (const context of listContexts(profile.id).filter((entry) => entry.active)) {
1783
+ recomputeContext(profile, context);
1784
+ }
1785
+ }
1786
+ return current;
1787
+ }
1664
1788
  export function createPreferenceItemFromEntity(input) {
1665
1789
  const parsed = enqueueEntityPreferenceItemSchema.parse(input);
1666
1790
  const profile = ensureProfile(parsed.userId, parsed.domain);