@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.
- package/dist/admin/admin.controller.d.ts +9 -4
- package/dist/admin/admin.controller.js +13 -4
- package/dist/admin/extensions.controller.d.ts +8 -1
- package/dist/admin/extensions.controller.js +22 -2
- package/dist/agents/agent-lookup.d.ts +3 -0
- package/dist/agents/agent-lookup.js +20 -2
- package/dist/agents/agent-profile.service.d.ts +29 -0
- package/dist/agents/agent-profile.service.js +117 -0
- package/dist/agents/agents.controller.d.ts +93 -3
- package/dist/agents/agents.controller.js +114 -4
- package/dist/agents/agents.module.js +6 -2
- package/dist/agents/agents.service.d.ts +13 -5
- package/dist/agents/agents.service.js +49 -13
- package/dist/agents/definition.d.ts +35 -8
- package/dist/agents/definition.js +203 -17
- package/dist/agents/instruction-files.d.ts +60 -0
- package/dist/agents/instruction-files.js +111 -0
- package/dist/agents/models.controller.d.ts +9 -3
- package/dist/agents/models.controller.js +19 -4
- package/dist/agents/profile.d.ts +96 -0
- package/dist/agents/profile.js +51 -0
- package/dist/agents/readiness.controller.d.ts +26 -0
- package/dist/agents/readiness.controller.js +56 -0
- package/dist/agents/templates.js +20 -5
- package/dist/background/background.controller.d.ts +38 -0
- package/dist/background/background.controller.js +87 -0
- package/dist/background/background.module.d.ts +10 -0
- package/dist/background/background.module.js +32 -0
- package/dist/background/finish.d.ts +43 -0
- package/dist/background/finish.js +94 -0
- package/dist/background/launch-lock.d.ts +14 -0
- package/dist/background/launch-lock.js +39 -0
- package/dist/background/launch.d.ts +36 -0
- package/dist/background/launch.js +70 -0
- package/dist/background/poll.job.d.ts +53 -0
- package/dist/background/poll.job.js +182 -0
- package/dist/background/render.d.ts +23 -0
- package/dist/background/render.js +48 -0
- package/dist/background/session.d.ts +37 -0
- package/dist/background/session.js +66 -0
- package/dist/background/status.d.ts +35 -0
- package/dist/background/status.js +57 -0
- package/dist/background/stop.d.ts +62 -0
- package/dist/background/stop.js +225 -0
- package/dist/background/store.d.ts +150 -0
- package/dist/background/store.js +351 -0
- package/dist/background/store.module.d.ts +6 -0
- package/dist/background/store.module.js +22 -0
- package/dist/background/tasks.service.d.ts +67 -0
- package/dist/background/tasks.service.js +186 -0
- package/dist/background/wake-rules.d.ts +57 -0
- package/dist/background/wake-rules.js +83 -0
- package/dist/background/wake.d.ts +28 -0
- package/dist/background/wake.js +57 -0
- package/dist/catalog/catalog-content.js +4 -1
- package/dist/catalog/claude/agents.js +15 -10
- package/dist/catalog/marketplaces.service.d.ts +1 -1
- package/dist/catalog/plugin-import.js +1 -1
- package/dist/catalog/plugin-upgrade.js +1 -1
- package/dist/channels/cards.d.ts +8 -2
- package/dist/channels/cards.js +17 -9
- package/dist/channels/connections.service.d.ts +16 -1
- package/dist/channels/connections.service.js +38 -4
- package/dist/channels/threads.d.ts +5 -0
- package/dist/channels/threads.js +8 -0
- package/dist/config/schema.d.ts +18 -0
- package/dist/config/schema.js +104 -2
- package/dist/conversations/access.js +2 -0
- package/dist/conversations/artifacts.d.ts +1 -1
- package/dist/conversations/artifacts.js +1 -1
- package/dist/conversations/attachments.service.d.ts +30 -0
- package/dist/conversations/attachments.service.js +130 -5
- package/dist/conversations/conversations.controller.d.ts +36 -5
- package/dist/conversations/conversations.controller.js +62 -12
- package/dist/conversations/conversations.module.js +3 -0
- package/dist/conversations/conversations.service.d.ts +44 -2
- package/dist/conversations/conversations.service.js +140 -12
- package/dist/conversations/event-log.d.ts +12 -1
- package/dist/conversations/event-log.js +29 -1
- package/dist/conversations/events.d.ts +81 -3
- package/dist/conversations/events.js +83 -16
- package/dist/conversations/queue.d.ts +3 -1
- package/dist/conversations/queue.js +13 -2
- package/dist/conversations/queue.service.js +3 -1
- package/dist/conversations/titles.d.ts +7 -0
- package/dist/conversations/titles.js +15 -0
- package/dist/db/kernel-schema.d.ts +60 -4
- package/dist/db/pool.d.ts +5 -0
- package/dist/db/pool.js +3 -0
- package/dist/errors.d.ts +8 -0
- package/dist/errors.js +8 -0
- package/dist/evals/recorded-tools.d.ts +27 -1
- package/dist/evals/recorded-tools.js +85 -10
- package/dist/evals/run-replay.d.ts +10 -2
- package/dist/evals/run-replay.js +49 -12
- package/dist/evals/step-replay.js +12 -6
- package/dist/evals/trajectory.d.ts +2 -2
- package/dist/evals/trajectory.js +1 -1
- package/dist/extensions/admin-ops.js +2 -0
- package/dist/extensions/admin-view.d.ts +3 -1
- package/dist/extensions/admin-view.js +10 -2
- package/dist/extensions/blobs.js +1 -0
- package/dist/extensions/config-issues.d.ts +13 -1
- package/dist/extensions/config-issues.js +22 -5
- package/dist/extensions/config-json-schema.d.ts +43 -7
- package/dist/extensions/config-json-schema.js +124 -58
- package/dist/extensions/deployment-config-store.d.ts +6 -0
- package/dist/extensions/deployment-config.d.ts +50 -3
- package/dist/extensions/deployment-config.js +125 -28
- package/dist/extensions/display-name.d.ts +22 -0
- package/dist/extensions/display-name.js +77 -0
- package/dist/extensions/extensions.module.js +6 -0
- package/dist/extensions/host.d.ts +26 -1
- package/dist/extensions/host.js +82 -9
- package/dist/extensions/org-config.controller.d.ts +11 -3
- package/dist/extensions/org-config.controller.js +14 -6
- package/dist/extensions/org-config.d.ts +6 -0
- package/dist/extensions/org-config.js +12 -13
- package/dist/extensions/package.d.ts +14 -1
- package/dist/extensions/package.js +51 -2
- package/dist/extensions/permission-view.d.ts +3 -0
- package/dist/extensions/point-defaults.controller.d.ts +34 -0
- package/dist/extensions/point-defaults.controller.js +71 -0
- package/dist/extensions/point-defaults.d.ts +64 -0
- package/dist/extensions/point-defaults.js +206 -0
- package/dist/extensions/registry.d.ts +1 -1
- package/dist/extensions/registry.js +2 -0
- package/dist/http/api.module.js +8 -0
- package/dist/http/nest-logger.d.ts +6 -5
- package/dist/http/nest-logger.js +24 -10
- package/dist/identity/auth.controller.js +9 -5
- package/dist/identity/cookies.d.ts +20 -5
- package/dist/identity/cookies.js +44 -6
- package/dist/identity/me.controller.d.ts +5 -1
- package/dist/identity/me.controller.js +12 -3
- package/dist/identity/sessions.d.ts +12 -0
- package/dist/identity/sessions.js +45 -0
- package/dist/identity/settings.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/infra/config/kernel-config.service.js +6 -0
- package/dist/infra/database/database.module.js +1 -0
- package/dist/infra/events/kernel-events.d.ts +8 -0
- package/dist/infra/lifecycle/process-guards.js +4 -0
- package/dist/infra/logging/logging.module.d.ts +4 -2
- package/dist/infra/logging/logging.module.js +24 -6
- package/dist/infra/queue/redis-connection.d.ts +6 -0
- package/dist/infra/queue/redis-connection.js +7 -0
- package/dist/infra/redis/redis.module.js +6 -2
- package/dist/infra/tokens.d.ts +6 -1
- package/dist/infra/tokens.js +6 -1
- package/dist/lifecycle/kernel-lifecycle.js +5 -0
- package/dist/log.d.ts +13 -1
- package/dist/log.js +93 -17
- package/dist/models/purposes.d.ts +23 -0
- package/dist/models/purposes.js +31 -10
- package/dist/orgs/offboarding.d.ts +3 -1
- package/dist/orgs/offboarding.js +26 -16
- package/dist/orgs/settings.service.d.ts +7 -2
- package/dist/orgs/settings.service.js +15 -4
- package/dist/roles/worker.module.js +6 -0
- package/dist/runtime/context/build.d.ts +15 -13
- package/dist/runtime/context/build.js +45 -27
- package/dist/runtime/context/claude-tools.d.ts +3 -1
- package/dist/runtime/context/claude-tools.js +12 -12
- package/dist/runtime/context/compaction.js +4 -0
- package/dist/runtime/context/legacy-text.d.ts +22 -0
- package/dist/runtime/context/legacy-text.js +37 -0
- package/dist/runtime/context/skills.d.ts +29 -11
- package/dist/runtime/context/skills.js +37 -30
- package/dist/runtime/langgraph-loop.js +3 -2
- package/dist/runtime/loop.d.ts +5 -4
- package/dist/runtime/maintenance.d.ts +4 -2
- package/dist/runtime/maintenance.js +17 -5
- package/dist/runtime/model-call.d.ts +17 -1
- package/dist/runtime/model-call.js +4 -3
- package/dist/runtime/providers.d.ts +6 -0
- package/dist/runtime/providers.js +8 -0
- package/dist/runtime/run-finish.d.ts +1 -1
- package/dist/runtime/run-queue.d.ts +28 -5
- package/dist/runtime/run-queue.js +131 -35
- package/dist/runtime/run-worker.d.ts +2 -2
- package/dist/runtime/run-worker.js +20 -7
- package/dist/runtime/runtime.module.js +8 -3
- package/dist/runtime/step-log.d.ts +6 -0
- package/dist/runtime/step-log.js +16 -2
- package/dist/runtime/subagents/definition.d.ts +1 -6
- package/dist/runtime/subagents/definition.js +14 -13
- package/dist/runtime/subagents/dispatch.d.ts +2 -2
- package/dist/runtime/subagents/dispatch.js +2 -2
- package/dist/runtime/subagents/org-agents.js +2 -1
- package/dist/runtime/subagents/roster.d.ts +1 -1
- package/dist/runtime/subagents/roster.js +1 -1
- package/dist/runtime/subagents/skill-fork.d.ts +2 -2
- package/dist/runtime/subagents/skill-fork.js +2 -2
- package/dist/runtime/subagents/subagents.service.d.ts +3 -3
- package/dist/runtime/subagents/subagents.service.js +3 -3
- package/dist/runtime/usage-report.d.ts +2 -1
- package/dist/runtime/usage-report.js +2 -1
- package/dist/runtime/waits.d.ts +13 -7
- package/dist/runtime/waits.js +42 -16
- package/dist/sandbox/sandbox.module.js +6 -1
- package/dist/sandbox/sandbox.service.d.ts +61 -3
- package/dist/sandbox/sandbox.service.js +141 -7
- package/dist/skills/catalog.d.ts +4 -4
- package/dist/skills/catalog.js +4 -4
- package/dist/skills/changes.service.d.ts +2 -2
- package/dist/skills/changes.service.js +5 -4
- package/dist/skills/invocation-keys.d.ts +1 -1
- package/dist/skills/invocation-keys.js +1 -1
- package/dist/skills/invoke.d.ts +1 -1
- package/dist/skills/invoke.js +5 -4
- package/dist/tools/approvals.service.d.ts +6 -0
- package/dist/tools/approvals.service.js +21 -1
- package/dist/tools/builtin-names.d.ts +91 -0
- package/dist/tools/builtin-names.js +143 -0
- package/dist/tools/builtins/agent.d.ts +7 -7
- package/dist/tools/builtins/agent.js +7 -11
- package/dist/tools/builtins/ask-user.d.ts +1 -1
- package/dist/tools/builtins/ask-user.js +2 -2
- package/dist/tools/builtins/builtin-tools.d.ts +17 -3
- package/dist/tools/builtins/builtin-tools.js +36 -4
- package/dist/tools/builtins/read-tool-output.d.ts +1 -1
- package/dist/tools/builtins/read-tool-output.js +2 -2
- package/dist/tools/builtins/sandbox/bash.d.ts +4 -4
- package/dist/tools/builtins/sandbox/bash.js +259 -29
- package/dist/tools/builtins/sandbox/common.d.ts +30 -7
- package/dist/tools/builtins/sandbox/common.js +9 -5
- package/dist/tools/builtins/sandbox/deliver.d.ts +1 -1
- package/dist/tools/builtins/sandbox/deliver.js +12 -12
- package/dist/tools/builtins/sandbox/edit.d.ts +1 -1
- package/dist/tools/builtins/sandbox/edit.js +41 -30
- package/dist/tools/builtins/sandbox/file-state.d.ts +34 -0
- package/dist/tools/builtins/sandbox/file-state.js +34 -0
- package/dist/tools/builtins/sandbox/glob-match.d.ts +1 -1
- package/dist/tools/builtins/sandbox/glob-match.js +1 -1
- package/dist/tools/builtins/sandbox/glob.d.ts +1 -1
- package/dist/tools/builtins/sandbox/glob.js +4 -4
- package/dist/tools/builtins/sandbox/grep.d.ts +7 -4
- package/dist/tools/builtins/sandbox/grep.js +147 -30
- package/dist/tools/builtins/sandbox/index.d.ts +3 -1
- package/dist/tools/builtins/sandbox/index.js +7 -1
- package/dist/tools/builtins/sandbox/read.d.ts +2 -2
- package/dist/tools/builtins/sandbox/read.js +21 -14
- package/dist/tools/builtins/sandbox/task-output.d.ts +63 -0
- package/dist/tools/builtins/sandbox/task-output.js +304 -0
- package/dist/tools/builtins/sandbox/task-stop.d.ts +10 -0
- package/dist/tools/builtins/sandbox/task-stop.js +112 -0
- package/dist/tools/builtins/sandbox/write.d.ts +1 -1
- package/dist/tools/builtins/sandbox/write.js +37 -33
- package/dist/tools/builtins/skill-load.d.ts +3 -3
- package/dist/tools/builtins/skill-load.js +32 -16
- package/dist/tools/builtins/skill-write.d.ts +4 -4
- package/dist/tools/builtins/skill-write.js +10 -10
- package/dist/tools/builtins/tool-search.d.ts +13 -9
- package/dist/tools/builtins/tool-search.js +25 -14
- package/dist/tools/builtins/web/errors.d.ts +8 -0
- package/dist/tools/builtins/web/errors.js +53 -0
- package/dist/tools/builtins/web/extract.d.ts +62 -0
- package/dist/tools/builtins/web/extract.js +267 -0
- package/dist/tools/builtins/web/fetch.d.ts +55 -0
- package/dist/tools/builtins/web/fetch.js +174 -0
- package/dist/tools/builtins/web/index.d.ts +13 -0
- package/dist/tools/builtins/web/index.js +12 -0
- package/dist/tools/builtins/web/page-cache.d.ts +25 -0
- package/dist/tools/builtins/web/page-cache.js +77 -0
- package/dist/tools/builtins/web/search.d.ts +25 -0
- package/dist/tools/builtins/web/search.js +209 -0
- package/dist/tools/builtins/web/url.d.ts +17 -0
- package/dist/tools/builtins/web/url.js +43 -0
- package/dist/tools/connectors.service.js +4 -4
- package/dist/tools/display-names.d.ts +9 -0
- package/dist/tools/display-names.js +24 -0
- package/dist/tools/drift.js +5 -5
- package/dist/tools/elicitation.js +1 -1
- package/dist/tools/info.d.ts +20 -4
- package/dist/tools/info.js +55 -32
- package/dist/tools/legacy-data.d.ts +34 -0
- package/dist/tools/legacy-data.js +76 -0
- package/dist/tools/legacy-keys.d.ts +51 -0
- package/dist/tools/legacy-keys.js +115 -0
- package/dist/tools/naming.d.ts +1 -5
- package/dist/tools/naming.js +1 -7
- package/dist/tools/oauth/flow-paths.d.ts +1 -1
- package/dist/tools/oauth/flow-paths.js +1 -1
- package/dist/tools/outputs.js +1 -1
- package/dist/tools/pipeline.d.ts +13 -7
- package/dist/tools/pipeline.js +112 -39
- package/dist/tools/policy.d.ts +1 -14
- package/dist/tools/policy.js +13 -19
- package/dist/tools/questions.controller.d.ts +1 -1
- package/dist/tools/questions.controller.js +1 -1
- package/dist/tools/questions.service.d.ts +1 -1
- package/dist/tools/registry.d.ts +38 -24
- package/dist/tools/registry.js +54 -47
- package/dist/tools/resources.d.ts +1 -1
- package/dist/tools/resources.js +1 -1
- package/dist/tools/tools.module.js +13 -1
- package/dist/tools/validate.js +1 -1
- package/package.json +3 -3
- package/src/migrations/0063_tool_keys_claude_code.sql +220 -0
- package/src/migrations/0064_account_last_org.sql +7 -0
- package/src/migrations/0065_conversation_archive.sql +8 -0
- package/src/migrations/0066_extension_display_name.sql +4 -0
- package/src/migrations/0067_agent_profiles.sql +20 -0
- package/src/migrations/0068_agent_avatar_uploads.sql +7 -0
- package/src/migrations/0069_attachments_agent_idx.sql +3 -0
- package/src/migrations/0070_background_tasks.sql +37 -0
- package/src/migrations/0071_background_tasks_reserve.sql +6 -0
- package/src/migrations/0072_background_tasks_stopping.sql +5 -0
- package/src/migrations/0073_org_extension_point_defaults.sql +11 -0
- package/dist/tools/builtins/sandbox/names.d.ts +0 -7
- 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">,
|
|
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
|
|
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,
|
|
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
|
|
153
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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(
|
|
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 = [
|
|
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) {
|