@vellumai/assistant 0.4.43 → 0.4.45

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 (713) hide show
  1. package/.prettierignore +4 -0
  2. package/ARCHITECTURE.md +46 -44
  3. package/README.md +15 -16
  4. package/bun.lock +10 -35
  5. package/docs/architecture/integrations.md +102 -215
  6. package/docs/architecture/keychain-broker.md +1 -1
  7. package/docs/architecture/memory.md +2 -2
  8. package/docs/architecture/scheduling.md +1 -1
  9. package/docs/architecture/security.md +11 -11
  10. package/docs/error-handling.md +1 -1
  11. package/docs/trusted-contact-access.md +3 -3
  12. package/drizzle/meta/0000_snapshot.json +34 -100
  13. package/drizzle/meta/_journal.json +1 -1
  14. package/drizzle.config.ts +4 -4
  15. package/package.json +3 -2
  16. package/scripts/capture-x-graphql.ts +237 -141
  17. package/scripts/generate-bundled-tool-registry.ts +223 -0
  18. package/src/__tests__/access-request-decision.test.ts +0 -1
  19. package/src/__tests__/actor-token-service.test.ts +23 -24
  20. package/src/__tests__/agent-loop.test.ts +0 -131
  21. package/src/__tests__/always-loaded-tools-guard.test.ts +71 -0
  22. package/src/__tests__/amazon-cdp-integration.test.ts +11 -9
  23. package/src/__tests__/approval-primitive.test.ts +0 -1
  24. package/src/__tests__/approval-routes-http.test.ts +11 -3
  25. package/src/__tests__/asset-materialize-tool.test.ts +0 -1
  26. package/src/__tests__/asset-search-tool.test.ts +0 -1
  27. package/src/__tests__/assistant-attachment-directive.test.ts +1 -1
  28. package/src/__tests__/assistant-events-sse-hardening.test.ts +0 -1
  29. package/src/__tests__/assistant-feature-flag-guardrails.test.ts +0 -2
  30. package/src/__tests__/assistant-feature-flags-integration.test.ts +70 -18
  31. package/src/__tests__/assistant-id-boundary-guard.test.ts +6 -6
  32. package/src/__tests__/attachments-store.test.ts +0 -1
  33. package/src/__tests__/avatar-e2e.test.ts +74 -115
  34. package/src/__tests__/avatar-router.test.ts +25 -62
  35. package/src/__tests__/browser-manager.test.ts +24 -0
  36. package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +4 -3
  37. package/src/__tests__/browser-skill-endstate.test.ts +8 -11
  38. package/src/__tests__/btw-routes.test.ts +326 -0
  39. package/src/__tests__/bundled-asset.test.ts +1 -1
  40. package/src/__tests__/bundled-skill-retrieval-guard.test.ts +23 -9
  41. package/src/__tests__/call-controller.test.ts +0 -1
  42. package/src/__tests__/call-conversation-messages.test.ts +0 -1
  43. package/src/__tests__/call-domain.test.ts +0 -1
  44. package/src/__tests__/call-pointer-messages.test.ts +0 -1
  45. package/src/__tests__/call-recovery.test.ts +0 -1
  46. package/src/__tests__/call-routes-http.test.ts +0 -1
  47. package/src/__tests__/call-store.test.ts +0 -1
  48. package/src/__tests__/canonical-guardian-store.test.ts +0 -1
  49. package/src/__tests__/channel-approval-routes.test.ts +1 -1
  50. package/src/__tests__/channel-approvals.test.ts +1 -1
  51. package/src/__tests__/channel-delivery-store.test.ts +0 -1
  52. package/src/__tests__/channel-guardian.test.ts +5 -7
  53. package/src/__tests__/channel-retry-sweep.test.ts +0 -1
  54. package/src/__tests__/checker.test.ts +32 -36
  55. package/src/__tests__/compaction.benchmark.test.ts +16 -14
  56. package/src/__tests__/computer-use-session-lifecycle.test.ts +10 -11
  57. package/src/__tests__/computer-use-session-working-dir.test.ts +2 -6
  58. package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +2 -5
  59. package/src/__tests__/computer-use-tools.test.ts +35 -31
  60. package/src/__tests__/config-schema.test.ts +11 -15
  61. package/src/__tests__/config-watcher.test.ts +0 -1
  62. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -1
  63. package/src/__tests__/conflict-store.test.ts +0 -1
  64. package/src/__tests__/connection-policy.test.ts +4 -7
  65. package/src/__tests__/contacts-tools.test.ts +0 -1
  66. package/src/__tests__/context-memory-e2e.test.ts +2 -4
  67. package/src/__tests__/context-overflow-reducer.test.ts +2 -4
  68. package/src/__tests__/context-window-manager.test.ts +147 -60
  69. package/src/__tests__/contradiction-checker.test.ts +0 -1
  70. package/src/__tests__/conversation-attention-store.test.ts +0 -1
  71. package/src/__tests__/conversation-attention-telegram.test.ts +1 -1
  72. package/src/__tests__/conversation-pairing.test.ts +2 -2
  73. package/src/__tests__/conversation-routes-guardian-reply.test.ts +31 -7
  74. package/src/__tests__/conversation-routes-slash-commands.test.ts +381 -0
  75. package/src/__tests__/conversation-store.test.ts +0 -1
  76. package/src/__tests__/conversation-unread-route.test.ts +1 -2
  77. package/src/__tests__/credential-security-invariants.test.ts +8 -8
  78. package/src/__tests__/cross-provider-web-search.test.ts +353 -0
  79. package/src/__tests__/daemon-assistant-events.test.ts +6 -7
  80. package/src/__tests__/db-schedule-syntax-migration.test.ts +15 -3
  81. package/src/__tests__/delete-managed-skill-tool.test.ts +5 -9
  82. package/src/__tests__/deterministic-verification-control-plane.test.ts +0 -1
  83. package/src/__tests__/diagnostics-export.test.ts +189 -0
  84. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
  85. package/src/__tests__/emit-signal-routing-intent.test.ts +3 -3
  86. package/src/__tests__/entity-extractor.test.ts +0 -1
  87. package/src/__tests__/entity-search.test.ts +0 -1
  88. package/src/__tests__/ephemeral-permissions.test.ts +2 -4
  89. package/src/__tests__/error-handler-friendly-messages.test.ts +46 -0
  90. package/src/__tests__/file-read-tool.test.ts +86 -0
  91. package/src/__tests__/followup-tools.test.ts +0 -1
  92. package/src/__tests__/frontmatter.test.ts +77 -34
  93. package/src/__tests__/gateway-only-enforcement.test.ts +0 -1
  94. package/src/__tests__/gateway-only-guard.test.ts +1 -1
  95. package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -1
  96. package/src/__tests__/guardian-action-followup-executor.test.ts +0 -1
  97. package/src/__tests__/guardian-action-followup-store.test.ts +0 -1
  98. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -1
  99. package/src/__tests__/guardian-action-late-reply.test.ts +0 -1
  100. package/src/__tests__/guardian-action-store.test.ts +0 -1
  101. package/src/__tests__/guardian-action-sweep.test.ts +0 -1
  102. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +0 -1
  103. package/src/__tests__/guardian-dispatch.test.ts +1 -2
  104. package/src/__tests__/guardian-grant-minting.test.ts +1 -1
  105. package/src/__tests__/guardian-outbound-http.test.ts +0 -1
  106. package/src/__tests__/guardian-principal-id-roundtrip.test.ts +0 -1
  107. package/src/__tests__/guardian-routing-invariants.test.ts +1 -1
  108. package/src/__tests__/guardian-routing-state.test.ts +0 -1
  109. package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -1
  110. package/src/__tests__/guardian-verify-setup-skill-regression.test.ts +3 -5
  111. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +28 -426
  112. package/src/__tests__/host-bash-proxy.test.ts +335 -0
  113. package/src/__tests__/host-file-proxy.test.ts +374 -0
  114. package/src/__tests__/host-shell-tool.test.ts +147 -1
  115. package/src/__tests__/http-user-message-parity.test.ts +361 -0
  116. package/src/__tests__/inbound-invite-redemption.test.ts +0 -1
  117. package/src/__tests__/integration-status.test.ts +3 -8
  118. package/src/__tests__/intent-routing.test.ts +7 -46
  119. package/src/__tests__/invite-redemption-service.test.ts +0 -1
  120. package/src/__tests__/invite-routes-http.test.ts +0 -1
  121. package/src/__tests__/llm-usage-store.test.ts +0 -1
  122. package/src/__tests__/managed-avatar-client.test.ts +101 -55
  123. package/src/__tests__/managed-skill-lifecycle.test.ts +9 -18
  124. package/src/__tests__/managed-store.test.ts +94 -21
  125. package/src/__tests__/media-reuse-story.e2e.test.ts +0 -1
  126. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +2 -4
  127. package/src/__tests__/memory-lifecycle-e2e.test.ts +0 -1
  128. package/src/__tests__/memory-recall-quality.test.ts +0 -1
  129. package/src/__tests__/memory-regressions.experimental.test.ts +0 -1
  130. package/src/__tests__/memory-regressions.test.ts +0 -1
  131. package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -1
  132. package/src/__tests__/memory-upsert-concurrency.test.ts +0 -1
  133. package/src/__tests__/messaging-send-tool.test.ts +35 -0
  134. package/src/__tests__/messaging-skill-split.test.ts +138 -0
  135. package/src/__tests__/migration-cross-version-compatibility.test.ts +0 -1
  136. package/src/__tests__/migration-export-http.test.ts +2 -3
  137. package/src/__tests__/migration-import-commit-http.test.ts +1 -2
  138. package/src/__tests__/migration-import-preflight-http.test.ts +1 -2
  139. package/src/__tests__/migration-validate-http.test.ts +1 -2
  140. package/src/__tests__/native-web-search.test.ts +475 -0
  141. package/src/__tests__/navigate-settings-tab.test.ts +84 -0
  142. package/src/__tests__/non-member-access-request.test.ts +0 -1
  143. package/src/__tests__/notification-broadcaster.test.ts +15 -15
  144. package/src/__tests__/notification-decision-strategy.test.ts +6 -6
  145. package/src/__tests__/notification-deep-link.test.ts +7 -7
  146. package/src/__tests__/notification-guardian-path.test.ts +2 -3
  147. package/src/__tests__/notification-telegram-adapter.test.ts +1 -1
  148. package/src/__tests__/notification-thread-candidates.test.ts +4 -4
  149. package/src/__tests__/onboarding-starter-tasks.test.ts +0 -1
  150. package/src/__tests__/onboarding-template-contract.test.ts +0 -10
  151. package/src/__tests__/playbook-execution.test.ts +0 -1
  152. package/src/__tests__/playbook-tools.test.ts +0 -1
  153. package/src/__tests__/profile-compiler.test.ts +0 -1
  154. package/src/__tests__/provider-fail-open-selection.test.ts +12 -2
  155. package/src/__tests__/provider-managed-proxy-integration.test.ts +25 -0
  156. package/src/__tests__/qdrant-collection-migration.test.ts +223 -0
  157. package/src/__tests__/recording-handler.test.ts +30 -94
  158. package/src/__tests__/registry.test.ts +28 -35
  159. package/src/__tests__/relay-server.test.ts +0 -1
  160. package/src/__tests__/ride-shotgun-handler.test.ts +4 -20
  161. package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
  162. package/src/__tests__/runtime-events-sse-parity.test.ts +3 -4
  163. package/src/__tests__/runtime-events-sse.test.ts +0 -1
  164. package/src/__tests__/sandbox-diagnostics.test.ts +0 -1
  165. package/src/__tests__/scaffold-managed-skill-tool.test.ts +30 -28
  166. package/src/__tests__/schedule-store.test.ts +441 -1
  167. package/src/__tests__/schedule-tools.test.ts +468 -7
  168. package/src/__tests__/scheduler-recurrence.test.ts +196 -23
  169. package/src/__tests__/scoped-approval-grants.test.ts +0 -1
  170. package/src/__tests__/scoped-grant-security-matrix.test.ts +0 -1
  171. package/src/__tests__/secret-prompt-log-hygiene.test.ts +6 -3
  172. package/src/__tests__/secret-response-routing.test.ts +4 -1
  173. package/src/__tests__/send-endpoint-busy.test.ts +14 -5
  174. package/src/__tests__/send-notification-tool.test.ts +0 -7
  175. package/src/__tests__/sequence-store.test.ts +0 -1
  176. package/src/__tests__/server-history-render.test.ts +1 -2
  177. package/src/__tests__/session-abort-tool-results.test.ts +0 -1
  178. package/src/__tests__/session-agent-loop.test.ts +46 -6
  179. package/src/__tests__/session-confirmation-signals.test.ts +7 -46
  180. package/src/__tests__/session-conflict-gate.test.ts +2 -6
  181. package/src/__tests__/session-error.test.ts +5 -14
  182. package/src/__tests__/session-init.benchmark.test.ts +3 -5
  183. package/src/__tests__/session-load-history-repair.test.ts +0 -1
  184. package/src/__tests__/session-media-retry.test.ts +12 -74
  185. package/src/__tests__/session-pre-run-repair.test.ts +0 -1
  186. package/src/__tests__/session-profile-injection.test.ts +2 -6
  187. package/src/__tests__/session-provider-retry-repair.test.ts +2 -6
  188. package/src/__tests__/session-queue.test.ts +94 -139
  189. package/src/__tests__/session-skill-tools.test.ts +115 -115
  190. package/src/__tests__/session-slash-known.test.ts +0 -1
  191. package/src/__tests__/session-slash-queue.test.ts +0 -1
  192. package/src/__tests__/session-slash-unknown.test.ts +0 -1
  193. package/src/__tests__/session-surfaces-task-progress.test.ts +34 -0
  194. package/src/__tests__/session-usage.test.ts +0 -1
  195. package/src/__tests__/session-workspace-cache-state.test.ts +2 -6
  196. package/src/__tests__/session-workspace-injection.test.ts +2 -6
  197. package/src/__tests__/session-workspace-tool-tracking.test.ts +2 -6
  198. package/src/__tests__/skill-feature-flags-integration.test.ts +180 -184
  199. package/src/__tests__/skill-feature-flags.test.ts +125 -18
  200. package/src/__tests__/skill-load-feature-flag.test.ts +1 -2
  201. package/src/__tests__/skill-load-tool.test.ts +194 -2
  202. package/src/__tests__/skill-projection-feature-flag.test.ts +27 -16
  203. package/src/__tests__/skill-projection.benchmark.test.ts +15 -14
  204. package/src/__tests__/skills.test.ts +14 -53
  205. package/src/__tests__/slack-channel-config.test.ts +0 -1
  206. package/src/__tests__/slack-inbound-verification.test.ts +0 -1
  207. package/src/__tests__/slack-skill.test.ts +1 -1
  208. package/src/__tests__/starter-task-flow.test.ts +9 -19
  209. package/src/__tests__/subagent-tools.test.ts +2 -2
  210. package/src/__tests__/system-prompt.test.ts +7 -7
  211. package/src/__tests__/task-compiler.test.ts +0 -1
  212. package/src/__tests__/task-management-tools.test.ts +0 -1
  213. package/src/__tests__/task-memory-cleanup.test.ts +0 -1
  214. package/src/__tests__/task-runner.test.ts +0 -1
  215. package/src/__tests__/task-scheduler.test.ts +0 -1
  216. package/src/__tests__/terminal-tools.test.ts +0 -1
  217. package/src/__tests__/test-support/computer-use-skill-harness.ts +2 -4
  218. package/src/__tests__/thread-seed-composer.test.ts +5 -5
  219. package/src/__tests__/tool-approval-handler.test.ts +0 -1
  220. package/src/__tests__/tool-execution-abort-cleanup.test.ts +0 -1
  221. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -1
  222. package/src/__tests__/tool-executor.test.ts +8 -86
  223. package/src/__tests__/tool-grant-request-escalation.test.ts +0 -1
  224. package/src/__tests__/tool-notification-listener.test.ts +1 -1
  225. package/src/__tests__/tool-preview-lifecycle.test.ts +416 -0
  226. package/src/__tests__/trust-store.test.ts +84 -8
  227. package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
  228. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +0 -1
  229. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +0 -1
  230. package/src/__tests__/trusted-contact-multichannel.test.ts +0 -1
  231. package/src/__tests__/trusted-contact-verification.test.ts +0 -1
  232. package/src/__tests__/twilio-provider.test.ts +0 -1
  233. package/src/__tests__/twilio-routes.test.ts +0 -1
  234. package/src/__tests__/{request-file-tool.test.ts → ui-file-upload-surface.test.ts} +11 -72
  235. package/src/__tests__/update-bulletin.test.ts +0 -1
  236. package/src/__tests__/usage-cache-backfill-migration.test.ts +0 -1
  237. package/src/__tests__/usage-routes.test.ts +0 -1
  238. package/src/__tests__/verification-control-plane-policy.test.ts +4 -4
  239. package/src/__tests__/voice-invite-redemption.test.ts +0 -1
  240. package/src/__tests__/voice-scoped-grant-consumer.test.ts +0 -1
  241. package/src/__tests__/voice-session-bridge.test.ts +9 -1
  242. package/src/__tests__/web-fetch.test.ts +57 -0
  243. package/src/__tests__/workspace-git-service.test.ts +5 -14
  244. package/src/__tests__/workspace-policy.test.ts +0 -1
  245. package/src/agent/loop.ts +22 -34
  246. package/src/bundler/bundle-signer.ts +4 -4
  247. package/src/calls/call-controller.ts +1 -1
  248. package/src/calls/relay-server.ts +1 -1
  249. package/src/calls/twilio-rest.ts +1 -1
  250. package/src/calls/voice-session-bridge.ts +3 -1
  251. package/src/cli/__tests__/notifications.test.ts +3 -4
  252. package/src/cli/commands/map.ts +2 -6
  253. package/src/cli/commands/mcp.ts +73 -15
  254. package/src/cli/commands/notifications.ts +4 -4
  255. package/src/cli/commands/sessions.ts +9 -1
  256. package/src/cli/commands/skills.ts +6 -10
  257. package/src/cli/http-client.ts +2 -3
  258. package/src/cli/main-screen.tsx +10 -10
  259. package/src/cli/program.ts +0 -4
  260. package/src/cli/reference.ts +0 -2
  261. package/src/cli.ts +15 -9
  262. package/src/config/__tests__/bundled-tool-registry-guard.test.ts +120 -0
  263. package/src/config/bundled-skills/_shared/CLI_RETRIEVAL_PATTERN.md +11 -0
  264. package/src/config/bundled-skills/app-builder/SKILL.md +6 -7
  265. package/src/config/bundled-skills/app-builder/TOOLS.json +0 -4
  266. package/src/config/bundled-skills/browser/SKILL.md +6 -1
  267. package/src/config/bundled-skills/chatgpt-import/SKILL.md +5 -1
  268. package/src/config/bundled-skills/claude-code/SKILL.md +5 -1
  269. package/src/config/bundled-skills/computer-use/SKILL.md +6 -1
  270. package/src/config/bundled-skills/computer-use/TOOLS.json +6 -69
  271. package/src/config/bundled-skills/computer-use/tools/computer-use-click.ts +10 -1
  272. package/src/config/bundled-skills/contacts/SKILL.md +10 -1
  273. package/src/config/bundled-skills/contacts/TOOLS.json +35 -0
  274. package/src/config/bundled-skills/{messaging → contacts}/tools/google-contacts.ts +9 -2
  275. package/src/config/bundled-skills/document/SKILL.md +4 -1
  276. package/src/config/bundled-skills/doordash/SKILL.md +8 -2
  277. package/src/config/bundled-skills/doordash/__tests__/doordash-session.test.ts +1 -82
  278. package/src/config/bundled-skills/doordash/doordash-cli.ts +17 -28
  279. package/src/config/bundled-skills/doordash/lib/session.ts +21 -17
  280. package/src/config/bundled-skills/doordash/lib/shared/platform.ts +4 -1
  281. package/src/config/bundled-skills/followups/SKILL.md +4 -1
  282. package/src/config/bundled-skills/gmail/SKILL.md +180 -0
  283. package/src/config/bundled-skills/gmail/TOOLS.json +506 -0
  284. package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +149 -0
  285. package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +110 -0
  286. package/src/config/bundled-skills/{messaging → gmail}/tools/gmail-draft.ts +1 -1
  287. package/src/config/bundled-skills/{messaging → gmail}/tools/gmail-filters.ts +1 -1
  288. package/src/config/bundled-skills/{messaging → gmail}/tools/gmail-follow-up.ts +1 -1
  289. package/src/config/bundled-skills/{messaging → gmail}/tools/gmail-forward.ts +1 -1
  290. package/src/config/bundled-skills/gmail/tools/gmail-label.ts +50 -0
  291. package/src/config/bundled-skills/{messaging → gmail}/tools/gmail-outreach-scan.ts +8 -90
  292. package/src/config/bundled-skills/{messaging → gmail}/tools/gmail-send-draft.ts +1 -1
  293. package/src/config/bundled-skills/{messaging → gmail}/tools/gmail-sender-digest.ts +2 -2
  294. package/src/config/bundled-skills/{messaging → gmail}/tools/gmail-trash.ts +1 -1
  295. package/src/config/bundled-skills/{messaging → gmail}/tools/gmail-unsubscribe.ts +1 -1
  296. package/src/config/bundled-skills/{messaging → gmail}/tools/gmail-vacation.ts +1 -1
  297. package/src/config/bundled-skills/gmail/tools/shared.ts +47 -0
  298. package/src/config/bundled-skills/google-calendar/SKILL.md +5 -1
  299. package/src/config/bundled-skills/image-studio/SKILL.md +5 -1
  300. package/src/config/bundled-skills/knowledge-graph/SKILL.md +4 -1
  301. package/src/config/bundled-skills/media-processing/SKILL.md +7 -13
  302. package/src/config/bundled-skills/media-processing/TOOLS.json +0 -22
  303. package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +12 -1
  304. package/src/config/bundled-skills/messaging/SKILL.md +23 -139
  305. package/src/config/bundled-skills/messaging/TOOLS.json +33 -1215
  306. package/src/config/bundled-skills/messaging/tools/gmail-mime-helpers.ts +42 -0
  307. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +165 -2
  308. package/src/config/bundled-skills/messaging/tools/messaging-sender-digest.ts +1 -13
  309. package/src/config/bundled-skills/messaging/tools/shared.ts +81 -34
  310. package/src/config/bundled-skills/notifications/SKILL.md +5 -1
  311. package/src/config/bundled-skills/orchestration/SKILL.md +30 -0
  312. package/src/config/bundled-skills/orchestration/TOOLS.json +35 -0
  313. package/src/config/bundled-skills/{reminder/tools/reminder-cancel.ts → orchestration/tools/swarm-delegate.ts} +3 -3
  314. package/src/config/bundled-skills/phone-calls/SKILL.md +9 -1
  315. package/src/config/bundled-skills/playbooks/SKILL.md +4 -1
  316. package/src/config/bundled-skills/schedule/SKILL.md +70 -9
  317. package/src/config/bundled-skills/schedule/TOOLS.json +38 -6
  318. package/src/config/bundled-skills/screen-watch/SKILL.md +28 -0
  319. package/src/config/bundled-skills/screen-watch/TOOLS.json +35 -0
  320. package/src/config/bundled-skills/{reminder/tools/reminder-create.ts → screen-watch/tools/start-screen-watch.ts} +3 -3
  321. package/src/config/bundled-skills/sequences/SKILL.md +47 -0
  322. package/src/config/bundled-skills/sequences/TOOLS.json +340 -0
  323. package/src/config/bundled-skills/sequences/tools/sequence-update.ts +128 -0
  324. package/src/config/bundled-skills/sequences/tools/shared.ts +9 -0
  325. package/src/config/bundled-skills/settings/SKILL.md +12 -0
  326. package/src/config/bundled-skills/settings/TOOLS.json +112 -0
  327. package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +43 -0
  328. package/src/config/bundled-skills/settings/tools/open-system-settings.ts +52 -0
  329. package/src/config/bundled-skills/{computer-use/tools/computer-use-right-click.ts → settings/tools/set-avatar.ts} +2 -6
  330. package/src/{tools/system/voice-config.ts → config/bundled-skills/settings/tools/voice-config-update.ts} +59 -96
  331. package/src/config/bundled-skills/skill-management/SKILL.md +18 -0
  332. package/src/config/bundled-skills/skill-management/TOOLS.json +90 -0
  333. package/src/config/bundled-skills/{computer-use/tools/computer-use-double-click.ts → skill-management/tools/delete-managed.ts} +2 -6
  334. package/src/config/bundled-skills/skill-management/tools/scaffold-managed.ts +12 -0
  335. package/src/config/bundled-skills/slack/SKILL.md +5 -1
  336. package/src/config/bundled-skills/subagent/SKILL.md +4 -1
  337. package/src/config/bundled-skills/tasks/SKILL.md +5 -2
  338. package/src/config/bundled-skills/transcribe/SKILL.md +4 -1
  339. package/src/config/bundled-skills/watcher/SKILL.md +4 -1
  340. package/src/config/bundled-tool-registry.ts +118 -107
  341. package/src/config/env.ts +5 -2
  342. package/src/config/feature-flag-registry.json +33 -9
  343. package/src/config/loader.ts +10 -2
  344. package/src/config/schema.ts +19 -16
  345. package/src/config/schemas/inference.ts +12 -22
  346. package/src/config/schemas/memory-storage.ts +19 -1
  347. package/src/config/schemas/platform.ts +0 -16
  348. package/src/config/skill-state.ts +11 -8
  349. package/src/config/skills.ts +83 -32
  350. package/src/context/token-estimator.ts +11 -0
  351. package/src/context/window-manager.ts +180 -151
  352. package/src/daemon/computer-use-session.ts +11 -43
  353. package/src/daemon/daemon-control.ts +4 -1
  354. package/src/daemon/handlers/config-channels.ts +5 -9
  355. package/src/daemon/handlers/config-ingress.ts +0 -4
  356. package/src/daemon/handlers/config-model.ts +7 -13
  357. package/src/daemon/handlers/config-telegram.ts +4 -8
  358. package/src/daemon/handlers/config-voice.ts +2 -5
  359. package/src/daemon/handlers/dictation.ts +2 -12
  360. package/src/daemon/handlers/identity.ts +0 -105
  361. package/src/daemon/handlers/recording.ts +3 -23
  362. package/src/daemon/handlers/session-history.ts +42 -10
  363. package/src/daemon/handlers/sessions.ts +53 -72
  364. package/src/daemon/handlers/shared.ts +7 -28
  365. package/src/daemon/handlers/skills.ts +31 -27
  366. package/src/daemon/host-bash-proxy.ts +148 -0
  367. package/src/daemon/host-file-proxy.ts +135 -0
  368. package/src/daemon/lifecycle.ts +53 -41
  369. package/src/daemon/mcp-reload-service.ts +123 -0
  370. package/src/daemon/message-protocol.ts +6 -0
  371. package/src/daemon/message-types/apps.ts +0 -25
  372. package/src/daemon/message-types/browser.ts +1 -1
  373. package/src/daemon/message-types/computer-use.ts +1 -4
  374. package/src/daemon/message-types/guardian-actions.ts +1 -1
  375. package/src/daemon/message-types/host-bash.ts +18 -0
  376. package/src/daemon/message-types/host-file.ts +44 -0
  377. package/src/daemon/message-types/integrations.ts +1 -73
  378. package/src/daemon/message-types/messages.ts +15 -0
  379. package/src/daemon/message-types/schedules.ts +11 -27
  380. package/src/daemon/message-types/sessions.ts +8 -2
  381. package/src/daemon/message-types/settings.ts +1 -1
  382. package/src/daemon/message-types/shared.ts +1 -1
  383. package/src/daemon/message-types/surfaces.ts +2 -0
  384. package/src/daemon/ride-shotgun-handler.ts +35 -43
  385. package/src/daemon/seed-files.ts +3 -27
  386. package/src/daemon/server.ts +45 -28
  387. package/src/daemon/session-agent-loop-handlers.ts +72 -9
  388. package/src/daemon/session-agent-loop.ts +97 -66
  389. package/src/daemon/session-attachments.ts +1 -1
  390. package/src/daemon/session-error.ts +17 -16
  391. package/src/daemon/session-lifecycle.ts +20 -1
  392. package/src/daemon/session-media-retry.ts +1 -15
  393. package/src/daemon/session-messaging.ts +14 -6
  394. package/src/daemon/session-process.ts +36 -7
  395. package/src/daemon/session-queue-manager.ts +62 -103
  396. package/src/daemon/session-runtime-assembly.ts +27 -7
  397. package/src/daemon/session-skill-tools.ts +12 -11
  398. package/src/daemon/session-slash.ts +7 -0
  399. package/src/daemon/session-surfaces.ts +192 -118
  400. package/src/daemon/session-tool-setup.ts +146 -6
  401. package/src/daemon/session.ts +75 -37
  402. package/src/errors.ts +0 -2
  403. package/src/export/formatter.ts +6 -0
  404. package/src/mcp/mcp-oauth-provider.ts +1 -3
  405. package/src/media/avatar-router.ts +20 -28
  406. package/src/media/avatar-types.ts +7 -14
  407. package/src/media/managed-avatar-client.ts +70 -34
  408. package/src/memory/app-store.ts +0 -18
  409. package/src/memory/conversation-title-service.ts +1 -2
  410. package/src/memory/db-init.ts +16 -0
  411. package/src/memory/embedding-backend.ts +129 -27
  412. package/src/memory/embedding-gemini.test.ts +256 -0
  413. package/src/memory/embedding-gemini.ts +47 -13
  414. package/src/memory/embedding-local.ts +14 -2
  415. package/src/memory/embedding-ollama.ts +15 -2
  416. package/src/memory/embedding-openai.ts +15 -2
  417. package/src/memory/embedding-types.test.ts +116 -0
  418. package/src/memory/embedding-types.ts +61 -0
  419. package/src/memory/fingerprint.ts +1 -1
  420. package/src/memory/indexer.ts +25 -1
  421. package/src/memory/job-handlers/embedding.test.ts +258 -0
  422. package/src/memory/job-handlers/embedding.ts +81 -1
  423. package/src/memory/job-handlers/index-maintenance.ts +35 -1
  424. package/src/memory/job-handlers/media-processing.ts +11 -1
  425. package/src/memory/job-utils.ts +21 -6
  426. package/src/memory/jobs-store.ts +5 -1
  427. package/src/memory/jobs-worker.ts +8 -0
  428. package/src/memory/message-content.ts +66 -0
  429. package/src/memory/migrations/100-core-tables.ts +1 -31
  430. package/src/memory/migrations/104-core-indexes.ts +0 -11
  431. package/src/memory/migrations/145-drop-accounts-table.ts +19 -0
  432. package/src/memory/migrations/146-schedule-oneshot-routing.ts +94 -0
  433. package/src/memory/migrations/147-migrate-reminders-to-schedules.ts +129 -0
  434. package/src/memory/migrations/148-drop-reminders-table.ts +18 -0
  435. package/src/memory/migrations/index.ts +4 -0
  436. package/src/memory/migrations/registry.ts +19 -0
  437. package/src/memory/qdrant-client.ts +158 -43
  438. package/src/memory/retriever.test.ts +0 -1
  439. package/src/memory/retriever.ts +12 -2
  440. package/src/memory/schema/infrastructure.ts +5 -37
  441. package/src/memory/search/formatting.ts +34 -9
  442. package/src/memory/search/semantic.ts +57 -2
  443. package/src/memory/search/types.ts +2 -1
  444. package/src/notifications/AGENTS.md +2 -2
  445. package/src/notifications/README.md +59 -58
  446. package/src/notifications/adapters/macos.ts +1 -1
  447. package/src/notifications/broadcaster.ts +5 -5
  448. package/src/notifications/copy-composer.ts +1 -1
  449. package/src/notifications/decision-engine.ts +2 -2
  450. package/src/notifications/destination-resolver.ts +2 -2
  451. package/src/notifications/emit-signal.ts +8 -8
  452. package/src/notifications/signal.ts +1 -1
  453. package/src/notifications/thread-seed-composer.ts +1 -1
  454. package/src/oauth/connect-orchestrator.ts +1 -1
  455. package/src/oauth/token-persistence.ts +1 -1
  456. package/src/permissions/checker.ts +12 -1
  457. package/src/permissions/defaults.ts +13 -17
  458. package/src/permissions/trust-store.ts +37 -0
  459. package/src/permissions/workspace-policy.ts +0 -1
  460. package/src/prompts/__tests__/build-cli-reference-section.test.ts +11 -0
  461. package/src/prompts/computer-use-prompt.ts +1 -1
  462. package/src/prompts/system-prompt.ts +33 -35
  463. package/src/prompts/templates/BOOTSTRAP.md +0 -3
  464. package/src/prompts/templates/SOUL.md +1 -2
  465. package/src/prompts/templates/UPDATES.md +16 -7
  466. package/src/providers/anthropic/client.ts +87 -33
  467. package/src/providers/gemini/client.ts +6 -0
  468. package/src/providers/managed-proxy/constants.ts +5 -0
  469. package/src/providers/openai/client.ts +15 -0
  470. package/src/providers/registry.ts +4 -6
  471. package/src/providers/types.ts +24 -2
  472. package/src/runtime/AGENTS.md +18 -0
  473. package/src/runtime/assistant-event-hub.ts +2 -3
  474. package/src/runtime/assistant-event.ts +4 -4
  475. package/src/runtime/auth/__tests__/context.test.ts +5 -5
  476. package/src/runtime/auth/__tests__/credential-service.test.ts +0 -1
  477. package/src/runtime/auth/__tests__/guard-tests.test.ts +3 -2
  478. package/src/runtime/auth/__tests__/{ipc-auth-context.test.ts → local-auth-context.test.ts} +21 -21
  479. package/src/runtime/auth/__tests__/route-policy.test.ts +2 -2
  480. package/src/runtime/auth/__tests__/scopes.test.ts +9 -8
  481. package/src/runtime/auth/__tests__/subject.test.ts +8 -8
  482. package/src/runtime/auth/__tests__/token-service.test.ts +0 -1
  483. package/src/runtime/auth/route-policy.ts +8 -8
  484. package/src/runtime/auth/scopes.ts +2 -1
  485. package/src/runtime/auth/subject.ts +4 -4
  486. package/src/runtime/auth/token-service.ts +1 -24
  487. package/src/runtime/auth/types.ts +3 -3
  488. package/src/runtime/guardian-action-followup-executor.ts +1 -1
  489. package/src/runtime/guardian-action-grant-minter.ts +1 -1
  490. package/src/runtime/guardian-action-service.ts +3 -3
  491. package/src/runtime/http-server.ts +15 -2
  492. package/src/runtime/http-types.ts +10 -0
  493. package/src/runtime/invite-service.ts +3 -3
  494. package/src/runtime/local-actor-identity.ts +17 -22
  495. package/src/runtime/middleware/error-handler.ts +14 -1
  496. package/src/runtime/pending-interactions.ts +21 -9
  497. package/src/runtime/routes/app-management-routes.ts +63 -67
  498. package/src/runtime/routes/approval-routes.ts +1 -3
  499. package/src/runtime/routes/brain-graph/brain-graph.html +1845 -0
  500. package/src/runtime/routes/brain-graph-routes.ts +4 -42
  501. package/src/runtime/routes/btw-routes.ts +155 -0
  502. package/src/runtime/routes/computer-use-routes.ts +77 -31
  503. package/src/runtime/routes/conversation-routes.ts +234 -47
  504. package/src/runtime/routes/diagnostics-routes.ts +154 -43
  505. package/src/runtime/routes/documents-routes.ts +2 -2
  506. package/src/runtime/routes/global-search-routes.ts +1 -1
  507. package/src/runtime/routes/host-bash-routes.ts +83 -0
  508. package/src/runtime/routes/host-file-routes.ts +79 -0
  509. package/src/runtime/routes/integrations/slack/share.ts +1 -1
  510. package/src/runtime/routes/log-export-routes.ts +120 -0
  511. package/src/runtime/routes/mcp-routes.ts +20 -0
  512. package/src/runtime/routes/migration-routes.ts +3 -3
  513. package/src/runtime/routes/pairing-routes.ts +1 -1
  514. package/src/runtime/routes/recording-routes.ts +6 -4
  515. package/src/runtime/routes/schedule-routes.ts +31 -5
  516. package/src/runtime/routes/session-management-routes.ts +2 -6
  517. package/src/runtime/routes/session-query-routes.ts +18 -15
  518. package/src/runtime/routes/settings-routes.ts +7 -351
  519. package/src/runtime/routes/skills-routes.ts +7 -6
  520. package/src/runtime/routes/subagents-routes.ts +4 -10
  521. package/src/runtime/routes/surface-action-routes.ts +3 -14
  522. package/src/runtime/routes/surface-content-routes.ts +22 -5
  523. package/src/runtime/routes/work-items-routes.ts +21 -25
  524. package/src/runtime/routes/workspace-routes.test.ts +3 -3
  525. package/src/runtime/routes/workspace-utils.ts +1 -1
  526. package/src/runtime/telegram-streaming-delivery.ts +3 -0
  527. package/src/runtime/verification-outbound-actions.ts +2 -2
  528. package/src/schedule/integration-status.ts +0 -6
  529. package/src/schedule/schedule-store.ts +234 -43
  530. package/src/schedule/scheduler.ts +73 -74
  531. package/src/security/oauth2.ts +1 -1
  532. package/src/sequence/store.ts +12 -2
  533. package/src/skills/frontmatter.ts +19 -77
  534. package/src/skills/managed-store.ts +11 -2
  535. package/src/subagent/manager.ts +5 -3
  536. package/src/tasks/ephemeral-permissions.ts +3 -5
  537. package/src/tools/AGENTS.md +37 -0
  538. package/src/tools/apps/executors.ts +0 -6
  539. package/src/tools/browser/browser-manager.ts +17 -11
  540. package/src/tools/browser/jit-auth.ts +4 -1
  541. package/src/tools/claude-code/claude-code.ts +1 -1
  542. package/src/tools/computer-use/definitions.ts +48 -60
  543. package/src/tools/document/document-tool.ts +6 -6
  544. package/src/tools/document/editor-template.ts +10 -8
  545. package/src/tools/filesystem/edit.ts +2 -1
  546. package/src/tools/filesystem/read.ts +20 -2
  547. package/src/tools/filesystem/write.ts +2 -1
  548. package/src/tools/host-filesystem/edit.ts +17 -1
  549. package/src/tools/host-filesystem/read.ts +16 -1
  550. package/src/tools/host-filesystem/write.ts +15 -1
  551. package/src/tools/host-terminal/host-shell.ts +24 -0
  552. package/src/tools/memory/definitions.ts +45 -81
  553. package/src/tools/memory/handlers.test.ts +0 -1
  554. package/src/tools/memory/handlers.ts +1 -1
  555. package/src/tools/memory/register.ts +26 -60
  556. package/src/tools/network/script-proxy/session-manager.ts +6 -8
  557. package/src/tools/network/web-fetch.ts +7 -1
  558. package/src/tools/network/web-search.ts +2 -1
  559. package/src/tools/registry.ts +23 -0
  560. package/src/tools/schedule/create.ts +113 -5
  561. package/src/tools/schedule/list.ts +57 -15
  562. package/src/tools/schedule/update.ts +73 -3
  563. package/src/tools/shared/filesystem/image-read.ts +192 -0
  564. package/src/tools/side-effects.ts +1 -7
  565. package/src/tools/skills/delete-managed.ts +27 -64
  566. package/src/tools/skills/execute.ts +54 -0
  567. package/src/tools/skills/load.ts +127 -5
  568. package/src/tools/skills/scaffold-managed.ts +93 -172
  569. package/src/tools/subagent/message.ts +0 -7
  570. package/src/tools/subagent/spawn.ts +1 -1
  571. package/src/tools/swarm/delegate.ts +0 -3
  572. package/src/tools/system/avatar-generator.ts +13 -19
  573. package/src/tools/system/request-permission.ts +2 -1
  574. package/src/tools/terminal/safe-env.ts +1 -0
  575. package/src/tools/tool-manifest.ts +41 -47
  576. package/src/tools/types.ts +6 -2
  577. package/src/tools/ui-surface/definitions.ts +0 -55
  578. package/src/util/errors.ts +12 -10
  579. package/src/workspace/git-service.ts +0 -2
  580. package/src/__tests__/account-registry.test.ts +0 -258
  581. package/src/__tests__/email-classifier.test.ts +0 -25
  582. package/src/__tests__/gmail-integration.test.ts +0 -97
  583. package/src/__tests__/handle-user-message-secret-resume.test.ts +0 -172
  584. package/src/__tests__/home-base-bootstrap.test.ts +0 -84
  585. package/src/__tests__/managed-twitter-guardrails.test.ts +0 -353
  586. package/src/__tests__/prebuilt-home-base-seed.test.ts +0 -79
  587. package/src/__tests__/recording-intent-fallback.test.ts +0 -199
  588. package/src/__tests__/recording-intent.test.ts +0 -985
  589. package/src/__tests__/recording-state-machine.test.ts +0 -1574
  590. package/src/__tests__/reminder-store.test.ts +0 -350
  591. package/src/__tests__/reminder.test.ts +0 -337
  592. package/src/__tests__/scan-result-store.test.ts +0 -121
  593. package/src/__tests__/twitter-platform-proxy-client.test.ts +0 -450
  594. package/src/__tests__/view-image-tool.test.ts +0 -241
  595. package/src/cli/commands/amazon/cart.ts +0 -513
  596. package/src/cli/commands/amazon/checkout.ts +0 -394
  597. package/src/cli/commands/amazon/client.ts +0 -513
  598. package/src/cli/commands/amazon/index.ts +0 -920
  599. package/src/cli/commands/amazon/product-details.ts +0 -145
  600. package/src/cli/commands/amazon/request-extractor.ts +0 -187
  601. package/src/cli/commands/amazon/search.ts +0 -76
  602. package/src/cli/commands/amazon/session.ts +0 -116
  603. package/src/cli/commands/twitter/__tests__/cli-error-shaping.test.ts +0 -265
  604. package/src/cli/commands/twitter/__tests__/cli-read-routing.test.ts +0 -483
  605. package/src/cli/commands/twitter/__tests__/cli-routing.test.ts +0 -412
  606. package/src/cli/commands/twitter/__tests__/oauth-client.test.ts +0 -197
  607. package/src/cli/commands/twitter/client.ts +0 -989
  608. package/src/cli/commands/twitter/index.ts +0 -1160
  609. package/src/cli/commands/twitter/oauth-client.ts +0 -94
  610. package/src/cli/commands/twitter/router.ts +0 -396
  611. package/src/cli/commands/twitter/session.ts +0 -121
  612. package/src/config/bundled-skills/agentmail/SKILL.md +0 -132
  613. package/src/config/bundled-skills/agentmail/icon.svg +0 -21
  614. package/src/config/bundled-skills/amazon/SKILL.md +0 -137
  615. package/src/config/bundled-skills/amazon/icon.svg +0 -13
  616. package/src/config/bundled-skills/api-mapping/SKILL.md +0 -78
  617. package/src/config/bundled-skills/api-mapping/icon.svg +0 -18
  618. package/src/config/bundled-skills/cli-discover/SKILL.md +0 -68
  619. package/src/config/bundled-skills/deploy-fullstack-vercel/SKILL.md +0 -179
  620. package/src/config/bundled-skills/document-writer/SKILL.md +0 -195
  621. package/src/config/bundled-skills/elevenlabs-voice/SKILL.md +0 -140
  622. package/src/config/bundled-skills/email-setup/SKILL.md +0 -68
  623. package/src/config/bundled-skills/frontend-design/SKILL.md +0 -44
  624. package/src/config/bundled-skills/frontend-design/icon.svg +0 -16
  625. package/src/config/bundled-skills/google-oauth-setup/SKILL.md +0 -452
  626. package/src/config/bundled-skills/guardian-verify-setup/SKILL.md +0 -203
  627. package/src/config/bundled-skills/influencer/SKILL.md +0 -144
  628. package/src/config/bundled-skills/influencer/scripts/client.ts +0 -1269
  629. package/src/config/bundled-skills/influencer/scripts/influencer.ts +0 -267
  630. package/src/config/bundled-skills/macos-automation/SKILL.md +0 -65
  631. package/src/config/bundled-skills/macos-automation/icon.svg +0 -12
  632. package/src/config/bundled-skills/mcp-setup/SKILL.md +0 -75
  633. package/src/config/bundled-skills/media-processing/tools/media-diagnostics.ts +0 -184
  634. package/src/config/bundled-skills/messaging/tools/gmail-archive-by-query.ts +0 -80
  635. package/src/config/bundled-skills/messaging/tools/gmail-archive.ts +0 -29
  636. package/src/config/bundled-skills/messaging/tools/gmail-batch-archive.ts +0 -56
  637. package/src/config/bundled-skills/messaging/tools/gmail-batch-label.ts +0 -34
  638. package/src/config/bundled-skills/messaging/tools/gmail-download-attachment.ts +0 -47
  639. package/src/config/bundled-skills/messaging/tools/gmail-label.ts +0 -31
  640. package/src/config/bundled-skills/messaging/tools/gmail-list-attachments.ts +0 -67
  641. package/src/config/bundled-skills/messaging/tools/gmail-send-with-attachments.ts +0 -97
  642. package/src/config/bundled-skills/messaging/tools/gmail-summarize-thread.ts +0 -87
  643. package/src/config/bundled-skills/messaging/tools/gmail-triage.ts +0 -135
  644. package/src/config/bundled-skills/messaging/tools/messaging-analyze-activity.ts +0 -24
  645. package/src/config/bundled-skills/messaging/tools/messaging-reply.ts +0 -201
  646. package/src/config/bundled-skills/messaging/tools/send-notification.ts +0 -1
  647. package/src/config/bundled-skills/messaging/tools/sequence-cancel.ts +0 -27
  648. package/src/config/bundled-skills/messaging/tools/sequence-pause.ts +0 -48
  649. package/src/config/bundled-skills/messaging/tools/sequence-resume.ts +0 -27
  650. package/src/config/bundled-skills/messaging/tools/sequence-update.ts +0 -56
  651. package/src/config/bundled-skills/notion/SKILL.md +0 -240
  652. package/src/config/bundled-skills/notion-oauth-setup/SKILL.md +0 -126
  653. package/src/config/bundled-skills/oauth-setup/SKILL.md +0 -143
  654. package/src/config/bundled-skills/public-ingress/SKILL.md +0 -258
  655. package/src/config/bundled-skills/reminder/SKILL.md +0 -79
  656. package/src/config/bundled-skills/reminder/TOOLS.json +0 -89
  657. package/src/config/bundled-skills/reminder/tools/reminder-list.ts +0 -12
  658. package/src/config/bundled-skills/restaurant-reservation/SKILL.md +0 -141
  659. package/src/config/bundled-skills/screen-recording/SKILL.md +0 -148
  660. package/src/config/bundled-skills/self-upgrade/SKILL.md +0 -69
  661. package/src/config/bundled-skills/skills-catalog/SKILL.md +0 -78
  662. package/src/config/bundled-skills/slack-app-setup/SKILL.md +0 -178
  663. package/src/config/bundled-skills/slack-digest-setup/SKILL.md +0 -163
  664. package/src/config/bundled-skills/slack-oauth-setup/SKILL.md +0 -157
  665. package/src/config/bundled-skills/start-the-day/SKILL.md +0 -70
  666. package/src/config/bundled-skills/start-the-day/icon.svg +0 -13
  667. package/src/config/bundled-skills/telegram-setup/SKILL.md +0 -105
  668. package/src/config/bundled-skills/time-based-actions/SKILL.md +0 -142
  669. package/src/config/bundled-skills/twilio-setup/SKILL.md +0 -232
  670. package/src/config/bundled-skills/twitter/SKILL.md +0 -319
  671. package/src/config/bundled-skills/twitter/icon.svg +0 -14
  672. package/src/config/bundled-skills/typescript-eval/SKILL.md +0 -60
  673. package/src/config/bundled-skills/vercel-token-setup/SKILL.md +0 -214
  674. package/src/config/bundled-skills/voice-setup/SKILL.md +0 -131
  675. package/src/config/bundled-skills/voice-setup/icon.svg +0 -20
  676. package/src/daemon/handlers/pairing.ts +0 -119
  677. package/src/daemon/handlers/session-user-message.ts +0 -961
  678. package/src/daemon/recording-executor.ts +0 -180
  679. package/src/daemon/recording-intent-fallback.ts +0 -162
  680. package/src/daemon/recording-intent.ts +0 -493
  681. package/src/home-base/app-link-store.ts +0 -78
  682. package/src/home-base/bootstrap.ts +0 -74
  683. package/src/home-base/prebuilt/brain-graph.html +0 -1483
  684. package/src/home-base/prebuilt/index.html +0 -702
  685. package/src/home-base/prebuilt/seed-metadata.json +0 -21
  686. package/src/home-base/prebuilt/seed.ts +0 -122
  687. package/src/home-base/prebuilt-home-base-updater.ts +0 -36
  688. package/src/memory/account-store.ts +0 -117
  689. package/src/messaging/activity-analyzer.ts +0 -76
  690. package/src/messaging/email-classifier.ts +0 -208
  691. package/src/messaging/index.ts +0 -2
  692. package/src/messaging/outreach-classifier.ts +0 -185
  693. package/src/messaging/thread-summarizer.ts +0 -346
  694. package/src/messaging/types.ts +0 -17
  695. package/src/tools/browser/x-auto-navigate.ts +0 -254
  696. package/src/tools/credentials/account-registry.ts +0 -144
  697. package/src/tools/filesystem/view-image.ts +0 -244
  698. package/src/tools/reminder/reminder-store.ts +0 -194
  699. package/src/tools/reminder/reminder.ts +0 -158
  700. package/src/tools/system/navigate-settings.ts +0 -74
  701. package/src/tools/system/open-system-settings.ts +0 -85
  702. package/src/tools/system/version.ts +0 -54
  703. package/src/twitter/platform-proxy-client.ts +0 -405
  704. package/src/util/cookie-session.ts +0 -98
  705. /package/src/config/bundled-skills/{messaging → gmail}/tools/scan-result-store.ts +0 -0
  706. /package/src/config/bundled-skills/{messaging → sequences}/tools/sequence-analytics.ts +0 -0
  707. /package/src/config/bundled-skills/{messaging → sequences}/tools/sequence-create.ts +0 -0
  708. /package/src/config/bundled-skills/{messaging → sequences}/tools/sequence-delete.ts +0 -0
  709. /package/src/config/bundled-skills/{messaging → sequences}/tools/sequence-enroll.ts +0 -0
  710. /package/src/config/bundled-skills/{messaging → sequences}/tools/sequence-enrollment-list.ts +0 -0
  711. /package/src/config/bundled-skills/{messaging → sequences}/tools/sequence-get.ts +0 -0
  712. /package/src/config/bundled-skills/{messaging → sequences}/tools/sequence-import.ts +0 -0
  713. /package/src/config/bundled-skills/{messaging → sequences}/tools/sequence-list.ts +0 -0
