@mastra/factory 0.24.0-alpha.8 → 0.24.0

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 (301) hide show
  1. package/README.md +4 -4
  2. package/dist/environment/build-runner.js +2 -1
  3. package/dist/environment/build-runner.js.map +1 -1
  4. package/dist/environment/build-workflow.js +1 -1
  5. package/dist/environment/build-workflow.js.map +1 -1
  6. package/dist/environment/build.js +16 -14
  7. package/dist/environment/build.js.map +1 -1
  8. package/dist/factory.d.ts.map +1 -1
  9. package/dist/factory.js +111 -19
  10. package/dist/factory.js.map +1 -1
  11. package/dist/integrations/base.d.ts +1 -1
  12. package/dist/integrations/base.d.ts.map +1 -1
  13. package/dist/integrations/github/app-identity.js.map +1 -1
  14. package/dist/integrations/github/integration.js +1 -1
  15. package/dist/integrations/github/integration.js.map +1 -1
  16. package/dist/integrations/github/reconcile-worker.js +1 -1
  17. package/dist/integrations/github/reconcile-worker.js.map +1 -1
  18. package/dist/integrations/github/routes.d.ts +1 -1
  19. package/dist/integrations/github/routes.d.ts.map +1 -1
  20. package/dist/integrations/github/routes.js +24 -13
  21. package/dist/integrations/github/routes.js.map +1 -1
  22. package/dist/integrations/github/rules.d.ts.map +1 -1
  23. package/dist/integrations/github/rules.js +53 -9
  24. package/dist/integrations/github/rules.js.map +1 -1
  25. package/dist/integrations/github/sandbox.js +2 -2
  26. package/dist/integrations/github/sandbox.js.map +1 -1
  27. package/dist/integrations/github/session-subscriptions.d.ts.map +1 -1
  28. package/dist/integrations/github/session-subscriptions.js +1 -2
  29. package/dist/integrations/github/session-subscriptions.js.map +1 -1
  30. package/dist/integrations/github/subscriptions.d.ts +7 -4
  31. package/dist/integrations/github/subscriptions.d.ts.map +1 -1
  32. package/dist/integrations/github/subscriptions.js +5 -5
  33. package/dist/integrations/github/subscriptions.js.map +1 -1
  34. package/dist/integrations/gitlab/api.js.map +1 -1
  35. package/dist/integrations/gitlab/integration.js.map +1 -1
  36. package/dist/integrations/gitlab/reconciliation-config.js +2 -1
  37. package/dist/integrations/gitlab/reconciliation-config.js.map +1 -1
  38. package/dist/integrations/gitlab/routes.d.ts.map +1 -1
  39. package/dist/integrations/gitlab/routes.js +10 -6
  40. package/dist/integrations/gitlab/routes.js.map +1 -1
  41. package/dist/integrations/gitlab/rules.js.map +1 -1
  42. package/dist/integrations/gitlab/session-subscriptions.d.ts.map +1 -1
  43. package/dist/integrations/gitlab/session-subscriptions.js +1 -2
  44. package/dist/integrations/gitlab/session-subscriptions.js.map +1 -1
  45. package/dist/integrations/gitlab/subscriptions.d.ts +7 -4
  46. package/dist/integrations/gitlab/subscriptions.d.ts.map +1 -1
  47. package/dist/integrations/gitlab/subscriptions.js +5 -5
  48. package/dist/integrations/gitlab/subscriptions.js.map +1 -1
  49. package/dist/integrations/incidentio/api.js.map +1 -1
  50. package/dist/integrations/incidentio/integration.js.map +1 -1
  51. package/dist/integrations/incidentio/rules.js.map +1 -1
  52. package/dist/integrations/issue-reconcile-worker.js +1 -1
  53. package/dist/integrations/issue-reconcile-worker.js.map +1 -1
  54. package/dist/integrations/issue-reconciler.js +1 -1
  55. package/dist/integrations/issue-reconciler.js.map +1 -1
  56. package/dist/integrations/jira/api.js.map +1 -1
  57. package/dist/integrations/jira/integration.js.map +1 -1
  58. package/dist/integrations/jira/rules.js.map +1 -1
  59. package/dist/integrations/linear/agent-tools.d.ts.map +1 -1
  60. package/dist/integrations/linear/agent-tools.js.map +1 -1
  61. package/dist/integrations/linear/integration.js.map +1 -1
  62. package/dist/integrations/linear/rules.js.map +1 -1
  63. package/dist/integrations/platform/api-client.js.map +1 -1
  64. package/dist/integrations/platform/github/event-worker.js +1 -1
  65. package/dist/integrations/platform/github/event-worker.js.map +1 -1
  66. package/dist/integrations/platform/github/integration.js +2 -2
  67. package/dist/integrations/platform/github/integration.js.map +1 -1
  68. package/dist/integrations/platform/gitlab/event-worker.js.map +1 -1
  69. package/dist/integrations/platform/gitlab/integration.js.map +1 -1
  70. package/dist/integrations/platform/incidentio/integration.js +2 -1
  71. package/dist/integrations/platform/incidentio/integration.js.map +1 -1
  72. package/dist/integrations/platform/jira/integration.js.map +1 -1
  73. package/dist/integrations/platform/linear/event-worker.js +1 -1
  74. package/dist/integrations/platform/linear/event-worker.js.map +1 -1
  75. package/dist/integrations/platform/linear/integration.js.map +1 -1
  76. package/dist/integrations/slack/connect-route.js +1 -1
  77. package/dist/integrations/slack/connect-route.js.map +1 -1
  78. package/dist/integrations/slack/feed-publisher.js +1 -1
  79. package/dist/integrations/slack/feed-publisher.js.map +1 -1
  80. package/dist/integrations/slack/integration.js +1 -1
  81. package/dist/integrations/slack/integration.js.map +1 -1
  82. package/dist/integrations/slack/slack.d.ts +16 -5
  83. package/dist/integrations/slack/slack.d.ts.map +1 -1
  84. package/dist/integrations/slack/slack.js +87 -34
  85. package/dist/integrations/slack/slack.js.map +1 -1
  86. package/dist/integrations/subscription-session.d.ts +0 -1
  87. package/dist/integrations/subscription-session.d.ts.map +1 -1
  88. package/dist/integrations/subscription-session.js +1 -4
  89. package/dist/integrations/subscription-session.js.map +1 -1
  90. package/dist/integrations/workos/integration.js.map +1 -1
  91. package/dist/mcp/routes.d.ts.map +1 -1
  92. package/dist/mcp/routes.js +1 -1
  93. package/dist/mcp/routes.js.map +1 -1
  94. package/dist/packages/_internals/workspace/dist/index.js +18 -1
  95. package/dist/packages/_internals/workspace/dist/index.js.map +1 -1
  96. package/dist/routes/attention-activity.d.ts.map +1 -1
  97. package/dist/routes/attention-activity.js +5 -2
  98. package/dist/routes/attention-activity.js.map +1 -1
  99. package/dist/routes/attention-parked.js.map +1 -1
  100. package/dist/routes/attention-providers.d.ts +0 -1
  101. package/dist/routes/attention-providers.d.ts.map +1 -1
  102. package/dist/routes/attention-providers.js +5 -6
  103. package/dist/routes/attention-providers.js.map +1 -1
  104. package/dist/routes/automation-runs.js.map +1 -1
  105. package/dist/routes/config.d.ts +6 -7
  106. package/dist/routes/config.d.ts.map +1 -1
  107. package/dist/routes/config.js +20 -19
  108. package/dist/routes/config.js.map +1 -1
  109. package/dist/routes/contracts.js +1 -1
  110. package/dist/routes/contracts.js.map +1 -1
  111. package/dist/routes/custom-provider-source.js.map +1 -1
  112. package/dist/routes/fs.d.ts +31 -23
  113. package/dist/routes/fs.d.ts.map +1 -1
  114. package/dist/routes/fs.js +218 -64
  115. package/dist/routes/fs.js.map +1 -1
  116. package/dist/routes/intake.js +2 -1
  117. package/dist/routes/intake.js.map +1 -1
  118. package/dist/routes/knowledge.js +1 -1
  119. package/dist/routes/knowledge.js.map +1 -1
  120. package/dist/routes/oauth.js +3 -2
  121. package/dist/routes/oauth.js.map +1 -1
  122. package/dist/routes/projects.d.ts +1 -1
  123. package/dist/routes/projects.d.ts.map +1 -1
  124. package/dist/routes/projects.js +33 -31
  125. package/dist/routes/projects.js.map +1 -1
  126. package/dist/routes/provider-credentials.d.ts.map +1 -1
  127. package/dist/routes/provider-credentials.js +6 -1
  128. package/dist/routes/provider-credentials.js.map +1 -1
  129. package/dist/routes/session-access.d.ts +55 -0
  130. package/dist/routes/session-access.d.ts.map +1 -0
  131. package/dist/routes/session-access.js +165 -0
  132. package/dist/routes/session-access.js.map +1 -0
  133. package/dist/routes/skills.d.ts +2 -4
  134. package/dist/routes/skills.d.ts.map +1 -1
  135. package/dist/routes/skills.js +19 -65
  136. package/dist/routes/skills.js.map +1 -1
  137. package/dist/routes/source-control-sessions.d.ts +6 -1
  138. package/dist/routes/source-control-sessions.d.ts.map +1 -1
  139. package/dist/routes/source-control-sessions.js +95 -12
  140. package/dist/routes/source-control-sessions.js.map +1 -1
  141. package/dist/routes/surface.d.ts +5 -0
  142. package/dist/routes/surface.d.ts.map +1 -1
  143. package/dist/routes/surface.js +10 -4
  144. package/dist/routes/surface.js.map +1 -1
  145. package/dist/routes/tenant-credentials.js.map +1 -1
  146. package/dist/routes/work-items.js.map +1 -1
  147. package/dist/rules/binding-context.js +11 -9
  148. package/dist/rules/binding-context.js.map +1 -1
  149. package/dist/rules/dispatcher.d.ts +1 -1
  150. package/dist/rules/dispatcher.d.ts.map +1 -1
  151. package/dist/rules/dispatcher.js +10 -10
  152. package/dist/rules/dispatcher.js.map +1 -1
  153. package/dist/rules/start-coordinator.d.ts +4 -0
  154. package/dist/rules/start-coordinator.d.ts.map +1 -1
  155. package/dist/rules/start-coordinator.js +36 -4
  156. package/dist/rules/start-coordinator.js.map +1 -1
  157. package/dist/rules/tools.js +2 -2
  158. package/dist/rules/tools.js.map +1 -1
  159. package/dist/rules/transition-service.js.map +1 -1
  160. package/dist/sandbox/callback-factory-sandbox.js.map +1 -1
  161. package/dist/sandbox/local-factory-sandbox.js.map +1 -1
  162. package/dist/sandbox/session-retirement.js.map +1 -1
  163. package/dist/secret-encryption.d.ts.map +1 -1
  164. package/dist/secret-encryption.js +65 -25
  165. package/dist/secret-encryption.js.map +1 -1
  166. package/dist/session/channel-reply-processor.d.ts +7 -6
  167. package/dist/session/channel-reply-processor.d.ts.map +1 -1
  168. package/dist/session/channel-reply-processor.js +9 -9
  169. package/dist/session/channel-reply-processor.js.map +1 -1
  170. package/dist/session/channel-session-link-processor.js +1 -0
  171. package/dist/session/channel-session-link-processor.js.map +1 -1
  172. package/dist/session/credential-policy.d.ts.map +1 -1
  173. package/dist/session/credential-policy.js +7 -6
  174. package/dist/session/credential-policy.js.map +1 -1
  175. package/dist/session/default-model-hydration.d.ts +1 -0
  176. package/dist/session/default-model-hydration.d.ts.map +1 -1
  177. package/dist/session/default-model-hydration.js +3 -2
  178. package/dist/session/default-model-hydration.js.map +1 -1
  179. package/dist/session/environment-state-processor.js +1 -1
  180. package/dist/session/environment-state-processor.js.map +1 -1
  181. package/dist/session/filesystem-capture.d.ts +3 -3
  182. package/dist/session/filesystem-capture.d.ts.map +1 -1
  183. package/dist/session/filesystem-capture.js +16 -17
  184. package/dist/session/filesystem-capture.js.map +1 -1
  185. package/dist/session/first-exec-capture.d.ts +1 -1
  186. package/dist/session/first-exec-capture.d.ts.map +1 -1
  187. package/dist/session/first-exec-capture.js +1 -1
  188. package/dist/session/first-exec-capture.js.map +1 -1
  189. package/dist/session/first-message-capture.d.ts +1 -1
  190. package/dist/session/first-message-capture.d.ts.map +1 -1
  191. package/dist/session/first-message-capture.js +1 -1
  192. package/dist/session/first-message-capture.js.map +1 -1
  193. package/dist/session/live-sessions.d.ts +1 -1
  194. package/dist/session/live-sessions.js +1 -1
  195. package/dist/session/live-sessions.js.map +1 -1
  196. package/dist/session/memory-settings-hydration.d.ts +1 -1
  197. package/dist/session/memory-settings-hydration.d.ts.map +1 -1
  198. package/dist/session/memory-settings-hydration.js +2 -1
  199. package/dist/session/memory-settings-hydration.js.map +1 -1
  200. package/dist/session/resource-migration.d.ts +105 -0
  201. package/dist/session/resource-migration.d.ts.map +1 -0
  202. package/dist/session/resource-migration.js +195 -0
  203. package/dist/session/resource-migration.js.map +1 -0
  204. package/dist/session/session-threads.d.ts +58 -0
  205. package/dist/session/session-threads.d.ts.map +1 -0
  206. package/dist/session/session-threads.js +90 -0
  207. package/dist/session/session-threads.js.map +1 -0
  208. package/dist/session/source-control-tools.js +8 -8
  209. package/dist/session/source-control-tools.js.map +1 -1
  210. package/dist/session/thinking-default.d.ts.map +1 -1
  211. package/dist/session/thinking-default.js +3 -4
  212. package/dist/session/thinking-default.js.map +1 -1
  213. package/dist/session/thread-session-stamp.d.ts +21 -0
  214. package/dist/session/thread-session-stamp.d.ts.map +1 -0
  215. package/dist/session/thread-session-stamp.js +20 -0
  216. package/dist/session/thread-session-stamp.js.map +1 -0
  217. package/dist/session/thread-title-mirror.d.ts +1 -1
  218. package/dist/session/thread-title-mirror.d.ts.map +1 -1
  219. package/dist/session/thread-title-mirror.js +1 -1
  220. package/dist/session/thread-title-mirror.js.map +1 -1
  221. package/dist/skills/service.d.ts +6 -8
  222. package/dist/skills/service.d.ts.map +1 -1
  223. package/dist/skills/service.js +3 -3
  224. package/dist/skills/service.js.map +1 -1
  225. package/dist/spa-static.js.map +1 -1
  226. package/dist/state-signing.js +1 -1
  227. package/dist/state-signing.js.map +1 -1
  228. package/dist/storage/domains/audit/agent-audit.d.ts.map +1 -1
  229. package/dist/storage/domains/audit/agent-audit.js +2 -2
  230. package/dist/storage/domains/audit/agent-audit.js.map +1 -1
  231. package/dist/storage/domains/audit/base.js.map +1 -1
  232. package/dist/storage/domains/audit/domain.js.map +1 -1
  233. package/dist/storage/domains/channel-identity/base.js.map +1 -1
  234. package/dist/storage/domains/comments/base.js.map +1 -1
  235. package/dist/storage/domains/comments/domain.js.map +1 -1
  236. package/dist/storage/domains/comments/feed-context.js.map +1 -1
  237. package/dist/storage/domains/credentials/base.js.map +1 -1
  238. package/dist/storage/domains/custom-providers/base.js.map +1 -1
  239. package/dist/storage/domains/intake/base.js.map +1 -1
  240. package/dist/storage/domains/integrations/base.d.ts +8 -0
  241. package/dist/storage/domains/integrations/base.d.ts.map +1 -1
  242. package/dist/storage/domains/integrations/base.js +7 -0
  243. package/dist/storage/domains/integrations/base.js.map +1 -1
  244. package/dist/storage/domains/memory-settings/base.js.map +1 -1
  245. package/dist/storage/domains/model-defaults/base.js.map +1 -1
  246. package/dist/storage/domains/projects/base.js.map +1 -1
  247. package/dist/storage/domains/queue-health/base.js.map +1 -1
  248. package/dist/storage/domains/source-control/base.d.ts +19 -0
  249. package/dist/storage/domains/source-control/base.d.ts.map +1 -1
  250. package/dist/storage/domains/source-control/base.js +42 -0
  251. package/dist/storage/domains/source-control/base.js.map +1 -1
  252. package/dist/storage/domains/source-control/inmemory.d.ts +5 -1
  253. package/dist/storage/domains/source-control/inmemory.d.ts.map +1 -1
  254. package/dist/storage/domains/source-control/inmemory.js +7 -0
  255. package/dist/storage/domains/source-control/inmemory.js.map +1 -1
  256. package/dist/storage/domains/work-items/base.d.ts +14 -0
  257. package/dist/storage/domains/work-items/base.d.ts.map +1 -1
  258. package/dist/storage/domains/work-items/base.js +25 -1
  259. package/dist/storage/domains/work-items/base.js.map +1 -1
  260. package/dist/storage/domains/work-items/board-version.js.map +1 -1
  261. package/dist/storage/domains/work-items/metrics.js.map +1 -1
  262. package/dist/tools/approval-tools.js +1 -1
  263. package/dist/tools/approval-tools.js.map +1 -1
  264. package/dist/tools/deliver.d.ts +8 -5
  265. package/dist/tools/deliver.d.ts.map +1 -1
  266. package/dist/tools/deliver.js +22 -8
  267. package/dist/tools/deliver.js.map +1 -1
  268. package/dist/tools/messaging-tools.d.ts +1 -1
  269. package/dist/tools/messaging-tools.d.ts.map +1 -1
  270. package/dist/tools/messaging-tools.js +203 -72
  271. package/dist/tools/messaging-tools.js.map +1 -1
  272. package/dist/tools/permissions.d.ts +1 -1
  273. package/dist/tools/permissions.d.ts.map +1 -1
  274. package/dist/tools/permissions.js +1 -1
  275. package/dist/tools/permissions.js.map +1 -1
  276. package/dist/tools/reports.d.ts.map +1 -1
  277. package/dist/tools/reports.js +10 -3
  278. package/dist/tools/reports.js.map +1 -1
  279. package/dist/tools/run-watch.d.ts +2 -0
  280. package/dist/tools/run-watch.d.ts.map +1 -1
  281. package/dist/tools/run-watch.js +42 -2
  282. package/dist/tools/run-watch.js.map +1 -1
  283. package/dist/tools/scope.d.ts +2 -1
  284. package/dist/tools/scope.d.ts.map +1 -1
  285. package/dist/tools/scope.js +3 -2
  286. package/dist/tools/scope.js.map +1 -1
  287. package/dist/tools/session-tools.d.ts +1 -1
  288. package/dist/tools/session-tools.d.ts.map +1 -1
  289. package/dist/tools/session-tools.js +33 -16
  290. package/dist/tools/session-tools.js.map +1 -1
  291. package/dist/tools/sessions-state-processor.js +2 -2
  292. package/dist/tools/sessions-state-processor.js.map +1 -1
  293. package/dist/tools/side-sessions.d.ts +1 -1
  294. package/dist/tools/side-sessions.d.ts.map +1 -1
  295. package/dist/tools/side-sessions.js +1 -1
  296. package/dist/tools/side-sessions.js.map +1 -1
  297. package/dist/workspace.d.ts +5 -2
  298. package/dist/workspace.d.ts.map +1 -1
  299. package/dist/workspace.js +28 -15
  300. package/dist/workspace.js.map +1 -1
  301. package/package.json +13 -13
