@nocobase/app-plugin-agents 1.0.0-beta.29 → 2.0.0-beta.1

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 (300) hide show
  1. package/CHANGELOG.md +80 -0
  2. package/README.md +8 -19
  3. package/dist/client/api/client.d.ts +2 -2
  4. package/dist/client/api/client.d.ts.map +1 -1
  5. package/dist/client/api/client.js +2 -2
  6. package/dist/client/api/client.js.map +1 -1
  7. package/dist/client/api/runners.d.ts +2 -2
  8. package/dist/client/api/runners.d.ts.map +1 -1
  9. package/dist/client/api/runners.js +5 -2
  10. package/dist/client/api/runners.js.map +1 -1
  11. package/dist/client/chat/api.d.ts +1 -1
  12. package/dist/client/chat/api.d.ts.map +1 -1
  13. package/dist/client/chat/api.js +6 -2
  14. package/dist/client/chat/api.js.map +1 -1
  15. package/dist/client/chat/coalesced-invalidation.d.ts +3 -8
  16. package/dist/client/chat/coalesced-invalidation.d.ts.map +1 -1
  17. package/dist/client/chat/coalesced-invalidation.js +16 -21
  18. package/dist/client/chat/coalesced-invalidation.js.map +1 -1
  19. package/dist/client/chat/controller.d.ts +31 -7
  20. package/dist/client/chat/controller.d.ts.map +1 -1
  21. package/dist/client/chat/controller.js +616 -120
  22. package/dist/client/chat/controller.js.map +1 -1
  23. package/dist/client/chat/provider.d.ts.map +1 -1
  24. package/dist/client/chat/provider.js +7 -4
  25. package/dist/client/chat/provider.js.map +1 -1
  26. package/dist/client/chat/submissions.d.ts +2 -0
  27. package/dist/client/chat/submissions.d.ts.map +1 -1
  28. package/dist/client/chat/submissions.js +1 -0
  29. package/dist/client/chat/submissions.js.map +1 -1
  30. package/dist/client/chat/use-chat.d.ts +4 -2
  31. package/dist/client/chat/use-chat.d.ts.map +1 -1
  32. package/dist/client/chat/use-chat.js +84 -55
  33. package/dist/client/chat/use-chat.js.map +1 -1
  34. package/dist/client/components/ag-tag-tones.d.ts +7 -0
  35. package/dist/client/components/ag-tag-tones.d.ts.map +1 -0
  36. package/dist/client/components/ag-tag-tones.js +14 -0
  37. package/dist/client/components/ag-tag-tones.js.map +1 -0
  38. package/dist/client/components/ag-tag.d.ts +2 -1
  39. package/dist/client/components/ag-tag.d.ts.map +1 -1
  40. package/dist/client/components/ag-tag.js +1 -13
  41. package/dist/client/components/ag-tag.js.map +1 -1
  42. package/dist/client/components/quota-status.d.ts +2 -1
  43. package/dist/client/components/quota-status.d.ts.map +1 -1
  44. package/dist/client/components/quota-status.js +2 -2
  45. package/dist/client/components/quota-status.js.map +1 -1
  46. package/dist/client/components/ui/pagination.d.ts +6 -0
  47. package/dist/client/components/ui/pagination.d.ts.map +1 -0
  48. package/dist/client/components/ui/pagination.js +13 -0
  49. package/dist/client/components/ui/pagination.js.map +1 -0
  50. package/dist/client/hooks/use-notify.d.ts.map +1 -1
  51. package/dist/client/hooks/use-notify.js +4 -0
  52. package/dist/client/hooks/use-notify.js.map +1 -1
  53. package/dist/client/hooks/use-realtime-topic.d.ts +1 -1
  54. package/dist/client/hooks/use-realtime-topic.d.ts.map +1 -1
  55. package/dist/client/hooks/use-realtime-topic.js +8 -4
  56. package/dist/client/hooks/use-realtime-topic.js.map +1 -1
  57. package/dist/client/kit.d.ts.map +1 -1
  58. package/dist/client/kit.js +30 -5
  59. package/dist/client/kit.js.map +1 -1
  60. package/dist/client/locales/de-DE.d.ts.map +1 -1
  61. package/dist/client/locales/de-DE.js +26 -10
  62. package/dist/client/locales/de-DE.js.map +1 -1
  63. package/dist/client/locales/en-US.d.ts +11 -0
  64. package/dist/client/locales/en-US.d.ts.map +1 -1
  65. package/dist/client/locales/en-US.js +21 -10
  66. package/dist/client/locales/en-US.js.map +1 -1
  67. package/dist/client/locales/es-ES.d.ts.map +1 -1
  68. package/dist/client/locales/es-ES.js +26 -10
  69. package/dist/client/locales/es-ES.js.map +1 -1
  70. package/dist/client/locales/fr-FR.d.ts.map +1 -1
  71. package/dist/client/locales/fr-FR.js +26 -10
  72. package/dist/client/locales/fr-FR.js.map +1 -1
  73. package/dist/client/locales/id-ID.d.ts.map +1 -1
  74. package/dist/client/locales/id-ID.js +26 -10
  75. package/dist/client/locales/id-ID.js.map +1 -1
  76. package/dist/client/locales/ja-JP.d.ts.map +1 -1
  77. package/dist/client/locales/ja-JP.js +26 -10
  78. package/dist/client/locales/ja-JP.js.map +1 -1
  79. package/dist/client/locales/pt-BR.d.ts.map +1 -1
  80. package/dist/client/locales/pt-BR.js +26 -10
  81. package/dist/client/locales/pt-BR.js.map +1 -1
  82. package/dist/client/locales/runtimes.en-US.d.ts +5 -0
  83. package/dist/client/locales/runtimes.en-US.d.ts.map +1 -1
  84. package/dist/client/locales/runtimes.en-US.js +5 -0
  85. package/dist/client/locales/runtimes.en-US.js.map +1 -1
  86. package/dist/client/locales/runtimes.zh-CN.d.ts.map +1 -1
  87. package/dist/client/locales/runtimes.zh-CN.js +5 -0
  88. package/dist/client/locales/runtimes.zh-CN.js.map +1 -1
  89. package/dist/client/locales/vi-VN.d.ts.map +1 -1
  90. package/dist/client/locales/vi-VN.js +26 -10
  91. package/dist/client/locales/vi-VN.js.map +1 -1
  92. package/dist/client/locales/zh-CN.d.ts.map +1 -1
  93. package/dist/client/locales/zh-CN.js +21 -10
  94. package/dist/client/locales/zh-CN.js.map +1 -1
  95. package/dist/client/locales/zh-TW.d.ts.map +1 -1
  96. package/dist/client/locales/zh-TW.js +26 -10
  97. package/dist/client/locales/zh-TW.js.map +1 -1
  98. package/dist/client/model-suggestion.d.ts +2 -0
  99. package/dist/client/model-suggestion.d.ts.map +1 -1
  100. package/dist/client/model-suggestion.js +27 -2
  101. package/dist/client/model-suggestion.js.map +1 -1
  102. package/dist/client/pages/agents/agent-dialog.d.ts.map +1 -1
  103. package/dist/client/pages/agents/agent-dialog.js +10 -8
  104. package/dist/client/pages/agents/agent-dialog.js.map +1 -1
  105. package/dist/client/pages/agents/available-models-drawer.d.ts.map +1 -1
  106. package/dist/client/pages/agents/available-models-drawer.js +2 -1
  107. package/dist/client/pages/agents/available-models-drawer.js.map +1 -1
  108. package/dist/client/pages/agents/good-at-templates.d.ts +8 -0
  109. package/dist/client/pages/agents/good-at-templates.d.ts.map +1 -0
  110. package/dist/client/pages/agents/good-at-templates.js +29 -0
  111. package/dist/client/pages/agents/good-at-templates.js.map +1 -0
  112. package/dist/client/pages/runtimes/runner-cells.d.ts.map +1 -1
  113. package/dist/client/pages/runtimes/runner-cells.js +3 -1
  114. package/dist/client/pages/runtimes/runner-cells.js.map +1 -1
  115. package/dist/client/pages/runtimes/runner-pagination.d.ts +15 -0
  116. package/dist/client/pages/runtimes/runner-pagination.d.ts.map +1 -0
  117. package/dist/client/pages/runtimes/runner-pagination.js +9 -0
  118. package/dist/client/pages/runtimes/runner-pagination.js.map +1 -0
  119. package/dist/client/pages/runtimes/runner-runs.d.ts +3 -2
  120. package/dist/client/pages/runtimes/runner-runs.d.ts.map +1 -1
  121. package/dist/client/pages/runtimes/runner-runs.js +38 -15
  122. package/dist/client/pages/runtimes/runner-runs.js.map +1 -1
  123. package/dist/client/pages/runtimes/runner-sheet.js +1 -1
  124. package/dist/client/pages/runtimes/runner-sheet.js.map +1 -1
  125. package/dist/client/pages/runtimes/runner-workspaces.d.ts.map +1 -1
  126. package/dist/client/pages/runtimes/runner-workspaces.js +7 -3
  127. package/dist/client/pages/runtimes/runner-workspaces.js.map +1 -1
  128. package/dist/client/runs/refresh-run.d.ts +4 -0
  129. package/dist/client/runs/refresh-run.d.ts.map +1 -0
  130. package/dist/client/runs/refresh-run.js +8 -0
  131. package/dist/client/runs/refresh-run.js.map +1 -0
  132. package/dist/client/runs/run-hooks.d.ts.map +1 -1
  133. package/dist/client/runs/run-hooks.js +2 -1
  134. package/dist/client/runs/run-hooks.js.map +1 -1
  135. package/dist/database/migrations/.manifest.json +8 -0
  136. package/dist/database/migrations/202611010003_ag_runner_run_history_index.d.ts +4 -0
  137. package/dist/database/migrations/202611010003_ag_runner_run_history_index.d.ts.map +1 -0
  138. package/dist/database/migrations/202611010003_ag_runner_run_history_index.js +17 -0
  139. package/dist/database/migrations/202611010003_ag_runner_run_history_index.js.map +1 -0
  140. package/dist/database/migrations/202611070001_ag_conversation_project.d.ts +4 -0
  141. package/dist/database/migrations/202611070001_ag_conversation_project.d.ts.map +1 -0
  142. package/dist/database/migrations/202611070001_ag_conversation_project.js +17 -0
  143. package/dist/database/migrations/202611070001_ag_conversation_project.js.map +1 -0
  144. package/dist/package.json +4 -6
  145. package/dist/server/composition.d.ts +0 -17
  146. package/dist/server/composition.d.ts.map +1 -1
  147. package/dist/server/composition.js +7 -20
  148. package/dist/server/composition.js.map +1 -1
  149. package/dist/server/core/conversations/conversation.service.d.ts.map +1 -1
  150. package/dist/server/core/conversations/conversation.service.js +174 -22
  151. package/dist/server/core/conversations/conversation.service.js.map +1 -1
  152. package/dist/server/core/conversations/conversation.store.d.ts +1 -0
  153. package/dist/server/core/conversations/conversation.store.d.ts.map +1 -1
  154. package/dist/server/core/conversations/conversation.store.js.map +1 -1
  155. package/dist/server/core/conversations/index.d.ts +1 -1
  156. package/dist/server/core/conversations/index.d.ts.map +1 -1
  157. package/dist/server/core/conversations/index.js.map +1 -1
  158. package/dist/server/core/conversations/links.d.ts +3 -0
  159. package/dist/server/core/conversations/links.d.ts.map +1 -1
  160. package/dist/server/core/conversations/links.js +33 -0
  161. package/dist/server/core/conversations/links.js.map +1 -1
  162. package/dist/server/core/conversations/ports.d.ts +14 -0
  163. package/dist/server/core/conversations/ports.d.ts.map +1 -1
  164. package/dist/server/core/runs/claim.d.ts +7 -1
  165. package/dist/server/core/runs/claim.d.ts.map +1 -1
  166. package/dist/server/core/runs/claim.js +69 -17
  167. package/dist/server/core/runs/claim.js.map +1 -1
  168. package/dist/server/core/runs/ports.d.ts +2 -0
  169. package/dist/server/core/runs/ports.d.ts.map +1 -1
  170. package/dist/server/core/runs/ports.js.map +1 -1
  171. package/dist/server/core/runs/run.service.d.ts +6 -1
  172. package/dist/server/core/runs/run.service.d.ts.map +1 -1
  173. package/dist/server/core/runs/run.service.js +29 -1
  174. package/dist/server/core/runs/run.service.js.map +1 -1
  175. package/dist/server/core/runs/run.store.d.ts +1 -1
  176. package/dist/server/core/runs/run.store.d.ts.map +1 -1
  177. package/dist/server/core/runs/run.store.js +1 -1
  178. package/dist/server/core/runs/run.store.js.map +1 -1
  179. package/dist/server/core/runs/runner-view.d.ts +8 -4
  180. package/dist/server/core/runs/runner-view.d.ts.map +1 -1
  181. package/dist/server/core/runs/runner-view.js +89 -32
  182. package/dist/server/core/runs/runner-view.js.map +1 -1
  183. package/dist/server/kernel/events.d.ts +2 -0
  184. package/dist/server/kernel/events.d.ts.map +1 -1
  185. package/dist/server/kernel/events.js.map +1 -1
  186. package/dist/server/locales/de-DE.d.ts.map +1 -1
  187. package/dist/server/locales/de-DE.js +5 -0
  188. package/dist/server/locales/de-DE.js.map +1 -1
  189. package/dist/server/locales/en-US.d.ts +7 -1
  190. package/dist/server/locales/en-US.d.ts.map +1 -1
  191. package/dist/server/locales/en-US.js +5 -0
  192. package/dist/server/locales/en-US.js.map +1 -1
  193. package/dist/server/locales/es-ES.d.ts.map +1 -1
  194. package/dist/server/locales/es-ES.js +5 -0
  195. package/dist/server/locales/es-ES.js.map +1 -1
  196. package/dist/server/locales/fr-FR.d.ts.map +1 -1
  197. package/dist/server/locales/fr-FR.js +5 -0
  198. package/dist/server/locales/fr-FR.js.map +1 -1
  199. package/dist/server/locales/id-ID.d.ts.map +1 -1
  200. package/dist/server/locales/id-ID.js +5 -0
  201. package/dist/server/locales/id-ID.js.map +1 -1
  202. package/dist/server/locales/ja-JP.d.ts.map +1 -1
  203. package/dist/server/locales/ja-JP.js +5 -0
  204. package/dist/server/locales/ja-JP.js.map +1 -1
  205. package/dist/server/locales/pt-BR.d.ts.map +1 -1
  206. package/dist/server/locales/pt-BR.js +5 -0
  207. package/dist/server/locales/pt-BR.js.map +1 -1
  208. package/dist/server/locales/vi-VN.d.ts.map +1 -1
  209. package/dist/server/locales/vi-VN.js +5 -0
  210. package/dist/server/locales/vi-VN.js.map +1 -1
  211. package/dist/server/locales/zh-CN.d.ts +2 -2
  212. package/dist/server/locales/zh-CN.d.ts.map +1 -1
  213. package/dist/server/locales/zh-CN.js +5 -0
  214. package/dist/server/locales/zh-CN.js.map +1 -1
  215. package/dist/server/locales/zh-TW.d.ts.map +1 -1
  216. package/dist/server/locales/zh-TW.js +5 -0
  217. package/dist/server/locales/zh-TW.js.map +1 -1
  218. package/dist/server/online/agent/native/backend.d.ts.map +1 -1
  219. package/dist/server/online/agent/native/backend.js +2 -0
  220. package/dist/server/online/agent/native/backend.js.map +1 -1
  221. package/dist/server/online/agent/native/session.d.ts.map +1 -1
  222. package/dist/server/online/agent/native/session.js +3 -1
  223. package/dist/server/online/agent/native/session.js.map +1 -1
  224. package/dist/server/online/agent/runner/backend.d.ts.map +1 -1
  225. package/dist/server/online/agent/runner/backend.js +21 -9
  226. package/dist/server/online/agent/runner/backend.js.map +1 -1
  227. package/dist/server/online/agent/runner/projection.d.ts +1 -1
  228. package/dist/server/online/agent/runner/projection.d.ts.map +1 -1
  229. package/dist/server/online/agent/runner/projection.js +33 -3
  230. package/dist/server/online/agent/runner/projection.js.map +1 -1
  231. package/dist/server/online/agent/service.d.ts.map +1 -1
  232. package/dist/server/online/agent/service.js +3 -0
  233. package/dist/server/online/agent/service.js.map +1 -1
  234. package/dist/server/online/agent/types.d.ts +3 -0
  235. package/dist/server/online/agent/types.d.ts.map +1 -1
  236. package/dist/server/providers/agents.d.ts +5 -9
  237. package/dist/server/providers/agents.d.ts.map +1 -1
  238. package/dist/server/providers/agents.js +12 -13
  239. package/dist/server/providers/agents.js.map +1 -1
  240. package/dist/server/routes/chat.d.ts.map +1 -1
  241. package/dist/server/routes/chat.js +37 -0
  242. package/dist/server/routes/chat.js.map +1 -1
  243. package/dist/server/routes/runners/admin.d.ts.map +1 -1
  244. package/dist/server/routes/runners/admin.js +16 -7
  245. package/dist/server/routes/runners/admin.js.map +1 -1
  246. package/dist/server/routes/runners/runner.d.ts.map +1 -1
  247. package/dist/server/routes/runners/runner.js +6 -4
  248. package/dist/server/routes/runners/runner.js.map +1 -1
  249. package/dist/server/routes/schemas.d.ts +8 -2
  250. package/dist/server/routes/schemas.d.ts.map +1 -1
  251. package/dist/server/routes/schemas.js +46 -3
  252. package/dist/server/routes/schemas.js.map +1 -1
  253. package/dist/server/tokens.d.ts +1 -2
  254. package/dist/server/tokens.d.ts.map +1 -1
  255. package/dist/server/tokens.js.map +1 -1
  256. package/dist/shared/conversations.d.ts +20 -0
  257. package/dist/shared/conversations.d.ts.map +1 -1
  258. package/dist/shared/conversations.js.map +1 -1
  259. package/dist/shared/runners.d.ts +18 -2
  260. package/dist/shared/runners.d.ts.map +1 -1
  261. package/dist/shared/runners.js +20 -0
  262. package/dist/shared/runners.js.map +1 -1
  263. package/dist/shared/ui-message.d.ts +4 -2
  264. package/dist/shared/ui-message.d.ts.map +1 -1
  265. package/dist/shared/ui-message.js +2 -1
  266. package/dist/shared/ui-message.js.map +1 -1
  267. package/docs/ui-message-clients.md +5 -1
  268. package/docs/ui-messages.md +3 -1
  269. package/docs/vectors-migration.md +91 -0
  270. package/package.json +14 -16
  271. package/skills/nocobase-app-plugin-agents/SKILL.md +1 -1
  272. package/skills/nocobase-app-plugin-agents/references/extension-points.md +0 -1
  273. package/skills/nocobase-app-plugin-agents/references/model-services.md +3 -3
  274. package/skills/nocobase-app-plugin-agents/references/online-agent.md +6 -0
  275. package/skills/nocobase-app-plugin-agents/references/setup-and-config.md +0 -4
  276. package/skills/nocobase-app-plugin-agents/references/verification-and-troubleshooting.md +0 -2
  277. package/dist/server/vectors/index.d.ts +0 -7
  278. package/dist/server/vectors/index.d.ts.map +0 -1
  279. package/dist/server/vectors/index.js +0 -6
  280. package/dist/server/vectors/index.js.map +0 -1
  281. package/dist/server/vectors/naming.d.ts +0 -6
  282. package/dist/server/vectors/naming.d.ts.map +0 -1
  283. package/dist/server/vectors/naming.js +0 -15
  284. package/dist/server/vectors/naming.js.map +0 -1
  285. package/dist/server/vectors/pgvector.d.ts +0 -26
  286. package/dist/server/vectors/pgvector.d.ts.map +0 -1
  287. package/dist/server/vectors/pgvector.js +0 -346
  288. package/dist/server/vectors/pgvector.js.map +0 -1
  289. package/dist/server/vectors/sqlite-vec.d.ts +0 -14
  290. package/dist/server/vectors/sqlite-vec.d.ts.map +0 -1
  291. package/dist/server/vectors/sqlite-vec.js +0 -286
  292. package/dist/server/vectors/sqlite-vec.js.map +0 -1
  293. package/dist/server/vectors/types.d.ts +0 -256
  294. package/dist/server/vectors/types.d.ts.map +0 -1
  295. package/dist/server/vectors/types.js +0 -2
  296. package/dist/server/vectors/types.js.map +0 -1
  297. package/dist/server/vectors/vectors.d.ts +0 -34
  298. package/dist/server/vectors/vectors.d.ts.map +0 -1
  299. package/dist/server/vectors/vectors.js +0 -866
  300. package/dist/server/vectors/vectors.js.map +0 -1