@@ -0,0 +1,1845 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>🧠 Knowledge Brain</title>
7
+ <link rel="preconnect" href="https://fonts.googleapis.com" />
8
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
9
+ <link
10
+ href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,300;0,9..40,400;0,9..40,500;0,9..40,600;0,9..40,700;0,9..40,800;1,9..40,400&family=DM+Mono:wght@400;500&display=swap"
11
+ rel="stylesheet"
12
+ />
13
+ <script src="https://cdn.jsdelivr.net/npm/d3@7/dist/d3.min.js"></script>
14
+ <script type="importmap">
15
+ {
16
+ "imports": {
17
+ "three": "https://cdn.jsdelivr.net/npm/three@0.169.0/build/three.module.js",
18
+ "three/addons/": "https://cdn.jsdelivr.net/npm/three@0.169.0/examples/jsm/"
19
+ }
20
+ }
21
+ </script>
22
+ <style>
23
+ :root {
24
+ --v-slate-950: #070d19;
25
+ --v-slate-900: #0f172a;
26
+ --v-slate-800: #1e293b;
27
+ --v-slate-700: #334155;
28
+ --v-slate-600: #475569;
29
+ --v-slate-500: #64748b;
30
+ --v-slate-400: #94a3b8;
31
+ --v-slate-300: #cbd5e1;
32
+ --v-slate-200: #e2e8f0;
33
+ --v-slate-100: #f1f5f9;
34
+ --v-slate-50: #f8fafc;
35
+
36
+ --v-violet-950: #321669;
37
+ --v-violet-900: #4a2390;
38
+ --v-violet-800: #5c2fb2;
39
+ --v-violet-700: #7240cc;
40
+ --v-violet-600: #8a5be0;
41
+ --v-violet-500: #9878ea;
42
+ --v-violet-400: #b8a6f1;
43
+ --v-violet-300: #d4c8f7;
44
+ --v-violet-200: #e8e1fb;
45
+ --v-violet-100: #f4f0fd;
46
+
47
+ --bg: var(--v-slate-950);
48
+ --surface: var(--v-slate-900);
49
+ --surface-raised: var(--v-slate-800);
50
+ --border: var(--v-slate-700);
51
+ --border-subtle: rgba(51, 65, 85, 0.5);
52
+ --text: var(--v-slate-50);
53
+ --text-secondary: var(--v-slate-400);
54
+ --text-muted: var(--v-slate-500);
55
+ --accent: var(--v-violet-600);
56
+ --accent-dim: rgba(138, 91, 224, 0.12);
57
+ }
58
+
59
+ * {
60
+ box-sizing: border-box;
61
+ margin: 0;
62
+ padding: 0;
63
+ }
64
+
65
+ body {
66
+ font-family:
67
+ "DM Sans",
68
+ -apple-system,
69
+ BlinkMacSystemFont,
70
+ sans-serif;
71
+ background: var(--bg);
72
+ color: var(--text);
73
+ min-height: 100vh;
74
+ -webkit-font-smoothing: antialiased;
75
+ overflow-x: hidden;
76
+ display: flex;
77
+ flex-direction: column;
78
+ }
79
+
80
+ body::before {
81
+ content: "";
82
+ position: fixed;
83
+ top: -20%;
84
+ left: 50%;
85
+ transform: translateX(-50%);
86
+ width: 120%;
87
+ height: 50%;
88
+ background: radial-gradient(
89
+ ellipse at center,
90
+ rgba(138, 91, 224, 0.06) 0%,
91
+ rgba(91, 78, 255, 0.02) 50%,
92
+ transparent 70%
93
+ );
94
+ pointer-events: none;
95
+ z-index: 0;
96
+ }
97
+
98
+ /* ── Header ─────────────────────────────────────────────── */
99
+ .page-header {
100
+ position: relative;
101
+ z-index: 1;
102
+ padding: 24px 32px 16px;
103
+ display: flex;
104
+ flex-direction: column;
105
+ gap: 12px;
106
+ border-bottom: 1px solid var(--border-subtle);
107
+ }
108
+
109
+ .header-top {
110
+ display: flex;
111
+ align-items: center;
112
+ justify-content: space-between;
113
+ flex-wrap: wrap;
114
+ gap: 12px;
115
+ }
116
+
117
+ .header-title-block {
118
+ display: flex;
119
+ flex-direction: column;
120
+ gap: 2px;
121
+ }
122
+
123
+ .header-title {
124
+ font-size: 24px;
125
+ font-weight: 800;
126
+ letter-spacing: -0.02em;
127
+ background: linear-gradient(180deg, #fff 30%, var(--v-slate-300) 100%);
128
+ -webkit-background-clip: text;
129
+ -webkit-text-fill-color: transparent;
130
+ background-clip: text;
131
+ }
132
+
133
+ .header-subtitle {
134
+ font-size: 12px;
135
+ color: var(--text-muted);
136
+ letter-spacing: 0.04em;
137
+ text-transform: uppercase;
138
+ font-weight: 500;
139
+ }
140
+
141
+ .stats-bar {
142
+ display: flex;
143
+ align-items: center;
144
+ gap: 6px;
145
+ flex-wrap: wrap;
146
+ font-size: 13px;
147
+ color: var(--text-secondary);
148
+ }
149
+
150
+ .stat-item {
151
+ display: flex;
152
+ align-items: center;
153
+ gap: 5px;
154
+ }
155
+
156
+ .stat-value {
157
+ font-weight: 700;
158
+ color: var(--v-violet-400);
159
+ font-variant-numeric: tabular-nums;
160
+ }
161
+
162
+ .stat-sep {
163
+ color: var(--border);
164
+ user-select: none;
165
+ }
166
+
167
+ .knowledge-count-badge {
168
+ display: inline-flex;
169
+ align-items: center;
170
+ gap: 6px;
171
+ padding: 4px 12px;
172
+ background: var(--accent-dim);
173
+ border: 1px solid rgba(138, 91, 224, 0.25);
174
+ border-radius: 8px;
175
+ font-size: 12px;
176
+ font-weight: 600;
177
+ color: var(--v-violet-400);
178
+ }
179
+
180
+ .brain-pulse {
181
+ width: 8px;
182
+ height: 8px;
183
+ border-radius: 50%;
184
+ background: var(--accent);
185
+ animation: pulse-dot 2s ease-in-out infinite;
186
+ }
187
+
188
+ @keyframes pulse-dot {
189
+ 0%,
190
+ 100% {
191
+ opacity: 1;
192
+ transform: scale(1);
193
+ }
194
+ 50% {
195
+ opacity: 0.5;
196
+ transform: scale(0.7);
197
+ }
198
+ }
199
+
200
+ /* ── Mode Toggle ─────────────────────────────────────────── */
201
+ .mode-toggle {
202
+ display: flex;
203
+ background: var(--surface);
204
+ border: 1px solid var(--border-subtle);
205
+ border-radius: 10px;
206
+ padding: 3px;
207
+ gap: 2px;
208
+ }
209
+
210
+ .mode-btn {
211
+ font-family: inherit;
212
+ font-size: 13px;
213
+ font-weight: 600;
214
+ padding: 6px 16px;
215
+ border: none;
216
+ border-radius: 7px;
217
+ cursor: pointer;
218
+ transition: all 0.15s ease;
219
+ background: transparent;
220
+ color: var(--text-muted);
221
+ }
222
+
223
+ .mode-btn.active {
224
+ background: var(--accent);
225
+ color: #fff;
226
+ box-shadow: 0 1px 4px rgba(138, 91, 224, 0.35);
227
+ }
228
+
229
+ .mode-btn:not(.active):hover {
230
+ background: var(--surface-raised);
231
+ color: var(--text-secondary);
232
+ }
233
+
234
+ /* ── Main Content ─────────────────────────────────────────── */
235
+ .main-content {
236
+ position: relative;
237
+ z-index: 1;
238
+ flex: 1;
239
+ display: flex;
240
+ flex-direction: column;
241
+ padding: 16px 24px;
242
+ gap: 16px;
243
+ min-height: 0;
244
+ }
245
+
246
+ /* ── Visualization Panels ─────────────────────────────────── */
247
+ .viz-panel {
248
+ flex: 1;
249
+ display: none;
250
+ position: relative;
251
+ background: var(--surface);
252
+ border: 1px solid var(--border-subtle);
253
+ border-radius: 16px;
254
+ overflow: hidden;
255
+ min-height: 460px;
256
+ }
257
+
258
+ .viz-panel.active {
259
+ display: flex;
260
+ flex-direction: column;
261
+ }
262
+
263
+ /* ── 2D Panel ─────────────────────────────────────────────── */
264
+ #panel-2d {
265
+ align-items: stretch;
266
+ }
267
+
268
+ #graph-svg {
269
+ width: 100%;
270
+ height: 100%;
271
+ min-height: 460px;
272
+ }
273
+
274
+ /* Brain silhouette animation — gentle breathing glow */
275
+ @keyframes brain-breathe {
276
+ 0%,
277
+ 100% {
278
+ opacity: 0.35;
279
+ filter: drop-shadow(0 0 4px rgba(138, 91, 224, 0.2));
280
+ }
281
+ 50% {
282
+ opacity: 0.75;
283
+ filter: drop-shadow(0 0 12px rgba(138, 91, 224, 0.55));
284
+ }
285
+ }
286
+
287
+ .brain-outline {
288
+ animation: brain-breathe 3.5s ease-in-out infinite;
289
+ }
290
+
291
+ /* ── Refresh button ─────────────────────────────────────────── */
292
+ .refresh-btn {
293
+ font-family: inherit;
294
+ display: inline-flex;
295
+ align-items: center;
296
+ justify-content: center;
297
+ width: 34px;
298
+ height: 34px;
299
+ border-radius: 8px;
300
+ border: 1px solid var(--border);
301
+ background: transparent;
302
+ color: var(--text-muted);
303
+ font-size: 18px;
304
+ cursor: pointer;
305
+ transition: all 0.15s ease;
306
+ line-height: 1;
307
+ padding: 0;
308
+ }
309
+
310
+ .refresh-btn:hover {
311
+ border-color: var(--accent);
312
+ background: var(--accent-dim);
313
+ color: var(--v-violet-400);
314
+ }
315
+
316
+ .refresh-btn:active {
317
+ transform: scale(0.93);
318
+ }
319
+
320
+ .refresh-btn.spinning #refresh-icon {
321
+ display: inline-block;
322
+ animation: spin 0.7s linear infinite;
323
+ }
324
+
325
+ @keyframes spin {
326
+ from {
327
+ transform: rotate(0deg);
328
+ }
329
+ to {
330
+ transform: rotate(360deg);
331
+ }
332
+ }
333
+
334
+ /* ── 3D Panel ─────────────────────────────────────────────── */
335
+ #panel-3d {
336
+ align-items: stretch;
337
+ justify-content: stretch;
338
+ position: relative;
339
+ }
340
+
341
+ #brain-canvas-3d {
342
+ display: block;
343
+ width: 100%;
344
+ height: 100%;
345
+ min-height: 460px;
346
+ border-radius: 16px;
347
+ }
348
+
349
+ /* ── Empty / Error states ─────────────────────────────────── */
350
+ .state-overlay {
351
+ position: absolute;
352
+ inset: 0;
353
+ display: flex;
354
+ flex-direction: column;
355
+ align-items: center;
356
+ justify-content: center;
357
+ gap: 12px;
358
+ text-align: center;
359
+ padding: 48px;
360
+ pointer-events: none;
361
+ }
362
+
363
+ .state-overlay .icon {
364
+ font-size: 48px;
365
+ line-height: 1;
366
+ }
367
+
368
+ @keyframes empty-brain-pulse {
369
+ 0%,
370
+ 100% {
371
+ transform: scale(1);
372
+ opacity: 0.8;
373
+ }
374
+ 50% {
375
+ transform: scale(1.12);
376
+ opacity: 1;
377
+ }
378
+ }
379
+
380
+ .empty-brain-icon {
381
+ animation: empty-brain-pulse 2.5s ease-in-out infinite;
382
+ display: inline-block;
383
+ }
384
+
385
+ .state-overlay h3 {
386
+ font-size: 18px;
387
+ font-weight: 700;
388
+ color: var(--text);
389
+ }
390
+
391
+ .state-overlay p {
392
+ font-size: 14px;
393
+ color: var(--text-muted);
394
+ line-height: 1.5;
395
+ max-width: 280px;
396
+ }
397
+
398
+ /* ── Tooltip ──────────────────────────────────────────────── */
399
+ #tooltip {
400
+ position: fixed;
401
+ display: none;
402
+ flex-direction: column;
403
+ gap: 4px;
404
+ padding: 10px 14px;
405
+ background: rgba(15, 23, 42, 0.92);
406
+ border: 1px solid rgba(138, 91, 224, 0.35);
407
+ border-radius: 10px;
408
+ backdrop-filter: blur(8px);
409
+ -webkit-backdrop-filter: blur(8px);
410
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
411
+ pointer-events: none;
412
+ z-index: 9999;
413
+ max-width: 220px;
414
+ font-size: 13px;
415
+ }
416
+
417
+ #tooltip.visible {
418
+ display: flex;
419
+ }
420
+
421
+ .tooltip-name {
422
+ font-weight: 700;
423
+ color: var(--text);
424
+ font-size: 14px;
425
+ }
426
+
427
+ .tooltip-type {
428
+ font-size: 11px;
429
+ text-transform: uppercase;
430
+ letter-spacing: 0.05em;
431
+ font-weight: 600;
432
+ color: var(--v-violet-400);
433
+ }
434
+
435
+ .tooltip-row {
436
+ font-size: 12px;
437
+ color: var(--text-secondary);
438
+ display: flex;
439
+ align-items: center;
440
+ gap: 4px;
441
+ }
442
+
443
+ .tooltip-dot {
444
+ width: 8px;
445
+ height: 8px;
446
+ border-radius: 50%;
447
+ flex-shrink: 0;
448
+ }
449
+
450
+ /* ── Legend ───────────────────────────────────────────────── */
451
+ .legend {
452
+ display: flex;
453
+ flex-wrap: wrap;
454
+ gap: 8px 16px;
455
+ padding: 4px 0;
456
+ }
457
+
458
+ .legend-item {
459
+ display: flex;
460
+ align-items: center;
461
+ gap: 6px;
462
+ font-size: 12px;
463
+ color: var(--text-muted);
464
+ }
465
+
466
+ .legend-dot {
467
+ width: 10px;
468
+ height: 10px;
469
+ border-radius: 50%;
470
+ flex-shrink: 0;
471
+ }
472
+
473
+ .legend-label {
474
+ font-size: 12px;
475
+ }
476
+
477
+ .legend-item input[type="checkbox"] {
478
+ width: 13px;
479
+ height: 13px;
480
+ accent-color: var(--accent);
481
+ cursor: pointer;
482
+ flex-shrink: 0;
483
+ }
484
+
485
+ .legend-item {
486
+ cursor: pointer;
487
+ user-select: none;
488
+ }
489
+
490
+ /* Dim legend item when hidden */
491
+ .legend-item.hidden-category {
492
+ opacity: 0.4;
493
+ }
494
+
495
+ /* ── Footer ───────────────────────────────────────────────── */
496
+ .page-footer {
497
+ position: relative;
498
+ z-index: 1;
499
+ padding: 16px 32px;
500
+ border-top: 1px solid var(--border-subtle);
501
+ display: flex;
502
+ align-items: center;
503
+ }
504
+
505
+ .back-link {
506
+ font-size: 13px;
507
+ font-weight: 600;
508
+ color: var(--text-secondary);
509
+ text-decoration: none;
510
+ display: inline-flex;
511
+ align-items: center;
512
+ gap: 6px;
513
+ transition: color 0.15s ease;
514
+ }
515
+
516
+ .back-link:hover {
517
+ color: var(--v-violet-400);
518
+ }
519
+
520
+ /* ── D3 node styles ───────────────────────────────────────── */
521
+ .node-circle {
522
+ cursor: pointer;
523
+ transition: filter 0.15s ease;
524
+ }
525
+
526
+ .node-circle:hover {
527
+ filter: brightness(1.3);
528
+ }
529
+
530
+ .link-line {
531
+ pointer-events: none;
532
+ }
533
+
534
+ .lobe-label {
535
+ pointer-events: none;
536
+ user-select: none;
537
+ }
538
+
539
+ /* ── Responsive ───────────────────────────────────────────── */
540
+ @media (max-width: 600px) {
541
+ .page-header {
542
+ padding: 16px;
543
+ }
544
+ .main-content {
545
+ padding: 12px;
546
+ }
547
+ .header-title {
548
+ font-size: 20px;
549
+ }
550
+ .stats-bar {
551
+ font-size: 12px;
552
+ }
553
+ }
554
+ </style>
555
+ </head>
556
+ <body>
557
+ <!-- ── Header ─────────────────────────────────────────────── -->
558
+ <header class="page-header">
559
+ <div class="header-top">
560
+ <div class="header-title-block">
561
+ <div class="header-title">🧠 Knowledge Brain</div>
562
+ <div class="header-subtitle">Identity Knowledge Graph</div>
563
+ </div>
564
+ <div
565
+ style="display: flex; align-items: center; gap: 12px; flex-wrap: wrap"
566
+ >
567
+ <div class="knowledge-count-badge">
568
+ <div class="brain-pulse"></div>
569
+ <span id="badge-knowledge-count">0 knowledge items</span>
570
+ </div>
571
+ <button
572
+ class="refresh-btn"
573
+ id="btn-refresh"
574
+ onclick="refreshBrainData()"
575
+ title="Refresh"
576
+ >
577
+ <span id="refresh-icon">&#x21BB;</span>
578
+ </button>
579
+ <div class="mode-toggle">
580
+ <button
581
+ class="mode-btn active"
582
+ id="btn-2d"
583
+ onclick="switchMode('2d')"
584
+ >
585
+ 2D
586
+ </button>
587
+ <button class="mode-btn" id="btn-3d" onclick="switchMode('3d')">
588
+ 3D
589
+ </button>
590
+ </div>
591
+ </div>
592
+ </div>
593
+ <div class="stats-bar" id="stats-bar">
594
+ <span class="stat-item">
595
+ <span id="stat-entities" class="stat-value">0</span> entities
596
+ </span>
597
+ <span class="stat-sep">·</span>
598
+ <span class="stat-item">
599
+ <span id="stat-relations" class="stat-value">0</span> relations
600
+ </span>
601
+ <span class="stat-sep">·</span>
602
+ <span class="stat-item">
603
+ <span id="stat-memory" class="stat-value">0</span> memory items
604
+ </span>
605
+ <span class="stat-sep">·</span>
606
+ <span class="stat-item"
607
+ >Last updated:
608
+ <span id="stat-updated" class="stat-value">—</span></span
609
+ >
610
+ </div>
611
+ </header>
612
+
613
+ <!-- ── Main Content ───────────────────────────────────────── -->
614
+ <main class="main-content">
615
+ <!-- 2D Panel -->
616
+ <div class="viz-panel active" id="panel-2d">
617
+ <svg id="graph-svg"></svg>
618
+ </div>
619
+
620
+ <!-- 3D Panel -->
621
+ <div class="viz-panel" id="panel-3d">
622
+ <canvas id="brain-canvas-3d"></canvas>
623
+ </div>
624
+
625
+ <!-- Legend with category filter checkboxes -->
626
+ <div class="legend" id="legend">
627
+ <label class="legend-item" data-category="person">
628
+ <input
629
+ type="checkbox"
630
+ checked
631
+ data-category="person"
632
+ onchange="toggleCategory(this)"
633
+ />
634
+ <div class="legend-dot" style="background: #22c55e"></div>
635
+ <span class="legend-label">👤 Person</span>
636
+ </label>
637
+ <label class="legend-item" data-category="project">
638
+ <input
639
+ type="checkbox"
640
+ checked
641
+ data-category="project"
642
+ onchange="toggleCategory(this)"
643
+ />
644
+ <div class="legend-dot" style="background: #f97316"></div>
645
+ <span class="legend-label">📁 Project</span>
646
+ </label>
647
+ <label class="legend-item" data-category="tool">
648
+ <input
649
+ type="checkbox"
650
+ checked
651
+ data-category="tool"
652
+ onchange="toggleCategory(this)"
653
+ />
654
+ <div class="legend-dot" style="background: #06b6d4"></div>
655
+ <span class="legend-label">🔧 Tool</span>
656
+ </label>
657
+ <label class="legend-item" data-category="organization">
658
+ <input
659
+ type="checkbox"
660
+ checked
661
+ data-category="organization"
662
+ onchange="toggleCategory(this)"
663
+ />
664
+ <div class="legend-dot" style="background: #a855f7"></div>
665
+ <span class="legend-label">🏢 Organization</span>
666
+ </label>
667
+ <label class="legend-item" data-category="concept">
668
+ <input
669
+ type="checkbox"
670
+ checked
671
+ data-category="concept"
672
+ onchange="toggleCategory(this)"
673
+ />
674
+ <div class="legend-dot" style="background: #eab308"></div>
675
+ <span class="legend-label">💡 Concept</span>
676
+ </label>
677
+ <label class="legend-item" data-category="location">
678
+ <input
679
+ type="checkbox"
680
+ checked
681
+ data-category="location"
682
+ onchange="toggleCategory(this)"
683
+ />
684
+ <div class="legend-dot" style="background: #14b8a6"></div>
685
+ <span class="legend-label">📍 Location</span>
686
+ </label>
687
+ <label class="legend-item" data-category="profile">
688
+ <input
689
+ type="checkbox"
690
+ checked
691
+ data-category="profile"
692
+ onchange="toggleCategory(this)"
693
+ />
694
+ <div class="legend-dot" style="background: #8b5cf6"></div>
695
+ <span class="legend-label">🟣 Profile</span>
696
+ </label>
697
+ <label class="legend-item" data-category="preference">
698
+ <input
699
+ type="checkbox"
700
+ checked
701
+ data-category="preference"
702
+ onchange="toggleCategory(this)"
703
+ />
704
+ <div class="legend-dot" style="background: #3b82f6"></div>
705
+ <span class="legend-label">🔵 Preference</span>
706
+ </label>
707
+ <label class="legend-item" data-category="constraint">
708
+ <input
709
+ type="checkbox"
710
+ checked
711
+ data-category="constraint"
712
+ onchange="toggleCategory(this)"
713
+ />
714
+ <div class="legend-dot" style="background: #ef4444"></div>
715
+ <span class="legend-label">🔴 Constraint</span>
716
+ </label>
717
+ <label class="legend-item" data-category="instruction">
718
+ <input
719
+ type="checkbox"
720
+ checked
721
+ data-category="instruction"
722
+ onchange="toggleCategory(this)"
723
+ />
724
+ <div class="legend-dot" style="background: #f59e0b"></div>
725
+ <span class="legend-label">🟡 Instruction</span>
726
+ </label>
727
+ <label class="legend-item" data-category="style">
728
+ <input
729
+ type="checkbox"
730
+ checked
731
+ data-category="style"
732
+ onchange="toggleCategory(this)"
733
+ />
734
+ <div class="legend-dot" style="background: #ec4899"></div>
735
+ <span class="legend-label">🩷 Style</span>
736
+ </label>
737
+ </div>
738
+ </main>
739
+
740
+ <!-- ── Footer ─────────────────────────────────────────────── -->
741
+ <footer class="page-footer">
742
+ <span class="back-link">Knowledge Brain</span>
743
+ </footer>
744
+
745
+ <!-- ── Tooltip ─────────────────────────────────────────────── -->
746
+ <div id="tooltip">
747
+ <div class="tooltip-name" id="tt-name"></div>
748
+ <div class="tooltip-type" id="tt-type"></div>
749
+ <div class="tooltip-row">
750
+ <div class="tooltip-dot" id="tt-dot"></div>
751
+ <span id="tt-lobe"></span>
752
+ </div>
753
+ <div class="tooltip-row">🔗 <span id="tt-connections"></span></div>
754
+ <div class="tooltip-row">📌 <span id="tt-mentions"></span></div>
755
+ </div>
756
+
757
+ <script>
758
+ (function () {
759
+ "use strict";
760
+
761
+ /* ── Category filter state ───────────────────────────────── */
762
+ // Tracks which entity/memory types are currently hidden
763
+ var hiddenCategories = {};
764
+
765
+ function toggleCategory(checkbox) {
766
+ var cat = checkbox.getAttribute("data-category");
767
+ var label = checkbox.closest(".legend-item");
768
+ if (!checkbox.checked) {
769
+ hiddenCategories[cat] = true;
770
+ if (label) label.classList.add("hidden-category");
771
+ } else {
772
+ delete hiddenCategories[cat];
773
+ if (label) label.classList.remove("hidden-category");
774
+ }
775
+ // Apply visibility to 2D D3 nodes
776
+ // Memory nodes carry type='Memory' with the real category in kind, so prefer kind for them.
777
+ d3.selectAll(".node-group").each(function (d) {
778
+ var type = (
779
+ d.type === "Memory" ? d.kind : d.type || d.kind || ""
780
+ ).toLowerCase();
781
+ var visible = !hiddenCategories[type];
782
+ d3.select(this).style("display", visible ? null : "none");
783
+ });
784
+ // Apply visibility to 3D nodes if they expose a filter hook
785
+ if (typeof window.filterThreeDCategories === "function") {
786
+ window.filterThreeDCategories(hiddenCategories);
787
+ }
788
+ }
789
+ window.toggleCategory = toggleCategory;
790
+
791
+ /* ── Refresh ─────────────────────────────────────────────── */
792
+ function refreshBrainData() {
793
+ var btn = document.getElementById("btn-refresh");
794
+ if (btn) btn.classList.add("spinning");
795
+ // Clear existing panels
796
+ var panel2d = document.getElementById("panel-2d");
797
+ var panel3d = document.getElementById("panel-3d");
798
+ // Remove overlays (empty/error states)
799
+ panel2d.querySelectorAll(".state-overlay").forEach(function (el) {
800
+ el.remove();
801
+ });
802
+ panel3d.querySelectorAll(".state-overlay").forEach(function (el) {
803
+ el.remove();
804
+ });
805
+ // Reset 3D so it can reinitialise with new data
806
+ if (typeof window.destroyThreeD === "function")
807
+ window.destroyThreeD();
808
+ loadBrainData(function () {
809
+ if (btn) btn.classList.remove("spinning");
810
+ });
811
+ }
812
+ window.refreshBrainData = refreshBrainData;
813
+
814
+ /* ── Mode toggle ─────────────────────────────────────────── */
815
+ function switchMode(mode) {
816
+ document
817
+ .getElementById("panel-2d")
818
+ .classList.toggle("active", mode === "2d");
819
+ document
820
+ .getElementById("panel-3d")
821
+ .classList.toggle("active", mode === "3d");
822
+ document
823
+ .getElementById("btn-2d")
824
+ .classList.toggle("active", mode === "2d");
825
+ document
826
+ .getElementById("btn-3d")
827
+ .classList.toggle("active", mode === "3d");
828
+ if (mode === "3d") {
829
+ if (window.brainData && typeof window.initThreeD === "function") {
830
+ window.initThreeD(window.brainData);
831
+ }
832
+ // Re-start the animation loop if it was previously stopped (e.g. after returning from 2D)
833
+ if (typeof window.resumeThreeD === "function") {
834
+ window.resumeThreeD();
835
+ }
836
+ } else {
837
+ // Stop the Three.js render loop when leaving 3D to avoid wasting CPU/GPU
838
+ if (typeof window.stopThreeD === "function") {
839
+ window.stopThreeD();
840
+ }
841
+ // Hide any lingering 3D tooltip - mouseleave is never fired when the
842
+ // parent panel is hidden via display:none, so we clear it explicitly.
843
+ document.getElementById("tooltip").classList.remove("visible");
844
+ }
845
+ }
846
+ window.switchMode = switchMode;
847
+
848
+ /* ── Stats helpers ────────────────────────────────────────── */
849
+ function updateStats(data) {
850
+ var entities = data.entities || [];
851
+ var relations = data.relations || [];
852
+ var memory = data.memorySummary || [];
853
+ var totalMemory = memory.reduce(function (s, m) {
854
+ return s + (m.count || 0);
855
+ }, 0);
856
+ var total = data.totalKnowledgeCount || entities.length + totalMemory;
857
+
858
+ document.getElementById("stat-entities").textContent =
859
+ entities.length;
860
+ document.getElementById("stat-relations").textContent =
861
+ relations.length;
862
+ document.getElementById("stat-memory").textContent = totalMemory;
863
+ document.getElementById("badge-knowledge-count").textContent =
864
+ total + " knowledge items";
865
+
866
+ if (data.generatedAt) {
867
+ var d = new Date(data.generatedAt);
868
+ document.getElementById("stat-updated").textContent =
869
+ d.toLocaleString(undefined, {
870
+ month: "short",
871
+ day: "numeric",
872
+ hour: "2-digit",
873
+ minute: "2-digit",
874
+ });
875
+ }
876
+ }
877
+
878
+ /* ── Brain SVG silhouette ─────────────────────────────────── */
879
+ function drawBrainSilhouette(svg, W, H) {
880
+ var cx = W / 2;
881
+ var cy = H / 2;
882
+
883
+ // Scale factors — brain occupies ~80% W, ~70% H
884
+ var bw = W * 0.78;
885
+ var bh = H * 0.68;
886
+ var lx = cx - bw * 0.26; // left lobe center x
887
+ var rx = cx + bw * 0.26; // right lobe center x
888
+ var lw = bw * 0.46;
889
+ var rw = bw * 0.46;
890
+ var lh = bh * 0.88;
891
+ var rh = bh * 0.88;
892
+
893
+ var brainG = svg.append("g").attr("class", "brain-silhouette");
894
+
895
+ // Left lobe outline (organic blob via cubic bezier)
896
+ var leftPath = [
897
+ "M",
898
+ cx - 4,
899
+ cy + lh * 0.42,
900
+ "C",
901
+ cx - 4,
902
+ cy + lh * 0.55,
903
+ lx - lw * 0.55,
904
+ cy + lh * 0.55,
905
+ lx - lw * 0.55,
906
+ cy + lh * 0.2,
907
+ "C",
908
+ lx - lw * 0.6,
909
+ cy - lh * 0.05,
910
+ lx - lw * 0.55,
911
+ cy - lh * 0.35,
912
+ lx - lw * 0.2,
913
+ cy - lh * 0.5,
914
+ "C",
915
+ lx + lw * 0.1,
916
+ cy - lh * 0.62,
917
+ lx + lw * 0.4,
918
+ cy - lh * 0.58,
919
+ lx + lw * 0.45,
920
+ cy - lh * 0.42,
921
+ "C",
922
+ lx + lw * 0.52,
923
+ cy - lh * 0.22,
924
+ cx - 4,
925
+ cy - lh * 0.2,
926
+ cx - 4,
927
+ cy + lh * 0.42,
928
+ "Z",
929
+ ].join(" ");
930
+
931
+ // Right lobe outline
932
+ var rightPath = [
933
+ "M",
934
+ cx + 4,
935
+ cy + rh * 0.42,
936
+ "C",
937
+ cx + 4,
938
+ cy + rh * 0.55,
939
+ rx + rw * 0.55,
940
+ cy + rh * 0.55,
941
+ rx + rw * 0.55,
942
+ cy + rh * 0.2,
943
+ "C",
944
+ rx + rw * 0.6,
945
+ cy - rh * 0.05,
946
+ rx + rw * 0.55,
947
+ cy - rh * 0.35,
948
+ rx + rw * 0.2,
949
+ cy - rh * 0.5,
950
+ "C",
951
+ rx - rw * 0.1,
952
+ cy - rh * 0.62,
953
+ rx - rw * 0.4,
954
+ cy - rh * 0.58,
955
+ rx - rw * 0.45,
956
+ cy - rh * 0.42,
957
+ "C",
958
+ rx - rw * 0.52,
959
+ cy - rh * 0.22,
960
+ cx + 4,
961
+ cy - rh * 0.2,
962
+ cx + 4,
963
+ cy + rh * 0.42,
964
+ "Z",
965
+ ].join(" ");
966
+
967
+ // Draw fills
968
+ brainG
969
+ .append("path")
970
+ .attr("d", leftPath)
971
+ .attr("fill", "rgba(138, 91, 224, 0.04)")
972
+ .attr("stroke", "rgba(138, 91, 224, 0.4)")
973
+ .attr("stroke-width", 1.5)
974
+ .attr("class", "brain-outline");
975
+
976
+ brainG
977
+ .append("path")
978
+ .attr("d", rightPath)
979
+ .attr("fill", "rgba(138, 91, 224, 0.04)")
980
+ .attr("stroke", "rgba(138, 91, 224, 0.4)")
981
+ .attr("stroke-width", 1.5)
982
+ .attr("class", "brain-outline");
983
+
984
+ // Corpus callosum divider (dashed vertical line in center)
985
+ var ccTop = cy - lh * 0.28;
986
+ var ccBottom = cy + lh * 0.35;
987
+ brainG
988
+ .append("line")
989
+ .attr("x1", cx)
990
+ .attr("y1", ccTop)
991
+ .attr("x2", cx)
992
+ .attr("y2", ccBottom)
993
+ .attr("stroke", "rgba(138, 91, 224, 0.25)")
994
+ .attr("stroke-width", 1)
995
+ .attr("stroke-dasharray", "4,4");
996
+
997
+ // Lobe labels
998
+ var labelStyle = {
999
+ "font-family": "'DM Sans', sans-serif",
1000
+ "font-size": "10px",
1001
+ "font-weight": "600",
1002
+ fill: "rgba(148, 163, 184, 0.45)",
1003
+ "letter-spacing": "0.06em",
1004
+ "text-transform": "uppercase",
1005
+ "pointer-events": "none",
1006
+ "user-select": "none",
1007
+ };
1008
+
1009
+ function addLabel(x, y, text) {
1010
+ var t = brainG
1011
+ .append("text")
1012
+ .attr("x", x)
1013
+ .attr("y", y)
1014
+ .attr("text-anchor", "middle")
1015
+ .attr("class", "lobe-label")
1016
+ .text(text);
1017
+ Object.keys(labelStyle).forEach(function (k) {
1018
+ t.attr(k, labelStyle[k]);
1019
+ });
1020
+ }
1021
+
1022
+ addLabel(lx - lw * 0.15, cy + 5, "Analytical");
1023
+ addLabel(rx + rw * 0.15, cy + 5, "Creative");
1024
+ addLabel(lx - lw * 0.1, cy - lh * 0.32, "Planning");
1025
+ addLabel(rx + rw * 0.1, cy - rh * 0.32, "Social");
1026
+ addLabel(cx, cy + lh * 0.5, "Memory");
1027
+
1028
+ // Return brain bounds for use in bounding force
1029
+ return {
1030
+ leftLobe: { cx: lx, cy: cy, rx: lw * 0.52, ry: lh * 0.52 },
1031
+ rightLobe: { cx: rx, cy: cy, rx: rw * 0.52, ry: rh * 0.52 },
1032
+ centerX: cx,
1033
+ centerY: cy,
1034
+ width: bw,
1035
+ height: bh,
1036
+ };
1037
+ }
1038
+
1039
+ /* ── Lobe target positions ────────────────────────────────── */
1040
+ function lobeTarget(lobeRegion, bounds) {
1041
+ var cx = bounds.centerX;
1042
+ var cy = bounds.centerY;
1043
+ var lx = bounds.leftLobe.cx;
1044
+ var rx = bounds.rightLobe.cx;
1045
+ var hw = bounds.width * 0.18;
1046
+ var hh = bounds.height * 0.18;
1047
+ switch (lobeRegion) {
1048
+ case "right-social":
1049
+ return { x: rx + hw * 0.3, y: cy - hh * 1.2 };
1050
+ case "left-planning":
1051
+ return { x: lx - hw * 0.3, y: cy - hh * 1.2 };
1052
+ case "left-technical":
1053
+ return { x: lx - hw * 0.2, y: cy };
1054
+ case "right-creative":
1055
+ return { x: rx + hw * 0.2, y: cy };
1056
+ case "right-spatial":
1057
+ return { x: rx + hw * 0.2, y: cy + hh * 1.2 };
1058
+ case "center":
1059
+ default:
1060
+ return { x: cx, y: cy + hh };
1061
+ }
1062
+ }
1063
+
1064
+ /* ── Tooltip logic ────────────────────────────────────────── */
1065
+ var tooltip = document.getElementById("tooltip");
1066
+
1067
+ function showTooltip(event, d, connectionCount) {
1068
+ document.getElementById("tt-name").textContent = d.name;
1069
+ document.getElementById("tt-type").textContent =
1070
+ d.type || d.kind || "memory";
1071
+ document.getElementById("tt-dot").style.background =
1072
+ d.color || "#8A5BE0";
1073
+ document.getElementById("tt-lobe").textContent =
1074
+ d.lobeRegion || "center";
1075
+ document.getElementById("tt-connections").textContent =
1076
+ (connectionCount || 0) + " connections";
1077
+ document.getElementById("tt-mentions").textContent =
1078
+ (d.mentionCount || d.count || 0) + " mentions";
1079
+ tooltip.classList.add("visible");
1080
+ moveTooltip(event);
1081
+ }
1082
+
1083
+ function moveTooltip(event) {
1084
+ var x = event.clientX + 14;
1085
+ var y = event.clientY - 10;
1086
+ var tw = tooltip.offsetWidth || 200;
1087
+ var th = tooltip.offsetHeight || 100;
1088
+ if (x + tw > window.innerWidth - 8) x = event.clientX - tw - 14;
1089
+ if (y + th > window.innerHeight - 8) y = event.clientY - th - 14;
1090
+ tooltip.style.left = x + "px";
1091
+ tooltip.style.top = y + "px";
1092
+ }
1093
+
1094
+ function hideTooltip() {
1095
+ tooltip.classList.remove("visible");
1096
+ }
1097
+
1098
+ /* ── Render graph ─────────────────────────────────────────── */
1099
+ function renderBrain(data) {
1100
+ var svgEl = document.getElementById("graph-svg");
1101
+ var W = svgEl.clientWidth || svgEl.parentElement.clientWidth || 800;
1102
+ var H = svgEl.clientHeight || svgEl.parentElement.clientHeight || 500;
1103
+
1104
+ var svg = d3
1105
+ .select("#graph-svg")
1106
+ .attr("viewBox", "0 0 " + W + " " + H)
1107
+ .attr("preserveAspectRatio", "xMidYMid meet");
1108
+
1109
+ svg.selectAll("*").remove();
1110
+
1111
+ var entities = data.entities || [];
1112
+ var relations = data.relations || [];
1113
+ var memorySummary = data.memorySummary || [];
1114
+
1115
+ // Empty state — improved with pulsing brain emoji
1116
+ if (entities.length === 0 && memorySummary.length === 0) {
1117
+ var panel = document.getElementById("panel-2d");
1118
+ var overlay = document.createElement("div");
1119
+ overlay.className = "state-overlay";
1120
+ overlay.innerHTML =
1121
+ '<div class="icon empty-brain-icon">&#x1F9E0;</div>' +
1122
+ "<h3>Your brain is empty</h3>" +
1123
+ "<p>Start a conversation to grow it!</p>";
1124
+ panel.appendChild(overlay);
1125
+ return;
1126
+ }
1127
+
1128
+ // Draw brain silhouette
1129
+ var bounds = drawBrainSilhouette(svg, W, H);
1130
+
1131
+ // Build node list
1132
+ var nodeMap = {};
1133
+ var nodes = entities.map(function (e) {
1134
+ var n = Object.assign({}, e, {
1135
+ _isEntity: true,
1136
+ r: Math.max(6, Math.min(20, 6 + (e.mentionCount || 1) * 1.5)),
1137
+ });
1138
+ nodeMap[e.id] = n;
1139
+ return n;
1140
+ });
1141
+
1142
+ // Add memory cluster nodes
1143
+ memorySummary.forEach(function (m, i) {
1144
+ var n = {
1145
+ id: "memory-" + i,
1146
+ name: m.kind,
1147
+ type: "Memory",
1148
+ kind: m.kind,
1149
+ lobeRegion: "center",
1150
+ color: m.color || "#8b5cf6",
1151
+ mentionCount: m.count || 0,
1152
+ count: m.count || 0,
1153
+ _isMemory: true,
1154
+ r: Math.max(4, Math.min(8, 4 + Math.sqrt(m.count || 1))),
1155
+ };
1156
+ nodes.push(n);
1157
+ });
1158
+
1159
+ // Build links
1160
+ var links = relations
1161
+ .map(function (rel) {
1162
+ return {
1163
+ source: rel.sourceId,
1164
+ target: rel.targetId,
1165
+ relation: rel.relation,
1166
+ };
1167
+ })
1168
+ .filter(function (l) {
1169
+ return nodeMap[l.source] && nodeMap[l.target];
1170
+ });
1171
+
1172
+ // Connection count map
1173
+ var connCount = {};
1174
+ links.forEach(function (l) {
1175
+ connCount[l.source] = (connCount[l.source] || 0) + 1;
1176
+ connCount[l.target] = (connCount[l.target] || 0) + 1;
1177
+ });
1178
+
1179
+ // Initial positions near lobe targets
1180
+ nodes.forEach(function (n) {
1181
+ var t = lobeTarget(n.lobeRegion, bounds);
1182
+ n.x = t.x + (Math.random() - 0.5) * 60;
1183
+ n.y = t.y + (Math.random() - 0.5) * 60;
1184
+ });
1185
+
1186
+ // ── D3 force simulation ──────────────────────────────────
1187
+ var sim = d3
1188
+ .forceSimulation(nodes)
1189
+ .force(
1190
+ "link",
1191
+ d3
1192
+ .forceLink(links)
1193
+ .id(function (d) {
1194
+ return d.id;
1195
+ })
1196
+ .distance(60)
1197
+ .strength(0.3),
1198
+ )
1199
+ .force("charge", d3.forceManyBody().strength(-80))
1200
+ .force(
1201
+ "collide",
1202
+ d3
1203
+ .forceCollide()
1204
+ .radius(function (d) {
1205
+ return d.r + 4;
1206
+ })
1207
+ .strength(0.8),
1208
+ )
1209
+ // Lobe attraction force
1210
+ .force("lobe", function (alpha) {
1211
+ nodes.forEach(function (n) {
1212
+ var t = lobeTarget(n.lobeRegion, bounds);
1213
+ n.vx += (t.x - n.x) * alpha * 0.12;
1214
+ n.vy += (t.y - n.y) * alpha * 0.12;
1215
+ });
1216
+ })
1217
+ // Bounding force — keep nodes inside brain bounding ellipse
1218
+ .force("bound", function (alpha) {
1219
+ nodes.forEach(function (n) {
1220
+ var lobe = bounds.leftLobe;
1221
+ var rlobe = bounds.rightLobe;
1222
+ // Check if inside either lobe (ellipse test)
1223
+ var inLeft =
1224
+ Math.pow((n.x - lobe.cx) / (lobe.rx + n.r), 2) +
1225
+ Math.pow((n.y - lobe.cy) / (lobe.ry + n.r), 2);
1226
+ var inRight =
1227
+ Math.pow((n.x - rlobe.cx) / (rlobe.rx + n.r), 2) +
1228
+ Math.pow((n.y - rlobe.cy) / (rlobe.ry + n.r), 2);
1229
+ if (inLeft > 1 && inRight > 1) {
1230
+ // Push toward closer lobe center
1231
+ var dl = Math.hypot(n.x - lobe.cx, n.y - lobe.cy);
1232
+ var dr = Math.hypot(n.x - rlobe.cx, n.y - rlobe.cy);
1233
+ var target = dl < dr ? lobe : rlobe;
1234
+ n.vx += (target.cx - n.x) * alpha * 0.5;
1235
+ n.vy += (target.cy - n.y) * alpha * 0.5;
1236
+ }
1237
+ });
1238
+ })
1239
+ .alphaDecay(0.025)
1240
+ .velocityDecay(0.4);
1241
+
1242
+ // Draw links
1243
+ var linkLayer = svg.append("g").attr("class", "links");
1244
+ var linkSel = linkLayer
1245
+ .selectAll("line")
1246
+ .data(links)
1247
+ .enter()
1248
+ .append("line")
1249
+ .attr("class", "link-line")
1250
+ .attr("stroke", function (d) {
1251
+ var src =
1252
+ typeof d.source === "object" ? d.source : nodeMap[d.source];
1253
+ return src ? src.color : "rgba(138,91,224,0.15)";
1254
+ })
1255
+ .attr("stroke-width", 1)
1256
+ .attr("stroke-opacity", 0.15);
1257
+
1258
+ // Draw nodes
1259
+ var nodeLayer = svg.append("g").attr("class", "nodes");
1260
+ var nodeSel = nodeLayer
1261
+ .selectAll("g")
1262
+ .data(nodes)
1263
+ .enter()
1264
+ .append("g")
1265
+ .attr("class", "node-group")
1266
+ .call(
1267
+ d3
1268
+ .drag()
1269
+ .on("start", function (event, d) {
1270
+ if (!event.active) sim.alphaTarget(0.3).restart();
1271
+ d.fx = d.x;
1272
+ d.fy = d.y;
1273
+ })
1274
+ .on("drag", function (event, d) {
1275
+ d.fx = event.x;
1276
+ d.fy = event.y;
1277
+ })
1278
+ .on("end", function (event, d) {
1279
+ if (!event.active) sim.alphaTarget(0);
1280
+ d.fx = null;
1281
+ d.fy = null;
1282
+ }),
1283
+ );
1284
+
1285
+ nodeSel
1286
+ .append("circle")
1287
+ .attr("class", "node-circle")
1288
+ .attr("r", function (d) {
1289
+ return d.r;
1290
+ })
1291
+ .attr("fill", function (d) {
1292
+ return d.color || "#8A5BE0";
1293
+ })
1294
+ .attr("stroke", function (d) {
1295
+ return d.color || "#8A5BE0";
1296
+ })
1297
+ .attr("stroke-width", 1.5)
1298
+ .attr("stroke-opacity", 0.6)
1299
+ .attr("fill-opacity", 0.85);
1300
+
1301
+ // Node hover events
1302
+ nodeSel
1303
+ .on("mouseenter", function (event, d) {
1304
+ d3.select(this)
1305
+ .select("circle")
1306
+ .transition()
1307
+ .duration(120)
1308
+ .attr("r", function (d) {
1309
+ return d.r * 1.35;
1310
+ })
1311
+ .attr("fill-opacity", 1);
1312
+ showTooltip(event, d, connCount[d.id] || 0);
1313
+ })
1314
+ .on("mousemove", function (event) {
1315
+ moveTooltip(event);
1316
+ })
1317
+ .on("mouseleave", function (event, d) {
1318
+ d3.select(this)
1319
+ .select("circle")
1320
+ .transition()
1321
+ .duration(120)
1322
+ .attr("r", function (d) {
1323
+ return d.r;
1324
+ })
1325
+ .attr("fill-opacity", 0.85);
1326
+ hideTooltip();
1327
+ });
1328
+
1329
+ // Tick
1330
+ sim.on("tick", function () {
1331
+ linkSel
1332
+ .attr("x1", function (d) {
1333
+ return d.source.x;
1334
+ })
1335
+ .attr("y1", function (d) {
1336
+ return d.source.y;
1337
+ })
1338
+ .attr("x2", function (d) {
1339
+ return d.target.x;
1340
+ })
1341
+ .attr("y2", function (d) {
1342
+ return d.target.y;
1343
+ });
1344
+
1345
+ nodeSel.attr("transform", function (d) {
1346
+ return "translate(" + d.x + "," + d.y + ")";
1347
+ });
1348
+ });
1349
+ }
1350
+
1351
+ /* ── Data fetch ───────────────────────────────────────────── */
1352
+ async function loadBrainData(done) {
1353
+ try {
1354
+ var apiToken = document.querySelector(
1355
+ 'meta[name="api-token"]',
1356
+ )?.content;
1357
+ var headers = {};
1358
+ if (apiToken) headers["Authorization"] = "Bearer " + apiToken;
1359
+ var res = await fetch("/v1/brain-graph", { headers });
1360
+ if (!res.ok) throw new Error("HTTP " + res.status);
1361
+ var data = await res.json();
1362
+ window.brainData = data;
1363
+ updateStats(data);
1364
+ renderBrain(data);
1365
+ // Re-apply active category filters after re-render.
1366
+ // Memory nodes carry type='Memory' with the real category in kind, so prefer kind for them.
1367
+ d3.selectAll(".node-group").each(function (d) {
1368
+ var type = (
1369
+ d.type === "Memory" ? d.kind : d.type || d.kind || ""
1370
+ ).toLowerCase();
1371
+ if (hiddenCategories[type])
1372
+ d3.select(this).style("display", "none");
1373
+ });
1374
+ // If the user switched to 3D before this fetch resolved, the panel is
1375
+ // already active but initThreeD was never called. Kick it off now.
1376
+ if (
1377
+ document
1378
+ .getElementById("panel-3d")
1379
+ .classList.contains("active") &&
1380
+ typeof window.initThreeD === "function"
1381
+ ) {
1382
+ window.initThreeD(data);
1383
+ }
1384
+ if (typeof done === "function") done();
1385
+ } catch (err) {
1386
+ console.error("Brain graph load error:", err);
1387
+ var panel = document.getElementById("panel-2d");
1388
+ var overlay = document.createElement("div");
1389
+ overlay.className = "state-overlay";
1390
+ overlay.innerHTML =
1391
+ '<div class="icon">⚠️</div>' +
1392
+ "<h3>Could not load brain data</h3>" +
1393
+ "<p></p>";
1394
+ overlay.querySelector("p").textContent =
1395
+ err.message || "An unexpected error occurred.";
1396
+ panel.appendChild(overlay);
1397
+ if (typeof done === "function") done();
1398
+ }
1399
+ }
1400
+
1401
+ // Kick off on load
1402
+ loadBrainData();
1403
+ })();
1404
+ </script>
1405
+
1406
+ <script type="module">
1407
+ import * as THREE from "three";
1408
+ import { OrbitControls } from "three/addons/controls/OrbitControls.js";
1409
+
1410
+ // Lobe region → scatter bounds in 3D space
1411
+ const LOBE_BOUNDS = {
1412
+ "right-social": {
1413
+ xMin: 0.5,
1414
+ xMax: 1.5,
1415
+ yMin: 0.5,
1416
+ yMax: 1.5,
1417
+ zMin: -1,
1418
+ zMax: 1,
1419
+ },
1420
+ "left-planning": {
1421
+ xMin: -1.5,
1422
+ xMax: -0.5,
1423
+ yMin: 0.5,
1424
+ yMax: 1.5,
1425
+ zMin: -1,
1426
+ zMax: 1,
1427
+ },
1428
+ "left-technical": {
1429
+ xMin: -1.5,
1430
+ xMax: -0.5,
1431
+ yMin: -0.5,
1432
+ yMax: 0.5,
1433
+ zMin: -1,
1434
+ zMax: 1,
1435
+ },
1436
+ "right-creative": {
1437
+ xMin: 0.5,
1438
+ xMax: 1.5,
1439
+ yMin: -0.5,
1440
+ yMax: 0.5,
1441
+ zMin: -1,
1442
+ zMax: 1,
1443
+ },
1444
+ "right-spatial": {
1445
+ xMin: 0.5,
1446
+ xMax: 1.5,
1447
+ yMin: -1.5,
1448
+ yMax: -0.5,
1449
+ zMin: -1,
1450
+ zMax: 1,
1451
+ },
1452
+ center: {
1453
+ xMin: -0.5,
1454
+ xMax: 0.5,
1455
+ yMin: -0.5,
1456
+ yMax: 0.5,
1457
+ zMin: -0.5,
1458
+ zMax: 0.5,
1459
+ },
1460
+ };
1461
+
1462
+ function rand(min, max) {
1463
+ return min + Math.random() * (max - min);
1464
+ }
1465
+
1466
+ function nodePosition(lobeRegion) {
1467
+ const b = LOBE_BOUNDS[lobeRegion] || LOBE_BOUNDS["center"];
1468
+ return new THREE.Vector3(
1469
+ rand(b.xMin, b.xMax) + rand(-0.2, 0.2),
1470
+ rand(b.yMin, b.yMax) + rand(-0.2, 0.2),
1471
+ rand(b.zMin, b.zMax) + rand(-0.2, 0.2),
1472
+ );
1473
+ }
1474
+
1475
+ function hexStringToInt(hex) {
1476
+ // Accepts strings like '#22c55e' or '22c55e'
1477
+ return parseInt(hex.replace("#", ""), 16);
1478
+ }
1479
+
1480
+ let initialized = false;
1481
+ let animFrameId = null;
1482
+ let renderer = null;
1483
+ // animate reference stored here so resumeThreeD can call it without a closure capture issue
1484
+ let animateFn = null;
1485
+ // Stored so destroyThreeD can remove it and prevent stale calls into a null renderer after refresh
1486
+ let resizeHandler = null;
1487
+
1488
+ function stopAnimation() {
1489
+ if (animFrameId !== null) {
1490
+ cancelAnimationFrame(animFrameId);
1491
+ animFrameId = null;
1492
+ }
1493
+ }
1494
+
1495
+ // Exposed so the tab-switch handler in the non-module script can call them
1496
+ window.stopThreeD = stopAnimation;
1497
+ window.resumeThreeD = function resumeThreeD() {
1498
+ // Only restart if the 3D scene is already initialised and loop is not running
1499
+ if (
1500
+ initialized &&
1501
+ animFrameId === null &&
1502
+ typeof animateFn === "function"
1503
+ ) {
1504
+ animateFn();
1505
+ }
1506
+ };
1507
+
1508
+ // Reset 3D state so initThreeD can be called again after a refresh
1509
+ window.destroyThreeD = function destroyThreeD() {
1510
+ stopAnimation();
1511
+ if (resizeHandler) {
1512
+ window.removeEventListener("resize", resizeHandler);
1513
+ resizeHandler = null;
1514
+ }
1515
+ if (renderer) {
1516
+ if (renderer.domElement && renderer.domElement.parentNode) {
1517
+ renderer.domElement.parentNode.removeChild(renderer.domElement);
1518
+ }
1519
+ renderer.dispose();
1520
+ renderer = null;
1521
+ }
1522
+ initialized = false;
1523
+ animateFn = null;
1524
+ };
1525
+
1526
+ window.addEventListener("beforeunload", stopAnimation);
1527
+
1528
+ window.initThreeD = function initThreeD(data) {
1529
+ // Avoid double-initialisation if already running
1530
+ if (initialized) return;
1531
+
1532
+ let canvas = document.getElementById("brain-canvas-3d");
1533
+ const panel = document.getElementById("panel-3d");
1534
+
1535
+ const entities = (data && data.entities) || [];
1536
+ const relations = (data && data.relations) || [];
1537
+ const memorySummary = (data && data.memorySummary) || [];
1538
+ const totalKnowledgeCount =
1539
+ (data && data.totalKnowledgeCount) ||
1540
+ entities.length +
1541
+ memorySummary.reduce((s, m) => s + (m.count || 0), 0);
1542
+
1543
+ // ── Empty state ──────────────────────────────────────────────
1544
+ if (entities.length === 0 && memorySummary.length === 0) {
1545
+ if (!panel.querySelector(".state-overlay")) {
1546
+ const overlay = document.createElement("div");
1547
+ overlay.className = "state-overlay";
1548
+ overlay.innerHTML =
1549
+ '<div class="icon">🌱</div><h3>No knowledge yet</h3><p>Start a conversation to grow it!</p>';
1550
+ panel.appendChild(overlay);
1551
+ }
1552
+ return;
1553
+ }
1554
+
1555
+ // Mark as initialized only after all early returns, so empty-data calls
1556
+ // don't permanently block a subsequent call with real data.
1557
+ initialized = true;
1558
+
1559
+ // ── Renderer ─────────────────────────────────────────────────
1560
+ // canvas may be null if destroyThreeD removed it from the DOM; in that
1561
+ // case omit the canvas option so Three.js creates a fresh canvas element.
1562
+ const rendererOpts = canvas
1563
+ ? { canvas, antialias: true, alpha: true }
1564
+ : { antialias: true, alpha: true };
1565
+ renderer = new THREE.WebGLRenderer(rendererOpts);
1566
+ if (!canvas) {
1567
+ renderer.domElement.id = "brain-canvas-3d";
1568
+ panel.appendChild(renderer.domElement);
1569
+ }
1570
+ canvas = canvas || renderer.domElement;
1571
+ renderer.setPixelRatio(window.devicePixelRatio);
1572
+ renderer.setClearColor(0x000000, 0);
1573
+
1574
+ function getSize() {
1575
+ return { w: panel.clientWidth || 800, h: panel.clientHeight || 500 };
1576
+ }
1577
+ const { w, h } = getSize();
1578
+ renderer.setSize(w, h);
1579
+
1580
+ // ── Scene ────────────────────────────────────────────────────
1581
+ const scene = new THREE.Scene();
1582
+
1583
+ // ── Camera ───────────────────────────────────────────────────
1584
+ const camera = new THREE.PerspectiveCamera(60, w / h, 0.1, 100);
1585
+ camera.position.z = 8;
1586
+
1587
+ // ── Lights ───────────────────────────────────────────────────
1588
+ scene.add(new THREE.AmbientLight(0xffffff, 0.4));
1589
+ const dirLight = new THREE.DirectionalLight(0xffffff, 0.8);
1590
+ dirLight.position.set(10, 10, 10);
1591
+ scene.add(dirLight);
1592
+ const pointLight = new THREE.PointLight(0x8a5be0, 1.5, 15);
1593
+ pointLight.position.set(0, 3, 3);
1594
+ scene.add(pointLight);
1595
+
1596
+ // ── Brain mesh ───────────────────────────────────────────────
1597
+ const brainGeo = new THREE.SphereGeometry(2.5, 64, 64);
1598
+
1599
+ // Deform to brain-like shape
1600
+ const positions = brainGeo.attributes.position;
1601
+ for (let i = 0; i < positions.count; i++) {
1602
+ const x = positions.getX(i),
1603
+ y = positions.getY(i),
1604
+ z = positions.getZ(i);
1605
+ const noise =
1606
+ 0.3 * Math.sin(3 * x) * Math.cos(3 * y) + 0.1 * Math.sin(7 * z);
1607
+ const scale = 1 + noise;
1608
+ positions.setXYZ(i, x * scale, y * scale, z * scale);
1609
+ }
1610
+ brainGeo.computeVertexNormals();
1611
+
1612
+ // Scale based on knowledge count
1613
+ const rawScale =
1614
+ 0.5 + Math.log10(Math.max(totalKnowledgeCount, 1)) * 0.5;
1615
+ const brainScale = Math.min(rawScale, 2.0);
1616
+
1617
+ // Wireframe shell (outer visual)
1618
+ const wireMat = new THREE.MeshBasicMaterial({
1619
+ color: 0x8a5be0,
1620
+ wireframe: true,
1621
+ transparent: true,
1622
+ opacity: 0.25,
1623
+ });
1624
+ const brainMesh = new THREE.Mesh(brainGeo, wireMat);
1625
+ brainMesh.scale.setScalar(brainScale);
1626
+ scene.add(brainMesh);
1627
+
1628
+ // Solid inner fill (BackSide for depth effect)
1629
+ const solidMat = new THREE.MeshPhongMaterial({
1630
+ color: 0x321669,
1631
+ transparent: true,
1632
+ opacity: 0.15,
1633
+ side: THREE.BackSide,
1634
+ });
1635
+ const solidMesh = new THREE.Mesh(brainGeo, solidMat);
1636
+ solidMesh.scale.setScalar(brainScale);
1637
+ scene.add(solidMesh);
1638
+
1639
+ // ── Build nodes ───────────────────────────────────────────────
1640
+ const nodeMeshes = []; // for raycasting
1641
+ const nodeEntityMap = new WeakMap(); // mesh → entity data
1642
+
1643
+ // Entity nodes
1644
+ entities.forEach(function (entity) {
1645
+ const radius = 0.04 + 0.02 * Math.min(entity.mentionCount || 1, 10);
1646
+ const geo = new THREE.SphereGeometry(radius, 16, 16);
1647
+ const color = hexStringToInt(entity.color || "#8a5be0");
1648
+ const mat = new THREE.MeshPhongMaterial({
1649
+ color,
1650
+ emissive: color,
1651
+ emissiveIntensity: 0.3,
1652
+ });
1653
+ const mesh = new THREE.Mesh(geo, mat);
1654
+ const pos = nodePosition(entity.lobeRegion || "center");
1655
+ mesh.position.copy(pos);
1656
+ scene.add(mesh);
1657
+ nodeMeshes.push(mesh);
1658
+ nodeEntityMap.set(mesh, entity);
1659
+ });
1660
+
1661
+ // Memory summary nodes (center, smaller)
1662
+ memorySummary.forEach(function (m, i) {
1663
+ const geo = new THREE.SphereGeometry(0.06, 16, 16);
1664
+ const color = hexStringToInt(m.color || "#8b5cf6");
1665
+ const mat = new THREE.MeshPhongMaterial({
1666
+ color,
1667
+ emissive: color,
1668
+ emissiveIntensity: 0.3,
1669
+ });
1670
+ const mesh = new THREE.Mesh(geo, mat);
1671
+ const pos = nodePosition("center");
1672
+ mesh.position.copy(pos);
1673
+ scene.add(mesh);
1674
+ nodeMeshes.push(mesh);
1675
+ nodeEntityMap.set(mesh, {
1676
+ name: m.kind,
1677
+ type: "Memory",
1678
+ kind: m.kind,
1679
+ lobeRegion: "center",
1680
+ color: m.color || "#8b5cf6",
1681
+ mentionCount: m.count || 0,
1682
+ count: m.count || 0,
1683
+ });
1684
+ });
1685
+
1686
+ // Build a position lookup by entity id for relation lines
1687
+ const entityPositions = {};
1688
+ let meshIdx = 0;
1689
+ entities.forEach(function (entity) {
1690
+ entityPositions[entity.id] = nodeMeshes[meshIdx].position;
1691
+ meshIdx++;
1692
+ });
1693
+
1694
+ // ── Relation lines ────────────────────────────────────────────
1695
+ relations.forEach(function (rel) {
1696
+ const srcPos = entityPositions[rel.sourceId];
1697
+ const tgtPos = entityPositions[rel.targetId];
1698
+ if (!srcPos || !tgtPos) return;
1699
+
1700
+ const srcEntity = entities.find(function (e) {
1701
+ return e.id === rel.sourceId;
1702
+ });
1703
+ const color = hexStringToInt(
1704
+ (srcEntity && srcEntity.color) || "#8a5be0",
1705
+ );
1706
+
1707
+ const points = [srcPos.clone(), tgtPos.clone()];
1708
+ const geo = new THREE.BufferGeometry().setFromPoints(points);
1709
+ const mat = new THREE.LineBasicMaterial({
1710
+ color,
1711
+ transparent: true,
1712
+ opacity: 0.15,
1713
+ });
1714
+ scene.add(new THREE.Line(geo, mat));
1715
+ });
1716
+
1717
+ // ── Controls ──────────────────────────────────────────────────
1718
+ const controls = new OrbitControls(camera, renderer.domElement);
1719
+ controls.enableDamping = true;
1720
+ controls.autoRotate = true;
1721
+ controls.autoRotateSpeed = 0.5;
1722
+
1723
+ // Pause auto-rotate while hovering
1724
+ canvas.addEventListener("mouseenter", function () {
1725
+ controls.autoRotate = false;
1726
+ });
1727
+ canvas.addEventListener("mouseleave", function () {
1728
+ controls.autoRotate = true;
1729
+ });
1730
+
1731
+ // ── Tooltip via raycasting ────────────────────────────────────
1732
+ const raycaster = new THREE.Raycaster();
1733
+ const mouse = new THREE.Vector2();
1734
+ const tooltip = document.getElementById("tooltip");
1735
+
1736
+ canvas.addEventListener("mousemove", function (event) {
1737
+ // Guard against stale listener firing after destroyThreeD nulls the renderer
1738
+ if (!renderer) return;
1739
+ const rect = canvas.getBoundingClientRect();
1740
+ mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
1741
+ mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;
1742
+
1743
+ raycaster.setFromCamera(mouse, camera);
1744
+ const intersects = raycaster.intersectObjects(nodeMeshes);
1745
+ if (intersects.length > 0) {
1746
+ const entity = nodeEntityMap.get(intersects[0].object);
1747
+ if (entity) {
1748
+ document.getElementById("tt-name").textContent =
1749
+ entity.name || "";
1750
+ document.getElementById("tt-type").textContent =
1751
+ entity.type || entity.kind || "memory";
1752
+ document.getElementById("tt-dot").style.background =
1753
+ entity.color || "#8A5BE0";
1754
+ document.getElementById("tt-lobe").textContent =
1755
+ entity.lobeRegion || "center";
1756
+ document.getElementById("tt-connections").textContent =
1757
+ "— connections";
1758
+ document.getElementById("tt-mentions").textContent =
1759
+ (entity.mentionCount || entity.count || 0) + " mentions";
1760
+ tooltip.classList.add("visible");
1761
+ // Position near cursor
1762
+ let x = event.clientX + 14;
1763
+ let y = event.clientY - 10;
1764
+ const tw = tooltip.offsetWidth || 200;
1765
+ const th = tooltip.offsetHeight || 100;
1766
+ if (x + tw > window.innerWidth - 8) x = event.clientX - tw - 14;
1767
+ if (y + th > window.innerHeight - 8) y = event.clientY - th - 14;
1768
+ tooltip.style.left = x + "px";
1769
+ tooltip.style.top = y + "px";
1770
+ }
1771
+ } else {
1772
+ tooltip.classList.remove("visible");
1773
+ }
1774
+ });
1775
+
1776
+ canvas.addEventListener("mouseleave", function () {
1777
+ tooltip.classList.remove("visible");
1778
+ });
1779
+
1780
+ // ── Category filter for 3D nodes ──────────────────────────────
1781
+ window.filterThreeDCategories = function (hiddenCategories) {
1782
+ nodeMeshes.forEach(function (mesh) {
1783
+ const entity = nodeEntityMap.get(mesh);
1784
+ if (!entity) return;
1785
+ // Memory nodes carry type='Memory' with the real category in kind, so prefer kind for them.
1786
+ const type = (
1787
+ entity.type === "Memory"
1788
+ ? entity.kind
1789
+ : entity.type || entity.kind || ""
1790
+ ).toLowerCase();
1791
+ mesh.visible = !hiddenCategories[type];
1792
+ });
1793
+ };
1794
+
1795
+ // Replay any checkbox state that was set before 3D was initialised.
1796
+ // If the user unchecked categories while in 2D view, those hidden states
1797
+ // are recorded in the checkboxes but filterThreeDCategories didn't exist yet.
1798
+ (function replayHiddenCategories() {
1799
+ var hidden = {};
1800
+ document
1801
+ .querySelectorAll('.legend-item input[type="checkbox"]')
1802
+ .forEach(function (cb) {
1803
+ if (!cb.checked) hidden[cb.dataset.category || cb.value] = true;
1804
+ });
1805
+ if (Object.keys(hidden).length > 0) {
1806
+ window.filterThreeDCategories(hidden);
1807
+ }
1808
+ })();
1809
+
1810
+ // ── Resize handler ────────────────────────────────────────────
1811
+ function onResize() {
1812
+ // Guard against stale listener firing after destroyThreeD nulls the renderer
1813
+ if (!renderer) return;
1814
+ const { w, h } = getSize();
1815
+ if (w === 0 || h === 0) return;
1816
+ camera.aspect = w / h;
1817
+ camera.updateProjectionMatrix();
1818
+ renderer.setSize(w, h);
1819
+ }
1820
+ resizeHandler = onResize;
1821
+ window.addEventListener("resize", resizeHandler);
1822
+
1823
+ // ── Animation loop ────────────────────────────────────────────
1824
+ function animate() {
1825
+ animFrameId = requestAnimationFrame(animate);
1826
+ controls.update();
1827
+
1828
+ // Slow y-rotation of the brain mesh
1829
+ brainMesh.rotation.y += 0.001;
1830
+ solidMesh.rotation.y += 0.001;
1831
+
1832
+ // Gentle breathing pulse
1833
+ const pulse = brainScale * (1 + 0.02 * Math.sin(Date.now() / 1000));
1834
+ brainMesh.scale.setScalar(pulse);
1835
+ solidMesh.scale.setScalar(pulse);
1836
+
1837
+ renderer.render(scene, camera);
1838
+ }
1839
+ // Store reference so resumeThreeD can restart the loop after stopThreeD cancels it
1840
+ animateFn = animate;
1841
+ animate();
1842
+ };
1843
+ </script>
1844
+ </body>
1845
+ </html>