@capybara-hq/kernel 0.1.0-rc.2 → 0.1.0-rc.4

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 (312) hide show
  1. package/dist/admin/admin.controller.d.ts +9 -4
  2. package/dist/admin/admin.controller.js +13 -4
  3. package/dist/admin/extensions.controller.d.ts +8 -1
  4. package/dist/admin/extensions.controller.js +22 -2
  5. package/dist/agents/agent-lookup.d.ts +3 -0
  6. package/dist/agents/agent-lookup.js +20 -2
  7. package/dist/agents/agent-profile.service.d.ts +29 -0
  8. package/dist/agents/agent-profile.service.js +117 -0
  9. package/dist/agents/agents.controller.d.ts +93 -3
  10. package/dist/agents/agents.controller.js +114 -4
  11. package/dist/agents/agents.module.js +6 -2
  12. package/dist/agents/agents.service.d.ts +13 -5
  13. package/dist/agents/agents.service.js +49 -13
  14. package/dist/agents/definition.d.ts +35 -8
  15. package/dist/agents/definition.js +203 -17
  16. package/dist/agents/instruction-files.d.ts +60 -0
  17. package/dist/agents/instruction-files.js +111 -0
  18. package/dist/agents/models.controller.d.ts +9 -3
  19. package/dist/agents/models.controller.js +19 -4
  20. package/dist/agents/profile.d.ts +96 -0
  21. package/dist/agents/profile.js +51 -0
  22. package/dist/agents/readiness.controller.d.ts +26 -0
  23. package/dist/agents/readiness.controller.js +56 -0
  24. package/dist/agents/templates.js +20 -5
  25. package/dist/background/background.controller.d.ts +38 -0
  26. package/dist/background/background.controller.js +87 -0
  27. package/dist/background/background.module.d.ts +10 -0
  28. package/dist/background/background.module.js +32 -0
  29. package/dist/background/finish.d.ts +43 -0
  30. package/dist/background/finish.js +94 -0
  31. package/dist/background/launch-lock.d.ts +14 -0
  32. package/dist/background/launch-lock.js +39 -0
  33. package/dist/background/launch.d.ts +36 -0
  34. package/dist/background/launch.js +70 -0
  35. package/dist/background/poll.job.d.ts +53 -0
  36. package/dist/background/poll.job.js +182 -0
  37. package/dist/background/render.d.ts +23 -0
  38. package/dist/background/render.js +48 -0
  39. package/dist/background/session.d.ts +37 -0
  40. package/dist/background/session.js +66 -0
  41. package/dist/background/status.d.ts +35 -0
  42. package/dist/background/status.js +57 -0
  43. package/dist/background/stop.d.ts +62 -0
  44. package/dist/background/stop.js +225 -0
  45. package/dist/background/store.d.ts +150 -0
  46. package/dist/background/store.js +351 -0
  47. package/dist/background/store.module.d.ts +6 -0
  48. package/dist/background/store.module.js +22 -0
  49. package/dist/background/tasks.service.d.ts +67 -0
  50. package/dist/background/tasks.service.js +186 -0
  51. package/dist/background/wake-rules.d.ts +57 -0
  52. package/dist/background/wake-rules.js +83 -0
  53. package/dist/background/wake.d.ts +28 -0
  54. package/dist/background/wake.js +57 -0
  55. package/dist/catalog/catalog-content.js +4 -1
  56. package/dist/catalog/claude/agents.js +15 -10
  57. package/dist/catalog/marketplaces.service.d.ts +1 -1
  58. package/dist/catalog/plugin-import.js +1 -1
  59. package/dist/catalog/plugin-upgrade.js +1 -1
  60. package/dist/channels/cards.d.ts +8 -2
  61. package/dist/channels/cards.js +17 -9
  62. package/dist/channels/connections.service.d.ts +16 -1
  63. package/dist/channels/connections.service.js +38 -4
  64. package/dist/channels/threads.d.ts +5 -0
  65. package/dist/channels/threads.js +8 -0
  66. package/dist/config/schema.d.ts +18 -0
  67. package/dist/config/schema.js +104 -2
  68. package/dist/conversations/access.js +2 -0
  69. package/dist/conversations/artifacts.d.ts +1 -1
  70. package/dist/conversations/artifacts.js +1 -1
  71. package/dist/conversations/attachments.service.d.ts +30 -0
  72. package/dist/conversations/attachments.service.js +130 -5
  73. package/dist/conversations/conversations.controller.d.ts +36 -5
  74. package/dist/conversations/conversations.controller.js +62 -12
  75. package/dist/conversations/conversations.module.js +3 -0
  76. package/dist/conversations/conversations.service.d.ts +44 -2
  77. package/dist/conversations/conversations.service.js +140 -12
  78. package/dist/conversations/event-log.d.ts +12 -1
  79. package/dist/conversations/event-log.js +29 -1
  80. package/dist/conversations/events.d.ts +81 -3
  81. package/dist/conversations/events.js +83 -16
  82. package/dist/conversations/queue.d.ts +3 -1
  83. package/dist/conversations/queue.js +13 -2
  84. package/dist/conversations/queue.service.js +3 -1
  85. package/dist/conversations/titles.d.ts +7 -0
  86. package/dist/conversations/titles.js +15 -0
  87. package/dist/db/kernel-schema.d.ts +60 -4
  88. package/dist/db/pool.d.ts +5 -0
  89. package/dist/db/pool.js +3 -0
  90. package/dist/errors.d.ts +8 -0
  91. package/dist/errors.js +8 -0
  92. package/dist/evals/recorded-tools.d.ts +27 -1
  93. package/dist/evals/recorded-tools.js +85 -10
  94. package/dist/evals/run-replay.d.ts +10 -2
  95. package/dist/evals/run-replay.js +49 -12
  96. package/dist/evals/step-replay.js +12 -6
  97. package/dist/evals/trajectory.d.ts +2 -2
  98. package/dist/evals/trajectory.js +1 -1
  99. package/dist/extensions/admin-ops.js +2 -0
  100. package/dist/extensions/admin-view.d.ts +3 -1
  101. package/dist/extensions/admin-view.js +10 -2
  102. package/dist/extensions/blobs.js +1 -0
  103. package/dist/extensions/config-issues.d.ts +13 -1
  104. package/dist/extensions/config-issues.js +22 -5
  105. package/dist/extensions/config-json-schema.d.ts +43 -7
  106. package/dist/extensions/config-json-schema.js +124 -58
  107. package/dist/extensions/deployment-config-store.d.ts +6 -0
  108. package/dist/extensions/deployment-config.d.ts +50 -3
  109. package/dist/extensions/deployment-config.js +125 -28
  110. package/dist/extensions/display-name.d.ts +22 -0
  111. package/dist/extensions/display-name.js +77 -0
  112. package/dist/extensions/extensions.module.js +6 -0
  113. package/dist/extensions/host.d.ts +26 -1
  114. package/dist/extensions/host.js +82 -9
  115. package/dist/extensions/org-config.controller.d.ts +11 -3
  116. package/dist/extensions/org-config.controller.js +14 -6
  117. package/dist/extensions/org-config.d.ts +6 -0
  118. package/dist/extensions/org-config.js +12 -13
  119. package/dist/extensions/package.d.ts +14 -1
  120. package/dist/extensions/package.js +51 -2
  121. package/dist/extensions/permission-view.d.ts +3 -0
  122. package/dist/extensions/point-defaults.controller.d.ts +34 -0
  123. package/dist/extensions/point-defaults.controller.js +71 -0
  124. package/dist/extensions/point-defaults.d.ts +64 -0
  125. package/dist/extensions/point-defaults.js +206 -0
  126. package/dist/extensions/registry.d.ts +1 -1
  127. package/dist/extensions/registry.js +2 -0
  128. package/dist/http/api.module.js +8 -0
  129. package/dist/http/nest-logger.d.ts +6 -5
  130. package/dist/http/nest-logger.js +24 -10
  131. package/dist/identity/auth.controller.js +9 -5
  132. package/dist/identity/cookies.d.ts +20 -5
  133. package/dist/identity/cookies.js +44 -6
  134. package/dist/identity/me.controller.d.ts +5 -1
  135. package/dist/identity/me.controller.js +12 -3
  136. package/dist/identity/sessions.d.ts +12 -0
  137. package/dist/identity/sessions.js +45 -0
  138. package/dist/identity/settings.js +1 -1
  139. package/dist/index.d.ts +1 -1
  140. package/dist/infra/config/kernel-config.service.js +6 -0
  141. package/dist/infra/database/database.module.js +1 -0
  142. package/dist/infra/events/kernel-events.d.ts +8 -0
  143. package/dist/infra/lifecycle/process-guards.js +4 -0
  144. package/dist/infra/logging/logging.module.d.ts +4 -2
  145. package/dist/infra/logging/logging.module.js +24 -6
  146. package/dist/infra/queue/redis-connection.d.ts +6 -0
  147. package/dist/infra/queue/redis-connection.js +7 -0
  148. package/dist/infra/redis/redis.module.js +6 -2
  149. package/dist/infra/tokens.d.ts +6 -1
  150. package/dist/infra/tokens.js +6 -1
  151. package/dist/lifecycle/kernel-lifecycle.js +5 -0
  152. package/dist/log.d.ts +13 -1
  153. package/dist/log.js +93 -17
  154. package/dist/models/purposes.d.ts +23 -0
  155. package/dist/models/purposes.js +31 -10
  156. package/dist/orgs/offboarding.d.ts +3 -1
  157. package/dist/orgs/offboarding.js +26 -16
  158. package/dist/orgs/settings.service.d.ts +7 -2
  159. package/dist/orgs/settings.service.js +15 -4
  160. package/dist/roles/worker.module.js +6 -0
  161. package/dist/runtime/context/build.d.ts +15 -13
  162. package/dist/runtime/context/build.js +45 -27
  163. package/dist/runtime/context/claude-tools.d.ts +3 -1
  164. package/dist/runtime/context/claude-tools.js +12 -12
  165. package/dist/runtime/context/compaction.js +4 -0
  166. package/dist/runtime/context/legacy-text.d.ts +22 -0
  167. package/dist/runtime/context/legacy-text.js +37 -0
  168. package/dist/runtime/context/skills.d.ts +29 -11
  169. package/dist/runtime/context/skills.js +37 -30
  170. package/dist/runtime/langgraph-loop.js +3 -2
  171. package/dist/runtime/loop.d.ts +5 -4
  172. package/dist/runtime/maintenance.d.ts +4 -2
  173. package/dist/runtime/maintenance.js +17 -5
  174. package/dist/runtime/model-call.d.ts +17 -1
  175. package/dist/runtime/model-call.js +4 -3
  176. package/dist/runtime/providers.d.ts +6 -0
  177. package/dist/runtime/providers.js +8 -0
  178. package/dist/runtime/run-finish.d.ts +1 -1
  179. package/dist/runtime/run-queue.d.ts +28 -5
  180. package/dist/runtime/run-queue.js +131 -35
  181. package/dist/runtime/run-worker.d.ts +2 -2
  182. package/dist/runtime/run-worker.js +20 -7
  183. package/dist/runtime/runtime.module.js +8 -3
  184. package/dist/runtime/step-log.d.ts +6 -0
  185. package/dist/runtime/step-log.js +16 -2
  186. package/dist/runtime/subagents/definition.d.ts +1 -6
  187. package/dist/runtime/subagents/definition.js +14 -13
  188. package/dist/runtime/subagents/dispatch.d.ts +2 -2
  189. package/dist/runtime/subagents/dispatch.js +2 -2
  190. package/dist/runtime/subagents/org-agents.js +2 -1
  191. package/dist/runtime/subagents/roster.d.ts +1 -1
  192. package/dist/runtime/subagents/roster.js +1 -1
  193. package/dist/runtime/subagents/skill-fork.d.ts +2 -2
  194. package/dist/runtime/subagents/skill-fork.js +2 -2
  195. package/dist/runtime/subagents/subagents.service.d.ts +3 -3
  196. package/dist/runtime/subagents/subagents.service.js +3 -3
  197. package/dist/runtime/usage-report.d.ts +2 -1
  198. package/dist/runtime/usage-report.js +2 -1
  199. package/dist/runtime/waits.d.ts +13 -7
  200. package/dist/runtime/waits.js +42 -16
  201. package/dist/sandbox/sandbox.module.js +6 -1
  202. package/dist/sandbox/sandbox.service.d.ts +61 -3
  203. package/dist/sandbox/sandbox.service.js +141 -7
  204. package/dist/skills/catalog.d.ts +4 -4
  205. package/dist/skills/catalog.js +4 -4
  206. package/dist/skills/changes.service.d.ts +2 -2
  207. package/dist/skills/changes.service.js +5 -4
  208. package/dist/skills/invocation-keys.d.ts +1 -1
  209. package/dist/skills/invocation-keys.js +1 -1
  210. package/dist/skills/invoke.d.ts +1 -1
  211. package/dist/skills/invoke.js +5 -4
  212. package/dist/tools/approvals.service.d.ts +6 -0
  213. package/dist/tools/approvals.service.js +21 -1
  214. package/dist/tools/builtin-names.d.ts +91 -0
  215. package/dist/tools/builtin-names.js +143 -0
  216. package/dist/tools/builtins/agent.d.ts +7 -7
  217. package/dist/tools/builtins/agent.js +7 -11
  218. package/dist/tools/builtins/ask-user.d.ts +1 -1
  219. package/dist/tools/builtins/ask-user.js +2 -2
  220. package/dist/tools/builtins/builtin-tools.d.ts +17 -3
  221. package/dist/tools/builtins/builtin-tools.js +36 -4
  222. package/dist/tools/builtins/read-tool-output.d.ts +1 -1
  223. package/dist/tools/builtins/read-tool-output.js +2 -2
  224. package/dist/tools/builtins/sandbox/bash.d.ts +4 -4
  225. package/dist/tools/builtins/sandbox/bash.js +259 -29
  226. package/dist/tools/builtins/sandbox/common.d.ts +30 -7
  227. package/dist/tools/builtins/sandbox/common.js +9 -5
  228. package/dist/tools/builtins/sandbox/deliver.d.ts +1 -1
  229. package/dist/tools/builtins/sandbox/deliver.js +12 -12
  230. package/dist/tools/builtins/sandbox/edit.d.ts +1 -1
  231. package/dist/tools/builtins/sandbox/edit.js +41 -30
  232. package/dist/tools/builtins/sandbox/file-state.d.ts +34 -0
  233. package/dist/tools/builtins/sandbox/file-state.js +34 -0
  234. package/dist/tools/builtins/sandbox/glob-match.d.ts +1 -1
  235. package/dist/tools/builtins/sandbox/glob-match.js +1 -1
  236. package/dist/tools/builtins/sandbox/glob.d.ts +1 -1
  237. package/dist/tools/builtins/sandbox/glob.js +4 -4
  238. package/dist/tools/builtins/sandbox/grep.d.ts +7 -4
  239. package/dist/tools/builtins/sandbox/grep.js +147 -30
  240. package/dist/tools/builtins/sandbox/index.d.ts +3 -1
  241. package/dist/tools/builtins/sandbox/index.js +7 -1
  242. package/dist/tools/builtins/sandbox/read.d.ts +2 -2
  243. package/dist/tools/builtins/sandbox/read.js +21 -14
  244. package/dist/tools/builtins/sandbox/task-output.d.ts +63 -0
  245. package/dist/tools/builtins/sandbox/task-output.js +304 -0
  246. package/dist/tools/builtins/sandbox/task-stop.d.ts +10 -0
  247. package/dist/tools/builtins/sandbox/task-stop.js +112 -0
  248. package/dist/tools/builtins/sandbox/write.d.ts +1 -1
  249. package/dist/tools/builtins/sandbox/write.js +37 -33
  250. package/dist/tools/builtins/skill-load.d.ts +3 -3
  251. package/dist/tools/builtins/skill-load.js +32 -16
  252. package/dist/tools/builtins/skill-write.d.ts +4 -4
  253. package/dist/tools/builtins/skill-write.js +10 -10
  254. package/dist/tools/builtins/tool-search.d.ts +13 -9
  255. package/dist/tools/builtins/tool-search.js +25 -14
  256. package/dist/tools/builtins/web/errors.d.ts +8 -0
  257. package/dist/tools/builtins/web/errors.js +53 -0
  258. package/dist/tools/builtins/web/extract.d.ts +62 -0
  259. package/dist/tools/builtins/web/extract.js +267 -0
  260. package/dist/tools/builtins/web/fetch.d.ts +55 -0
  261. package/dist/tools/builtins/web/fetch.js +174 -0
  262. package/dist/tools/builtins/web/index.d.ts +13 -0
  263. package/dist/tools/builtins/web/index.js +12 -0
  264. package/dist/tools/builtins/web/page-cache.d.ts +25 -0
  265. package/dist/tools/builtins/web/page-cache.js +77 -0
  266. package/dist/tools/builtins/web/search.d.ts +25 -0
  267. package/dist/tools/builtins/web/search.js +209 -0
  268. package/dist/tools/builtins/web/url.d.ts +17 -0
  269. package/dist/tools/builtins/web/url.js +43 -0
  270. package/dist/tools/connectors.service.js +4 -4
  271. package/dist/tools/display-names.d.ts +9 -0
  272. package/dist/tools/display-names.js +24 -0
  273. package/dist/tools/drift.js +5 -5
  274. package/dist/tools/elicitation.js +1 -1
  275. package/dist/tools/info.d.ts +20 -4
  276. package/dist/tools/info.js +55 -32
  277. package/dist/tools/legacy-data.d.ts +34 -0
  278. package/dist/tools/legacy-data.js +76 -0
  279. package/dist/tools/legacy-keys.d.ts +51 -0
  280. package/dist/tools/legacy-keys.js +115 -0
  281. package/dist/tools/naming.d.ts +1 -5
  282. package/dist/tools/naming.js +1 -7
  283. package/dist/tools/oauth/flow-paths.d.ts +1 -1
  284. package/dist/tools/oauth/flow-paths.js +1 -1
  285. package/dist/tools/outputs.js +1 -1
  286. package/dist/tools/pipeline.d.ts +13 -7
  287. package/dist/tools/pipeline.js +112 -39
  288. package/dist/tools/policy.d.ts +1 -14
  289. package/dist/tools/policy.js +13 -19
  290. package/dist/tools/questions.controller.d.ts +1 -1
  291. package/dist/tools/questions.controller.js +1 -1
  292. package/dist/tools/questions.service.d.ts +1 -1
  293. package/dist/tools/registry.d.ts +38 -24
  294. package/dist/tools/registry.js +54 -47
  295. package/dist/tools/resources.d.ts +1 -1
  296. package/dist/tools/resources.js +1 -1
  297. package/dist/tools/tools.module.js +13 -1
  298. package/dist/tools/validate.js +1 -1
  299. package/package.json +3 -3
  300. package/src/migrations/0063_tool_keys_claude_code.sql +220 -0
  301. package/src/migrations/0064_account_last_org.sql +7 -0
  302. package/src/migrations/0065_conversation_archive.sql +8 -0
  303. package/src/migrations/0066_extension_display_name.sql +4 -0
  304. package/src/migrations/0067_agent_profiles.sql +20 -0
  305. package/src/migrations/0068_agent_avatar_uploads.sql +7 -0
  306. package/src/migrations/0069_attachments_agent_idx.sql +3 -0
  307. package/src/migrations/0070_background_tasks.sql +37 -0
  308. package/src/migrations/0071_background_tasks_reserve.sql +6 -0
  309. package/src/migrations/0072_background_tasks_stopping.sql +5 -0
  310. package/src/migrations/0073_org_extension_point_defaults.sql +11 -0
  311. package/dist/tools/builtins/sandbox/names.d.ts +0 -7
  312. package/dist/tools/builtins/sandbox/names.js +0 -20