@@ -17,7 +17,7 @@ import { nativeExecutionStatus, runnerBinding, } from '../../online/agent/persis
17
17
  * and bumps `thread`, so every switch starts a new session.
18
18
  */
19
19
  import { isDeepStrictEqual } from 'node:util';
20
- import { quotaForAgent } from '../../../shared/runners.js';
20
+ import { quotaForAgent, chatEffortsFor, chatEffortCapabilities, } from '../../../shared/runners.js';
21
21
  import { CONVERSATION_HISTORY_IN_PROMPT, CONVERSATION_PAGE_DEFAULT, CONVERSATION_PAGE_MAX, CONVERSATION_SOURCE_PATTERN, CONVERSATION_SUBJECT, CONVERSATION_TITLE_AGENT_MAX, CONVERSATION_TITLE_AUTO_CHARS, CONVERSATION_TITLE_MAX, MESSAGE_CONTENT_MAX, MESSAGE_PAGE_DEFAULT, MESSAGE_PAGE_MAX, } from '../../../shared/conversations.js';
22
22
  import { runnerTakesAgent } from '../../../shared/runners.js';
23
23
  import { CONSULT_MAX_DEPTH } from '../../../shared/runs.js';
@@ -27,7 +27,7 @@ import { forbidden, conflict, invalid, notFound, ProtocolError, } from '../../ke
27
27
  import { asJson, jsonObject } from '../../kernel/values.js';