@@ -1 +1 @@
1
- {"version":3,"file":"knowledge.js","names":["#inWindow","#windowIds","#fallbackCache","#store","#maxFallbackLookups","#trackOutOfWindow","#fallbackLookups","#cappedSeen","#limits","#resolveTenant","#resolveView","#vouchIdentityScopes","#hiddenSessionIds","#sessionNames","#pinnedNodeIds","#pinnedRecords","#selectedView","#visibleActivity"],"sources":["../../src/routes/knowledge.ts"],"sourcesContent":["/**\n * Read-only Mastra `apiRoutes` exposing the factory project's knowledge graph.\n *\n * Serves the Knowledge page in factory-ui through bounded scope-tree,\n * selected-subgraph, node-detail, and activity reads. Every endpoint is a GET;\n * this module never writes knowledge.\n *\n * Scoping is fail-closed: the org and resource rungs are derived server-side\n * from the authenticated caller and the validated `:id` project. The DEFAULT\n * view queries `[org:<orgId>, resource:<projectId>]` (org + project records).\n * Thread-scoped records are reachable ONLY via an explicit, server-validated\n * `threadId` query parameter (the drill-down view), which appends the thread\n * rung to the query scope. A thread is drillable iff it produced knowledge\n * visible under the caller's org/project prefix; unknown or cross-org threads\n * 404 — never a silent fallback to the default view.\n */\n\nimport type { Knowledge } from '@mastra/core/knowledge';\nimport type { ApiRoute } from '@mastra/core/server';\nimport { registerApiRoute } from '@mastra/core/server';\nimport type {\n KnowledgeActivityEvent,\n KnowledgeNode,\n KnowledgeRecord,\n KnowledgeScope,\n KnowledgeScopeNodeSummary,\n KnowledgeStorage,\n ListKnowledgeScopeMembersOutput,\n} from '@mastra/core/storage';\nimport {\n canonicalizeKnowledgeScope,\n isKnowledgeScopeVisible,\n KnowledgeUnsupportedCapabilityError,\n knowledgeScopeKey,\n parseKnowledgeWikilinks,\n} from '@mastra/core/storage';\nimport type { Context } from 'hono';\n\nimport type { FactoryProjectsStorage } from '../storage/domains/projects/base.js';\nimport type { RouteDependencies } from './route.js';\nimport { Route } from './route.js';\n\n/** Reserved node that anchors pinned records (see subconscious/pinned.ts). */\nconst PINNED_NODE_NAME = 'pinned';\n\n/** Hover-card budget for record text shipped in the graph payload. */\nconst RECORD_TEXT_LIMIT = 240;\n\nfunction truncateRecordText(text: string): string {\n return text.length > RECORD_TEXT_LIMIT ? `${text.slice(0, RECORD_TEXT_LIMIT - 1)}…` : text;\n}\n\nconst UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/** Authorized visible events returned per activity page. */\nconst ACTIVITY_PAGE_SIZE = 25;\n/** Events fetched per `listActivity` batch while filling a visible page. */\nconst ACTIVITY_BATCH = 100;\n/** Hard scan cap so a huge hidden backlog cannot spin the request. */\nconst ACTIVITY_SCAN_CAP = 1000;\n/** Maximum visible nodes inspected for non-prefix name matches. */\nconst SEARCH_SCAN_CAP = 1000;\n/** Compact result window returned to the search popover. */\nconst SEARCH_RESULT_LIMIT = 20;\n/** Prevent an unbounded query string from reaching storage. */\nconst SEARCH_QUERY_LIMIT = 128;\n/** Scope nodes returned per parent while lazily expanding the tree. */\nconst SCOPE_PAGE_SIZE = 25;\n/** Bound storage pressure while calculating direct-member counts. */\nconst SCOPE_COUNT_CONCURRENCY = 8;\n\nexport type KnowledgeScopeLevel = 'org' | 'resource' | 'thread';\n\nexport interface KnowledgeScopeTreeNode extends KnowledgeScopeNodeSummary {\n /** Viewer-visible direct members, capped by the route's node window. */\n memberCount: number;\n /** True when more direct members may exist beyond `memberCount`. */\n memberCountTruncated: boolean;\n /** Viewer-visible content nodes directly assigned to this scope. */\n contentNodeCount: number;\n /** Direct child scopes available for lazy expansion. */\n childScopeCount: number;\n}\n\nexport interface KnowledgeSearchResult {\n id: string;\n name: string;\n kind: string;\n type: 'scope' | 'node';\n rung: KnowledgeScopeLevel | null;\n /** Present when a thread-scoped content result needs to restore its owning session lens. */\n threadId?: string;\n}\n\nexport interface KnowledgeSearchPayload {\n results: KnowledgeSearchResult[];\n truncated: boolean;\n}\n\nexport interface KnowledgeScopeTreePayload {\n roots: Array<{\n level: KnowledgeScopeLevel;\n id: string;\n available: boolean;\n /**\n * Set when a reconciled structural scope node has this rung's canonical\n * address (`org:<orgId>`, `resource:<projectId>`, `thread:<threadId>`) —\n * the client renders ONE merged entry (structural name, identity marker)\n * that opens the structural lens instead of duplicating the scope.\n */\n scopeNodeId?: string;\n /** Structural name of the matched scope node (present iff scopeNodeId). */\n name?: string;\n }>;\n defaultLevel: 'resource';\n /**\n * Reconciled structural scope tree (isScope nodes with membership edges),\n * e.g. `mastra → features → memory` and `repo:mastra → issues/prs`. Omitted\n * only when the storage adapter does not expose structural scope nodes.\n */\n scopeNodes?: KnowledgeScopeTreeNode[];\n /** Cursor for the next sibling page (roots when parentId is omitted). */\n nextCursor?: string;\n /** Initial child-page cursors keyed by parent scope id. */\n childCursors?: Record<string, string>;\n /** True when the org has more scopes than the route reads, so whole scopes are missing from the tree. */\n truncated?: boolean;\n}\n\n/** Window caps. Injectable at construction only — never per-request. */\nexport interface KnowledgeRouteLimits {\n /** Max nodes in a graph snapshot (newest-first). */\n maxNodes: number;\n /** Max records parsed for edges per snapshot (newest-first). */\n maxRecords: number;\n /** Max fallback `resolveNode` store lookups per request (deduped per unique name+scope). */\n maxFallbackLookups: number;\n /** Max `listScopeNodes` pages (of up to 1,000 scopes) read for one org; past it responses report `truncated`. */\n maxOrgScopePages: number;\n}\n\nconst DEFAULT_LIMITS: KnowledgeRouteLimits = {\n maxNodes: 500,\n maxRecords: 2000,\n maxFallbackLookups: 100,\n maxOrgScopePages: 10,\n};\n\nexport interface KnowledgeRoutesDeps extends RouteDependencies {\n /** Factory projects domain — validates the `:id` project belongs to the caller's org. */\n projects: FactoryProjectsStorage;\n /** Lazy handle to the host-registered Knowledge runtime; endpoints 503 when absent. */\n knowledge: (key: string) => Promise<Knowledge | undefined>;\n /** Host-selected Knowledge key. Requests cannot override it; endpoints 503 when it does not resolve. */\n defaultKnowledgeKey?: string;\n limits?: Partial<KnowledgeRouteLimits>;\n /**\n * Current title of a thread, read when a session scope is listed so its label follows renames.\n * Sessions fall back to `session <first 8 of id>` when absent or untitled.\n */\n threadTitle?: (threadId: string) => Promise<string | undefined>;\n}\n\n/** A graph node. `recordCount` is window-derived (records inside the snapshot window only). */\nexport interface KnowledgeGraphNode {\n id: string;\n name: string;\n kind: string;\n description?: string;\n /** Null for structural scope-node members (global, not identity-scoped). */\n scope: KnowledgeScope | null;\n /** Deepest rung of the record's scope: org | resource | thread; null for scope nodes. */\n rung: 'org' | 'resource' | 'thread' | null;\n /** True for structural scope nodes (member-lens drill targets); absent for content nodes. */\n isScope?: boolean;\n /**\n * True when a non-deleted pinned record's wikilinks reference ONLY this\n * node (A9: multi-target pins mark their edges instead — the pin is\n * about the relationship; a single-target pin has no edge to carry it).\n */\n pinned: boolean;\n /** Records owned by this node INSIDE the snapshot window (not a total). */\n recordCount: number;\n /** Viewer-visible direct-member counts, present only for structural scope nodes. */\n memberCount?: number;\n memberCountTruncated?: boolean;\n contentNodeCount?: number;\n childScopeCount?: number;\n /** Omitted for a structural lens root when the adapter cannot read it back as a node. */\n createdAt?: string;\n updatedAt?: string;\n}\n\nexport interface KnowledgeGraphEdge {\n id: string;\n /** The owning node of the record (its `node`); the scope node for containment edges. */\n source: string;\n /** The wikilink-resolved node; the contained member for containment edges. */\n target: string;\n /**\n * 'wikilink': derived from a record's wikilinks (the record's `node` is the\n * edge SOURCE, so the plan's \"parent link\" collapses into the wikilink edge).\n * 'contains': structural lens only — the selected scope node contains the\n * target member, so the clicked scope is visible as its own graph root.\n */\n type: 'wikilink' | 'contains';\n /** The record whose text produced the edge; absent on containment edges. */\n recordId?: string;\n /**\n * True when the edge is derived from a PINNED record linking two nodes —\n * the pin marks the relationship, so the accent lives on the edge (A9).\n */\n pinned?: boolean;\n}\n\n/**\n * A knowledge record as a first-class graph element (A11): every record in the window,\n * with the in-window nodes it touches. The client renders by arity —\n * 1 node: a small dot linked to it; 2: the connecting line; 3+: a midpoint\n * junction splitting to each node. Pin records have their hidden reserved\n * owner omitted, so their arity comes purely from wikilink targets.\n */\nexport interface KnowledgeGraphRecord {\n /** The record id. */\n id: string;\n /** Owner node first (omitted for pins), then resolved wikilink targets. */\n nodeIds: string[];\n pinned: boolean;\n /** Record text, truncated for hover cards. */\n text: string;\n}\n\nexport interface KnowledgeGraphPayload {\n view: 'project' | 'thread';\n threadId?: string;\n nodes: KnowledgeGraphNode[];\n edges: KnowledgeGraphEdge[];\n records: KnowledgeGraphRecord[];\n /** True when the node or record window cap was hit (newest-first window). */\n truncated: boolean;\n /** Authorized wikilink targets outside the node window, including enough scope context for a deep-link jump. */\n outOfWindow: Array<{\n id: string;\n name: string;\n scope: KnowledgeScope;\n rung: 'org' | 'resource' | 'thread';\n }>;\n /** Unique unknown names skipped once the fallback-lookup cap was hit. */\n unresolvedCapped: { count: number; names: string[] };\n /** Pin counts per rung of the active view (thread is null in the default view). */\n pinCensus: { resource: number; thread: number | null };\n /** Change hint: newest knowledge activity id (per-process monotonic — hint only). */\n version: string | null;\n}\n\nexport interface KnowledgeNodeRecordPayload {\n id: string;\n node: string;\n /** 'owned' when the node is the record's parent, 'mentions' when it only wikilinks it. */\n relation: 'owned' | 'mentions';\n text: string;\n scope: KnowledgeScope;\n rung: 'org' | 'resource' | 'thread';\n sourceThreadId: string;\n capturedAt: string;\n when?: string;\n pinned: boolean;\n metadata?: Record<string, unknown>;\n}\n\nexport interface KnowledgeActivityPayload {\n events: Array<{\n id: string;\n action: string;\n recordType: string;\n recordId?: string;\n scope: KnowledgeScope;\n node: {\n id: string;\n name: string;\n rung: 'org' | 'resource' | 'thread';\n threadId?: string;\n };\n createdAt: string;\n }>;\n nextCursor?: string;\n /** The scope has more members than the activity filter reads; events for the rest are omitted. */\n truncated?: true;\n}\n\ninterface VisibleKnowledgeActivity {\n event: KnowledgeActivityEvent;\n node: KnowledgeNode;\n}\n\nexport interface KnowledgeNodePayload {\n node: {\n id: string;\n name: string;\n kind: string;\n content: string;\n description?: string;\n scope: KnowledgeScope;\n rung: 'org' | 'resource' | 'thread';\n createdAt: string;\n updatedAt: string;\n };\n records: KnowledgeNodeRecordPayload[];\n}\n\nfunction loose(c: unknown): Context {\n return c as Context;\n}\n\nfunction deepestRung(scope: KnowledgeScope): 'org' | 'resource' | 'thread' {\n let rung: 'org' | 'resource' | 'thread' = 'org';\n for (const entry of scope) {\n const ns = entry.slice(0, entry.indexOf(':'));\n if (ns === 'thread') return 'thread';\n if (ns === 'resource') rung = 'resource';\n }\n return rung;\n}\n\n/**\n * True when `scope` is a prefix of the active view scope — the content lives\n * inside the current org + project boundary. Structural member lenses use\n * this to keep sibling-project knowledge out of a project-scoped view.\n */\nfunction withinViewBoundary(scope: KnowledgeScope, viewScope: KnowledgeScope): boolean {\n return scope.length <= viewScope.length && scope.every((entry, index) => entry === viewScope[index]);\n}\n\nfunction directScopeMemberCounts(\n { members, hasMore }: ListKnowledgeScopeMembersOutput,\n childScopeCount: number,\n viewScope: KnowledgeScope,\n maxNodes: number,\n): Pick<KnowledgeScopeTreeNode, 'memberCount' | 'memberCountTruncated' | 'contentNodeCount' | 'childScopeCount'> {\n const contentNodeCount = members.filter(\n member => !member.isScope && Array.isArray(member.scope) && withinViewBoundary(member.scope, viewScope),\n ).length;\n const directMemberCount = childScopeCount + contentNodeCount;\n return {\n memberCount: Math.min(directMemberCount, maxNodes),\n memberCountTruncated: directMemberCount > maxNodes || hasMore,\n contentNodeCount,\n childScopeCount,\n };\n}\n\nfunction compareScopeNodes(a: KnowledgeScopeNodeSummary, b: KnowledgeScopeNodeSummary): number {\n return a.name.localeCompare(b.name) || a.id.localeCompare(b.id);\n}\n\n/**\n * Scopes in one org (the org scope and everything beneath it), filtered and paged in storage.\n * Reads at most `maxPages` pages; `truncated` is true when scopes remain past that point.\n */\nasync function listOrgScopeNodes(\n store: KnowledgeStorage,\n orgId: string,\n maxPages: number,\n): Promise<{ scopes: KnowledgeScopeNodeSummary[]; truncated: boolean }> {\n const scopes: KnowledgeScopeNodeSummary[] = [];\n let cursor: string | undefined;\n for (let page = 0; page < maxPages; page += 1) {\n const result = await store.listScopeNodes({ withinAddress: `org:${orgId}`, cursor });\n scopes.push(...result.scopes);\n if (!result.nextCursor) return { scopes, truncated: false };\n cursor = result.nextCursor;\n }\n return { scopes, truncated: true };\n}\n\n/** One scope node, only if it is in the org. Independent of how many scopes the org has. */\nasync function findOrgScopeNode(\n store: KnowledgeStorage,\n orgId: string,\n scopeNodeId: string,\n): Promise<KnowledgeScopeNodeSummary | undefined> {\n const { scopes } = await store.listScopeNodes({ withinAddress: `org:${orgId}`, ids: [scopeNodeId], limit: 1 });\n return scopes[0];\n}\n\nfunction knowledgeSearchRank(name: string, query: string): number {\n const normalized = name.toLocaleLowerCase();\n if (normalized === query) return 0;\n if (normalized.startsWith(query)) return 1;\n return 2;\n}\n\n/**\n * What a scope node is, for labels: the rung name for vouched identity scopes, otherwise the\n * stored kind, or \"topic\" for structural scopes created without one. Matches the scope list.\n */\nfunction scopeKindLabel(address: string, kind: string | null | undefined): string {\n if (address.startsWith('org:')) return 'org';\n if (address.startsWith('resource:')) return 'project';\n if (address.startsWith('thread:')) return 'session';\n return kind || 'topic';\n}\n\n/** The thread id of a session (`thread:<id>`) scope node, or undefined for any other scope. */\nfunction sessionThreadId(node: { address: string }): string | undefined {\n return node.address.startsWith('thread:') ? boundedThreadId(node.address.slice('thread:'.length)) : undefined;\n}\n\nfunction boundedThreadId(raw: string | undefined): string | undefined {\n if (!raw) return undefined;\n const trimmed = raw.trim();\n return trimmed.length > 0 && trimmed.length <= 512 ? trimmed : undefined;\n}\n\ninterface ResolvedView {\n orgId: string;\n userId: string;\n factoryProjectId: string;\n factoryProjectName: string;\n /** The host-owned Knowledge runtime (source of host-vouched scope materialization). */\n knowledge: Knowledge;\n store: KnowledgeStorage;\n view: 'project' | 'thread';\n threadId?: string;\n /** The query scope for the active view. */\n scope: KnowledgeScope;\n /** Exact scopes where a reserved `pinned` node may live for this view. */\n pinRungs: Array<{ rung: 'resource' | 'thread'; scope: KnowledgeScope }>;\n}\n\n/**\n * In-record + capped-fallback wikilink resolver, shared by both endpoints.\n * Resolution uses the store's own algorithm: a descending-prefix walk over the\n * record's canonical scope matching canonical name + exact scope key at each\n * prefix, so an edge never depends on whether the target landed in the window.\n */\nclass WikilinkResolver {\n /** exact `${scopeKey}\\u0000${lowerName}` → node, from the fetched window. */\n readonly #inWindow = new Map<string, KnowledgeNode>();\n readonly #windowIds = new Set<string>();\n /** `${recordScopeKey}\\u0000${lowerName}` → fallback result (null = dangling). */\n readonly #fallbackCache = new Map<string, KnowledgeNode | null>();\n #fallbackLookups = 0;\n readonly #store: KnowledgeStorage;\n readonly #maxFallbackLookups: number;\n readonly outOfWindow = new Map<\n string,\n { id: string; name: string; scope: KnowledgeScope; rung: 'org' | 'resource' | 'thread' }\n >();\n readonly cappedNames: string[] = [];\n #cappedSeen = new Set<string>();\n\n constructor(store: KnowledgeStorage, nodes: KnowledgeNode[], maxFallbackLookups: number) {\n this.#store = store;\n this.#maxFallbackLookups = maxFallbackLookups;\n for (const node of nodes) {\n this.#inWindow.set(`${knowledgeScopeKey(node.scope)}\\u0000${node.name.trim().toLocaleLowerCase()}`, node);\n this.#windowIds.add(node.id);\n }\n }\n\n inWindowId(id: string): boolean {\n return this.#windowIds.has(id);\n }\n\n /** Resolve a wikilink name from a knowledge record's scope. Returns the node or null (dangling/capped). */\n async resolve(name: string, recordScope: KnowledgeScope): Promise<KnowledgeNode | null> {\n const canonical = canonicalizeKnowledgeScope(recordScope);\n const lower = name.trim().toLocaleLowerCase();\n for (let length = canonical.length; length > 0; length--) {\n const hit = this.#inWindow.get(`${knowledgeScopeKey(canonical.slice(0, length))}\\u0000${lower}`);\n if (hit) return hit;\n }\n const cacheKey = `${knowledgeScopeKey(canonical)}\\u0000${lower}`;\n if (this.#fallbackCache.has(cacheKey)) {\n return this.#trackOutOfWindow(this.#fallbackCache.get(cacheKey) ?? null);\n }\n if (this.#fallbackLookups >= this.#maxFallbackLookups) {\n if (!this.#cappedSeen.has(lower) && this.cappedNames.length < 100) {\n this.#cappedSeen.add(lower);\n this.cappedNames.push(name.trim());\n } else if (!this.#cappedSeen.has(lower)) {\n this.#cappedSeen.add(lower);\n }\n return null;\n }\n this.#fallbackLookups += 1;\n let resolved: KnowledgeNode | null = null;\n try {\n resolved = await this.#store.resolveNode({ name, scope: canonical });\n } catch {\n resolved = null;\n }\n this.#fallbackCache.set(cacheKey, resolved);\n return this.#trackOutOfWindow(resolved);\n }\n\n #trackOutOfWindow(node: KnowledgeNode | null): KnowledgeNode | null {\n if (node && !this.#windowIds.has(node.id)) {\n this.outOfWindow.set(node.id, {\n id: node.id,\n name: node.name,\n scope: node.scope,\n rung: deepestRung(node.scope),\n });\n }\n return node;\n }\n\n get cappedCount(): number {\n return this.#cappedSeen.size;\n }\n}\n\nexport class KnowledgeRoutes extends Route<KnowledgeRoutesDeps> {\n readonly #limits: KnowledgeRouteLimits;\n\n constructor(deps: KnowledgeRoutesDeps) {\n super(deps);\n this.#limits = { ...DEFAULT_LIMITS, ...deps.limits };\n }\n\n /** Resolve the `(orgId, userId)` tenant or a ready-to-return error response. */\n async #resolveTenant(c: Context): Promise<{ orgId: string; userId: string } | { response: Response }> {\n await this.deps.auth.ensureUser(c);\n const tenant = this.deps.auth.tenant(c);\n if (!tenant) return { response: c.json({ error: 'unauthorized' }, 401) };\n if (!tenant.orgId) {\n return {\n response: c.json(\n { error: 'organization_required', message: 'The knowledge graph requires an organization.' },\n 403,\n ),\n };\n }\n return { orgId: tenant.orgId, userId: tenant.userId };\n }\n\n /**\n * Resolve tenant + org-owned project + knowledge store + the active view's\n * query scope. The ONE seam both endpoints share, so the default/thread view\n * scope and the pin rungs cannot drift between them.\n *\n * threadId validation runs a single `listRecordsBySource` lookup with\n * `limit: 1` AT THE CANDIDATE SCOPE `[org, resource, thread:<id>]` — the\n * store's own visibility predicate is the authorization: the thread's own\n * records (equal scope key) and its project/org captures (prefix) match, while\n * a cross-org thread's records match nothing → zero rows → 404.\n */\n async #resolveView(c: Context): Promise<ResolvedView | { response: Response }> {\n const tenant = await this.#resolveTenant(c);\n if ('response' in tenant) return tenant;\n\n const projectId = c.req.param('id');\n if (!projectId || !UUID_RE.test(projectId)) {\n return { response: c.json({ error: 'Project not found' }, 404) };\n }\n const { projects } = this.deps;\n await projects.ensureReady();\n const project = await projects.get({ orgId: tenant.orgId, id: projectId });\n if (!project) {\n return { response: c.json({ error: 'Project not found' }, 404) };\n }\n\n let knowledge: Knowledge | undefined;\n let store: KnowledgeStorage | undefined;\n try {\n // Host-selected key only: an untrusted query parameter must never select\n // another registered Knowledge runtime.\n const key = this.deps.defaultKnowledgeKey ?? 'default';\n knowledge = await this.deps.knowledge(key);\n store = knowledge ? await knowledge.getStorage() : undefined;\n } catch {\n store = undefined;\n }\n if (!store || !knowledge) {\n return {\n response: c.json(\n { error: 'knowledge_unavailable', message: 'The configured Knowledge runtime is unavailable.' },\n 503,\n ),\n };\n }\n\n const defaultScope: KnowledgeScope = [`org:${tenant.orgId}`, `resource:${projectId}`];\n const resourceRungScope = defaultScope;\n\n const threadId = boundedThreadId(c.req.query('threadId'));\n if (c.req.query('threadId') !== undefined && !threadId) {\n return { response: c.json({ error: 'thread_not_found' }, 404) };\n }\n if (!threadId) {\n return {\n ...tenant,\n factoryProjectId: projectId,\n factoryProjectName: project.name,\n knowledge,\n store,\n view: 'project',\n scope: defaultScope,\n pinRungs: [{ rung: 'resource', scope: resourceRungScope }],\n };\n }\n\n const candidateScope: KnowledgeScope = [...defaultScope, `thread:${threadId}`];\n const probe = await store.knowledgeBySource({ sourceThreadId: threadId, scope: candidateScope, limit: 1 });\n if (probe.records.length === 0) {\n return { response: c.json({ error: 'thread_not_found' }, 404) };\n }\n return {\n ...tenant,\n factoryProjectId: projectId,\n factoryProjectName: project.name,\n knowledge,\n store,\n view: 'thread',\n threadId,\n scope: candidateScope,\n pinRungs: [\n { rung: 'resource', scope: resourceRungScope },\n { rung: 'thread', scope: candidateScope },\n ],\n };\n }\n\n #selectedView(view: ResolvedView, level: string | undefined): ResolvedView | undefined {\n const projectView = {\n orgId: view.orgId,\n userId: view.userId,\n factoryProjectId: view.factoryProjectId,\n factoryProjectName: view.factoryProjectName,\n knowledge: view.knowledge,\n store: view.store,\n view: 'project' as const,\n };\n if (level === 'org') return { ...projectView, scope: view.scope.slice(0, 1), pinRungs: [] };\n if (level === 'resource') {\n return {\n ...projectView,\n scope: view.scope.slice(0, 2),\n pinRungs: view.pinRungs.filter(rung => rung.rung === 'resource'),\n };\n }\n if (level === 'thread' && view.threadId) return view;\n return undefined;\n }\n\n /**\n * Host-vouched identity chain: the org → project → session rungs this\n * request resolved are materialized as ordinary scope nodes linked by\n * membership edges, so the sidebar tree is built from scopes that EXIST —\n * never from the declared structure plan or the rung config. Materialization\n * is create-only and deduped by address (existing nodes keep their declared\n * names/parents), so the steady state is a no-op skip. No companion scope is\n * created here or anywhere automatically. A vouch failure never fails the read —\n * the tree simply serves the scopes that already exist.\n */\n async #vouchIdentityScopes(view: ResolvedView): Promise<void> {\n const orgAddress = `org:${view.orgId}`;\n const resourceAddress = `resource:${view.factoryProjectId}`;\n const chain = [\n { address: orgAddress, contextualScopeAddress: orgAddress },\n {\n address: resourceAddress,\n name: view.factoryProjectName,\n contextualScopeAddress: orgAddress,\n parentAddresses: [orgAddress],\n },\n ...(view.threadId\n ? [\n {\n address: `thread:${view.threadId}`,\n contextualScopeAddress: resourceAddress,\n parentAddresses: [resourceAddress],\n },\n ]\n : []),\n ];\n let existing: Set<string>;\n try {\n const { scopes } = await view.store.listScopeNodes({ addresses: chain.map(link => link.address) });\n existing = new Set(scopes.map(node => node.address));\n } catch (error) {\n // Adapters without structural scope nodes have no tree to vouch into.\n if (error instanceof KnowledgeUnsupportedCapabilityError) return;\n throw error;\n }\n for (const link of chain) {\n if (existing.has(link.address)) continue;\n await view.knowledge.materializeScope(link).catch(() => undefined);\n }\n }\n\n /**\n * Session scopes are listed only under the viewed project, and only when the `threadId`\n * drill-down would accept them (a visible record captured in that session), so no listing\n * exposes a session id the drill-down would reject. Returns the ids of hidden session nodes.\n * With `probe: false` only the cheap project check runs; callers probe the bounded page they return.\n */\n async #hiddenSessionIds(\n view: ResolvedView,\n nodes: KnowledgeScopeNodeSummary[],\n projectNodeId: string | undefined,\n { probe }: { probe: boolean },\n ): Promise<Set<string>> {\n const hidden = new Set<string>();\n const projectScope: KnowledgeScope = [`org:${view.orgId}`, `resource:${view.factoryProjectId}`];\n const sessions: Array<{ id: string; threadId: string }> = [];\n for (const node of nodes) {\n if (!node.address.startsWith('thread:')) continue;\n const threadId = sessionThreadId(node);\n if (!threadId || !projectNodeId || !node.parentIds.includes(projectNodeId)) {\n hidden.add(node.id);\n } else if (probe && threadId !== view.threadId) {\n sessions.push({ id: node.id, threadId });\n }\n }\n for (let index = 0; index < sessions.length; index += SCOPE_COUNT_CONCURRENCY) {\n await Promise.all(\n sessions.slice(index, index + SCOPE_COUNT_CONCURRENCY).map(async ({ id, threadId }) => {\n const { records } = await view.store.knowledgeBySource({\n sourceThreadId: threadId,\n scope: [...projectScope, `thread:${threadId}`],\n limit: 1,\n });\n if (records.length === 0) hidden.add(id);\n }),\n );\n }\n return hidden;\n }\n\n /**\n * Display names for session scopes, resolved at read time from the thread's current title\n * (never stored on the scope node, where it would go stale). Keyed by scope node id.\n */\n async #sessionNames(nodes: Array<{ id: string; address: string }>): Promise<Map<string, string>> {\n const names = new Map<string, string>();\n const sessions = nodes.flatMap(node => {\n const threadId = node.address.startsWith('thread:') ? node.address.slice('thread:'.length) : '';\n return threadId ? [{ id: node.id, threadId }] : [];\n });\n for (let index = 0; index < sessions.length; index += SCOPE_COUNT_CONCURRENCY) {\n await Promise.all(\n sessions.slice(index, index + SCOPE_COUNT_CONCURRENCY).map(async ({ id, threadId }) => {\n let title: string | undefined;\n try {\n title = (await this.deps.threadTitle?.(threadId))?.trim();\n } catch {\n // A failed title read only costs the label; fall back below.\n }\n names.set(id, title || `session ${threadId.slice(0, 8)}`);\n }),\n );\n }\n return names;\n }\n\n /** Reserved `pinned` node ids at the active view's rungs (one exact-scope lookup per rung). */\n async #pinnedNodeIds(view: ResolvedView): Promise<Array<{ rung: 'resource' | 'thread'; id: string }>> {\n const out: Array<{ rung: 'resource' | 'thread'; id: string }> = [];\n for (const { rung, scope } of view.pinRungs) {\n const node = await view.store.getNodeByName({ name: PINNED_NODE_NAME, scope });\n if (node && !node.mergedInto) out.push({ rung, id: node.id });\n }\n return out;\n }\n\n /** Non-deleted pinned records for the given pinned-node ids, visible in the view. */\n async #pinnedRecords(\n view: ResolvedView,\n pinnedNodeIds: Array<{ rung: 'resource' | 'thread'; id: string }>,\n ): Promise<Array<{ rung: 'resource' | 'thread'; record: KnowledgeRecord }>> {\n const out: Array<{ rung: 'resource' | 'thread'; record: KnowledgeRecord }> = [];\n for (const { rung, id } of pinnedNodeIds) {\n const { records } = await view.store.listKnowledgeAbout({ node: id, scope: view.scope, limit: 200 });\n for (const record of records) out.push({ rung, record });\n }\n return out;\n }\n\n routes(): ApiRoute[] {\n return [\n registerApiRoute('/web/factory/projects/:id/knowledge/scopes', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n const view = await this.#resolveView(loose(c));\n if ('response' in view) return view.response;\n // The identity chain exists as scope nodes before the tree is read.\n await this.#vouchIdentityScopes(view);\n // The structural tree comes from the reconciled scope nodes\n // themselves — never synthesized from the identity rungs. Adapters\n // without the structural read (MySQL/MongoDB) omit the tree; any\n // other failure is a real storage error and must surface as one.\n let scopeNodes: KnowledgeScopeTreeNode[] | undefined;\n let nextCursor: string | undefined;\n const childCursors: Record<string, string> = {};\n let storedScopeNodes: KnowledgeScopeNodeSummary[] = [];\n let scopesTruncated = false;\n try {\n ({ scopes: storedScopeNodes, truncated: scopesTruncated } = await listOrgScopeNodes(\n view.store,\n view.orgId,\n this.#limits.maxOrgScopePages,\n ));\n const projectNodeId = storedScopeNodes.find(\n node => node.address === `resource:${view.factoryProjectId}`,\n )?.id;\n const foreignSessions = await this.#hiddenSessionIds(view, storedScopeNodes, projectNodeId, {\n probe: false,\n });\n storedScopeNodes = storedScopeNodes.filter(node => !foreignSessions.has(node.id));\n const scopeNodeById = new Map(storedScopeNodes.map(node => [node.id, node]));\n const childScopeCountByParent = new Map<string, number>();\n for (const node of storedScopeNodes) {\n for (const parentId of node.parentIds) {\n childScopeCountByParent.set(parentId, (childScopeCountByParent.get(parentId) ?? 0) + 1);\n }\n }\n\n const parentId = loose(c).req.query('parentId');\n const cursor = loose(c).req.query('cursor');\n if (parentId && !UUID_RE.test(parentId)) {\n return c.json({ error: 'invalid_scope_parent' }, 400);\n }\n if (cursor && !UUID_RE.test(cursor)) {\n return c.json({ error: 'invalid_scope_cursor' }, 400);\n }\n if (parentId && !scopeNodeById.has(parentId)) {\n return c.json({ error: 'scope_not_found' }, 404);\n }\n\n const siblings = storedScopeNodes\n .filter(node => (parentId ? node.parentIds.includes(parentId) : node.parentIds.length === 0))\n .sort(compareScopeNodes);\n const cursorIndex = cursor ? siblings.findIndex(node => node.id === cursor) : -1;\n if (cursor && cursorIndex < 0) {\n return c.json({ error: 'invalid_scope_cursor' }, 400);\n }\n const pageStart = cursorIndex + 1;\n const siblingPage = siblings.slice(pageStart, pageStart + SCOPE_PAGE_SIZE);\n if (pageStart + siblingPage.length < siblings.length) {\n nextCursor = siblingPage.at(-1)?.id;\n }\n\n const returnedById = new Map(siblingPage.map(node => [node.id, node]));\n if (!parentId) {\n // Initial load: roots plus their first page of immediate children.\n // Deeper levels are fetched only when that scope is expanded.\n const addFirstChildPage = (parent: KnowledgeScopeNodeSummary): void => {\n const children = storedScopeNodes\n .filter(node => node.parentIds.includes(parent.id))\n .sort(compareScopeNodes);\n const returnedChildren: KnowledgeScopeNodeSummary[] = [];\n for (const child of children.slice(0, SCOPE_PAGE_SIZE)) {\n if (returnedById.size >= this.#limits.maxNodes) break;\n returnedById.set(child.id, child);\n returnedChildren.push(child);\n }\n if (returnedChildren.length < children.length) {\n const lastChild = returnedChildren.at(-1);\n if (lastChild) childCursors[parent.id] = lastChild.id;\n }\n };\n for (const root of siblingPage) addFirstChildPage(root);\n\n // The active identity chain must remain present even when it is\n // deeper than one level, or rung/tree dedup would regress on a\n // thread view. Include only those matched nodes and ancestors.\n const identityAddresses = new Set([\n `org:${view.orgId}`,\n `resource:${view.factoryProjectId}`,\n ...(view.threadId ? [`thread:${view.threadId}`] : []),\n ]);\n const includeWithParents = (node: KnowledgeScopeNodeSummary): void => {\n if (returnedById.has(node.id) || returnedById.size >= this.#limits.maxNodes) return;\n for (const ancestorId of node.parentIds) {\n const ancestor = scopeNodeById.get(ancestorId);\n if (ancestor) includeWithParents(ancestor);\n }\n returnedById.set(node.id, node);\n };\n for (const node of storedScopeNodes) {\n if (identityAddresses.has(node.address)) includeWithParents(node);\n }\n // The viewed project's first page of children (its sessions among them) loads\n // with the tree, so a session is listed without expanding the project first.\n const projectNode = projectNodeId ? scopeNodeById.get(projectNodeId) : undefined;\n if (projectNode && returnedById.has(projectNode.id)) addFirstChildPage(projectNode);\n }\n\n const unreadableSessions = await this.#hiddenSessionIds(view, [...returnedById.values()], projectNodeId, {\n probe: true,\n });\n for (const id of unreadableSessions) returnedById.delete(id);\n\n scopeNodes = [];\n const returnedNodes = [...returnedById.values()].sort(compareScopeNodes);\n for (let index = 0; index < returnedNodes.length; index += SCOPE_COUNT_CONCURRENCY) {\n const batch = returnedNodes.slice(index, index + SCOPE_COUNT_CONCURRENCY);\n scopeNodes.push(\n ...(await Promise.all(\n batch.map(async node => {\n const members = await view.store.listScopeMembers({\n scopeNodeId: node.id,\n limit: this.#limits.maxNodes,\n });\n return {\n ...node,\n ...(node.address === `resource:${view.factoryProjectId}`\n ? { name: view.factoryProjectName }\n : {}),\n ...directScopeMemberCounts(\n members,\n childScopeCountByParent.get(node.id) ?? 0,\n view.scope,\n this.#limits.maxNodes,\n ),\n };\n }),\n )),\n );\n }\n const sessionNames = await this.#sessionNames(scopeNodes);\n scopeNodes = scopeNodes.map(node => ({\n ...node,\n kind: scopeKindLabel(node.address, node.kind),\n ...(sessionNames.has(node.id) ? { name: sessionNames.get(node.id)! } : {}),\n }));\n } catch (error) {\n if (!(error instanceof KnowledgeUnsupportedCapabilityError)) throw error;\n scopeNodes = undefined;\n }\n // Deduplicate: when a reconciled scope node owns an identity rung's\n // canonical address, the rung entry carries the structural match so\n // the client renders one entry instead of two labels for one scope.\n const scopeNodeByAddress = new Map(storedScopeNodes.map(node => [node.address, node]));\n const threadRoot = view.threadId ? scopeNodeByAddress.get(`thread:${view.threadId}`) : undefined;\n const threadRootName = threadRoot ? (await this.#sessionNames([threadRoot])).get(threadRoot.id) : undefined;\n const rungs = [\n { level: 'org' as const, id: view.orgId, available: true },\n { level: 'resource' as const, id: view.factoryProjectId, available: true },\n ...(view.threadId ? [{ level: 'thread' as const, id: view.threadId, available: true }] : []),\n ];\n return c.json({\n roots: rungs.map(rung => {\n const match = scopeNodeByAddress.get(`${rung.level}:${rung.id}`);\n const name =\n match?.address === `resource:${view.factoryProjectId}`\n ? view.factoryProjectName\n : match && rung.level === 'thread'\n ? threadRootName\n : match?.name;\n return match ? { ...rung, scopeNodeId: match.id, name } : rung;\n }),\n defaultLevel: 'resource',\n ...(scopeNodes ? { scopeNodes } : {}),\n ...(nextCursor ? { nextCursor } : {}),\n ...(Object.keys(childCursors).length > 0 ? { childCursors } : {}),\n ...(scopesTruncated ? { truncated: true } : {}),\n } satisfies KnowledgeScopeTreePayload);\n },\n }),\n registerApiRoute('/web/factory/projects/:id/knowledge/search', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n const view = await this.#resolveView(loose(c));\n if ('response' in view) return view.response;\n\n const query = (loose(c).req.query('q') ?? '').trim().slice(0, SEARCH_QUERY_LIMIT).toLocaleLowerCase();\n if (query.length < 2) {\n return c.json({ results: [], truncated: false } satisfies KnowledgeSearchPayload);\n }\n\n const scanLimit = Math.min(SEARCH_SCAN_CAP, this.#limits.maxNodes);\n const [prefixNodes, scannedNodes] = await Promise.all([\n view.store.listNodes({ scope: view.scope, namePrefix: query, limit: SEARCH_RESULT_LIMIT + 1 }),\n view.store.listNodes({ scope: view.scope, limit: scanLimit }),\n ]);\n const contentById = new Map<string, KnowledgeNode>();\n for (const node of [...prefixNodes, ...scannedNodes]) {\n if (\n node.isScope ||\n !Array.isArray(node.scope) ||\n !withinViewBoundary(node.scope, view.scope) ||\n !node.name.toLocaleLowerCase().includes(query)\n ) {\n continue;\n }\n contentById.set(node.id, node);\n }\n\n let structuralScopes: KnowledgeScopeNodeSummary[] = [];\n let scopesTruncated = false;\n try {\n const orgScopes = await listOrgScopeNodes(view.store, view.orgId, this.#limits.maxOrgScopePages);\n scopesTruncated = orgScopes.truncated;\n // Sessions are reached from the tree, which checks each one; search lists only the open one.\n structuralScopes = orgScopes.scopes.filter(\n node =>\n (!node.address.startsWith('thread:') || sessionThreadId(node) === view.threadId) &&\n node.name.toLocaleLowerCase().includes(query),\n );\n } catch (error) {\n if (!(error instanceof KnowledgeUnsupportedCapabilityError)) throw error;\n }\n\n const scopeSessionNames = await this.#sessionNames(structuralScopes);\n const results: KnowledgeSearchResult[] = [\n ...structuralScopes.map(scopeNode => {\n const rung =\n scopeNode.address === `org:${view.orgId}`\n ? ('org' as const)\n : scopeNode.address === `resource:${view.factoryProjectId}`\n ? ('resource' as const)\n : view.threadId && scopeNode.address === `thread:${view.threadId}`\n ? ('thread' as const)\n : null;\n return {\n id: scopeNode.id,\n name:\n scopeNode.address === `resource:${view.factoryProjectId}`\n ? view.factoryProjectName\n : (scopeSessionNames.get(scopeNode.id) ?? scopeNode.name),\n kind: scopeKindLabel(scopeNode.address, scopeNode.kind),\n type: 'scope' as const,\n rung,\n address: scopeNode.address,\n ...(scopeNode.description ? { description: scopeNode.description } : {}),\n };\n }),\n ...[...contentById.values()].map(node => {\n const rung = deepestRung(node.scope);\n const threadId =\n rung === 'thread' ? node.scope.find(address => address.startsWith('thread:'))?.slice(7) : undefined;\n return {\n id: node.id,\n name: node.name,\n kind: node.kind,\n type: 'node' as const,\n rung,\n ...(threadId ? { threadId } : {}),\n };\n }),\n ].sort(\n (a, b) =>\n knowledgeSearchRank(a.name, query) - knowledgeSearchRank(b.name, query) ||\n a.name.localeCompare(b.name) ||\n a.id.localeCompare(b.id),\n );\n\n return c.json({\n results: results.slice(0, SEARCH_RESULT_LIMIT),\n truncated:\n scopesTruncated ||\n results.length > SEARCH_RESULT_LIMIT ||\n prefixNodes.length > SEARCH_RESULT_LIMIT ||\n scannedNodes.length >= scanLimit,\n } satisfies KnowledgeSearchPayload);\n },\n }),\n registerApiRoute('/web/factory/projects/:id/knowledge/subgraph', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n const resolved = await this.#resolveView(loose(c));\n if ('response' in resolved) return resolved.response;\n\n // Structural lens: a reconciled scope node selected in the scope\n // tree. The lens renders the scope node ITSELF as the graph root\n // (containment edges to every member), child scopes as drill\n // targets, and content members with their records and wikilink\n // edges — the same window machinery as the identity lens. Content\n // members stay inside the active project boundary: this is a\n // project-scoped view, so knowledge stamped at another resource of\n // the same org never rolls up here. Pins do not apply to\n // structural members.\n const scopeNodeId = loose(c).req.query('scopeNodeId');\n if (scopeNodeId !== undefined) {\n if (!UUID_RE.test(scopeNodeId)) return c.json({ error: 'scope_not_found' }, 404);\n const limits = this.#limits;\n const { store } = resolved;\n\n let storedRoot: KnowledgeScopeNodeSummary | undefined;\n try {\n storedRoot = await findOrgScopeNode(store, resolved.orgId, scopeNodeId);\n } catch (error) {\n // Adapters without the structural read have no lenses to serve.\n if (error instanceof KnowledgeUnsupportedCapabilityError) {\n return c.json({ error: 'scope_not_found' }, 404);\n }\n throw error;\n }\n if (!storedRoot) return c.json({ error: 'scope_not_found' }, 404);\n // Child-scope counts for the lens come from the org's scopes, which may be capped.\n const { scopes: scopeNodes, truncated: scopesTruncated } = await listOrgScopeNodes(\n store,\n resolved.orgId,\n limits.maxOrgScopePages,\n );\n const rootSessionName = (await this.#sessionNames([storedRoot])).get(storedRoot.id);\n const root =\n storedRoot.address === `resource:${resolved.factoryProjectId}`\n ? { ...storedRoot, name: resolved.factoryProjectName }\n : rootSessionName\n ? { ...storedRoot, name: rootSessionName }\n : storedRoot;\n // A session lens needs the same check as the tree; other lenses never list sessions,\n // which are reached from the tree instead.\n if (storedRoot.address.startsWith('thread:')) {\n const projectNodeId = scopeNodes.find(\n node => node.address === `resource:${resolved.factoryProjectId}`,\n )?.id;\n const hidden = await this.#hiddenSessionIds(resolved, [storedRoot], projectNodeId, { probe: true });\n if (hidden.has(storedRoot.id)) return c.json({ error: 'scope_not_found' }, 404);\n }\n const otherSessionIds = new Set(\n scopeNodes\n .filter(node => node.address.startsWith('thread:') && sessionThreadId(node) !== resolved.threadId)\n .map(node => node.id),\n );\n\n const fetched = await store.listScopeMembers({ scopeNodeId, limit: limits.maxNodes });\n const bounded = fetched.members.filter(\n node =>\n (node.isScope && !otherSessionIds.has(node.id)) ||\n (!node.isScope && Array.isArray(node.scope) && withinViewBoundary(node.scope, resolved.scope)),\n );\n let truncated = scopesTruncated || fetched.hasMore || bounded.length > limits.maxNodes;\n const members = bounded.slice(0, limits.maxNodes);\n const contentMembers = members.filter(\n (node): node is KnowledgeNode => !node.isScope && Array.isArray(node.scope),\n );\n const childScopeCountByParent = new Map<string, number>();\n for (const node of scopeNodes) {\n for (const parentId of node.parentIds) {\n childScopeCountByParent.set(parentId, (childScopeCountByParent.get(parentId) ?? 0) + 1);\n }\n }\n const scopeCountsById = new Map<\n string,\n Pick<\n KnowledgeScopeTreeNode,\n 'memberCount' | 'memberCountTruncated' | 'contentNodeCount' | 'childScopeCount'\n >\n >([\n [\n root.id,\n directScopeMemberCounts(\n fetched,\n childScopeCountByParent.get(root.id) ?? 0,\n resolved.scope,\n limits.maxNodes,\n ),\n ],\n ]);\n const memberScopes = members.filter(node => node.isScope);\n for (let index = 0; index < memberScopes.length; index += SCOPE_COUNT_CONCURRENCY) {\n const batch = memberScopes.slice(index, index + SCOPE_COUNT_CONCURRENCY);\n await Promise.all(\n batch.map(async node => {\n const directMembers = await store.listScopeMembers({\n scopeNodeId: node.id,\n limit: limits.maxNodes,\n });\n scopeCountsById.set(\n node.id,\n directScopeMemberCounts(\n directMembers,\n childScopeCountByParent.get(node.id) ?? 0,\n resolved.scope,\n limits.maxNodes,\n ),\n );\n }),\n );\n }\n\n // Record window over content members (newest-first overall), then\n // wikilink resolution — member lenses are connected, not dots.\n const recordWindow: KnowledgeRecord[] = [];\n for (const node of contentMembers) {\n if (recordWindow.length > limits.maxRecords) break;\n const { records } = await store.listKnowledgeAbout({\n node: node.id,\n scope: node.scope as KnowledgeScope,\n limit: limits.maxRecords + 1 - recordWindow.length,\n });\n recordWindow.push(...records);\n }\n recordWindow.sort((a, b) => b.id.localeCompare(a.id));\n if (recordWindow.length > limits.maxRecords) {\n truncated = true;\n recordWindow.length = limits.maxRecords;\n }\n\n const resolver = new WikilinkResolver(store, contentMembers, limits.maxFallbackLookups);\n const edges: KnowledgeGraphEdge[] = [];\n const graphRecords: KnowledgeGraphRecord[] = [];\n const edgeSeen = new Set<string>();\n const recordCounts = new Map<string, number>();\n for (const record of recordWindow) {\n recordCounts.set(record.node, (recordCounts.get(record.node) ?? 0) + 1);\n const nodeIds = [record.node];\n for (const name of parseKnowledgeWikilinks(record.text)) {\n const target = await resolver.resolve(name, record.scope);\n if (!target) continue;\n if (target.id === record.node) continue;\n if (!resolver.inWindowId(target.id)) continue;\n if (!nodeIds.includes(target.id)) nodeIds.push(target.id);\n const key = `${record.node}\\u0000${target.id}`;\n if (edgeSeen.has(key)) continue;\n edgeSeen.add(key);\n edges.push({\n id: `wikilink:${record.node}:${target.id}`,\n source: record.node,\n target: target.id,\n type: 'wikilink',\n recordId: record.id,\n });\n }\n graphRecords.push({ id: record.id, nodeIds, pinned: false, text: truncateRecordText(record.text) });\n }\n\n // Containment: the clicked scope node is the root of its own view.\n for (const member of members) {\n edges.push({\n id: `contains:${root.id}:${member.id}`,\n source: root.id,\n target: member.id,\n type: 'contains',\n });\n }\n\n const scopeAddressById = new Map(scopeNodes.map(node => [node.id, node.address]));\n const memberSessionNames = await this.#sessionNames(\n members.flatMap(node => {\n const address = node.isScope ? scopeAddressById.get(node.id) : undefined;\n return address ? [{ id: node.id, address }] : [];\n }),\n );\n const rootRow = await store.getNode(root.id);\n const activity = await store.listActivity({ scope: resolved.scope, limit: 1 });\n\n const payload: KnowledgeGraphPayload = {\n view: resolved.view,\n ...(resolved.threadId ? { threadId: resolved.threadId } : {}),\n nodes: [\n {\n id: root.id,\n name: root.name,\n kind: scopeKindLabel(root.address, root.kind),\n ...(root.description ? { description: root.description } : {}),\n scope: null,\n rung: null,\n isScope: true,\n pinned: false,\n recordCount: 0,\n ...scopeCountsById.get(root.id),\n ...(rootRow\n ? { createdAt: rootRow.createdAt.toISOString(), updatedAt: rootRow.updatedAt.toISOString() }\n : {}),\n },\n ...members.map(node => {\n const nodeScope = Array.isArray(node.scope) ? node.scope : null;\n const address = node.isScope ? scopeAddressById.get(node.id) : undefined;\n return {\n id: node.id,\n name: memberSessionNames.get(node.id) ?? node.name,\n kind: node.isScope ? scopeKindLabel(address ?? '', node.kind) : node.kind,\n ...(node.description ? { description: node.description } : {}),\n scope: nodeScope,\n rung: nodeScope ? deepestRung(nodeScope) : null,\n ...(node.isScope ? { isScope: true, ...scopeCountsById.get(node.id) } : {}),\n pinned: false,\n recordCount: recordCounts.get(node.id) ?? 0,\n createdAt: node.createdAt.toISOString(),\n updatedAt: node.updatedAt.toISOString(),\n };\n }),\n ],\n edges,\n records: graphRecords,\n truncated,\n outOfWindow: [...resolver.outOfWindow.values()],\n unresolvedCapped: { count: resolver.cappedCount, names: resolver.cappedNames },\n pinCensus: { resource: 0, thread: null },\n version: activity[0]?.id ?? null,\n };\n return c.json(payload);\n }\n\n const view = this.#selectedView(resolved, loose(c).req.query('scopeLevel'));\n if (!view) return c.json({ error: 'scope_not_found' }, 404);\n const { store, scope } = view;\n const limits = this.#limits;\n\n // Nodes, newest-first; +1 to detect truncation. SQL adapters return\n // global scope nodes (scope: null) from identity-scope reads — they\n // are already surfaced via /knowledge/scopes, so exclude them here\n // before truncation, records, and wikilink resolution.\n const fetched = (await store.listNodes({ scope, limit: limits.maxNodes + 1 })).filter(node =>\n Array.isArray(node.scope),\n );\n let truncated = fetched.length > limits.maxNodes;\n const pinnedNodeIds = await this.#pinnedNodeIds(view);\n const pinnedNodeIdSet = new Set(pinnedNodeIds.map(p => p.id));\n const nodes = fetched.slice(0, limits.maxNodes).filter(node => !pinnedNodeIdSet.has(node.id));\n\n // Record window: per-node owned records, then newest-first overall.\n const recordWindow: KnowledgeRecord[] = [];\n for (const node of nodes) {\n if (recordWindow.length > limits.maxRecords) break;\n const { records } = await store.listKnowledgeAbout({\n node: node.id,\n scope,\n limit: limits.maxRecords + 1 - recordWindow.length,\n });\n recordWindow.push(...records);\n }\n // Record ids are ULIDs — descending id = newest-first.\n recordWindow.sort((a, b) => b.id.localeCompare(a.id));\n if (recordWindow.length > limits.maxRecords) {\n truncated = true;\n recordWindow.length = limits.maxRecords;\n }\n\n const resolver = new WikilinkResolver(store, nodes, limits.maxFallbackLookups);\n\n // Edges: owner node (the record's parent link) → wikilinked node.\n // Graph records: every windowed record with its in-window node set,\n // owner first. The client renders dots, lines, or junctions by arity.\n const edges: KnowledgeGraphEdge[] = [];\n const graphRecords: KnowledgeGraphRecord[] = [];\n const edgeSeen = new Set<string>();\n const recordCounts = new Map<string, number>();\n for (const record of recordWindow) {\n recordCounts.set(record.node, (recordCounts.get(record.node) ?? 0) + 1);\n const nodeIds = [record.node];\n for (const name of parseKnowledgeWikilinks(record.text)) {\n const target = await resolver.resolve(name, record.scope);\n if (!target) continue;\n if (target.id === record.node) continue;\n if (!resolver.inWindowId(target.id)) continue;\n if (!nodeIds.includes(target.id)) nodeIds.push(target.id);\n const key = `${record.node}\\u0000${target.id}`;\n if (edgeSeen.has(key)) continue;\n edgeSeen.add(key);\n edges.push({\n id: `wikilink:${record.node}:${target.id}`,\n source: record.node,\n target: target.id,\n type: 'wikilink',\n recordId: record.id,\n });\n }\n graphRecords.push({ id: record.id, nodeIds, pinned: false, text: truncateRecordText(record.text) });\n }\n\n // Pins mark relationships. The reserved owner node is omitted, so\n // arity comes purely from wikilink targets.\n const pinnedRecords = await this.#pinnedRecords(view, pinnedNodeIds);\n const accented = new Set<string>();\n for (const { record } of pinnedRecords) {\n const targets: string[] = [];\n for (const name of parseKnowledgeWikilinks(record.text)) {\n const target = await resolver.resolve(name, record.scope);\n if (target && resolver.inWindowId(target.id) && !targets.includes(target.id)) {\n targets.push(target.id);\n }\n }\n graphRecords.push({ id: record.id, nodeIds: targets, pinned: true, text: truncateRecordText(record.text) });\n if (targets.length === 1) {\n accented.add(targets[0]!);\n continue;\n }\n for (let a = 0; a < targets.length; a += 1) {\n for (let b = a + 1; b < targets.length; b += 1) {\n const key = `${targets[a]}\\u0000${targets[b]}\\u0000pin`;\n if (edgeSeen.has(key)) continue;\n edgeSeen.add(key);\n edges.push({\n id: `pin:${record.id}:${targets[a]}:${targets[b]}`,\n source: targets[a]!,\n target: targets[b]!,\n type: 'wikilink',\n recordId: record.id,\n pinned: true,\n });\n }\n }\n }\n const pinCensus = {\n resource: pinnedRecords.filter(p => p.rung === 'resource').length,\n thread: view.view === 'thread' ? pinnedRecords.filter(p => p.rung === 'thread').length : null,\n };\n\n const activity = await store.listActivity({ scope, limit: 1 });\n\n const payload: KnowledgeGraphPayload = {\n view: view.view,\n ...(view.threadId ? { threadId: view.threadId } : {}),\n nodes: nodes.map(node => ({\n id: node.id,\n name: node.name,\n kind: node.kind,\n // Empty string is a curator clear — omit it so the payload carries no dead keys.\n ...(node.description ? { description: node.description } : {}),\n scope: node.scope,\n rung: deepestRung(node.scope),\n pinned: accented.has(node.id),\n recordCount: recordCounts.get(node.id) ?? 0,\n createdAt: node.createdAt.toISOString(),\n updatedAt: node.updatedAt.toISOString(),\n })),\n edges,\n records: graphRecords,\n truncated,\n outOfWindow: [...resolver.outOfWindow.values()],\n unresolvedCapped: { count: resolver.cappedCount, names: resolver.cappedNames },\n pinCensus,\n version: activity[0]?.id ?? null,\n };\n return c.json(payload);\n },\n }),\n\n // ── Node flyout payload: details + provenance-rich records ───────────────\n registerApiRoute('/web/factory/projects/:id/knowledge/nodes/:nodeId', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n const resolved = await this.#resolveView(loose(c));\n if ('response' in resolved) return resolved.response;\n const nodeId = loose(c).req.param('nodeId');\n if (!nodeId || nodeId.length > 512) return c.json({ error: 'node_not_found' }, 404);\n\n const view = this.#selectedView(resolved, loose(c).req.query('scopeLevel'));\n if (!view) return c.json({ error: 'scope_not_found' }, 404);\n const { store, scope } = view;\n\n const node = await store.getNode(nodeId);\n // getNode is a bare id lookup with no scope predicate. This explicit\n // visibility check prevents an IDOR.\n // Structural scope nodes carry no identity scope; they are read through /scopes.\n if (!node?.scope || !isKnowledgeScopeVisible(node.scope, scope)) {\n return c.json({ error: 'node_not_found' }, 404);\n }\n\n const pinnedNodeIds = await this.#pinnedNodeIds(view);\n const pinnedNodeIdSet = new Set(pinnedNodeIds.map(p => p.id));\n\n const [owned, mentioning] = await Promise.all([\n store.listKnowledgeAbout({ node: node.id, scope, limit: 200 }),\n store.listKnowledgeMentioning({ node: node.id, scope, limit: 200 }),\n ]);\n const seen = new Set<string>();\n const records: KnowledgeNodeRecordPayload[] = [];\n const push = (record: KnowledgeRecord, relation: 'owned' | 'mentions') => {\n if (seen.has(record.id)) return;\n seen.add(record.id);\n records.push({\n id: record.id,\n node: record.node,\n relation,\n text: record.text,\n scope: record.scope,\n rung: deepestRung(record.scope),\n sourceThreadId: record.sourceThreadId,\n capturedAt: record.capturedAt.toISOString(),\n ...(record.when ? { when: record.when.toISOString() } : {}),\n pinned: pinnedNodeIdSet.has(record.node),\n ...(record.metadata ? { metadata: record.metadata } : {}),\n });\n };\n // Owned first, newest-first within each group. Record ids are ULIDs.\n for (const record of [...owned.records].sort((a, b) => b.id.localeCompare(a.id))) push(record, 'owned');\n for (const record of [...mentioning.records].sort((a, b) => b.id.localeCompare(a.id))) {\n push(record, 'mentions');\n }\n\n const payload: KnowledgeNodePayload = {\n node: {\n id: node.id,\n name: node.name,\n kind: node.kind,\n content: node.content ?? '',\n ...(node.description ? { description: node.description } : {}),\n scope: node.scope,\n rung: deepestRung(node.scope),\n createdAt: node.createdAt.toISOString(),\n updatedAt: node.updatedAt.toISOString(),\n },\n records,\n };\n return c.json(payload);\n },\n }),\n\n // ── Recent activity feed for the live-arrival affordance ───────────────\n registerApiRoute('/web/factory/projects/:id/knowledge/activity', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n const resolved = await this.#resolveView(loose(c));\n if ('response' in resolved) return resolved.response;\n const scopeNodeId = loose(c).req.query('scopeNodeId');\n let view: ResolvedView;\n let memberIds: Set<string> | undefined;\n let membersTruncated = false;\n if (scopeNodeId !== undefined) {\n if (!UUID_RE.test(scopeNodeId)) return c.json({ error: 'scope_not_found' }, 404);\n let scopeNode: KnowledgeScopeNodeSummary | undefined;\n try {\n scopeNode = await findOrgScopeNode(resolved.store, resolved.orgId, scopeNodeId);\n } catch (error) {\n if (error instanceof KnowledgeUnsupportedCapabilityError) {\n return c.json({ error: 'scope_not_found' }, 404);\n }\n throw error;\n }\n if (!scopeNode) return c.json({ error: 'scope_not_found' }, 404);\n const { members, hasMore } = await resolved.store.listScopeMembers({\n scopeNodeId,\n limit: this.#limits.maxNodes,\n });\n membersTruncated = hasMore || members.length > this.#limits.maxNodes;\n memberIds = new Set(\n members\n .slice(0, this.#limits.maxNodes)\n .filter(member => !member.isScope)\n .map(member => member.id),\n );\n view = resolved;\n } else {\n const selected = this.#selectedView(resolved, loose(c).req.query('scopeLevel'));\n if (!selected) return c.json({ error: 'scope_not_found' }, 404);\n view = selected;\n }\n const cursor = loose(c).req.query('cursor');\n if (cursor && cursor.length > 128) return c.json({ error: 'invalid_activity_cursor' }, 400);\n const { items: activity, scanCursor } = await this.#visibleActivity(\n view,\n memberIds,\n cursor,\n ACTIVITY_PAGE_SIZE + 1,\n );\n const visiblePage = activity.slice(0, ACTIVITY_PAGE_SIZE);\n const lastVisible = visiblePage.at(-1);\n const payload: KnowledgeActivityPayload = {\n events: visiblePage.map(({ event, node }) => {\n const rung = deepestRung(node.scope);\n const threadId =\n rung === 'thread' ? node.scope.find(address => address.startsWith('thread:'))?.slice(7) : undefined;\n return {\n id: event.id,\n action: event.action,\n recordType: event.recordType,\n ...(event.recordType === 'record' ? { recordId: event.recordId } : {}),\n scope: event.scope,\n node: {\n id: node.id,\n name: node.name,\n rung,\n ...(threadId ? { threadId } : {}),\n },\n createdAt: event.createdAt.toISOString(),\n };\n }),\n ...(activity.length > ACTIVITY_PAGE_SIZE && lastVisible\n ? { nextCursor: lastVisible.event.id }\n : scanCursor\n ? { nextCursor: scanCursor }\n : {}),\n ...(membersTruncated ? { truncated: true as const } : {}),\n };\n return c.json(payload);\n },\n }),\n ];\n }\n\n /**\n * Newest-first activity page where EVERY returned event targets a node\n * visible in the view. Visibility is decided BEFORE window shaping: hidden\n * rows are skipped entirely (no action/type/id/time metadata leaks) and a\n * hidden backlog can never displace visible events from the window.\n * When the scan cap stops the walk before the stream ends, `scanCursor` is\n * the last scanned event so the caller can continue past hidden rows.\n */\n async #visibleActivity(\n view: ResolvedView,\n memberIds: Set<string> | undefined,\n cursor: string | undefined,\n limit: number,\n ): Promise<{ items: VisibleKnowledgeActivity[]; scanCursor?: string }> {\n const out: VisibleKnowledgeActivity[] = [];\n // Many events in a scan target the same node; read each node once per request.\n const nodes = new Map<string, Promise<KnowledgeNode | null>>();\n const getNode = (id: string) => {\n let node = nodes.get(id);\n if (!node) {\n node = view.store.getNode(id);\n nodes.set(id, node);\n }\n return node;\n };\n let after = cursor;\n let scanned = 0;\n let exhausted = false;\n while (out.length < limit && scanned < ACTIVITY_SCAN_CAP) {\n const batch = await view.store.listActivity({ scope: view.scope, after, limit: ACTIVITY_BATCH });\n if (batch.length === 0) {\n exhausted = true;\n break;\n }\n for (const event of batch) {\n after = event.id;\n scanned += 1;\n let targetNode: KnowledgeNode | null;\n if (event.recordType === 'node') {\n targetNode = await getNode(event.recordId);\n } else {\n const record = await view.store.getKnowledge({ id: event.recordId });\n targetNode = record ? await getNode(record.node) : null;\n if (record && !isKnowledgeScopeVisible(record.scope, view.scope)) targetNode = null;\n }\n if (\n targetNode?.scope &&\n isKnowledgeScopeVisible(targetNode.scope, view.scope) &&\n (memberIds === undefined || memberIds.has(targetNode.id))\n ) {\n out.push({ event, node: targetNode });\n }\n if (out.length >= limit || scanned >= ACTIVITY_SCAN_CAP) break;\n }\n }\n const stoppedAtCap = !exhausted && out.length < limit && scanned >= ACTIVITY_SCAN_CAP;\n return { items: out, ...(stoppedAtCap && after ? { scanCursor: after } : {}) };\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"knowledge.js","names":[],"sources":["../../src/routes/knowledge.ts"],"sourcesContent":["/**\n * Read-only Mastra `apiRoutes` exposing the factory project's knowledge graph.\n *\n * Serves the Knowledge page in factory-ui through bounded scope-tree,\n * selected-subgraph, node-detail, and activity reads. Every endpoint is a GET;\n * this module never writes knowledge.\n *\n * Scoping is fail-closed: the org and resource rungs are derived server-side\n * from the authenticated caller and the validated `:id` project. The DEFAULT\n * view queries `[org:<orgId>, resource:<projectId>]` (org + project records).\n * Thread-scoped records are reachable ONLY via an explicit, server-validated\n * `threadId` query parameter (the drill-down view), which appends the thread\n * rung to the query scope. A thread is drillable iff it produced knowledge\n * visible under the caller's org/project prefix; unknown or cross-org threads\n * 404 — never a silent fallback to the default view.\n */\n\nimport type { Knowledge } from '@mastra/core/knowledge';\nimport type { ApiRoute } from '@mastra/core/server';\nimport { registerApiRoute } from '@mastra/core/server';\nimport type {\n KnowledgeActivityEvent,\n KnowledgeNode,\n KnowledgeRecord,\n KnowledgeScope,\n KnowledgeScopeNodeSummary,\n KnowledgeStorage,\n ListKnowledgeScopeMembersOutput,\n} from '@mastra/core/storage';\nimport {\n canonicalizeKnowledgeScope,\n isKnowledgeScopeVisible,\n KnowledgeUnsupportedCapabilityError,\n knowledgeScopeKey,\n parseKnowledgeWikilinks,\n} from '@mastra/core/storage';\nimport type { Context } from 'hono';\n\nimport type { FactoryProjectsStorage } from '../storage/domains/projects/base.js';\nimport type { RouteDependencies } from './route.js';\nimport { Route } from './route.js';\n\n/** Reserved node that anchors pinned records (see subconscious/pinned.ts). */\nconst PINNED_NODE_NAME = 'pinned';\n\n/** Hover-card budget for record text shipped in the graph payload. */\nconst RECORD_TEXT_LIMIT = 240;\n\nfunction truncateRecordText(text: string): string {\n return text.length > RECORD_TEXT_LIMIT ? `${text.slice(0, RECORD_TEXT_LIMIT - 1)}…` : text;\n}\n\nconst UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/** Authorized visible events returned per activity page. */\nconst ACTIVITY_PAGE_SIZE = 25;\n/** Events fetched per `listActivity` batch while filling a visible page. */\nconst ACTIVITY_BATCH = 100;\n/** Hard scan cap so a huge hidden backlog cannot spin the request. */\nconst ACTIVITY_SCAN_CAP = 1000;\n/** Maximum visible nodes inspected for non-prefix name matches. */\nconst SEARCH_SCAN_CAP = 1000;\n/** Compact result window returned to the search popover. */\nconst SEARCH_RESULT_LIMIT = 20;\n/** Prevent an unbounded query string from reaching storage. */\nconst SEARCH_QUERY_LIMIT = 128;\n/** Scope nodes returned per parent while lazily expanding the tree. */\nconst SCOPE_PAGE_SIZE = 25;\n/** Bound storage pressure while calculating direct-member counts. */\nconst SCOPE_COUNT_CONCURRENCY = 8;\n\nexport type KnowledgeScopeLevel = 'org' | 'resource' | 'thread';\n\nexport interface KnowledgeScopeTreeNode extends KnowledgeScopeNodeSummary {\n /** Viewer-visible direct members, capped by the route's node window. */\n memberCount: number;\n /** True when more direct members may exist beyond `memberCount`. */\n memberCountTruncated: boolean;\n /** Viewer-visible content nodes directly assigned to this scope. */\n contentNodeCount: number;\n /** Direct child scopes available for lazy expansion. */\n childScopeCount: number;\n}\n\nexport interface KnowledgeSearchResult {\n id: string;\n name: string;\n kind: string;\n type: 'scope' | 'node';\n rung: KnowledgeScopeLevel | null;\n /** Present when a thread-scoped content result needs to restore its owning session lens. */\n threadId?: string;\n}\n\nexport interface KnowledgeSearchPayload {\n results: KnowledgeSearchResult[];\n truncated: boolean;\n}\n\nexport interface KnowledgeScopeTreePayload {\n roots: Array<{\n level: KnowledgeScopeLevel;\n id: string;\n available: boolean;\n /**\n * Set when a reconciled structural scope node has this rung's canonical\n * address (`org:<orgId>`, `resource:<projectId>`, `thread:<threadId>`) —\n * the client renders ONE merged entry (structural name, identity marker)\n * that opens the structural lens instead of duplicating the scope.\n */\n scopeNodeId?: string;\n /** Structural name of the matched scope node (present iff scopeNodeId). */\n name?: string;\n }>;\n defaultLevel: 'resource';\n /**\n * Reconciled structural scope tree (isScope nodes with membership edges),\n * e.g. `mastra → features → memory` and `repo:mastra → issues/prs`. Omitted\n * only when the storage adapter does not expose structural scope nodes.\n */\n scopeNodes?: KnowledgeScopeTreeNode[];\n /** Cursor for the next sibling page (roots when parentId is omitted). */\n nextCursor?: string;\n /** Initial child-page cursors keyed by parent scope id. */\n childCursors?: Record<string, string>;\n /** True when the org has more scopes than the route reads, so whole scopes are missing from the tree. */\n truncated?: boolean;\n}\n\n/** Window caps. Injectable at construction only — never per-request. */\nexport interface KnowledgeRouteLimits {\n /** Max nodes in a graph snapshot (newest-first). */\n maxNodes: number;\n /** Max records parsed for edges per snapshot (newest-first). */\n maxRecords: number;\n /** Max fallback `resolveNode` store lookups per request (deduped per unique name+scope). */\n maxFallbackLookups: number;\n /** Max `listScopeNodes` pages (of up to 1,000 scopes) read for one org; past it responses report `truncated`. */\n maxOrgScopePages: number;\n}\n\nconst DEFAULT_LIMITS: KnowledgeRouteLimits = {\n maxNodes: 500,\n maxRecords: 2000,\n maxFallbackLookups: 100,\n maxOrgScopePages: 10,\n};\n\nexport interface KnowledgeRoutesDeps extends RouteDependencies {\n /** Factory projects domain — validates the `:id` project belongs to the caller's org. */\n projects: FactoryProjectsStorage;\n /** Lazy handle to the host-registered Knowledge runtime; endpoints 503 when absent. */\n knowledge: (key: string) => Promise<Knowledge | undefined>;\n /** Host-selected Knowledge key. Requests cannot override it; endpoints 503 when it does not resolve. */\n defaultKnowledgeKey?: string;\n limits?: Partial<KnowledgeRouteLimits>;\n /**\n * Current title of a thread, read when a session scope is listed so its label follows renames.\n * Sessions fall back to `session <first 8 of id>` when absent or untitled.\n */\n threadTitle?: (threadId: string) => Promise<string | undefined>;\n}\n\n/** A graph node. `recordCount` is window-derived (records inside the snapshot window only). */\nexport interface KnowledgeGraphNode {\n id: string;\n name: string;\n kind: string;\n description?: string;\n /** Null for structural scope-node members (global, not identity-scoped). */\n scope: KnowledgeScope | null;\n /** Deepest rung of the record's scope: org | resource | thread; null for scope nodes. */\n rung: 'org' | 'resource' | 'thread' | null;\n /** True for structural scope nodes (member-lens drill targets); absent for content nodes. */\n isScope?: boolean;\n /**\n * True when a non-deleted pinned record's wikilinks reference ONLY this\n * node (A9: multi-target pins mark their edges instead — the pin is\n * about the relationship; a single-target pin has no edge to carry it).\n */\n pinned: boolean;\n /** Records owned by this node INSIDE the snapshot window (not a total). */\n recordCount: number;\n /** Viewer-visible direct-member counts, present only for structural scope nodes. */\n memberCount?: number;\n memberCountTruncated?: boolean;\n contentNodeCount?: number;\n childScopeCount?: number;\n /** Omitted for a structural lens root when the adapter cannot read it back as a node. */\n createdAt?: string;\n updatedAt?: string;\n}\n\nexport interface KnowledgeGraphEdge {\n id: string;\n /** The owning node of the record (its `node`); the scope node for containment edges. */\n source: string;\n /** The wikilink-resolved node; the contained member for containment edges. */\n target: string;\n /**\n * 'wikilink': derived from a record's wikilinks (the record's `node` is the\n * edge SOURCE, so the plan's \"parent link\" collapses into the wikilink edge).\n * 'contains': structural lens only — the selected scope node contains the\n * target member, so the clicked scope is visible as its own graph root.\n */\n type: 'wikilink' | 'contains';\n /** The record whose text produced the edge; absent on containment edges. */\n recordId?: string;\n /**\n * True when the edge is derived from a PINNED record linking two nodes —\n * the pin marks the relationship, so the accent lives on the edge (A9).\n */\n pinned?: boolean;\n}\n\n/**\n * A knowledge record as a first-class graph element (A11): every record in the window,\n * with the in-window nodes it touches. The client renders by arity —\n * 1 node: a small dot linked to it; 2: the connecting line; 3+: a midpoint\n * junction splitting to each node. Pin records have their hidden reserved\n * owner omitted, so their arity comes purely from wikilink targets.\n */\nexport interface KnowledgeGraphRecord {\n /** The record id. */\n id: string;\n /** Owner node first (omitted for pins), then resolved wikilink targets. */\n nodeIds: string[];\n pinned: boolean;\n /** Record text, truncated for hover cards. */\n text: string;\n}\n\nexport interface KnowledgeGraphPayload {\n view: 'project' | 'thread';\n threadId?: string;\n nodes: KnowledgeGraphNode[];\n edges: KnowledgeGraphEdge[];\n records: KnowledgeGraphRecord[];\n /** True when the node or record window cap was hit (newest-first window). */\n truncated: boolean;\n /** Authorized wikilink targets outside the node window, including enough scope context for a deep-link jump. */\n outOfWindow: Array<{\n id: string;\n name: string;\n scope: KnowledgeScope;\n rung: 'org' | 'resource' | 'thread';\n }>;\n /** Unique unknown names skipped once the fallback-lookup cap was hit. */\n unresolvedCapped: { count: number; names: string[] };\n /** Pin counts per rung of the active view (thread is null in the default view). */\n pinCensus: { resource: number; thread: number | null };\n /** Change hint: newest knowledge activity id (per-process monotonic — hint only). */\n version: string | null;\n}\n\nexport interface KnowledgeNodeRecordPayload {\n id: string;\n node: string;\n /** 'owned' when the node is the record's parent, 'mentions' when it only wikilinks it. */\n relation: 'owned' | 'mentions';\n text: string;\n scope: KnowledgeScope;\n rung: 'org' | 'resource' | 'thread';\n sourceThreadId: string;\n capturedAt: string;\n when?: string;\n pinned: boolean;\n metadata?: Record<string, unknown>;\n}\n\nexport interface KnowledgeActivityPayload {\n events: Array<{\n id: string;\n action: string;\n recordType: string;\n recordId?: string;\n scope: KnowledgeScope;\n node: {\n id: string;\n name: string;\n rung: 'org' | 'resource' | 'thread';\n threadId?: string;\n };\n createdAt: string;\n }>;\n nextCursor?: string;\n /** The scope has more members than the activity filter reads; events for the rest are omitted. */\n truncated?: true;\n}\n\ninterface VisibleKnowledgeActivity {\n event: KnowledgeActivityEvent;\n node: KnowledgeNode;\n}\n\nexport interface KnowledgeNodePayload {\n node: {\n id: string;\n name: string;\n kind: string;\n content: string;\n description?: string;\n scope: KnowledgeScope;\n rung: 'org' | 'resource' | 'thread';\n createdAt: string;\n updatedAt: string;\n };\n records: KnowledgeNodeRecordPayload[];\n}\n\nfunction loose(c: unknown): Context {\n return c as Context;\n}\n\nfunction deepestRung(scope: KnowledgeScope): 'org' | 'resource' | 'thread' {\n let rung: 'org' | 'resource' | 'thread' = 'org';\n for (const entry of scope) {\n const ns = entry.slice(0, entry.indexOf(':'));\n if (ns === 'thread') return 'thread';\n if (ns === 'resource') rung = 'resource';\n }\n return rung;\n}\n\n/**\n * True when `scope` is a prefix of the active view scope — the content lives\n * inside the current org + project boundary. Structural member lenses use\n * this to keep sibling-project knowledge out of a project-scoped view.\n */\nfunction withinViewBoundary(scope: KnowledgeScope, viewScope: KnowledgeScope): boolean {\n return scope.length <= viewScope.length && scope.every((entry, index) => entry === viewScope[index]);\n}\n\nfunction directScopeMemberCounts(\n { members, hasMore }: ListKnowledgeScopeMembersOutput,\n childScopeCount: number,\n viewScope: KnowledgeScope,\n maxNodes: number,\n): Pick<KnowledgeScopeTreeNode, 'memberCount' | 'memberCountTruncated' | 'contentNodeCount' | 'childScopeCount'> {\n const contentNodeCount = members.filter(\n member => !member.isScope && Array.isArray(member.scope) && withinViewBoundary(member.scope, viewScope),\n ).length;\n const directMemberCount = childScopeCount + contentNodeCount;\n return {\n memberCount: Math.min(directMemberCount, maxNodes),\n memberCountTruncated: directMemberCount > maxNodes || hasMore,\n contentNodeCount,\n childScopeCount,\n };\n}\n\nfunction compareScopeNodes(a: KnowledgeScopeNodeSummary, b: KnowledgeScopeNodeSummary): number {\n return a.name.localeCompare(b.name) || a.id.localeCompare(b.id);\n}\n\n/**\n * Scopes in one org (the org scope and everything beneath it), filtered and paged in storage.\n * Reads at most `maxPages` pages; `truncated` is true when scopes remain past that point.\n */\nasync function listOrgScopeNodes(\n store: KnowledgeStorage,\n orgId: string,\n maxPages: number,\n): Promise<{ scopes: KnowledgeScopeNodeSummary[]; truncated: boolean }> {\n const scopes: KnowledgeScopeNodeSummary[] = [];\n let cursor: string | undefined;\n for (let page = 0; page < maxPages; page += 1) {\n const result = await store.listScopeNodes({ withinAddress: `org:${orgId}`, cursor });\n scopes.push(...result.scopes);\n if (!result.nextCursor) return { scopes, truncated: false };\n cursor = result.nextCursor;\n }\n return { scopes, truncated: true };\n}\n\n/** One scope node, only if it is in the org. Independent of how many scopes the org has. */\nasync function findOrgScopeNode(\n store: KnowledgeStorage,\n orgId: string,\n scopeNodeId: string,\n): Promise<KnowledgeScopeNodeSummary | undefined> {\n const { scopes } = await store.listScopeNodes({ withinAddress: `org:${orgId}`, ids: [scopeNodeId], limit: 1 });\n return scopes[0];\n}\n\nfunction knowledgeSearchRank(name: string, query: string): number {\n const normalized = name.toLocaleLowerCase();\n if (normalized === query) return 0;\n if (normalized.startsWith(query)) return 1;\n return 2;\n}\n\n/**\n * What a scope node is, for labels: the rung name for vouched identity scopes, otherwise the\n * stored kind, or \"topic\" for structural scopes created without one. Matches the scope list.\n */\nfunction scopeKindLabel(address: string, kind: string | null | undefined): string {\n if (address.startsWith('org:')) return 'org';\n if (address.startsWith('resource:')) return 'project';\n if (address.startsWith('thread:')) return 'session';\n return kind || 'topic';\n}\n\n/** The thread id of a session (`thread:<id>`) scope node, or undefined for any other scope. */\nfunction sessionThreadId(node: { address: string }): string | undefined {\n return node.address.startsWith('thread:') ? boundedThreadId(node.address.slice('thread:'.length)) : undefined;\n}\n\nfunction boundedThreadId(raw: string | undefined): string | undefined {\n if (!raw) return undefined;\n const trimmed = raw.trim();\n return trimmed.length > 0 && trimmed.length <= 512 ? trimmed : undefined;\n}\n\ninterface ResolvedView {\n orgId: string;\n userId: string;\n factoryProjectId: string;\n factoryProjectName: string;\n /** The host-owned Knowledge runtime (source of host-vouched scope materialization). */\n knowledge: Knowledge;\n store: KnowledgeStorage;\n view: 'project' | 'thread';\n threadId?: string;\n /** The query scope for the active view. */\n scope: KnowledgeScope;\n /** Exact scopes where a reserved `pinned` node may live for this view. */\n pinRungs: Array<{ rung: 'resource' | 'thread'; scope: KnowledgeScope }>;\n}\n\n/**\n * In-record + capped-fallback wikilink resolver, shared by both endpoints.\n * Resolution uses the store's own algorithm: a descending-prefix walk over the\n * record's canonical scope matching canonical name + exact scope key at each\n * prefix, so an edge never depends on whether the target landed in the window.\n */\nclass WikilinkResolver {\n /** exact `${scopeKey}\\u0000${lowerName}` → node, from the fetched window. */\n readonly #inWindow = new Map<string, KnowledgeNode>();\n readonly #windowIds = new Set<string>();\n /** `${recordScopeKey}\\u0000${lowerName}` → fallback result (null = dangling). */\n readonly #fallbackCache = new Map<string, KnowledgeNode | null>();\n #fallbackLookups = 0;\n readonly #store: KnowledgeStorage;\n readonly #maxFallbackLookups: number;\n readonly outOfWindow = new Map<\n string,\n { id: string; name: string; scope: KnowledgeScope; rung: 'org' | 'resource' | 'thread' }\n >();\n readonly cappedNames: string[] = [];\n #cappedSeen = new Set<string>();\n\n constructor(store: KnowledgeStorage, nodes: KnowledgeNode[], maxFallbackLookups: number) {\n this.#store = store;\n this.#maxFallbackLookups = maxFallbackLookups;\n for (const node of nodes) {\n this.#inWindow.set(`${knowledgeScopeKey(node.scope)}\\u0000${node.name.trim().toLocaleLowerCase()}`, node);\n this.#windowIds.add(node.id);\n }\n }\n\n inWindowId(id: string): boolean {\n return this.#windowIds.has(id);\n }\n\n /** Resolve a wikilink name from a knowledge record's scope. Returns the node or null (dangling/capped). */\n async resolve(name: string, recordScope: KnowledgeScope): Promise<KnowledgeNode | null> {\n const canonical = canonicalizeKnowledgeScope(recordScope);\n const lower = name.trim().toLocaleLowerCase();\n for (let length = canonical.length; length > 0; length--) {\n const hit = this.#inWindow.get(`${knowledgeScopeKey(canonical.slice(0, length))}\\u0000${lower}`);\n if (hit) return hit;\n }\n const cacheKey = `${knowledgeScopeKey(canonical)}\\u0000${lower}`;\n if (this.#fallbackCache.has(cacheKey)) {\n return this.#trackOutOfWindow(this.#fallbackCache.get(cacheKey) ?? null);\n }\n if (this.#fallbackLookups >= this.#maxFallbackLookups) {\n if (!this.#cappedSeen.has(lower) && this.cappedNames.length < 100) {\n this.#cappedSeen.add(lower);\n this.cappedNames.push(name.trim());\n } else if (!this.#cappedSeen.has(lower)) {\n this.#cappedSeen.add(lower);\n }\n return null;\n }\n this.#fallbackLookups += 1;\n let resolved: KnowledgeNode | null = null;\n try {\n resolved = await this.#store.resolveNode({ name, scope: canonical });\n } catch {\n resolved = null;\n }\n this.#fallbackCache.set(cacheKey, resolved);\n return this.#trackOutOfWindow(resolved);\n }\n\n #trackOutOfWindow(node: KnowledgeNode | null): KnowledgeNode | null {\n if (node && !this.#windowIds.has(node.id)) {\n this.outOfWindow.set(node.id, {\n id: node.id,\n name: node.name,\n scope: node.scope,\n rung: deepestRung(node.scope),\n });\n }\n return node;\n }\n\n get cappedCount(): number {\n return this.#cappedSeen.size;\n }\n}\n\nexport class KnowledgeRoutes extends Route<KnowledgeRoutesDeps> {\n readonly #limits: KnowledgeRouteLimits;\n\n constructor(deps: KnowledgeRoutesDeps) {\n super(deps);\n this.#limits = { ...DEFAULT_LIMITS, ...deps.limits };\n }\n\n /** Resolve the `(orgId, userId)` tenant or a ready-to-return error response. */\n async #resolveTenant(c: Context): Promise<{ orgId: string; userId: string } | { response: Response }> {\n await this.deps.auth.ensureUser(c);\n const tenant = this.deps.auth.tenant(c);\n if (!tenant) return { response: c.json({ error: 'unauthorized' }, 401) };\n if (!tenant.orgId) {\n return {\n response: c.json(\n { error: 'organization_required', message: 'The knowledge graph requires an organization.' },\n 403,\n ),\n };\n }\n return { orgId: tenant.orgId, userId: tenant.userId };\n }\n\n /**\n * Resolve tenant + org-owned project + knowledge store + the active view's\n * query scope. The ONE seam both endpoints share, so the default/thread view\n * scope and the pin rungs cannot drift between them.\n *\n * threadId validation runs a single `listRecordsBySource` lookup with\n * `limit: 1` AT THE CANDIDATE SCOPE `[org, resource, thread:<id>]` — the\n * store's own visibility predicate is the authorization: the thread's own\n * records (equal scope key) and its project/org captures (prefix) match, while\n * a cross-org thread's records match nothing → zero rows → 404.\n */\n async #resolveView(c: Context): Promise<ResolvedView | { response: Response }> {\n const tenant = await this.#resolveTenant(c);\n if ('response' in tenant) return tenant;\n\n const projectId = c.req.param('id');\n if (!projectId || !UUID_RE.test(projectId)) {\n return { response: c.json({ error: 'Project not found' }, 404) };\n }\n const { projects } = this.deps;\n await projects.ensureReady();\n const project = await projects.get({ orgId: tenant.orgId, id: projectId });\n if (!project) {\n return { response: c.json({ error: 'Project not found' }, 404) };\n }\n\n let knowledge: Knowledge | undefined;\n let store: KnowledgeStorage | undefined;\n try {\n // Host-selected key only: an untrusted query parameter must never select\n // another registered Knowledge runtime.\n const key = this.deps.defaultKnowledgeKey ?? 'default';\n knowledge = await this.deps.knowledge(key);\n store = knowledge ? await knowledge.getStorage() : undefined;\n } catch {\n store = undefined;\n }\n if (!store || !knowledge) {\n return {\n response: c.json(\n { error: 'knowledge_unavailable', message: 'The configured Knowledge runtime is unavailable.' },\n 503,\n ),\n };\n }\n\n const defaultScope: KnowledgeScope = [`org:${tenant.orgId}`, `resource:${projectId}`];\n const resourceRungScope = defaultScope;\n\n const threadId = boundedThreadId(c.req.query('threadId'));\n if (c.req.query('threadId') !== undefined && !threadId) {\n return { response: c.json({ error: 'thread_not_found' }, 404) };\n }\n if (!threadId) {\n return {\n ...tenant,\n factoryProjectId: projectId,\n factoryProjectName: project.name,\n knowledge,\n store,\n view: 'project',\n scope: defaultScope,\n pinRungs: [{ rung: 'resource', scope: resourceRungScope }],\n };\n }\n\n const candidateScope: KnowledgeScope = [...defaultScope, `thread:${threadId}`];\n const probe = await store.knowledgeBySource({ sourceThreadId: threadId, scope: candidateScope, limit: 1 });\n if (probe.records.length === 0) {\n return { response: c.json({ error: 'thread_not_found' }, 404) };\n }\n return {\n ...tenant,\n factoryProjectId: projectId,\n factoryProjectName: project.name,\n knowledge,\n store,\n view: 'thread',\n threadId,\n scope: candidateScope,\n pinRungs: [\n { rung: 'resource', scope: resourceRungScope },\n { rung: 'thread', scope: candidateScope },\n ],\n };\n }\n\n #selectedView(view: ResolvedView, level: string | undefined): ResolvedView | undefined {\n const projectView = {\n orgId: view.orgId,\n userId: view.userId,\n factoryProjectId: view.factoryProjectId,\n factoryProjectName: view.factoryProjectName,\n knowledge: view.knowledge,\n store: view.store,\n view: 'project' as const,\n };\n if (level === 'org') return { ...projectView, scope: view.scope.slice(0, 1), pinRungs: [] };\n if (level === 'resource') {\n return {\n ...projectView,\n scope: view.scope.slice(0, 2),\n pinRungs: view.pinRungs.filter(rung => rung.rung === 'resource'),\n };\n }\n if (level === 'thread' && view.threadId) return view;\n return undefined;\n }\n\n /**\n * Host-vouched identity chain: the org → project → session rungs this\n * request resolved are materialized as ordinary scope nodes linked by\n * membership edges, so the sidebar tree is built from scopes that EXIST —\n * never from the declared structure plan or the rung config. Materialization\n * is create-only and deduped by address (existing nodes keep their declared\n * names/parents), so the steady state is a no-op skip. No companion scope is\n * created here or anywhere automatically. A vouch failure never fails the read —\n * the tree simply serves the scopes that already exist.\n */\n async #vouchIdentityScopes(view: ResolvedView): Promise<void> {\n const orgAddress = `org:${view.orgId}`;\n const resourceAddress = `resource:${view.factoryProjectId}`;\n const chain = [\n { address: orgAddress, contextualScopeAddress: orgAddress },\n {\n address: resourceAddress,\n name: view.factoryProjectName,\n contextualScopeAddress: orgAddress,\n parentAddresses: [orgAddress],\n },\n ...(view.threadId\n ? [\n {\n address: `thread:${view.threadId}`,\n contextualScopeAddress: resourceAddress,\n parentAddresses: [resourceAddress],\n },\n ]\n : []),\n ];\n let existing: Set<string>;\n try {\n const { scopes } = await view.store.listScopeNodes({ addresses: chain.map(link => link.address) });\n existing = new Set(scopes.map(node => node.address));\n } catch (error) {\n // Adapters without structural scope nodes have no tree to vouch into.\n if (error instanceof KnowledgeUnsupportedCapabilityError) return;\n throw error;\n }\n for (const link of chain) {\n if (existing.has(link.address)) continue;\n await view.knowledge.materializeScope(link).catch(() => undefined);\n }\n }\n\n /**\n * Session scopes are listed only under the viewed project, and only when the `threadId`\n * drill-down would accept them (a visible record captured in that session), so no listing\n * exposes a session id the drill-down would reject. Returns the ids of hidden session nodes.\n * With `probe: false` only the cheap project check runs; callers probe the bounded page they return.\n */\n async #hiddenSessionIds(\n view: ResolvedView,\n nodes: KnowledgeScopeNodeSummary[],\n projectNodeId: string | undefined,\n { probe }: { probe: boolean },\n ): Promise<Set<string>> {\n const hidden = new Set<string>();\n const projectScope: KnowledgeScope = [`org:${view.orgId}`, `resource:${view.factoryProjectId}`];\n const sessions: Array<{ id: string; threadId: string }> = [];\n for (const node of nodes) {\n if (!node.address.startsWith('thread:')) continue;\n const threadId = sessionThreadId(node);\n if (!threadId || !projectNodeId || !node.parentIds.includes(projectNodeId)) {\n hidden.add(node.id);\n } else if (probe && threadId !== view.threadId) {\n sessions.push({ id: node.id, threadId });\n }\n }\n for (let index = 0; index < sessions.length; index += SCOPE_COUNT_CONCURRENCY) {\n await Promise.all(\n sessions.slice(index, index + SCOPE_COUNT_CONCURRENCY).map(async ({ id, threadId }) => {\n const { records } = await view.store.knowledgeBySource({\n sourceThreadId: threadId,\n scope: [...projectScope, `thread:${threadId}`],\n limit: 1,\n });\n if (records.length === 0) hidden.add(id);\n }),\n );\n }\n return hidden;\n }\n\n /**\n * Display names for session scopes, resolved at read time from the thread's current title\n * (never stored on the scope node, where it would go stale). Keyed by scope node id.\n */\n async #sessionNames(nodes: Array<{ id: string; address: string }>): Promise<Map<string, string>> {\n const names = new Map<string, string>();\n const sessions = nodes.flatMap(node => {\n const threadId = node.address.startsWith('thread:') ? node.address.slice('thread:'.length) : '';\n return threadId ? [{ id: node.id, threadId }] : [];\n });\n for (let index = 0; index < sessions.length; index += SCOPE_COUNT_CONCURRENCY) {\n await Promise.all(\n sessions.slice(index, index + SCOPE_COUNT_CONCURRENCY).map(async ({ id, threadId }) => {\n let title: string | undefined;\n try {\n title = (await this.deps.threadTitle?.(threadId))?.trim();\n } catch {\n // A failed title read only costs the label; fall back below.\n }\n names.set(id, title || `session ${threadId.slice(0, 8)}`);\n }),\n );\n }\n return names;\n }\n\n /** Reserved `pinned` node ids at the active view's rungs (one exact-scope lookup per rung). */\n async #pinnedNodeIds(view: ResolvedView): Promise<Array<{ rung: 'resource' | 'thread'; id: string }>> {\n const out: Array<{ rung: 'resource' | 'thread'; id: string }> = [];\n for (const { rung, scope } of view.pinRungs) {\n const node = await view.store.getNodeByName({ name: PINNED_NODE_NAME, scope });\n if (node && !node.mergedInto) out.push({ rung, id: node.id });\n }\n return out;\n }\n\n /** Non-deleted pinned records for the given pinned-node ids, visible in the view. */\n async #pinnedRecords(\n view: ResolvedView,\n pinnedNodeIds: Array<{ rung: 'resource' | 'thread'; id: string }>,\n ): Promise<Array<{ rung: 'resource' | 'thread'; record: KnowledgeRecord }>> {\n const out: Array<{ rung: 'resource' | 'thread'; record: KnowledgeRecord }> = [];\n for (const { rung, id } of pinnedNodeIds) {\n const { records } = await view.store.listKnowledgeAbout({ node: id, scope: view.scope, limit: 200 });\n for (const record of records) out.push({ rung, record });\n }\n return out;\n }\n\n routes(): ApiRoute[] {\n return [\n registerApiRoute('/web/factory/projects/:id/knowledge/scopes', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n const view = await this.#resolveView(loose(c));\n if ('response' in view) return view.response;\n // The identity chain exists as scope nodes before the tree is read.\n await this.#vouchIdentityScopes(view);\n // The structural tree comes from the reconciled scope nodes\n // themselves — never synthesized from the identity rungs. Adapters\n // without the structural read (MySQL/MongoDB) omit the tree; any\n // other failure is a real storage error and must surface as one.\n let scopeNodes: KnowledgeScopeTreeNode[] | undefined;\n let nextCursor: string | undefined;\n const childCursors: Record<string, string> = {};\n let storedScopeNodes: KnowledgeScopeNodeSummary[] = [];\n let scopesTruncated = false;\n try {\n ({ scopes: storedScopeNodes, truncated: scopesTruncated } = await listOrgScopeNodes(\n view.store,\n view.orgId,\n this.#limits.maxOrgScopePages,\n ));\n const projectNodeId = storedScopeNodes.find(\n node => node.address === `resource:${view.factoryProjectId}`,\n )?.id;\n const foreignSessions = await this.#hiddenSessionIds(view, storedScopeNodes, projectNodeId, {\n probe: false,\n });\n storedScopeNodes = storedScopeNodes.filter(node => !foreignSessions.has(node.id));\n const scopeNodeById = new Map(storedScopeNodes.map(node => [node.id, node]));\n const childScopeCountByParent = new Map<string, number>();\n for (const node of storedScopeNodes) {\n for (const parentId of node.parentIds) {\n childScopeCountByParent.set(parentId, (childScopeCountByParent.get(parentId) ?? 0) + 1);\n }\n }\n\n const parentId = loose(c).req.query('parentId');\n const cursor = loose(c).req.query('cursor');\n if (parentId && !UUID_RE.test(parentId)) {\n return c.json({ error: 'invalid_scope_parent' }, 400);\n }\n if (cursor && !UUID_RE.test(cursor)) {\n return c.json({ error: 'invalid_scope_cursor' }, 400);\n }\n if (parentId && !scopeNodeById.has(parentId)) {\n return c.json({ error: 'scope_not_found' }, 404);\n }\n\n const siblings = storedScopeNodes\n .filter(node => (parentId ? node.parentIds.includes(parentId) : node.parentIds.length === 0))\n .sort(compareScopeNodes);\n const cursorIndex = cursor ? siblings.findIndex(node => node.id === cursor) : -1;\n if (cursor && cursorIndex < 0) {\n return c.json({ error: 'invalid_scope_cursor' }, 400);\n }\n const pageStart = cursorIndex + 1;\n const siblingPage = siblings.slice(pageStart, pageStart + SCOPE_PAGE_SIZE);\n if (pageStart + siblingPage.length < siblings.length) {\n nextCursor = siblingPage.at(-1)?.id;\n }\n\n const returnedById = new Map(siblingPage.map(node => [node.id, node]));\n if (!parentId) {\n // Initial load: roots plus their first page of immediate children.\n // Deeper levels are fetched only when that scope is expanded.\n const addFirstChildPage = (parent: KnowledgeScopeNodeSummary): void => {\n const children = storedScopeNodes\n .filter(node => node.parentIds.includes(parent.id))\n .sort(compareScopeNodes);\n const returnedChildren: KnowledgeScopeNodeSummary[] = [];\n for (const child of children.slice(0, SCOPE_PAGE_SIZE)) {\n if (returnedById.size >= this.#limits.maxNodes) break;\n returnedById.set(child.id, child);\n returnedChildren.push(child);\n }\n if (returnedChildren.length < children.length) {\n const lastChild = returnedChildren.at(-1);\n if (lastChild) childCursors[parent.id] = lastChild.id;\n }\n };\n for (const root of siblingPage) addFirstChildPage(root);\n\n // The active identity chain must remain present even when it is\n // deeper than one level, or rung/tree dedup would regress on a\n // thread view. Include only those matched nodes and ancestors.\n const identityAddresses = new Set([\n `org:${view.orgId}`,\n `resource:${view.factoryProjectId}`,\n ...(view.threadId ? [`thread:${view.threadId}`] : []),\n ]);\n const includeWithParents = (node: KnowledgeScopeNodeSummary): void => {\n if (returnedById.has(node.id) || returnedById.size >= this.#limits.maxNodes) return;\n for (const ancestorId of node.parentIds) {\n const ancestor = scopeNodeById.get(ancestorId);\n if (ancestor) includeWithParents(ancestor);\n }\n returnedById.set(node.id, node);\n };\n for (const node of storedScopeNodes) {\n if (identityAddresses.has(node.address)) includeWithParents(node);\n }\n // The viewed project's first page of children (its sessions among them) loads\n // with the tree, so a session is listed without expanding the project first.\n const projectNode = projectNodeId ? scopeNodeById.get(projectNodeId) : undefined;\n if (projectNode && returnedById.has(projectNode.id)) addFirstChildPage(projectNode);\n }\n\n const unreadableSessions = await this.#hiddenSessionIds(view, [...returnedById.values()], projectNodeId, {\n probe: true,\n });\n for (const id of unreadableSessions) returnedById.delete(id);\n\n scopeNodes = [];\n const returnedNodes = [...returnedById.values()].sort(compareScopeNodes);\n for (let index = 0; index < returnedNodes.length; index += SCOPE_COUNT_CONCURRENCY) {\n const batch = returnedNodes.slice(index, index + SCOPE_COUNT_CONCURRENCY);\n scopeNodes.push(\n ...(await Promise.all(\n batch.map(async node => {\n const members = await view.store.listScopeMembers({\n scopeNodeId: node.id,\n limit: this.#limits.maxNodes,\n });\n return {\n ...node,\n ...(node.address === `resource:${view.factoryProjectId}`\n ? { name: view.factoryProjectName }\n : {}),\n ...directScopeMemberCounts(\n members,\n childScopeCountByParent.get(node.id) ?? 0,\n view.scope,\n this.#limits.maxNodes,\n ),\n };\n }),\n )),\n );\n }\n const sessionNames = await this.#sessionNames(scopeNodes);\n scopeNodes = scopeNodes.map(node => ({\n ...node,\n kind: scopeKindLabel(node.address, node.kind),\n ...(sessionNames.has(node.id) ? { name: sessionNames.get(node.id)! } : {}),\n }));\n } catch (error) {\n if (!(error instanceof KnowledgeUnsupportedCapabilityError)) throw error;\n scopeNodes = undefined;\n }\n // Deduplicate: when a reconciled scope node owns an identity rung's\n // canonical address, the rung entry carries the structural match so\n // the client renders one entry instead of two labels for one scope.\n const scopeNodeByAddress = new Map(storedScopeNodes.map(node => [node.address, node]));\n const threadRoot = view.threadId ? scopeNodeByAddress.get(`thread:${view.threadId}`) : undefined;\n const threadRootName = threadRoot ? (await this.#sessionNames([threadRoot])).get(threadRoot.id) : undefined;\n const rungs = [\n { level: 'org' as const, id: view.orgId, available: true },\n { level: 'resource' as const, id: view.factoryProjectId, available: true },\n ...(view.threadId ? [{ level: 'thread' as const, id: view.threadId, available: true }] : []),\n ];\n return c.json({\n roots: rungs.map(rung => {\n const match = scopeNodeByAddress.get(`${rung.level}:${rung.id}`);\n const name =\n match?.address === `resource:${view.factoryProjectId}`\n ? view.factoryProjectName\n : match && rung.level === 'thread'\n ? threadRootName\n : match?.name;\n return match ? { ...rung, scopeNodeId: match.id, name } : rung;\n }),\n defaultLevel: 'resource',\n ...(scopeNodes ? { scopeNodes } : {}),\n ...(nextCursor ? { nextCursor } : {}),\n ...(Object.keys(childCursors).length > 0 ? { childCursors } : {}),\n ...(scopesTruncated ? { truncated: true } : {}),\n } satisfies KnowledgeScopeTreePayload);\n },\n }),\n registerApiRoute('/web/factory/projects/:id/knowledge/search', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n const view = await this.#resolveView(loose(c));\n if ('response' in view) return view.response;\n\n const query = (loose(c).req.query('q') ?? '').trim().slice(0, SEARCH_QUERY_LIMIT).toLocaleLowerCase();\n if (query.length < 2) {\n return c.json({ results: [], truncated: false } satisfies KnowledgeSearchPayload);\n }\n\n const scanLimit = Math.min(SEARCH_SCAN_CAP, this.#limits.maxNodes);\n const [prefixNodes, scannedNodes] = await Promise.all([\n view.store.listNodes({ scope: view.scope, namePrefix: query, limit: SEARCH_RESULT_LIMIT + 1 }),\n view.store.listNodes({ scope: view.scope, limit: scanLimit }),\n ]);\n const contentById = new Map<string, KnowledgeNode>();\n for (const node of [...prefixNodes, ...scannedNodes]) {\n if (\n node.isScope ||\n !Array.isArray(node.scope) ||\n !withinViewBoundary(node.scope, view.scope) ||\n !node.name.toLocaleLowerCase().includes(query)\n ) {\n continue;\n }\n contentById.set(node.id, node);\n }\n\n let structuralScopes: KnowledgeScopeNodeSummary[] = [];\n let scopesTruncated = false;\n try {\n const orgScopes = await listOrgScopeNodes(view.store, view.orgId, this.#limits.maxOrgScopePages);\n scopesTruncated = orgScopes.truncated;\n // Sessions are reached from the tree, which checks each one; search lists only the open one.\n structuralScopes = orgScopes.scopes.filter(\n node =>\n (!node.address.startsWith('thread:') || sessionThreadId(node) === view.threadId) &&\n node.name.toLocaleLowerCase().includes(query),\n );\n } catch (error) {\n if (!(error instanceof KnowledgeUnsupportedCapabilityError)) throw error;\n }\n\n const scopeSessionNames = await this.#sessionNames(structuralScopes);\n const results: KnowledgeSearchResult[] = [\n ...structuralScopes.map(scopeNode => {\n const rung =\n scopeNode.address === `org:${view.orgId}`\n ? ('org' as const)\n : scopeNode.address === `resource:${view.factoryProjectId}`\n ? ('resource' as const)\n : view.threadId && scopeNode.address === `thread:${view.threadId}`\n ? ('thread' as const)\n : null;\n return {\n id: scopeNode.id,\n name:\n scopeNode.address === `resource:${view.factoryProjectId}`\n ? view.factoryProjectName\n : (scopeSessionNames.get(scopeNode.id) ?? scopeNode.name),\n kind: scopeKindLabel(scopeNode.address, scopeNode.kind),\n type: 'scope' as const,\n rung,\n address: scopeNode.address,\n ...(scopeNode.description ? { description: scopeNode.description } : {}),\n };\n }),\n ...[...contentById.values()].map(node => {\n const rung = deepestRung(node.scope);\n const threadId =\n rung === 'thread' ? node.scope.find(address => address.startsWith('thread:'))?.slice(7) : undefined;\n return {\n id: node.id,\n name: node.name,\n kind: node.kind,\n type: 'node' as const,\n rung,\n ...(threadId ? { threadId } : {}),\n };\n }),\n ].sort(\n (a, b) =>\n knowledgeSearchRank(a.name, query) - knowledgeSearchRank(b.name, query) ||\n a.name.localeCompare(b.name) ||\n a.id.localeCompare(b.id),\n );\n\n return c.json({\n results: results.slice(0, SEARCH_RESULT_LIMIT),\n truncated:\n scopesTruncated ||\n results.length > SEARCH_RESULT_LIMIT ||\n prefixNodes.length > SEARCH_RESULT_LIMIT ||\n scannedNodes.length >= scanLimit,\n } satisfies KnowledgeSearchPayload);\n },\n }),\n registerApiRoute('/web/factory/projects/:id/knowledge/subgraph', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n const resolved = await this.#resolveView(loose(c));\n if ('response' in resolved) return resolved.response;\n\n // Structural lens: a reconciled scope node selected in the scope\n // tree. The lens renders the scope node ITSELF as the graph root\n // (containment edges to every member), child scopes as drill\n // targets, and content members with their records and wikilink\n // edges — the same window machinery as the identity lens. Content\n // members stay inside the active project boundary: this is a\n // project-scoped view, so knowledge stamped at another resource of\n // the same org never rolls up here. Pins do not apply to\n // structural members.\n const scopeNodeId = loose(c).req.query('scopeNodeId');\n if (scopeNodeId !== undefined) {\n if (!UUID_RE.test(scopeNodeId)) return c.json({ error: 'scope_not_found' }, 404);\n const limits = this.#limits;\n const { store } = resolved;\n\n let storedRoot: KnowledgeScopeNodeSummary | undefined;\n try {\n storedRoot = await findOrgScopeNode(store, resolved.orgId, scopeNodeId);\n } catch (error) {\n // Adapters without the structural read have no lenses to serve.\n if (error instanceof KnowledgeUnsupportedCapabilityError) {\n return c.json({ error: 'scope_not_found' }, 404);\n }\n throw error;\n }\n if (!storedRoot) return c.json({ error: 'scope_not_found' }, 404);\n // Child-scope counts for the lens come from the org's scopes, which may be capped.\n const { scopes: scopeNodes, truncated: scopesTruncated } = await listOrgScopeNodes(\n store,\n resolved.orgId,\n limits.maxOrgScopePages,\n );\n const rootSessionName = (await this.#sessionNames([storedRoot])).get(storedRoot.id);\n const root =\n storedRoot.address === `resource:${resolved.factoryProjectId}`\n ? { ...storedRoot, name: resolved.factoryProjectName }\n : rootSessionName\n ? { ...storedRoot, name: rootSessionName }\n : storedRoot;\n // A session lens needs the same check as the tree; other lenses never list sessions,\n // which are reached from the tree instead.\n if (storedRoot.address.startsWith('thread:')) {\n const projectNodeId = scopeNodes.find(\n node => node.address === `resource:${resolved.factoryProjectId}`,\n )?.id;\n const hidden = await this.#hiddenSessionIds(resolved, [storedRoot], projectNodeId, { probe: true });\n if (hidden.has(storedRoot.id)) return c.json({ error: 'scope_not_found' }, 404);\n }\n const otherSessionIds = new Set(\n scopeNodes\n .filter(node => node.address.startsWith('thread:') && sessionThreadId(node) !== resolved.threadId)\n .map(node => node.id),\n );\n\n const fetched = await store.listScopeMembers({ scopeNodeId, limit: limits.maxNodes });\n const bounded = fetched.members.filter(\n node =>\n (node.isScope && !otherSessionIds.has(node.id)) ||\n (!node.isScope && Array.isArray(node.scope) && withinViewBoundary(node.scope, resolved.scope)),\n );\n let truncated = scopesTruncated || fetched.hasMore || bounded.length > limits.maxNodes;\n const members = bounded.slice(0, limits.maxNodes);\n const contentMembers = members.filter(\n (node): node is KnowledgeNode => !node.isScope && Array.isArray(node.scope),\n );\n const childScopeCountByParent = new Map<string, number>();\n for (const node of scopeNodes) {\n for (const parentId of node.parentIds) {\n childScopeCountByParent.set(parentId, (childScopeCountByParent.get(parentId) ?? 0) + 1);\n }\n }\n const scopeCountsById = new Map<\n string,\n Pick<\n KnowledgeScopeTreeNode,\n 'memberCount' | 'memberCountTruncated' | 'contentNodeCount' | 'childScopeCount'\n >\n >([\n [\n root.id,\n directScopeMemberCounts(\n fetched,\n childScopeCountByParent.get(root.id) ?? 0,\n resolved.scope,\n limits.maxNodes,\n ),\n ],\n ]);\n const memberScopes = members.filter(node => node.isScope);\n for (let index = 0; index < memberScopes.length; index += SCOPE_COUNT_CONCURRENCY) {\n const batch = memberScopes.slice(index, index + SCOPE_COUNT_CONCURRENCY);\n await Promise.all(\n batch.map(async node => {\n const directMembers = await store.listScopeMembers({\n scopeNodeId: node.id,\n limit: limits.maxNodes,\n });\n scopeCountsById.set(\n node.id,\n directScopeMemberCounts(\n directMembers,\n childScopeCountByParent.get(node.id) ?? 0,\n resolved.scope,\n limits.maxNodes,\n ),\n );\n }),\n );\n }\n\n // Record window over content members (newest-first overall), then\n // wikilink resolution — member lenses are connected, not dots.\n const recordWindow: KnowledgeRecord[] = [];\n for (const node of contentMembers) {\n if (recordWindow.length > limits.maxRecords) break;\n const { records } = await store.listKnowledgeAbout({\n node: node.id,\n scope: node.scope as KnowledgeScope,\n limit: limits.maxRecords + 1 - recordWindow.length,\n });\n recordWindow.push(...records);\n }\n recordWindow.sort((a, b) => b.id.localeCompare(a.id));\n if (recordWindow.length > limits.maxRecords) {\n truncated = true;\n recordWindow.length = limits.maxRecords;\n }\n\n const resolver = new WikilinkResolver(store, contentMembers, limits.maxFallbackLookups);\n const edges: KnowledgeGraphEdge[] = [];\n const graphRecords: KnowledgeGraphRecord[] = [];\n const edgeSeen = new Set<string>();\n const recordCounts = new Map<string, number>();\n for (const record of recordWindow) {\n recordCounts.set(record.node, (recordCounts.get(record.node) ?? 0) + 1);\n const nodeIds = [record.node];\n for (const name of parseKnowledgeWikilinks(record.text)) {\n const target = await resolver.resolve(name, record.scope);\n if (!target) continue;\n if (target.id === record.node) continue;\n if (!resolver.inWindowId(target.id)) continue;\n if (!nodeIds.includes(target.id)) nodeIds.push(target.id);\n const key = `${record.node}\\u0000${target.id}`;\n if (edgeSeen.has(key)) continue;\n edgeSeen.add(key);\n edges.push({\n id: `wikilink:${record.node}:${target.id}`,\n source: record.node,\n target: target.id,\n type: 'wikilink',\n recordId: record.id,\n });\n }\n graphRecords.push({ id: record.id, nodeIds, pinned: false, text: truncateRecordText(record.text) });\n }\n\n // Containment: the clicked scope node is the root of its own view.\n for (const member of members) {\n edges.push({\n id: `contains:${root.id}:${member.id}`,\n source: root.id,\n target: member.id,\n type: 'contains',\n });\n }\n\n const scopeAddressById = new Map(scopeNodes.map(node => [node.id, node.address]));\n const memberSessionNames = await this.#sessionNames(\n members.flatMap(node => {\n const address = node.isScope ? scopeAddressById.get(node.id) : undefined;\n return address ? [{ id: node.id, address }] : [];\n }),\n );\n const rootRow = await store.getNode(root.id);\n const activity = await store.listActivity({ scope: resolved.scope, limit: 1 });\n\n const payload: KnowledgeGraphPayload = {\n view: resolved.view,\n ...(resolved.threadId ? { threadId: resolved.threadId } : {}),\n nodes: [\n {\n id: root.id,\n name: root.name,\n kind: scopeKindLabel(root.address, root.kind),\n ...(root.description ? { description: root.description } : {}),\n scope: null,\n rung: null,\n isScope: true,\n pinned: false,\n recordCount: 0,\n ...scopeCountsById.get(root.id),\n ...(rootRow\n ? { createdAt: rootRow.createdAt.toISOString(), updatedAt: rootRow.updatedAt.toISOString() }\n : {}),\n },\n ...members.map(node => {\n const nodeScope = Array.isArray(node.scope) ? node.scope : null;\n const address = node.isScope ? scopeAddressById.get(node.id) : undefined;\n return {\n id: node.id,\n name: memberSessionNames.get(node.id) ?? node.name,\n kind: node.isScope ? scopeKindLabel(address ?? '', node.kind) : node.kind,\n ...(node.description ? { description: node.description } : {}),\n scope: nodeScope,\n rung: nodeScope ? deepestRung(nodeScope) : null,\n ...(node.isScope ? { isScope: true, ...scopeCountsById.get(node.id) } : {}),\n pinned: false,\n recordCount: recordCounts.get(node.id) ?? 0,\n createdAt: node.createdAt.toISOString(),\n updatedAt: node.updatedAt.toISOString(),\n };\n }),\n ],\n edges,\n records: graphRecords,\n truncated,\n outOfWindow: [...resolver.outOfWindow.values()],\n unresolvedCapped: { count: resolver.cappedCount, names: resolver.cappedNames },\n pinCensus: { resource: 0, thread: null },\n version: activity[0]?.id ?? null,\n };\n return c.json(payload);\n }\n\n const view = this.#selectedView(resolved, loose(c).req.query('scopeLevel'));\n if (!view) return c.json({ error: 'scope_not_found' }, 404);\n const { store, scope } = view;\n const limits = this.#limits;\n\n // Nodes, newest-first; +1 to detect truncation. SQL adapters return\n // global scope nodes (scope: null) from identity-scope reads — they\n // are already surfaced via /knowledge/scopes, so exclude them here\n // before truncation, records, and wikilink resolution.\n const fetched = (await store.listNodes({ scope, limit: limits.maxNodes + 1 })).filter(node =>\n Array.isArray(node.scope),\n );\n let truncated = fetched.length > limits.maxNodes;\n const pinnedNodeIds = await this.#pinnedNodeIds(view);\n const pinnedNodeIdSet = new Set(pinnedNodeIds.map(p => p.id));\n const nodes = fetched.slice(0, limits.maxNodes).filter(node => !pinnedNodeIdSet.has(node.id));\n\n // Record window: per-node owned records, then newest-first overall.\n const recordWindow: KnowledgeRecord[] = [];\n for (const node of nodes) {\n if (recordWindow.length > limits.maxRecords) break;\n const { records } = await store.listKnowledgeAbout({\n node: node.id,\n scope,\n limit: limits.maxRecords + 1 - recordWindow.length,\n });\n recordWindow.push(...records);\n }\n // Record ids are ULIDs — descending id = newest-first.\n recordWindow.sort((a, b) => b.id.localeCompare(a.id));\n if (recordWindow.length > limits.maxRecords) {\n truncated = true;\n recordWindow.length = limits.maxRecords;\n }\n\n const resolver = new WikilinkResolver(store, nodes, limits.maxFallbackLookups);\n\n // Edges: owner node (the record's parent link) → wikilinked node.\n // Graph records: every windowed record with its in-window node set,\n // owner first. The client renders dots, lines, or junctions by arity.\n const edges: KnowledgeGraphEdge[] = [];\n const graphRecords: KnowledgeGraphRecord[] = [];\n const edgeSeen = new Set<string>();\n const recordCounts = new Map<string, number>();\n for (const record of recordWindow) {\n recordCounts.set(record.node, (recordCounts.get(record.node) ?? 0) + 1);\n const nodeIds = [record.node];\n for (const name of parseKnowledgeWikilinks(record.text)) {\n const target = await resolver.resolve(name, record.scope);\n if (!target) continue;\n if (target.id === record.node) continue;\n if (!resolver.inWindowId(target.id)) continue;\n if (!nodeIds.includes(target.id)) nodeIds.push(target.id);\n const key = `${record.node}\\u0000${target.id}`;\n if (edgeSeen.has(key)) continue;\n edgeSeen.add(key);\n edges.push({\n id: `wikilink:${record.node}:${target.id}`,\n source: record.node,\n target: target.id,\n type: 'wikilink',\n recordId: record.id,\n });\n }\n graphRecords.push({ id: record.id, nodeIds, pinned: false, text: truncateRecordText(record.text) });\n }\n\n // Pins mark relationships. The reserved owner node is omitted, so\n // arity comes purely from wikilink targets.\n const pinnedRecords = await this.#pinnedRecords(view, pinnedNodeIds);\n const accented = new Set<string>();\n for (const { record } of pinnedRecords) {\n const targets: string[] = [];\n for (const name of parseKnowledgeWikilinks(record.text)) {\n const target = await resolver.resolve(name, record.scope);\n if (target && resolver.inWindowId(target.id) && !targets.includes(target.id)) {\n targets.push(target.id);\n }\n }\n graphRecords.push({ id: record.id, nodeIds: targets, pinned: true, text: truncateRecordText(record.text) });\n if (targets.length === 1) {\n accented.add(targets[0]!);\n continue;\n }\n for (let a = 0; a < targets.length; a += 1) {\n for (let b = a + 1; b < targets.length; b += 1) {\n const key = `${targets[a]}\\u0000${targets[b]}\\u0000pin`;\n if (edgeSeen.has(key)) continue;\n edgeSeen.add(key);\n edges.push({\n id: `pin:${record.id}:${targets[a]}:${targets[b]}`,\n source: targets[a]!,\n target: targets[b]!,\n type: 'wikilink',\n recordId: record.id,\n pinned: true,\n });\n }\n }\n }\n const pinCensus = {\n resource: pinnedRecords.filter(p => p.rung === 'resource').length,\n thread: view.view === 'thread' ? pinnedRecords.filter(p => p.rung === 'thread').length : null,\n };\n\n const activity = await store.listActivity({ scope, limit: 1 });\n\n const payload: KnowledgeGraphPayload = {\n view: view.view,\n ...(view.threadId ? { threadId: view.threadId } : {}),\n nodes: nodes.map(node => ({\n id: node.id,\n name: node.name,\n kind: node.kind,\n // Empty string is a curator clear — omit it so the payload carries no dead keys.\n ...(node.description ? { description: node.description } : {}),\n scope: node.scope,\n rung: deepestRung(node.scope),\n pinned: accented.has(node.id),\n recordCount: recordCounts.get(node.id) ?? 0,\n createdAt: node.createdAt.toISOString(),\n updatedAt: node.updatedAt.toISOString(),\n })),\n edges,\n records: graphRecords,\n truncated,\n outOfWindow: [...resolver.outOfWindow.values()],\n unresolvedCapped: { count: resolver.cappedCount, names: resolver.cappedNames },\n pinCensus,\n version: activity[0]?.id ?? null,\n };\n return c.json(payload);\n },\n }),\n\n // ── Node flyout payload: details + provenance-rich records ───────────────\n registerApiRoute('/web/factory/projects/:id/knowledge/nodes/:nodeId', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n const resolved = await this.#resolveView(loose(c));\n if ('response' in resolved) return resolved.response;\n const nodeId = loose(c).req.param('nodeId');\n if (!nodeId || nodeId.length > 512) return c.json({ error: 'node_not_found' }, 404);\n\n const view = this.#selectedView(resolved, loose(c).req.query('scopeLevel'));\n if (!view) return c.json({ error: 'scope_not_found' }, 404);\n const { store, scope } = view;\n\n const node = await store.getNode(nodeId);\n // getNode is a bare id lookup with no scope predicate. This explicit\n // visibility check prevents an IDOR.\n // Structural scope nodes carry no identity scope; they are read through /scopes.\n if (!node?.scope || !isKnowledgeScopeVisible(node.scope, scope)) {\n return c.json({ error: 'node_not_found' }, 404);\n }\n\n const pinnedNodeIds = await this.#pinnedNodeIds(view);\n const pinnedNodeIdSet = new Set(pinnedNodeIds.map(p => p.id));\n\n const [owned, mentioning] = await Promise.all([\n store.listKnowledgeAbout({ node: node.id, scope, limit: 200 }),\n store.listKnowledgeMentioning({ node: node.id, scope, limit: 200 }),\n ]);\n const seen = new Set<string>();\n const records: KnowledgeNodeRecordPayload[] = [];\n const push = (record: KnowledgeRecord, relation: 'owned' | 'mentions') => {\n if (seen.has(record.id)) return;\n seen.add(record.id);\n records.push({\n id: record.id,\n node: record.node,\n relation,\n text: record.text,\n scope: record.scope,\n rung: deepestRung(record.scope),\n sourceThreadId: record.sourceThreadId,\n capturedAt: record.capturedAt.toISOString(),\n ...(record.when ? { when: record.when.toISOString() } : {}),\n pinned: pinnedNodeIdSet.has(record.node),\n ...(record.metadata ? { metadata: record.metadata } : {}),\n });\n };\n // Owned first, newest-first within each group. Record ids are ULIDs.\n for (const record of [...owned.records].sort((a, b) => b.id.localeCompare(a.id))) push(record, 'owned');\n for (const record of [...mentioning.records].sort((a, b) => b.id.localeCompare(a.id))) {\n push(record, 'mentions');\n }\n\n const payload: KnowledgeNodePayload = {\n node: {\n id: node.id,\n name: node.name,\n kind: node.kind,\n content: node.content ?? '',\n ...(node.description ? { description: node.description } : {}),\n scope: node.scope,\n rung: deepestRung(node.scope),\n createdAt: node.createdAt.toISOString(),\n updatedAt: node.updatedAt.toISOString(),\n },\n records,\n };\n return c.json(payload);\n },\n }),\n\n // ── Recent activity feed for the live-arrival affordance ───────────────\n registerApiRoute('/web/factory/projects/:id/knowledge/activity', {\n method: 'GET',\n requiresAuth: false,\n handler: async c => {\n const resolved = await this.#resolveView(loose(c));\n if ('response' in resolved) return resolved.response;\n const scopeNodeId = loose(c).req.query('scopeNodeId');\n let view: ResolvedView;\n let memberIds: Set<string> | undefined;\n let membersTruncated = false;\n if (scopeNodeId !== undefined) {\n if (!UUID_RE.test(scopeNodeId)) return c.json({ error: 'scope_not_found' }, 404);\n let scopeNode: KnowledgeScopeNodeSummary | undefined;\n try {\n scopeNode = await findOrgScopeNode(resolved.store, resolved.orgId, scopeNodeId);\n } catch (error) {\n if (error instanceof KnowledgeUnsupportedCapabilityError) {\n return c.json({ error: 'scope_not_found' }, 404);\n }\n throw error;\n }\n if (!scopeNode) return c.json({ error: 'scope_not_found' }, 404);\n const { members, hasMore } = await resolved.store.listScopeMembers({\n scopeNodeId,\n limit: this.#limits.maxNodes,\n });\n membersTruncated = hasMore || members.length > this.#limits.maxNodes;\n memberIds = new Set(\n members\n .slice(0, this.#limits.maxNodes)\n .filter(member => !member.isScope)\n .map(member => member.id),\n );\n view = resolved;\n } else {\n const selected = this.#selectedView(resolved, loose(c).req.query('scopeLevel'));\n if (!selected) return c.json({ error: 'scope_not_found' }, 404);\n view = selected;\n }\n const cursor = loose(c).req.query('cursor');\n if (cursor && cursor.length > 128) return c.json({ error: 'invalid_activity_cursor' }, 400);\n const { items: activity, scanCursor } = await this.#visibleActivity(\n view,\n memberIds,\n cursor,\n ACTIVITY_PAGE_SIZE + 1,\n );\n const visiblePage = activity.slice(0, ACTIVITY_PAGE_SIZE);\n const lastVisible = visiblePage.at(-1);\n const payload: KnowledgeActivityPayload = {\n events: visiblePage.map(({ event, node }) => {\n const rung = deepestRung(node.scope);\n const threadId =\n rung === 'thread' ? node.scope.find(address => address.startsWith('thread:'))?.slice(7) : undefined;\n return {\n id: event.id,\n action: event.action,\n recordType: event.recordType,\n ...(event.recordType === 'record' ? { recordId: event.recordId } : {}),\n scope: event.scope,\n node: {\n id: node.id,\n name: node.name,\n rung,\n ...(threadId ? { threadId } : {}),\n },\n createdAt: event.createdAt.toISOString(),\n };\n }),\n ...(activity.length > ACTIVITY_PAGE_SIZE && lastVisible\n ? { nextCursor: lastVisible.event.id }\n : scanCursor\n ? { nextCursor: scanCursor }\n : {}),\n ...(membersTruncated ? { truncated: true as const } : {}),\n };\n return c.json(payload);\n },\n }),\n ];\n }\n\n /**\n * Newest-first activity page where EVERY returned event targets a node\n * visible in the view. Visibility is decided BEFORE window shaping: hidden\n * rows are skipped entirely (no action/type/id/time metadata leaks) and a\n * hidden backlog can never displace visible events from the window.\n * When the scan cap stops the walk before the stream ends, `scanCursor` is\n * the last scanned event so the caller can continue past hidden rows.\n */\n async #visibleActivity(\n view: ResolvedView,\n memberIds: Set<string> | undefined,\n cursor: string | undefined,\n limit: number,\n ): Promise<{ items: VisibleKnowledgeActivity[]; scanCursor?: string }> {\n const out: VisibleKnowledgeActivity[] = [];\n // Many events in a scan target the same node; read each node once per request.\n const nodes = new Map<string, Promise<KnowledgeNode | null>>();\n const getNode = (id: string) => {\n let node = nodes.get(id);\n if (!node) {\n node = view.store.getNode(id);\n nodes.set(id, node);\n }\n return node;\n };\n let after = cursor;\n let scanned = 0;\n let exhausted = false;\n while (out.length < limit && scanned < ACTIVITY_SCAN_CAP) {\n const batch = await view.store.listActivity({ scope: view.scope, after, limit: ACTIVITY_BATCH });\n if (batch.length === 0) {\n exhausted = true;\n break;\n }\n for (const event of batch) {\n after = event.id;\n scanned += 1;\n let targetNode: KnowledgeNode | null;\n if (event.recordType === 'node') {\n targetNode = await getNode(event.recordId);\n } else {\n const record = await view.store.getKnowledge({ id: event.recordId });\n targetNode = record ? await getNode(record.node) : null;\n if (record && !isKnowledgeScopeVisible(record.scope, view.scope)) targetNode = null;\n }\n if (\n targetNode?.scope &&\n isKnowledgeScopeVisible(targetNode.scope, view.scope) &&\n (memberIds === undefined || memberIds.has(targetNode.id))\n ) {\n out.push({ event, node: targetNode });\n }\n if (out.length >= limit || scanned >= ACTIVITY_SCAN_CAP) break;\n }\n }\n const stoppedAtCap = !exhausted && out.length < limit && scanned >= ACTIVITY_SCAN_CAP;\n return { items: out, ...(stoppedAtCap && after ? { scanCursor: after } : {}) };\n }\n}\n"],"mappings":"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"}
@@ -24,7 +24,7 @@ import { pollXAIDeviceLogin, startXAIDeviceLogin } from "@mastra/code-sdk/auth/p
24
24
  * user codes, poll delays).
25
25
  */
26
26
  /** Lifetime of a paste-code session (Anthropic gives no explicit expiry). */
27
- const PASTE_CODE_TTL_MS = 600 * 1e3;
27
+ const PASTE_CODE_TTL_MS = 6e5;
28
28
  /** Web OAuth flows keyed by *catalog* provider id (mirrors {@link WEB_OAUTH_PROVIDERS}). */
29
29
  const OAUTH_FLOWS = {
30
30
  anthropic: {
@@ -444,7 +444,8 @@ var OAuthRoutes = class extends Route {
444
444
  credentials: modelCredentials
445
445
  });
446
446
  if ("response" in ctx) return ctx.response;
447
- const authProviderId = getAuthProviderId(c.req.param("provider"));
447
+ const provider = c.req.param("provider");
448
+ const authProviderId = getAuthProviderId(provider);
448
449
  const scope = c.req.query("scope") === "org" ? "org" : "user";
449
450
  try {
450
451
  if (ctx.mode === "tenant") {