@@ -1,5 +1,6 @@
1
1
  import { AgentLookup } from "../agents/agent-lookup.js";
2
2
  import type { ModelRef } from "../agents/definition.js";
3
+ import { BackgroundTaskStopper } from "../background/stop.js";
3
4
  import type { TitleSource } from "../db/kernel-schema.js";
4
5
  import { KernelDb } from "../infra/database/kernel-db.js";
5
6
  import { TransactionHost } from "../infra/database/transaction-host.js";
@@ -31,7 +32,16 @@ export interface ConversationView {
31
32
  parentConversationId: string | null;
32
33
  /** A sub-agent's type as resolved at its first dispatch; null for any other conversation. */
33
34
  subagentType: string | null;
35
+ /** When the owner archived it; null = not archived (hidden from the default lists). */
36
+ archivedAt: Date | null;
37
+ /**
38
+ * What the latest run is doing: `running` (queued or running), `waiting` (an approval or a
39
+ * question), else null. Only the list queries compute it; other responses carry null.
40
+ */
41
+ activity: "running" | "waiting" | null;
34
42
  }
43
+ /** Escapes `\`, `%` and `_` so user text is matched literally by `LIKE … ESCAPE '\'`. */
44
+ export declare function escapeLike(s: string): string;
35
45
  /** One agent's group in the sidebar list: the caller's newest conversations with that agent. */