28
28
  import { findAgent } from '../agents/index.js';
29
29
  import { agentNamesOf } from '../agents/agent.store.js';
30
- import { eventsAfter, hasSession, lastSummary, openRunsOnEach, queuedRun, } from '../runs/index.js';
30
+ import { eventsAfter, fitsActor, hasSession, lastSummary, openRunsOnEach, queuedRun, } from '../runs/index.js';
31
31
  import { eventsRepo, findRunRecord, inputsRepo, runsRepo, sessionsRepo, toRun, } from '../runs/run.store.js';
32
32
  import { readAttachmentIds, storedAttachments, } from './attachments.js';
33
33
  import { availabilityOf, wakeable } from './availability.js';
@@ -126,6 +126,7 @@ function refOf(record) {
126
126
  agentId: record.agentId,
127
127
  title: record.title,
128
128
  source: record.source,
129
+ projectId: record.projectId ?? null,
129
130
  };
130
131
  }
131
132
  function onlineExecution(record, open = [], active = false) {
@@ -196,6 +197,14 @@ export function createConversationService(deps) {
196
197
  /** A message as the API answers it, its files' URLs under the application's base path. */
197
198
  const viewOf = (record) => toMessage(record, deps.basePath?.() ?? '');
198
199
  const rules = [];
200
+ let workspace;
201
+ async function projectWorkspace(conn, conversation, prepared) {
202
+ if (!conversation.projectId)
203
+ return { dirs: [], scopes: [] };
204
+ if (!workspace)
205
+ throw invalid('Project workspaces are not available.');
206
+ return workspace.resolve(conn, conversation, prepared);
207
+ }
199
208
  const sources = createSources();
200
209
  const links = createConversationLinks({
201
210
  tx,
@@ -214,6 +223,20 @@ export function createConversationService(deps) {
214
223
  throw notFound('Conversation');
215
224
  return record;
216
225
  }
226
+ /** Titles for the current owner's conversations, read together for runner history. */
227
+ async function ownedTitles(conn, userId, ids) {
228
+ const uniqueIds = [...new Set(ids)];
229
+ if (uniqueIds.length === 0)
230
+ return new Map();
231
+ const records = await conversationsRepo(conn).findMany({
232
+ filter: (f) => f.and([
233
+ f.string('userId').eq(userId),
234
+ f.or(uniqueIds.map((id) => f.string('id').eq(id))),
235
+ ]),
236
+ select: (s) => s.fields('id', 'title'),
237
+ });
238
+ return new Map(records.map((record) => [String(record.id), record.title ?? '']));
239
+ }
217
240
  /** Locks the conversation if `userId` owns it; 404 otherwise. */
218
241
  async function lockOwned(unit, userId, id, allowOnline = false) {
219
242
  const record = (await lockConversation(unit.conn, id, now()));
@@ -241,6 +264,7 @@ export function createConversationService(deps) {
241
264
  : null;
242
265
  }
243
266
  async function summaries(conn, records) {
267
+ const linked = await links.summaries(conn, records);
244
268
  const agents = new Map();
245
269
  for (const record of records)
246
270
  for (const id of [
@@ -256,11 +280,13 @@ export function createConversationService(deps) {
256
280
  const machines = new Map((runnerIds.length ? await deps.runners.all(conn, runnerIds) : []).map((runner) => [runner.id, runner]));
257
281
  return Promise.all(records.map(async (record) => ({
258
282
  id: record.id,
283
+ links: linked.get(record.id) ?? [],
259
284
  title: record.title,
260
285
  titleSource: record.titleSource,
261
286
  category: record.category,
262
287
  showInChatList: record.showInChatList,
263
288
  source: record.source,
289
+ projectId: record.projectId ?? null,
264
290
  mode: record.mode,
265
291
  execution: onlineExecution(record, open, jsonObject(record.metadata).onlineAgent
266
292
  ? await deps.nativeActive?.(record.id, jsonObject(jsonObject(jsonObject(record.metadata).onlineAgent).call).state === 'running')
@@ -312,6 +338,22 @@ export function createConversationService(deps) {
312
338
  const now = clock.now().getTime();
313
339
  return availabilityOf(agent, userId, deps.agents.mayInvoke, eligible.length, online.some((runner) => runnerTakesAgent(runner, agent, now)));
314
340
  }
341
+ async function effortOptions(conn, agent, userId) {
342
+ const eligible = await deps.eligibility.runnersFor(conn, agent, {
343
+ actorUserId: userId,
344
+ });
345
+ return chatEffortCapabilities(agent.modelEntries[0], eligible);
346
+ }
347
+ async function validateEffort(conn, agent, userId, selected) {
348
+ // Only an explicitly selected non-default value is a conversation preference.
349
+ const preference = selected && selected !== agent.modelEntries[0]?.effort ? selected : null;
350
+ if (preference === null)
351
+ return null;
352
+ const options = await effortOptions(conn, agent, userId);
353
+ if (options !== undefined && !options.includes(preference))
354
+ throw invalid('The reasoning effort is no longer supported. Select an available effort or restore the Agent default.', { reason: 'EFFORT_UNSUPPORTED', effort: preference, efforts: options });
355
+ return preference;
356
+ }
315
357
  async function canAnswer(conn, agent, userId) {
316
358
  if (!wakeable(agent, userId, deps.agents.mayInvoke))
317
359
  return false;
@@ -349,6 +391,9 @@ export function createConversationService(deps) {
349
391
  const target = await fallbackTarget(conn, record);
350
392
  return {
351
393
  ...summary,
394
+ effort: typeof jsonObject(record.metadata).effort === 'string'
395
+ ? jsonObject(record.metadata).effort
396
+ : null,
352
397
  availability: await availabilityFor(conn, agent, record.userId),
353
398
  canFallback: Boolean(target),
354
399
  canRestore: !runnerBinding(record.metadata) &&
@@ -493,6 +538,12 @@ export function createConversationService(deps) {
493
538
  const binding = runnerBinding(record.metadata);
494
539
  if (binding && !idleOnly)
495
540
  throw conflict('Start a new conversation to choose another agent.');
541
+ if (record.projectId && record.runnerId) {
542
+ const [runner] = await deps.runners.all(unit.conn, [record.runnerId]);
543
+ if (!runner ||
544
+ !fitsActor(runner, agent, record.userId, [], clock.now().getTime()))
545
+ throw conflict('This runner cannot run the selected Agent. Switch runner or start a new conversation.', { reason: 'boundRunnerIncompatible' });
546
+ }
496
547
  const withdrawn = idleOnly
497
548
  ? []
498
549
  : await deps.runs.withdrawQueued({ subject: subjectOf(record.id), byUserId: record.userId }, unit);
@@ -516,7 +567,7 @@ export function createConversationService(deps) {
516
567
  }
517
568
  : {}),
518
569
  thread: Number(record.thread) + 1,
519
- runnerId: null,
570
+ runnerId: record.projectId ? record.runnerId : null,
520
571
  updatedAt: now(),
521
572
  },
522
573
  });
@@ -545,10 +596,11 @@ export function createConversationService(deps) {
545
596
  // Turning run events into messages
546
597
  // -------------------------------------------------------------------------------------------------------------------
547
598
  /** Appends the run's new text events as assistant messages, in the caller's transaction. */
548
- async function syncIn(unit, run) {
599
+ async function syncIn(unit, run, lockedRecord) {
549
600
  if (run.subject.kind !== CONVERSATION_SUBJECT)
550
601
  return;
551
- const record = await lockConversation(unit.conn, run.subject.id, now());
602
+ const record = lockedRecord ??
603
+ (await lockConversation(unit.conn, run.subject.id, now()));
552
604
  if (!record || record.userId !== run.actorUserId)
553
605
  return;
554
606
  let after;
@@ -723,8 +775,9 @@ export function createConversationService(deps) {
723
775
  // The run subject
724
776
  // -------------------------------------------------------------------------------------------------------------------
725
777
  /** A run's assembly in `conversation`, as its owner (`ownerName`) left it: `history` the messages before its inputs. */
726
- async function conversationAssembly(conn, claim, conversation, ownerName, history, previousSummary) {
778
+ async function conversationAssembly(conn, claim, conversation, ownerName, history, previousSummary, prepared) {
727
779
  const key = conversationKey(conversation.id);
780
+ const directories = await projectWorkspace(conn, conversation, prepared);
728
781
  const added = await Promise.all(rules.map((source) => source(conn, {
729
782
  conversation: {
730
783
  id: conversation.id,
@@ -732,6 +785,7 @@ export function createConversationService(deps) {
732
785
  agentId: conversation.agentId,
733
786
  title: conversation.title,
734
787
  source: conversation.source,
788
+ projectId: conversation.projectId ?? null,
735
789
  },
736
790
  agent: claim.agent,
737
791
  ownerName,
@@ -784,8 +838,7 @@ export function createConversationService(deps) {
784
838
  source: conversation.source,
785
839
  },
786
840
  },
787
- dirs: [],
788
- scopes: [],
841
+ ...directories,
789
842
  };
790
843
  }
791
844
  /**
@@ -862,7 +915,13 @@ export function createConversationService(deps) {
862
915
  },
863
916
  },
864
917
  context: {
865
- async assemble(conn, claim) {
918
+ async prepare(run) {
919
+ const record = await findConversation(tx.read(), run.subject.id);
920
+ return record?.projectId
921
+ ? workspace?.prepare(record.userId)
922
+ : undefined;
923
+ },
924
+ async assemble(conn, claim, prepared) {
866
925
  const record = await findConversation(conn, claim.run.subject.id);
867
926
  // Only the owner's runs see a conversation (a preview by anyone else, too, finds nothing).
868
927
  if (!record || record.userId !== claim.run.actorUserId)
@@ -916,16 +975,28 @@ export function createConversationService(deps) {
916
975
  }
917
976
  const previousSummary = fresh
918
977
  ? await lastSummary(conn, {
919
- agentId: claim.agent.id,
978
+ ...(record.projectId ? {} : { agentId: claim.agent.id }),
920
979
  subjectKind: CONVERSATION_SUBJECT,
921
980
  subjectId: record.id,
922
981
  }, claim.run.id)
923
982
  : null;
924
- const assembly = await conversationAssembly(conn, claim, { ...refOf(record), createdAt: record.createdAt }, ownerName, history, previousSummary);
983
+ const assembly = await conversationAssembly(conn, claim, { ...refOf(record), createdAt: record.createdAt }, ownerName, history, previousSummary, prepared);
984
+ const preference = jsonObject(record.metadata).turnEffort ??
985
+ jsonObject(record.metadata).effort;
986
+ const defaultEffort = claim.agent.modelEntries[0]?.effort;
987
+ const capabilities = chatEffortCapabilities(claim.agent.modelEntries[0], claim.runner ? [claim.runner] : []);
988
+ const effort = typeof preference === 'string'
989
+ ? preference
990
+ : capabilities === undefined
991
+ ? null
992
+ : defaultEffort && !capabilities.includes(defaultEffort)
993
+ ? null
994
+ : undefined;
995
+ const withEffort = effort === undefined ? assembly : { ...assembly, effort };
925
996
  const config = runnerBinding(record.metadata)?.config;
926
997
  return config
927
998
  ? {
928
- ...assembly,
999
+ ...withEffort,
929
1000
  system: [
930
1001
  assembly.system,
931
1002
  config.systemPrompt,
@@ -936,7 +1007,7 @@ export function createConversationService(deps) {
936
1007
  .filter(Boolean)
937
1008
  .join('\n\n'),
938
1009
  }
939
- : assembly;
1010
+ : withEffort;
940
1011
  },
941
1012
  },
942
1013
  runnerPin: {
@@ -1076,6 +1147,8 @@ export function createConversationService(deps) {
1076
1147
  }
1077
1148
  }
1078
1149
  async function sendMessage(userId, id, request, realtime) {
1150
+ if (request.effort !== undefined && !realtime)
1151
+ throw invalid('Reasoning effort selection requires a Runner realtime message.', { reason: 'EFFORT_UNSUPPORTED' });
1079
1152
  if (request.agentId !== undefined && (!realtime || !request.agentId.trim()))
1080
1153
  throw invalid('Agent selection requires a Runner realtime message.');
1081
1154
  const content = request.content;
@@ -1112,6 +1185,7 @@ export function createConversationService(deps) {
1112
1185
  const sent = await measuredAdmission(async (unit) => {
1113
1186
  let record = await lockOwned(unit, userId, id, Boolean(realtime));
1114
1187
  let binding = runnerBinding(record.metadata);
1188
+ let turnEffort = null;
1115
1189
  if (realtime) {
1116
1190
  if (!binding)
1117
1191
  throw invalid('This is not a Runner OnlineAgent conversation.');
@@ -1140,6 +1214,33 @@ export function createConversationService(deps) {
1140
1214
  ((switching || !record.runnerId) &&
1141
1215
  !(await canAnswer(unit.conn, agent, userId))))
1142
1216
  throw conflict('This agent has no available execution environment.');
1217
+ if (realtime.queue) {
1218
+ // Keep already received replies before this follow-up, under the admission's existing conversation lock.
1219
+ const working = open.find((run) => run.status !== 'queued');
1220
+ await syncIn(unit, working, record);
1221
+ record = (await findConversation(unit.conn, id));
1222
+ }
1223
+ turnEffort =
1224
+ typeof jsonObject(record.metadata).turnEffort === 'string'
1225
+ ? jsonObject(record.metadata).turnEffort
1226
+ : null;
1227
+ if (!realtime.queue) {
1228
+ const effort = await validateEffort(unit.conn, agent, userId, request.effort);
1229
+ turnEffort = effort;
1230
+ if (effort !== (jsonObject(record.metadata).effort ?? null) ||
1231
+ turnEffort !== (jsonObject(record.metadata).turnEffort ?? null)) {
1232
+ const metadata = asJson({
1233
+ ...jsonObject(record.metadata),
1234
+ effort,
1235
+ turnEffort,
1236
+ });
1237
+ await conversationsRepo(unit.conn).updateMany({
1238
+ filter: { id },
1239
+ values: { metadata },
1240
+ });
1241
+ record = { ...record, metadata };
1242
+ }
1243
+ }
1143
1244
  if (switching) {
1144
1245
  record = await rebind(unit, record, agent, null, {
1145
1246
  code: 'switchedAgent',
@@ -1179,6 +1280,7 @@ export function createConversationService(deps) {
1179
1280
  workContext: realtime?.workContext ?? context,
1180
1281
  metadata: {
1181
1282
  ...(request.clientId ? { clientId: request.clientId } : {}),
1283
+ ...(turnEffort ? { effort: turnEffort } : {}),
1182
1284
  ...(realtime?.queue ? { queued: true } : {}),
1183
1285
  },
1184
1286
  });
@@ -1228,6 +1330,7 @@ export function createConversationService(deps) {
1228
1330
  binding,
1229
1331
  contextKinds: deps.contextKinds,
1230
1332
  sources,
1333
+ ownedTitles,
1231
1334
  async list(userId, query) {
1232
1335
  const conn = tx.read();
1233
1336
  const limit = limitOf(query.pageSize, CONVERSATION_PAGE_DEFAULT, CONVERSATION_PAGE_MAX);
@@ -1310,6 +1413,9 @@ export function createConversationService(deps) {
1310
1413
  };
1311
1414
  },
1312
1415
  async create(userId, request, runnerConfig) {
1416
+ const prepared = request.projectId
1417
+ ? await workspace?.prepare(userId)
1418
+ : undefined;
1313
1419
  const id = await tx.run(async (unit) => {
1314
1420
  const agent = await pickAgent(unit.conn, userId, request.agentId);
1315
1421
  if (runnerConfig &&
@@ -1317,6 +1423,7 @@ export function createConversationService(deps) {
1317
1423
  throw invalid('Select a callable Runner agent.');
1318
1424
  if (runnerConfig && !(await canAnswer(unit.conn, agent, userId)))
1319
1425
  throw conflict('This agent has no available execution environment.');
1426
+ const effort = await validateEffort(unit.conn, agent, userId, request.effort);
1320
1427
  const title = request.title === undefined
1321
1428
  ? null
1322
1429
  : titleFor(request.title, CONVERSATION_TITLE_MAX);
@@ -1325,17 +1432,27 @@ export function createConversationService(deps) {
1325
1432
  if (source !== 'panel' && !sources.has(source))
1326
1433
  throw invalid(`${source} is not a source conversations start from.`);
1327
1434
  const conversationId = ids.next();
1435
+ if (request.projectId && agent.source !== 'runner')
1436
+ throw invalid('Only Runner conversations can select a project.');
1437
+ await projectWorkspace(unit.conn, {
1438
+ id: conversationId,
1439
+ userId,
1440
+ agentId: agent.id,
1441
+ title,
1442
+ source,
1443
+ projectId: request.projectId,
1444
+ }, prepared);
1328
1445
  await conversationsRepo(unit.conn).createOne({
1329
1446
  values: {
1330
1447
  id: conversationId,
1331
- ...(runnerConfig
1332
- ? {
1333
- metadata: asJson({
1334
- runnerOnlineAgent: { version: 1, config: runnerConfig },
1335
- }),
1336
- }
1337
- : {}),
1448
+ metadata: asJson({
1449
+ effort,
1450
+ ...(runnerConfig
1451
+ ? { runnerOnlineAgent: { version: 1, config: runnerConfig } }
1452
+ : {}),
1453
+ }),
1338
1454
  userId,
1455
+ projectId: request.projectId ?? null,
1339
1456
  agentId: agent.id,
1340
1457
  mode: 'runner',
1341
1458
  fallbackFromAgentId: null,
@@ -1365,9 +1482,24 @@ export function createConversationService(deps) {
1365
1482
  },
1366
1483
  get: detail,
1367
1484
  async update(userId, id, patch) {
1485
+ const prepared = patch.projectId
1486
+ ? await workspace?.prepare(userId)
1487
+ : undefined;
1368
1488
  await tx.run(async (unit) => {
1369
1489
  const record = await lockOwned(unit, userId, id, true);
1370
1490
  const values = {};
1491
+ if (patch.projectId !== undefined &&
1492
+ patch.projectId !== (record.projectId ?? null)) {
1493
+ if (Number(record.lastSeq) > 0 &&
1494
+ (await messagesRepo(unit.conn).findOne({
1495
+ filter: { conversationId: id, role: 'user' },
1496
+ })))
1497
+ throw conflict('Start a new conversation to choose another project.');
1498
+ if (record.mode !== 'runner')
1499
+ throw invalid('Only Runner conversations can select a project.');
1500
+ await projectWorkspace(unit.conn, { ...refOf(record), projectId: patch.projectId }, prepared);
1501
+ values.projectId = patch.projectId;
1502
+ }
1371
1503
  if (patch.title !== undefined) {
1372
1504
  values.title = titleFor(patch.title, CONVERSATION_TITLE_MAX);
1373
1505
  values.titleSource = 'user';
@@ -1567,7 +1699,9 @@ export function createConversationService(deps) {
1567
1699
  filter: { id },
1568
1700
  values,
1569
1701
  });
1570
- changed(unit, record);
1702
+ changed(unit, record, values.seenSeq === undefined
1703
+ ? undefined
1704
+ : { seenSeq: values.seenSeq });
1571
1705
  });
1572
1706
  return detail(userId, id);
1573
1707
  },
@@ -1959,6 +2093,17 @@ export function createConversationService(deps) {
1959
2093
  return { id: enqueued.runId, outcome: enqueued.outcome };
1960
2094
  },
1961
2095
  lock: async (conn, id) => (await lockConversation(conn, id, now())) !== null,
2096
+ workspace: {
2097
+ provide(provider) {
2098
+ if (workspace)
2099
+ throw new Error('A conversation workspace provider is already registered.');
2100
+ workspace = provider;
2101
+ return () => {
2102
+ if (workspace === provider)
2103
+ workspace = undefined;
2104
+ };
2105
+ },
2106
+ },
1962
2107
  rules: {
1963
2108
  provide(source) {
1964
2109
  rules.push(source);
@@ -2001,7 +2146,14 @@ export function createConversationService(deps) {
2001
2146
  .filter((agent) => agent.source === 'runner' && deps.agents.mayInvoke(agent, userId))
2002
2147
  .map(async (agent) => {
2003
2148
  const fitting = runners.filter((runner) => runnerTakesAgent(runner, agent, now));
2149
+ const eligible = await deps.eligibility.runnersFor(conn, agent, {
2150
+ actorUserId: userId,
2151
+ runners,
2152
+ variableState: variableStates?.get(agent.id),
2153
+ });
2004
2154
  return {
2155
+ efforts: chatEffortsFor(agent.modelEntries[0], eligible),
2156
+ effort: agent.modelEntries[0]?.effort ?? null,
2005
2157
  id: agent.id,
2006
2158
  name: agent.name,
2007
2159
  description: agent.description,
@@ -2010,7 +2162,7 @@ export function createConversationService(deps) {
2010
2162
  avatar: agent.avatar,
2011
2163
  isSystemDefault: agent.id === settings.defaultAgentId,
2012
2164
  isMyDefault: agent.id === preferences.defaultAgentId,
2013
- availability: await availabilityFor(conn, agent, userId, runners, variableStates?.get(agent.id)),
2165
+ availability: availabilityOf(agent, userId, deps.agents.mayInvoke, eligible.length, fitting.length > 0),
2014
2166
  quota: quotaForAgent(runners, agent, userId, now),
2015
2167
  runnerNames: fitting
2016
2168
  .filter((runner) => runner.trust === 'team' || runner.ownerUserId === userId)