36
46
  export interface ConversationGroup {
37
47
  agent: {
@@ -150,6 +160,8 @@ export declare class ConversationsService {
150
160
  private readonly attachments;
151
161
  /** Renders a person's skill invocation (phase D). */
152
162
  private readonly invoker;
163
+ /** Stops a deleted conversation's background tasks. */
164
+ private readonly background;
153
165
  readonly txHost: TransactionHost;
154
166
  constructor(db: KernelDb, eventLog: EventLog, agents: Pick<AgentLookup, "get">, access: Pick<ConversationAccess, "isAccessible">,
155
167
  /** Starts and stops the conversation's runs. */
@@ -161,7 +173,9 @@ export declare class ConversationsService {
161
173
  /** Binds uploaded files to the conversation in the message's transaction. */
162
174
  attachments: Pick<AttachmentsService, "bindForMessage">,
163
175
  /** Renders a person's skill invocation (phase D). */
164
- invoker: Pick<SkillInvoker, "prepare">, txHost: TransactionHost);
176
+ invoker: Pick<SkillInvoker, "prepare">,
177
+ /** Stops a deleted conversation's background tasks. */
178
+ background: Pick<BackgroundTaskStopper, "stopForDeletedConversation">, txHost: TransactionHost);
165
179
  /**
166
180
  * 不阻塞地调用一次 `ensureRun`(打开 SSE、读事件时):它幂等,并顺带清理孤儿运行,
167
181
  * 会话不会因为某次运行结束后的投递失败而永远停在「运行中」。失败只记日志。
@@ -175,12 +189,15 @@ export declare class ConversationsService {
175
189
  * message's `clientMessageId` (`create_request_id`), so a retried send returns the same
176
190
  * conversation and event with `created: false`. An agent the caller cannot use →
177
191
  * `agent.not_found` and nothing is written. Queues a run afterwards, like `postUserMessage`.
192
+ * `events` are every event the create wrote, in seq order from seq 1 (a `model_switched` before
193
+ * the message when the send switched), so a client can seed the new conversation's log whole.
178
194
  */
179
195
  createWithMessage(orgId: string, accountId: string, agentId: string, input: UserMessageInput & {
180
196
  clientMessageId: string;
181
197
  }): Promise<{
182
198
  conversation: ConversationView;
183
199
  event: ConversationEvent;
200
+ events: ConversationEvent[];
184
201
  created: boolean;
185
202
  }>;
186
203
  /** The conversation row and its first message (undefined: a concurrent retry committed first). */
@@ -197,6 +214,23 @@ export declare class ConversationsService {
197
214
  * job never overwrites it. Publishes `changed: title`. `title` is already trimmed and checked.
198
215
  */
199
216
  rename(orgId: string, conversationId: string, accountId: string, title: string): Promise<ConversationView>;
217
+ /**
218
+ * Archives or unarchives a conversation (owner only, as {@link rename}). An archived
219
+ * conversation leaves the default lists but stays readable. Publishes `changed: archived`.
220
+ */
221
+ setArchived(orgId: string, conversationId: string, accountId: string, archived: boolean): Promise<ConversationView>;
222
+ /**
223
+ * Soft-deletes a conversation (owner only; a sub-agent's conversation is `conversation.read_only`):
224
+ * stops an active run, hides the conversation everywhere for its owner and drops its channel
225
+ * bindings, then stops every running background task in its sandbox (its sub-agents' too;
226
+ * outside the delete's transaction, a task whose sandbox cannot be reached ends `lost` with
227
+ * `kill_pending`). The tasks are stopped after the conversation is marked deleted, so no finish
228
+ * (the stops' own, or one the poller writes meanwhile) can wake a run on it: `ensureRun` refuses a
229
+ * deleted conversation (the stops' finishes carry `stoppedBy: "deletion"` as well). The event
230
+ * log, runs and trajectory stay (enterprise admins still see them).
231
+ */
232
+ remove(orgId: string, conversationId: string, accountId: string): Promise<void>;
233
+ private markDeleted;
200
234
  /** 读会话:非所有者、或 agent 已对所有者不可用 → `conversation.not_found`。 */
201
235
  get(orgId: string, conversationId: string, viewerAccountId: string): Promise<ConversationView>;
202
236
  /**
@@ -216,6 +250,9 @@ export declare class ConversationsService {
216
250
  after?: string;
217
251
  limit: number;
218
252
  agentId?: string;
253
+ archived?: boolean;
254
+ q?: string;
255
+ state?: "running" | "waiting";
219
256
  }): Promise<ConversationView[]>;
220
257
  /**
221
258
  * My chat conversations grouped by agent (the sidebar, UI refresh spec §5.9), in one statement.
@@ -228,9 +265,13 @@ export declare class ConversationsService {
228
265
  perGroup: number;
229
266
  limit: number;
230
267
  after?: string;
268
+ archived?: boolean;
269
+ q?: string;
270
+ state?: "running" | "waiting";
231
271
  }): Promise<{
232
272
  groups: ConversationGroup[];
233
273
  next: string | null;
274
+ archivedCount: number;
234
275
  }>;
235
276
  /**
236
277
  * The sub-agents of a conversation, newest first (sub-agents spec §14.3); owner only, as
@@ -292,7 +333,8 @@ export declare class ConversationsService {
292
333
  checkPick(orgId: string, conv: ConversationView, accountId: string, pick: Pick<UserMessageInput, "model" | "effort">): Promise<void>;
293
334
  /**
294
335
  * Writes the message (preceded by its `model_switched` when it switches) inside `t` and updates
295
- * `conversations.selection`; returns the `user_message` event. The caller notifies after commit.
336
+ * `conversations.selection`; returns the events written, in seq order (the `user_message`
337
+ * last). The caller notifies after commit.
296
338
  */
297
339
  private writeMessage;
298
340
  /**
@@ -11,6 +11,8 @@ import { randomUUID } from "node:crypto";
11
11
  import { Inject, Injectable } from "@nestjs/common";
12
12
  import { sql } from "kysely";
13
13
  import { AgentLookup, visibleTo } from "../agents/agent-lookup.js";
14
+ import { BackgroundTaskStopper } from "../background/stop.js";
15
+ import { unbindConversation } from "../channels/threads.js";
14
16
  import { ApiError } from "../errors.js";
15
17
  import { KernelDb } from "../infra/database/kernel-db.js";
16
18
  import { TransactionHost } from "../infra/database/transaction-host.js";
@@ -25,6 +27,27 @@ import { AttachmentsService } from "./attachments.service.js";
25
27
  import { EventLog } from "./event-log.js";
26
28
  import { userMessageIdemKey } from "./idem-keys.js";
27
29
  import { NO_SELECTION, planSelection, readSelection } from "./selection.js";
30
+ /** Escapes `\`, `%` and `_` so user text is matched literally by `LIKE … ESCAPE '\'`. */
31
+ export function escapeLike(s) {
32
+ return s.replace(/[\\%_]/g, (ch) => `\\${ch}`);
33
+ }
34
+ /**
35
+ * SQL for the activity of the conversation row aliased/named `ref` (a correlated subquery).
36
+ * A run waiting on its sub-agents (status waiting with `wait_expires_at`, set only for a
37
+ * `subagents` wait) is still working, so it reports `running`; only a wait for a person
38
+ * (approval, input) reports `waiting`.
39
+ */
40
+ function activitySql(ref) {
41
+ const id = sql.ref(`${ref}.id`);
42
+ return sql `(
43
+ SELECT CASE
44
+ WHEN lr.status IN ('queued', 'running') THEN 'running'
45
+ WHEN lr.status = 'waiting' AND lr.wait_expires_at IS NOT NULL THEN 'running'
46
+ WHEN lr.status = 'waiting' THEN 'waiting' END
47
+ FROM capybara.runs lr WHERE lr.conversation_id = ${id}
48
+ ORDER BY lr.created_at DESC, lr.id DESC LIMIT 1
49
+ )`;
50
+ }
28
51
  const notFound = () => new ApiError("conversation.not_found", "Conversation not found");
29
52
  export { userMessageIdemKey } from "./idem-keys.js";
30
53
  const COLUMNS = [
@@ -40,6 +63,7 @@ const COLUMNS = [
40
63
  "updated_at",
41
64
  "parent_conversation_id",
42
65
  "subagent_type",
66
+ "archived_at",
43
67
  ];
44
68
  function toView(r) {
45
69
  return {
@@ -55,6 +79,8 @@ function toView(r) {
55
79
  updatedAt: r.updated_at,
56
80
  parentConversationId: r.parent_conversation_id,
57
81
  subagentType: r.subagent_type,
82
+ archivedAt: r.archived_at,
83
+ activity: r.activity ?? null,
58
84
  };
59
85
  }
60
86
  /** The longest conversation title (characters, after trimming). */
@@ -90,6 +116,7 @@ let ConversationsService = class ConversationsService {
90
116
  content;
91
117
  attachments;
92
118
  invoker;
119
+ background;
93
120
  txHost;
94
121
  constructor(db, eventLog, agents, access,
95
122
  /** Starts and stops the conversation's runs. */
@@ -101,7 +128,9 @@ let ConversationsService = class ConversationsService {
101
128
  /** Binds uploaded files to the conversation in the message's transaction. */
102
129
  attachments,
103
130
  /** Renders a person's skill invocation (phase D). */
104
- invoker, txHost) {
131
+ invoker,
132
+ /** Stops a deleted conversation's background tasks. */
133
+ background, txHost) {
105
134
  this.db = db;
106
135
  this.eventLog = eventLog;
107
136
  this.agents = agents;
@@ -112,6 +141,7 @@ let ConversationsService = class ConversationsService {
112
141
  this.content = content;
113
142
  this.attachments = attachments;
114
143
  this.invoker = invoker;
144
+ this.background = background;
115
145
  this.txHost = txHost;
116
146
  }
117
147
  /**
@@ -139,6 +169,8 @@ let ConversationsService = class ConversationsService {
139
169
  * message's `clientMessageId` (`create_request_id`), so a retried send returns the same
140
170
  * conversation and event with `created: false`. An agent the caller cannot use →
141
171
  * `agent.not_found` and nothing is written. Queues a run afterwards, like `postUserMessage`.
172
+ * `events` are every event the create wrote, in seq order from seq 1 (a `model_switched` before
173
+ * the message when the send switched), so a client can seed the new conversation's log whole.
142
174
  */
143
175
  async createWithMessage(orgId, accountId, agentId, input) {
144
176
  const agent = await this.agents.get(orgId, agentId, accountId);
@@ -149,8 +181,9 @@ let ConversationsService = class ConversationsService {
149
181
  const extra = await this.resolveExtra(orgId, accountId, input);
150
182
  const invoked = await this.prepareSkill(orgId, agentId, undefined, agent.definition, input);
151
183
  const id = randomUUID();
152
- const written = await this.createIn(id, orgId, accountId, agentId, input, next, extra, invoked);
153
- if (!written) {
184
+ const all = await this.createIn(id, orgId, accountId, agentId, input, next, extra, invoked);
185
+ const written = all?.at(-1);
186
+ if (!all || !written) {
154
187
  const raced = await this.findCreated(orgId, accountId, agentId, input.clientMessageId);
155
188
  if (!raced)
156
189
  throw new ApiError("conflict", "The conversation is being created; retry");
@@ -158,7 +191,7 @@ let ConversationsService = class ConversationsService {
158
191
  }
159
192
  await this.eventLog.notify(id, written.seq);
160
193
  await this.runs.ensureRun(id);
161
- return { conversation: await this.rowView(id), event: written, created: true };
194
+ return { conversation: await this.rowView(id), event: written, events: all, created: true };
162
195
  }
163
196
  /** The conversation row and its first message (undefined: a concurrent retry committed first). */
164
197
  // its own transaction: the caller notifies and publishes after the commit
@@ -206,8 +239,10 @@ let ConversationsService = class ConversationsService {
206
239
  const event = await this.eventLog.findByIdem(row.id, userMessageIdemKey(clientMessageId));
207
240
  if (!event)
208
241
  return null;
242
+ // what the first send wrote: the log from its start up to the message
243
+ const events = (await this.eventLog.readAfter(row.id, 0, event.seq)).filter((e) => e.seq <= event.seq);
209
244
  await this.runs.ensureRun(row.id);
210
- return { conversation: toView(row), event, created: false };
245
+ return { conversation: toView(row), event, events, created: false };
211
246
  }
212
247
  async rowView(id) {
213
248
  return toView(await this.db
@@ -231,6 +266,58 @@ let ConversationsService = class ConversationsService {
231
266
  await this.eventLog.publishChanged(conversationId, "title");
232
267
  return toView(row);
233
268
  }
269
+ /**
270
+ * Archives or unarchives a conversation (owner only, as {@link rename}). An archived
271
+ * conversation leaves the default lists but stays readable. Publishes `changed: archived`.
272
+ */
273
+ async setArchived(orgId, conversationId, accountId, archived) {
274
+ await this.writable(orgId, conversationId, accountId);
275
+ const row = await this.db
276
+ .updateTable("conversations")
277
+ .set({ archived_at: archived ? sql `now()` : null })
278
+ .where("id", "=", conversationId)
279
+ .returning(COLUMNS)
280
+ .executeTakeFirstOrThrow();
281
+ await this.eventLog.publishChanged(conversationId, "archived");
282
+ return toView(row);
283
+ }
284
+ /**
285
+ * Soft-deletes a conversation (owner only; a sub-agent's conversation is `conversation.read_only`):
286
+ * stops an active run, hides the conversation everywhere for its owner and drops its channel
287
+ * bindings, then stops every running background task in its sandbox (its sub-agents' too;
288
+ * outside the delete's transaction, a task whose sandbox cannot be reached ends `lost` with
289
+ * `kill_pending`). The tasks are stopped after the conversation is marked deleted, so no finish
290
+ * (the stops' own, or one the poller writes meanwhile) can wake a run on it: `ensureRun` refuses a
291
+ * deleted conversation (the stops' finishes carry `stoppedBy: "deletion"` as well). The event
292
+ * log, runs and trajectory stay (enterprise admins still see them).
293
+ */
294
+ async remove(orgId, conversationId, accountId) {
295
+ await this.writable(orgId, conversationId, accountId);
296
+ try {
297
+ await this.abort(orgId, conversationId, accountId);
298
+ }
299
+ catch (e) {
300
+ if (!(e instanceof ApiError && e.code === "run.not_active"))
301
+ throw e;
302
+ }
303
+ await this.markDeleted(conversationId);
304
+ await this.background.stopForDeletedConversation(conversationId);
305
+ await this.eventLog.publishChanged(conversationId, "deleted");
306
+ }
307
+ // the publish happens after the commit, in the caller
308
+ async markDeleted(conversationId) {
309
+ await this.db
310
+ .updateTable("conversations")
311
+ .set({ deleted_at: sql `now()` })
312
+ .where("id", "=", conversationId)
313
+ .execute();
314
+ // Queued messages must not start a run on a deleted conversation
315
+ await this.db
316
+ .deleteFrom("conversation_queue")
317
+ .where("conversation_id", "=", conversationId)
318
+ .execute();
319
+ await unbindConversation(this.db.db, conversationId);
320
+ }
234
321
  /** 读会话:非所有者、或 agent 已对所有者不可用 → `conversation.not_found`。 */
235
322
  async get(orgId, conversationId, viewerAccountId) {
236
323
  const { kind: _kind, ...row } = await this.readable(orgId, conversationId, viewerAccountId);
@@ -255,6 +342,7 @@ let ConversationsService = class ConversationsService {
255
342
  .where("id", "=", conversationId)
256
343
  .where("org_id", "=", orgId)
257
344
  .where("account_id", "=", viewerAccountId)
345
+ .where("deleted_at", "is", null)
258
346
  // Hidden eval replay conversations (kind 'eval') are not reachable here (only the trajectory
259
347
  // viewer shows them); a sub-agent's conversation is, for its owner (sub-agents spec §6.1)
260
348
  .where("kind", "in", ["chat", "subagent"])
@@ -284,10 +372,13 @@ let ConversationsService = class ConversationsService {
284
372
  let q = this.db
285
373
  .selectFrom("conversations")
286
374
  .select(COLUMNS)
375
+ .select(activitySql("conversations").as("activity"))
287
376
  .where("org_id", "=", orgId)
288
377
  .where("account_id", "=", accountId)
289
378
  // 评测回放的隐藏对话(kind = 'eval')不在「我的对话」里
290
379
  .where("kind", "=", "chat")
380
+ .where("deleted_at", "is", null)
381
+ .where("archived_at", opts.archived === true ? "is not" : "is", null)
291
382
  .where((eb) => eb.exists(eb
292
383
  .selectFrom("agents")
293
384
  .select(sql `1`.as("one"))
@@ -299,6 +390,12 @@ let ConversationsService = class ConversationsService {
299
390
  .limit(opts.limit);
300
391
  if (opts.agentId !== undefined)
301
392
  q = q.where("agent_id", "=", opts.agentId);
393
+ if (opts.q !== undefined) {
394
+ q = q.where(sql `conversations.title ILIKE ${`%${escapeLike(opts.q)}%`} ESCAPE '\\'`);
395
+ }
396
+ if (opts.state !== undefined) {
397
+ q = q.where(sql `${activitySql("conversations")} = ${opts.state}`);
398
+ }
302
399
  if (opts.after !== undefined) {
303
400
  const after = opts.after;
304
401
  q = q.where((eb) => sql `(${eb.ref("updated_at")}, ${eb.ref("id")}) < (
@@ -322,6 +419,11 @@ let ConversationsService = class ConversationsService {
322
419
  .where("agents.org_id", "=", orgId)
323
420
  .where("agents.deleted_at", "is", null)
324
421
  .where(visibleTo(accountId));
422
+ const archivedFilter = opts.archived === true ? sql `c.archived_at IS NOT NULL` : sql `c.archived_at IS NULL`;
423
+ const searchFilter = opts.q === undefined
424
+ ? sql ``
425
+ : sql `AND c.title ILIKE ${`%${escapeLike(opts.q)}%`} ESCAPE '\\'`;
426
+ const stateFilter = opts.state === undefined ? sql `` : sql `AND ${activitySql("c")} = ${opts.state}`;
325
427
  const cursor = opts.after === undefined
326
428
  ? sql ``
327
429
  : sql `WHERE (g.latest_at, g.agent_id) < (
@@ -332,6 +434,7 @@ let ConversationsService = class ConversationsService {
332
434
  SELECT c.agent_id, max(c.updated_at) AS latest_at
333
435
  FROM capybara.conversations c
334
436
  WHERE c.org_id = ${orgId} AND c.account_id = ${accountId} AND c.kind = 'chat'
437
+ AND c.deleted_at IS NULL AND ${archivedFilter} ${searchFilter} ${stateFilter}
335
438
  AND c.agent_id IN ${visibleAgents}
336
439
  GROUP BY c.agent_id
337
440
  ), page AS (
@@ -342,15 +445,18 @@ let ConversationsService = class ConversationsService {
342
445
  )
343
446
  SELECT page.agent_id, page.latest_at, v.definition ->> 'name' AS agent_name,
344
447
  cs.id, cs.org_id, cs.account_id, cs.title, cs.title_source, cs.selection, cs.last_seq,
345
- cs.created_at, cs.updated_at, cs.parent_conversation_id, cs.subagent_type
448
+ cs.created_at, cs.updated_at, cs.parent_conversation_id, cs.subagent_type,
449
+ cs.archived_at, cs.activity
346
450
  FROM page
347
451
  JOIN capybara.agents a ON a.id = page.agent_id
348
452
  JOIN capybara.agent_versions v ON v.agent_id = a.id AND v.version = a.current_version
349
453
  CROSS JOIN LATERAL (
350
454
  SELECT c.id, c.org_id, c.account_id, c.title, c.title_source, c.selection, c.last_seq,
351
- c.created_at, c.updated_at, c.parent_conversation_id, c.subagent_type
455
+ c.created_at, c.updated_at, c.parent_conversation_id, c.subagent_type,
456
+ c.archived_at, ${activitySql("c")} AS activity
352
457
  FROM capybara.conversations c
353
458
  WHERE c.org_id = ${orgId} AND c.account_id = ${accountId} AND c.kind = 'chat'
459
+ AND c.deleted_at IS NULL AND ${archivedFilter} ${searchFilter} ${stateFilter}
354
460
  AND c.agent_id = page.agent_id
355
461
  ORDER BY c.updated_at DESC, c.id DESC
356
462
  LIMIT ${opts.perGroup + 1}
@@ -377,7 +483,14 @@ let ConversationsService = class ConversationsService {
377
483
  }
378
484
  const page = groups.slice(0, opts.limit);
379
485
  const next = groups.length > opts.limit ? (page.at(-1)?.agent.id ?? null) : null;
380
- return { groups: page, next };
486
+ // The archived entry's count ignores `q` and `state`
487
+ const counted = await sql `
488
+ SELECT count(*) AS n FROM capybara.conversations c
489
+ WHERE c.org_id = ${orgId} AND c.account_id = ${accountId} AND c.kind = 'chat'
490
+ AND c.deleted_at IS NULL AND c.archived_at IS NOT NULL
491
+ AND c.agent_id IN ${visibleAgents}
492
+ `.execute(this.db.db);
493
+ return { groups: page, next, archivedCount: Number(counted.rows[0]?.n ?? 0) };
381
494
  }
382
495
  /**
383
496
  * The sub-agents of a conversation, newest first (sub-agents spec §14.3); owner only, as
@@ -544,6 +657,15 @@ let ConversationsService = class ConversationsService {
544
657
  this.userMessage(accountId, input, extra, [], invoked),
545
658
  ]);
546
659
  }
660
+ // The message is written: it brings an archived conversation back into the default lists
661
+ if (conv.archivedAt !== null) {
662
+ await this.db
663
+ .updateTable("conversations")
664
+ .set({ archived_at: null })
665
+ .where("id", "=", conversationId)
666
+ .execute();
667
+ await this.eventLog.publishChanged(conversationId, "archived");
668
+ }
547
669
  await this.runs.ensureRun(conversationId);
548
670
  return event;
549
671
  }
@@ -551,7 +673,8 @@ let ConversationsService = class ConversationsService {
551
673
  // its own transaction: the caller notifies and publishes after the commit
552
674
  async postIn(orgId, conversationId, accountId, input, next, extra, invoked) {
553
675
  const blocks = await this.bindIn(orgId, conversationId, accountId, input.files ?? []);
554
- return this.writeMessage(conversationId, accountId, input, next, extra, blocks, invoked);
676
+ const written = await this.writeMessage(conversationId, accountId, input, next, extra, blocks, invoked);
677
+ return written.at(-1);
555
678
  }
556
679
  /**
557
680
  * The selection a message switches to, or null when it changes nothing. A model equal to the
@@ -591,7 +714,8 @@ let ConversationsService = class ConversationsService {
591
714
  }
592
715
  /**
593
716
  * Writes the message (preceded by its `model_switched` when it switches) inside `t` and updates
594
- * `conversations.selection`; returns the `user_message` event. The caller notifies after commit.
717
+ * `conversations.selection`; returns the events written, in seq order (the `user_message`
718
+ * last). The caller notifies after commit.
595
719
  */
596
720
  async writeMessage(conversationId, accountId, input, next, extra, blocks, invoked) {
597
721
  const events = [];
@@ -616,7 +740,7 @@ let ConversationsService = class ConversationsService {
616
740
  .where("id", "=", conversationId)
617
741
  .execute();
618
742
  }
619
- return written.at(-1);
743
+ return written;
620
744
  }
621
745
  /**
622
746
  * Renders the message's skill invocation (phase D), or undefined when it has none. Resolved
@@ -718,6 +842,9 @@ let ConversationsService = class ConversationsService {
718
842
  __decorate([
719
843
  Transactional({ propagation: "requires_new" })
720
844
  ], ConversationsService.prototype, "createIn", null);
845
+ __decorate([
846
+ Transactional()
847
+ ], ConversationsService.prototype, "markDeleted", null);
721
848
  __decorate([
722
849
  Transactional({ propagation: "requires_new" })
723
850
  ], ConversationsService.prototype, "postIn", null);
@@ -739,6 +866,7 @@ ConversationsService = __decorate([
739
866
  __param(7, Inject(ConnectorContent)),
740
867
  __param(8, Inject(AttachmentsService)),
741
868
  __param(9, Inject(SkillInvoker)),
742
- __param(10, Inject(TransactionHost))
869
+ __param(10, Inject(BackgroundTaskStopper)),
870
+ __param(11, Inject(TransactionHost))
743
871
  ], ConversationsService);
744
872
  export { ConversationsService };
@@ -64,7 +64,7 @@ export type ConversationBusMessage = {
64
64
  * What changed about a conversation outside its event log (UI refresh B): the SSE stream sends it as
65
65
  * `event: changed` with `{ what }`, and the web refetches that part. Never model-visible.
66
66
  */
67
- export declare const CONVERSATION_CHANGES: readonly ["title", "queue", "selection", "files"];
67
+ export declare const CONVERSATION_CHANGES: readonly ["title", "queue", "selection", "files", "archived", "deleted", "background"];
68
68
  export type ConversationChange = (typeof CONVERSATION_CHANGES)[number];
69
69
  /**
70
70
  * 会话事件日志:只追加,是 agent 运行的唯一事实来源。
@@ -123,6 +123,17 @@ export declare class EventLog {
123
123
  latestOfType<T extends ConversationEventType>(conversationId: string, type: T): Promise<Extract<ConversationEvent, {
124
124
  type: T;
125
125
  }> | undefined>;
126
+ /**
127
+ * The file state (`tool_result.view.file`) the latest `Read` / `Write` / `Edit` result of this
128
+ * conversation recorded for `path` (a normalized sandbox path); null when none did. One index
129
+ * probe per call: the partial index `conversation_events_file_state_idx` of migration 0063
130
+ * (conversation_id, view.file.path, seq DESC; tool_result rows only) answers it, also for a path
131
+ * never read (the query's predicate must keep matching that index's expression).
132
+ */
133
+ latestFileState(conversationId: string, path: string): Promise<{
134
+ size: number;
135
+ sha256: string;
136
+ } | null>;
126
137
  private validate;
127
138
  /** The append itself, in the context's transaction (both callers open or join one). */
128
139
  private write;
@@ -41,7 +41,16 @@ export const conversationTopic = (conversationId) => `conv.${conversationId}`;
41
41
  * What changed about a conversation outside its event log (UI refresh B): the SSE stream sends it as
42
42
  * `event: changed` with `{ what }`, and the web refetches that part. Never model-visible.
43
43
  */
44
- export const CONVERSATION_CHANGES = ["title", "queue", "selection", "files"];
44
+ export const CONVERSATION_CHANGES = [
45
+ "title",
46
+ "queue",
47
+ "selection",
48
+ "files",
49
+ "archived",
50
+ "deleted",
51
+ // the conversation's background tasks (or its sub-agents')
52
+ "background",
53
+ ];
45
54
  const notFound = () => new ApiError("conversation.not_found", "Conversation not found");
46
55
  function toEvent(r) {
47
56
  return {
@@ -191,6 +200,25 @@ let EventLog = class EventLog {
191
200
  .executeTakeFirst();
192
201
  return r && toEvent(r);
193
202
  }
203
+ /**
204
+ * The file state (`tool_result.view.file`) the latest `Read` / `Write` / `Edit` result of this
205
+ * conversation recorded for `path` (a normalized sandbox path); null when none did. One index
206
+ * probe per call: the partial index `conversation_events_file_state_idx` of migration 0063
207
+ * (conversation_id, view.file.path, seq DESC; tool_result rows only) answers it, also for a path
208
+ * never read (the query's predicate must keep matching that index's expression).
209
+ */
210
+ async latestFileState(conversationId, path) {
211
+ const r = await this.db
212
+ .selectFrom("conversation_events")
213
+ .select(sql `payload->'view'->'file'`.as("file"))
214
+ .where("conversation_id", "=", conversationId)
215
+ .where("type", "=", "tool_result")
216
+ .where(sql `payload->'view'->'file'->>'path' = ${path}`)
217
+ .orderBy("seq", "desc")
218
+ .limit(1)
219
+ .executeTakeFirst();
220
+ return r ? { size: r.file.size, sha256: r.file.sha256 } : null;
221
+ }
194
222
  validate(e) {
195
223
  const r = ConversationEventSchema.safeParse({ type: e.type, payload: e.payload });
196
224
  if (!r.success) {