@capybara-hq/kernel 0.1.0-rc.24 → 0.1.0-rc.26
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.module.js +2 -0
- package/dist/admin/health.service.d.ts +2 -2
- package/dist/admin/health.service.js +6 -2
- package/dist/admin/outbox-dead.service.d.ts +7 -3
- package/dist/admin/outbox-dead.service.js +17 -6
- package/dist/admin/overview.service.d.ts +6 -6
- package/dist/admin/overview.service.js +7 -7
- package/dist/agents/agents.module.js +3 -0
- package/dist/agents/agents.service.d.ts +6 -1
- package/dist/agents/agents.service.js +11 -2
- package/dist/background/wake-rules.d.ts +5 -0
- package/dist/background/wake-rules.js +16 -5
- package/dist/catalog/catalog-import.module.js +0 -4
- package/dist/catalog/catalog-scope.service.d.ts +63 -26
- package/dist/catalog/catalog-scope.service.js +146 -71
- package/dist/catalog/catalog.module.js +4 -3
- package/dist/catalog/catalog.service.d.ts +11 -4
- package/dist/catalog/catalog.service.js +21 -5
- package/dist/catalog/cli.d.ts +4 -3
- package/dist/catalog/cli.js +8 -13
- package/dist/catalog/job-ids.d.ts +8 -20
- package/dist/catalog/job-ids.js +11 -16
- package/dist/catalog/marketplaces.service.d.ts +1 -1
- package/dist/catalog/org-plugin-uploads.d.ts +11 -4
- package/dist/catalog/org-plugin-uploads.js +21 -6
- package/dist/catalog/owned-items.d.ts +6 -13
- package/dist/catalog/owned-items.js +11 -29
- package/dist/catalog/owned-plugin-cleanup.d.ts +8 -17
- package/dist/catalog/owned-plugin-cleanup.js +12 -66
- package/dist/catalog/platform-components.d.ts +56 -34
- package/dist/catalog/platform-components.js +68 -78
- package/dist/catalog/plugin-import.d.ts +28 -25
- package/dist/catalog/plugin-import.js +90 -76
- package/dist/catalog/plugin-upgrade.d.ts +4 -4
- package/dist/catalog/plugin-upgrade.js +7 -19
- package/dist/catalog/scope-jobs.d.ts +59 -0
- package/dist/catalog/scope-jobs.js +65 -0
- package/dist/catalog/scope-sync-workers.d.ts +22 -0
- package/dist/catalog/scope-sync-workers.js +58 -2
- package/dist/catalog/scope-sync.d.ts +40 -65
- package/dist/catalog/scope-sync.js +114 -122
- package/dist/channels/channels.module.d.ts +9 -8
- package/dist/channels/channels.module.js +11 -10
- package/dist/channels/connections.service.d.ts +3 -3
- package/dist/channels/connections.service.js +6 -6
- package/dist/channels/deliver-job.d.ts +10 -3
- package/dist/channels/deliver-job.js +7 -3
- package/dist/channels/inbound.js +2 -2
- package/dist/channels/jobs.d.ts +11 -4
- package/dist/channels/jobs.js +18 -3
- package/dist/channels/outbound.d.ts +5 -2
- package/dist/channels/outbound.js +9 -2
- package/dist/channels/threads.d.ts +4 -3
- package/dist/channels/threads.js +4 -3
- package/dist/conversations/conversations.controller.d.ts +4 -2
- package/dist/conversations/conversations.controller.js +8 -4
- package/dist/conversations/conversations.service.d.ts +54 -9
- package/dist/conversations/conversations.service.js +94 -25
- package/dist/conversations/events.d.ts +27 -0
- package/dist/conversations/events.js +30 -1
- package/dist/conversations/source.d.ts +10 -0
- package/dist/conversations/source.js +17 -0
- package/dist/conversations/tree.d.ts +10 -0
- package/dist/conversations/tree.js +35 -0
- package/dist/db/kernel-schema.d.ts +19 -15
- package/dist/devices/devices.service.d.ts +25 -14
- package/dist/devices/devices.service.js +36 -30
- package/dist/evals/evals.module.js +5 -1
- package/dist/evals/jobs.d.ts +14 -6
- package/dist/evals/jobs.js +21 -19
- package/dist/evals/run-replay.d.ts +13 -2
- package/dist/evals/run-replay.js +28 -1
- package/dist/evals/runs.service.d.ts +5 -6
- package/dist/evals/runs.service.js +11 -33
- package/dist/evals/snapshot.d.ts +7 -1
- package/dist/evals/snapshot.js +2 -1
- package/dist/extensions/hooks.d.ts +22 -3
- package/dist/extensions/hooks.js +15 -1
- package/dist/extensions/lifecycle-hooks.d.ts +99 -0
- package/dist/extensions/lifecycle-hooks.js +273 -0
- package/dist/extensions/org-config.d.ts +13 -1
- package/dist/extensions/org-config.js +19 -2
- package/dist/extensions/permission-view.d.ts +9 -5
- package/dist/extensions/permission-view.js +9 -4
- package/dist/extensions/point-defaults.d.ts +28 -1
- package/dist/extensions/point-defaults.js +44 -13
- package/dist/inbox/inbox.service.d.ts +6 -20
- package/dist/inbox/inbox.service.js +0 -3
- package/dist/inbox/org-notices.d.ts +1 -1
- package/dist/inbox/platform-inbox.service.d.ts +3 -32
- package/dist/inbox/platform-inbox.service.js +5 -54
- package/dist/infra/outbox/outbox.service.d.ts +6 -0
- package/dist/infra/outbox/outbox.service.js +51 -17
- package/dist/infra/outbox/registry.d.ts +176 -0
- package/dist/infra/outbox/registry.js +183 -0
- package/dist/infra/queue/queue-names.d.ts +1 -1
- package/dist/infra/queue/queue-names.js +1 -0
- package/dist/local-devices/device-policy.service.d.ts +131 -26
- package/dist/local-devices/device-policy.service.js +341 -145
- package/dist/local-devices/machines.d.ts +17 -2
- package/dist/local-devices/machines.js +66 -5
- package/dist/local-devices/policy-op-jobs.d.ts +15 -0
- package/dist/local-devices/policy-op-jobs.js +12 -0
- package/dist/local-devices/policy-summary.d.ts +36 -3
- package/dist/local-devices/policy-summary.js +47 -1
- package/dist/orgs/directory-sync.js +1 -1
- package/dist/orgs/members.service.js +1 -1
- package/dist/orgs/offboarding.d.ts +50 -57
- package/dist/orgs/offboarding.js +117 -162
- package/dist/orgs/orgs.module.js +3 -0
- package/dist/orgs/orgs.service.d.ts +11 -5
- package/dist/orgs/orgs.service.js +19 -7
- package/dist/overrides.d.ts +7 -0
- package/dist/overrides.js +4 -0
- package/dist/platform/ops/conversations.ops.js +1 -1
- package/dist/platform/ops/inbox.ops.js +4 -20
- package/dist/platform/ops/platform.ops.d.ts +1 -0
- package/dist/platform/ops/platform.ops.js +6 -3
- package/dist/roles/worker-role.d.ts +10 -5
- package/dist/roles/worker-role.js +19 -9
- package/dist/roles/worker.module.js +2 -4
- package/dist/runtime/context/build.d.ts +3 -0
- package/dist/runtime/context/build.js +15 -0
- package/dist/runtime/context/plugin-context.d.ts +20 -0
- package/dist/runtime/context/plugin-context.js +30 -0
- package/dist/runtime/langgraph-loop.js +4 -0
- package/dist/runtime/loop.d.ts +8 -0
- package/dist/runtime/maintenance.d.ts +9 -4
- package/dist/runtime/maintenance.js +52 -13
- package/dist/runtime/model-call.d.ts +6 -1
- package/dist/runtime/model-call.js +1 -0
- package/dist/runtime/plugin-context.service.d.ts +75 -0
- package/dist/runtime/plugin-context.service.js +220 -0
- package/dist/runtime/run-context.service.d.ts +7 -3
- package/dist/runtime/run-context.service.js +12 -2
- package/dist/runtime/run-control.d.ts +6 -1
- package/dist/runtime/run-control.js +13 -0
- package/dist/runtime/run-finish.d.ts +2 -2
- package/dist/runtime/run-finish.js +11 -6
- package/dist/runtime/run-jobs.d.ts +21 -4
- package/dist/runtime/run-jobs.js +29 -6
- package/dist/runtime/run-orphan-check.job.d.ts +14 -11
- package/dist/runtime/run-orphan-check.job.js +16 -11
- package/dist/runtime/run-queue.d.ts +29 -24
- package/dist/runtime/run-queue.js +118 -57
- package/dist/runtime/run-status.d.ts +12 -0
- package/dist/runtime/run-status.js +21 -0
- package/dist/runtime/run-worker.d.ts +32 -3
- package/dist/runtime/run-worker.js +143 -21
- package/dist/runtime/runtime.module.js +2 -0
- package/dist/runtime/step-log.d.ts +6 -5
- package/dist/runtime/step-log.js +6 -5
- package/dist/runtime/subagents/dispatch.js +1 -0
- package/dist/runtime/subagents/skill-fork.js +3 -0
- package/dist/runtime/usage-report.d.ts +3 -1
- package/dist/runtime/usage-report.js +4 -2
- package/dist/runtime/waits.d.ts +16 -6
- package/dist/runtime/waits.js +37 -15
- package/dist/sandbox/jobs.d.ts +20 -10
- package/dist/sandbox/jobs.js +23 -17
- package/dist/schedules/fire.d.ts +25 -4
- package/dist/schedules/fire.js +87 -13
- package/dist/schedules/job-ids.d.ts +66 -17
- package/dist/schedules/job-ids.js +81 -13
- package/dist/schedules/run-tracker.js +2 -1
- package/dist/schedules/scan.job.d.ts +16 -35
- package/dist/schedules/scan.job.js +38 -147
- package/dist/schedules/scheduled-tasks.service.d.ts +6 -6
- package/dist/schedules/scheduled-tasks.service.js +17 -14
- package/dist/schedules/schedules.module.js +0 -4
- package/dist/skills/catalog.d.ts +7 -3
- package/dist/skills/catalog.js +12 -2
- package/dist/skills/changes.controller.d.ts +1 -1
- package/dist/skills/changes.controller.js +1 -1
- package/dist/tools/approvals.service.d.ts +111 -23
- package/dist/tools/approvals.service.js +326 -77
- package/dist/tools/builtins/sandbox/task-output.js +21 -6
- package/dist/tools/device-op-resolver.d.ts +40 -0
- package/dist/tools/device-op-resolver.js +121 -0
- package/dist/tools/network-asks.d.ts +1 -1
- package/dist/tools/network-asks.js +4 -3
- package/dist/tools/pipeline.d.ts +11 -2
- package/dist/tools/pipeline.js +5 -0
- package/dist/tools/questions.service.d.ts +15 -4
- package/dist/tools/questions.service.js +21 -19
- package/dist/tools/registry.d.ts +8 -1
- package/dist/tools/registry.js +9 -0
- package/dist/tools/tools.module.js +4 -0
- package/package.json +3 -3
- package/src/migrations/0121_drop_platform_migration_runs.sql +12 -0
- package/src/migrations/0122_drop_scheduled_task_runs_rechecked_at.sql +5 -0
- package/src/migrations/0123_pgvector.sql +27 -0
- package/src/migrations/0124_conversation_source.sql +36 -0
- package/src/migrations/0125_conversation_tree_stopped_at.sql +5 -0
- package/dist/catalog/platform-migration.d.ts +0 -115
- package/dist/catalog/platform-migration.js +0 -583
- package/dist/infra/queue/add-job-once.d.ts +0 -14
- package/dist/infra/queue/add-job-once.js +0 -16
|
@@ -4,15 +4,22 @@ import type { OutboxService } from "../infra/outbox/outbox.service.js";
|
|
|
4
4
|
* with no BullMQ and no Nest class, so the event log can owe it without importing the channels
|
|
5
5
|
* domain. `jobs.ts` (the producer) re-exports these names.
|
|
6
6
|
*/
|
|
7
|
-
/**
|
|
7
|
+
/** The channel queue (outbound deliveries and inbound jobs; no `:`), consumed only by workers. */
|
|
8
8
|
export declare const CHANNEL_QUEUE_NAME = "cap-kernel-channels";
|
|
9
|
-
/**
|
|
9
|
+
/** The delivery job's name. */
|
|
10
10
|
export declare const DELIVER_JOB = "deliver";
|
|
11
|
-
/**
|
|
11
|
+
/** The delivery job id (no `:`): one job per conversation and log position. */
|
|
12
12
|
export declare const deliverJobId: (conversationId: string, seq: number) => string;
|
|
13
13
|
export interface DeliverJobData {
|
|
14
14
|
conversationId: string;
|
|
15
15
|
}
|
|
16
|
+
/** The catch-up job of a connection that can deliver again (re-enabled, credentials rotated). */
|
|
17
|
+
export declare const RESUME_JOB = "channel-resume";
|
|
18
|
+
/** Its job id: one per connection and update (`updated_at` in ms; no `:`). */
|
|
19
|
+
export declare const resumeJobId: (connectionId: string, at: Date) => string;
|
|
20
|
+
export interface ResumeJobData {
|
|
21
|
+
connectionId: string;
|
|
22
|
+
}
|
|
16
23
|
/**
|
|
17
24
|
* Owes the conversation's delivery job for the log position `seq` in the transaction in context:
|
|
18
25
|
* the caller knows the conversation has a channel target. Shares the job id of
|
|
@@ -3,12 +3,16 @@
|
|
|
3
3
|
* with no BullMQ and no Nest class, so the event log can owe it without importing the channels
|
|
4
4
|
* domain. `jobs.ts` (the producer) re-exports these names.
|
|
5
5
|
*/
|
|
6
|
-
/**
|
|
6
|
+
/** The channel queue (outbound deliveries and inbound jobs; no `:`), consumed only by workers. */
|
|
7
7
|
export const CHANNEL_QUEUE_NAME = "cap-kernel-channels";
|
|
8
|
-
/**
|
|
8
|
+
/** The delivery job's name. */
|
|
9
9
|
export const DELIVER_JOB = "deliver";
|
|
10
|
-
/**
|
|
10
|
+
/** The delivery job id (no `:`): one job per conversation and log position. */
|
|
11
11
|
export const deliverJobId = (conversationId, seq) => `deliver-${conversationId}-${seq}`;
|
|
12
|
+
/** The catch-up job of a connection that can deliver again (re-enabled, credentials rotated). */
|
|
13
|
+
export const RESUME_JOB = "channel-resume";
|
|
14
|
+
/** Its job id: one per connection and update (`updated_at` in ms; no `:`). */
|
|
15
|
+
export const resumeJobId = (connectionId, at) => `channel-resume-${connectionId}-${at.getTime()}`;
|
|
12
16
|
/**
|
|
13
17
|
* Owes the conversation's delivery job for the log position `seq` in the transaction in context:
|
|
14
18
|
* the caller knows the conversation has a channel target. Shares the job id of
|
package/dist/channels/inbound.js
CHANGED
|
@@ -11,6 +11,7 @@ import { randomUUID } from "node:crypto";
|
|
|
11
11
|
import { Inject, Injectable } from "@nestjs/common";
|
|
12
12
|
import { ConversationsService } from "../conversations/conversations.service.js";
|
|
13
13
|
import { imSenderContext, recipientLocale, } from "../conversations/message-context.js";
|
|
14
|
+
import { channelSource } from "../conversations/source.js";
|
|
14
15
|
import { DevicesService } from "../devices/devices.service.js";
|
|
15
16
|
import { DEFAULT_DEVICE_ALIASES, deviceLabel } from "../devices/label.js";
|
|
16
17
|
import { ApiError } from "../errors.js";
|
|
@@ -227,8 +228,7 @@ let ChannelInbound = class ChannelInbound {
|
|
|
227
228
|
try {
|
|
228
229
|
// bound to the person's IM default device, else their default (environments spec
|
|
229
230
|
// §4.2 item 8); an anonymous sender's stays unbound (no sandbox)
|
|
230
|
-
created = (await this.conversations.create(row.orgId, accountId, row.agentId, { im: true }))
|
|
231
|
-
.id;
|
|
231
|
+
created = (await this.conversations.create(row.orgId, accountId, row.agentId, { im: true }, { source: channelSource(e.target.chatType) })).id;
|
|
232
232
|
}
|
|
233
233
|
catch (err) {
|
|
234
234
|
if (err instanceof ApiError && err.code === "agent.not_found") {
|
package/dist/channels/jobs.d.ts
CHANGED
|
@@ -5,8 +5,8 @@ import { KernelDb } from "../infra/database/kernel-db.js";
|
|
|
5
5
|
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
6
6
|
import { KernelEvents } from "../infra/events/kernel-events.js";
|
|
7
7
|
import { OutboxService } from "../infra/outbox/outbox.service.js";
|
|
8
|
-
import { type DeliverJobData } from "./deliver-job.js";
|
|
9
|
-
export { CHANNEL_QUEUE_NAME, DELIVER_JOB, type DeliverJobData, deliverJobId, oweDelivery, } from "./deliver-job.js";
|
|
8
|
+
import { type DeliverJobData, type ResumeJobData } from "./deliver-job.js";
|
|
9
|
+
export { CHANNEL_QUEUE_NAME, DELIVER_JOB, type DeliverJobData, deliverJobId, oweDelivery, RESUME_JOB, type ResumeJobData, resumeJobId, } from "./deliver-job.js";
|
|
10
10
|
/**
|
|
11
11
|
* 结束后复核排的任务 id:每次唯一(带随机后缀,不含 `:`)。不能复用 `deliver-<会话>-<seq>`——同 id 的任务
|
|
12
12
|
* 可能正因锁被占而即将直接结束,BullMQ 会把新任务去重进它,于是没人投递。
|
|
@@ -24,7 +24,7 @@ export interface InboundJobData {
|
|
|
24
24
|
connectionId: string;
|
|
25
25
|
event: InboundEvent;
|
|
26
26
|
}
|
|
27
|
-
export type ChannelJobData = DeliverJobData | InboundJobData;
|
|
27
|
+
export type ChannelJobData = DeliverJobData | InboundJobData | ResumeJobData;
|
|
28
28
|
/** 入站任务名。 */
|
|
29
29
|
export declare const INBOUND_JOB = "inbound";
|
|
30
30
|
/**
|
|
@@ -86,7 +86,14 @@ export declare class ChannelJobs implements OnApplicationShutdown {
|
|
|
86
86
|
*/
|
|
87
87
|
enqueueInbound(connectionId: string, event: InboundEvent, jobId: string): Promise<void>;
|
|
88
88
|
/**
|
|
89
|
-
*
|
|
89
|
+
* Owes the catch-up of a connection that can deliver again in the caller's transaction (the
|
|
90
|
+
* update that re-enabled it or rotated its credentials while active): the deliveries that ran
|
|
91
|
+
* while it could not deliver have ended, and nothing else finds the conversations lagging the log.
|
|
92
|
+
* `at` is the connection row's new `updated_at`.
|
|
93
|
+
*/
|
|
94
|
+
oweResume(connectionId: string, orgId: string, at: Date): Promise<boolean>;
|
|
95
|
+
/**
|
|
96
|
+
* The {@link RESUME_JOB} job: 连接重新可投递(轮换了凭据 / 重新启用):给该连接投递进度落后于会话日志的会话各排一个出站任务,
|
|
90
97
|
* 补投停用或凭据失效期间落下的内容(不必等下一条消息)。
|
|
91
98
|
*/
|
|
92
99
|
resumeConnection(connectionId: string): Promise<void>;
|
package/dist/channels/jobs.js
CHANGED
|
@@ -17,8 +17,8 @@ import { KernelEvents } from "../infra/events/kernel-events.js";
|
|
|
17
17
|
import { OutboxService } from "../infra/outbox/outbox.service.js";
|
|
18
18
|
import { OUTBOX_JOBS } from "../infra/outbox/registry.js";
|
|
19
19
|
import { KERNEL_LOGGER } from "../infra/tokens.js";
|
|
20
|
-
import { CHANNEL_QUEUE_NAME, DELIVER_JOB, deliverJobId, oweDelivery, } from "./deliver-job.js";
|
|
21
|
-
export { CHANNEL_QUEUE_NAME, DELIVER_JOB, deliverJobId, oweDelivery, } from "./deliver-job.js";
|
|
20
|
+
import { CHANNEL_QUEUE_NAME, DELIVER_JOB, deliverJobId, oweDelivery, RESUME_JOB, resumeJobId, } from "./deliver-job.js";
|
|
21
|
+
export { CHANNEL_QUEUE_NAME, DELIVER_JOB, deliverJobId, oweDelivery, RESUME_JOB, resumeJobId, } from "./deliver-job.js";
|
|
22
22
|
/**
|
|
23
23
|
* 结束后复核排的任务 id:每次唯一(带随机后缀,不含 `:`)。不能复用 `deliver-<会话>-<seq>`——同 id 的任务
|
|
24
24
|
* 可能正因锁被占而即将直接结束,BullMQ 会把新任务去重进它,于是没人投递。
|
|
@@ -160,7 +160,22 @@ let ChannelJobs = class ChannelJobs {
|
|
|
160
160
|
});
|
|
161
161
|
}
|
|
162
162
|
/**
|
|
163
|
-
*
|
|
163
|
+
* Owes the catch-up of a connection that can deliver again in the caller's transaction (the
|
|
164
|
+
* update that re-enabled it or rotated its credentials while active): the deliveries that ran
|
|
165
|
+
* while it could not deliver have ended, and nothing else finds the conversations lagging the log.
|
|
166
|
+
* `at` is the connection row's new `updated_at`.
|
|
167
|
+
*/
|
|
168
|
+
oweResume(connectionId, orgId, at) {
|
|
169
|
+
return this.outbox.add({
|
|
170
|
+
queue: CHANNEL_QUEUE_NAME,
|
|
171
|
+
name: RESUME_JOB,
|
|
172
|
+
jobId: resumeJobId(connectionId, at),
|
|
173
|
+
data: { connectionId },
|
|
174
|
+
orgId,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* The {@link RESUME_JOB} job: 连接重新可投递(轮换了凭据 / 重新启用):给该连接投递进度落后于会话日志的会话各排一个出站任务,
|
|
164
179
|
* 补投停用或凭据失效期间落下的内容(不必等下一条消息)。
|
|
165
180
|
*/
|
|
166
181
|
async resumeConnection(connectionId) {
|
|
@@ -97,7 +97,7 @@ export declare class ChannelOutbound {
|
|
|
97
97
|
/** The conversation lock provider; delivery takes it under its own key prefix. */
|
|
98
98
|
lock: Pick<ConversationLock, "scoped">, connections: Pick<ChannelConnections, "outbound">, approvals: Pick<ApprovalsService, "describe">, questions: Pick<QuestionsService, "forCall">, registry: Pick<ToolRegistry, "builtinSpecs" | "extensionToolSpecs">,
|
|
99
99
|
/** 入站任务的处理(worker 上执行 `ChannelInbound.handle`);失败抛错由 BullMQ 重试。 */
|
|
100
|
-
inbound: Pick<ChannelInbound, "handle">, jobs: Pick<ChannelJobs, "enqueue">, config: Pick<KernelConfigService, "get">, log: Pick<Logger, "error" | "warn" | "info">, txHost: TransactionHost,
|
|
100
|
+
inbound: Pick<ChannelInbound, "handle">, jobs: Pick<ChannelJobs, "enqueue" | "resumeConnection">, config: Pick<KernelConfigService, "get">, log: Pick<Logger, "error" | "warn" | "info">, txHost: TransactionHost,
|
|
101
101
|
/** Pauses the task of a scheduled run whose chat is gone (scheduled tasks spec §8). */
|
|
102
102
|
schedules: Pick<ScheduledTasksService, "pauseFor">);
|
|
103
103
|
/**
|
|
@@ -112,7 +112,10 @@ export declare class ChannelOutbound {
|
|
|
112
112
|
/** Stops: the jobs in flight are handed back at their next checkpoint, the worker closes. */
|
|
113
113
|
close(): Promise<void>;
|
|
114
114
|
private requeue;
|
|
115
|
-
/**
|
|
115
|
+
/**
|
|
116
|
+
* One job of the channel queue (the `@Processor` calls it): a delivery, an inbound event or a
|
|
117
|
+
* connection's catch-up.
|
|
118
|
+
*/
|
|
116
119
|
process(job: Job<ChannelJobData>, token: string | undefined): Promise<void>;
|
|
117
120
|
/** 本副本没有加载连接的插件:推迟重试(不消耗重试次数),排队超过上限才放弃。 */
|
|
118
121
|
private pluginPending;
|
|
@@ -28,7 +28,7 @@ import { ToolRegistry } from "../tools/registry.js";
|
|
|
28
28
|
import { approvalCard, cardMessage, connectUrl, conversationUrl, questionCard, webBase, } from "./cards.js";
|
|
29
29
|
import { ChannelConnections } from "./connections.service.js";
|
|
30
30
|
import { ChannelInbound } from "./inbound.js";
|
|
31
|
-
import { ChannelJobs, INBOUND_JOB, idempotencyKey, } from "./jobs.js";
|
|
31
|
+
import { ChannelJobs, INBOUND_JOB, idempotencyKey, RESUME_JOB, } from "./jobs.js";
|
|
32
32
|
import { phrase } from "./phrases.js";
|
|
33
33
|
import { insertThread } from "./threads.js";
|
|
34
34
|
// The queue, job names, ids and priorities live with the producer (jobs.ts)
|
|
@@ -233,12 +233,19 @@ let ChannelOutbound = class ChannelOutbound {
|
|
|
233
233
|
await job.moveToDelayed(Date.now() + delayMs, token);
|
|
234
234
|
throw new DelayedError();
|
|
235
235
|
}
|
|
236
|
-
/**
|
|
236
|
+
/**
|
|
237
|
+
* One job of the channel queue (the `@Processor` calls it): a delivery, an inbound event or a
|
|
238
|
+
* connection's catch-up.
|
|
239
|
+
*/
|
|
237
240
|
async process(job, token) {
|
|
238
241
|
if (this.stopping.signal.aborted)
|
|
239
242
|
return this.requeue(job, token, 0);
|
|
240
243
|
if (job.name === INBOUND_JOB)
|
|
241
244
|
return this.processInbound(job, token);
|
|
245
|
+
// a connection's catch-up: a failed add fails the job, which BullMQ retries
|
|
246
|
+
if (job.name === RESUME_JOB) {
|
|
247
|
+
return this.jobs.resumeConnection(job.data.connectionId);
|
|
248
|
+
}
|
|
242
249
|
return this.deliver(job, token);
|
|
243
250
|
}
|
|
244
251
|
/** 本副本没有加载连接的插件:推迟重试(不消耗重试次数),排队超过上限才放弃。 */
|
|
@@ -21,9 +21,10 @@ export declare class AnonymousGuests {
|
|
|
21
21
|
readonly txHost: TransactionHost;
|
|
22
22
|
constructor(db: KernelDb, guestLock: AnonymousGuestLock, txHost: TransactionHost);
|
|
23
23
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
24
|
+
* The org's one anonymous guest account id, created on first use (`accounts.status = active`, no
|
|
25
|
+
* way to sign in, org role guest). Every call makes sure the membership exists (a removed guest
|
|
26
|
+
* is added back on its next use). Concurrent first creations across replicas are serialized by
|
|
27
|
+
* {@link AnonymousGuestLock}: one account.
|
|
27
28
|
*/
|
|
28
29
|
ensure(orgId: string): Promise<string>;
|
|
29
30
|
}
|
package/dist/channels/threads.js
CHANGED
|
@@ -55,9 +55,10 @@ let AnonymousGuests = class AnonymousGuests {
|
|
|
55
55
|
this.txHost = txHost;
|
|
56
56
|
}
|
|
57
57
|
/**
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
58
|
+
* The org's one anonymous guest account id, created on first use (`accounts.status = active`, no
|
|
59
|
+
* way to sign in, org role guest). Every call makes sure the membership exists (a removed guest
|
|
60
|
+
* is added back on its next use). Concurrent first creations across replicas are serialized by
|
|
61
|
+
* {@link AnonymousGuestLock}: one account.
|
|
61
62
|
*/
|
|
62
63
|
async ensure(orgId) {
|
|
63
64
|
await this.guestLock.lock(orgId);
|
|
@@ -312,8 +312,10 @@ export declare class ConversationsController {
|
|
|
312
312
|
}>;
|
|
313
313
|
/**
|
|
314
314
|
* Stops the current run (the conversation's owner): 202 `{ runId }`; nothing active → 409
|
|
315
|
-
* `run.not_active`. On a root conversation
|
|
316
|
-
*
|
|
315
|
+
* `run.not_active`. On a root conversation every active sub-agent of its tree stops with its
|
|
316
|
+
* run, and nothing they deliver starts the root again until its next run (agent map spec §6,
|
|
317
|
+
* changed in acceptance 2026-11-13); `runId` is the stopped run (the latest when only sub-agents
|
|
318
|
+
* ran). On a sub-agent's conversation (the root's owner, at any depth) the
|
|
317
319
|
* sub-agent stops with every sub-agent below it, and `runId` is the sub-agent's latest run,
|
|
318
320
|
* which may already be finished when only its descendants were working (agent map spec §6).
|
|
319
321
|
*/
|
|
@@ -190,7 +190,9 @@ let ConversationsController = class ConversationsController {
|
|
|
190
190
|
const pick = body.deviceId === undefined ? {} : { deviceId: body.deviceId };
|
|
191
191
|
if (body.message === undefined) {
|
|
192
192
|
return {
|
|
193
|
-
conversation: await this.conversations.create(org.orgId, p.account.id, body.agentId, pick
|
|
193
|
+
conversation: await this.conversations.create(org.orgId, p.account.id, body.agentId, pick, {
|
|
194
|
+
source: "web",
|
|
195
|
+
}),
|
|
194
196
|
};
|
|
195
197
|
}
|
|
196
198
|
const m = body.message;
|
|
@@ -204,7 +206,7 @@ let ConversationsController = class ConversationsController {
|
|
|
204
206
|
...(m.skill !== undefined && { skill: m.skill }),
|
|
205
207
|
...(m.timeZone !== undefined && { timeZone: m.timeZone }),
|
|
206
208
|
...(m.locale !== undefined && { locale: m.locale }),
|
|
207
|
-
}, pick);
|
|
209
|
+
}, pick, { source: "web" });
|
|
208
210
|
reply.status(out.created ? 201 : 200);
|
|
209
211
|
return { conversation: out.conversation, event: out.event, events: out.events };
|
|
210
212
|
}
|
|
@@ -454,8 +456,10 @@ let ConversationsController = class ConversationsController {
|
|
|
454
456
|
}
|
|
455
457
|
/**
|
|
456
458
|
* Stops the current run (the conversation's owner): 202 `{ runId }`; nothing active → 409
|
|
457
|
-
* `run.not_active`. On a root conversation
|
|
458
|
-
*
|
|
459
|
+
* `run.not_active`. On a root conversation every active sub-agent of its tree stops with its
|
|
460
|
+
* run, and nothing they deliver starts the root again until its next run (agent map spec §6,
|
|
461
|
+
* changed in acceptance 2026-11-13); `runId` is the stopped run (the latest when only sub-agents
|
|
462
|
+
* ran). On a sub-agent's conversation (the root's owner, at any depth) the
|
|
459
463
|
* sub-agent stops with every sub-agent below it, and `runId` is the sub-agent's latest run,
|
|
460
464
|
* which may already be finished when only its descendants were working (agent map spec §6).
|
|
461
465
|
*/
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ConversationSource } from "@capybara-hq/plugin-sdk";
|
|
1
2
|
import { AgentLookup } from "../agents/agent-lookup.js";
|
|
2
3
|
import type { ModelRef } from "../agents/definition.js";
|
|
3
4
|
import type { AgentAvatar } from "../agents/profile.js";
|
|
@@ -146,6 +147,8 @@ export interface RunControl {
|
|
|
146
147
|
* 否则返回 false。
|
|
147
148
|
*/
|
|
148
149
|
abortIfOrphaned(conversationId: string, runId: string, byAccountId: string): Promise<boolean>;
|
|
150
|
+
/** Ends a run still `queued` in PG before a worker started it (`RunQueue.abortQueued`): true. */
|
|
151
|
+
abortQueued(conversationId: string, runId: string, byAccountId: string): Promise<boolean>;
|
|
149
152
|
/**
|
|
150
153
|
* 运行在等人处理(waiting)时直接中止:取消等待、写 `run_aborted`、删除 checkpoint,返回 true;
|
|
151
154
|
* 运行已不是 waiting(例如刚被续跑)→ false。
|
|
@@ -228,6 +231,14 @@ export interface InternalMessageOptions {
|
|
|
228
231
|
schedule?: ScheduleMarker;
|
|
229
232
|
timeZoneOverride?: string;
|
|
230
233
|
}
|
|
234
|
+
/** Options of a conversation created by a kernel caller (never an HTTP body). */
|
|
235
|
+
export interface NewConversationOptions {
|
|
236
|
+
/**
|
|
237
|
+
* Where it comes from (memory plugin spec §6.2), fixed at creation. Required: a channel or group
|
|
238
|
+
* conversation that fell back to `web` would be offered what only the web and DMs may see.
|
|
239
|
+
*/
|
|
240
|
+
source: ConversationSource;
|
|
241
|
+
}
|
|
231
242
|
/**
|
|
232
243
|
* 会话:只有所有者可见;每次访问都实时复核 agent 对所有者仍可用(spec 3.6 R5),
|
|
233
244
|
* 不可用时会话也按不存在处理(`conversation.not_found`)。
|
|
@@ -280,9 +291,10 @@ export declare class ConversationsService {
|
|
|
280
291
|
* Creates an empty conversation bound to its device (`pick`, see
|
|
281
292
|
* {@link ConversationDevices.forNew}); an agent the account cannot use →
|
|
282
293
|
* `agent.not_found`, a device it may not use → `device.not_found` /
|
|
283
|
-
* `device.unavailable`, and nothing is created.
|
|
294
|
+
* `device.unavailable`, and nothing is created. `opts.source` records where the conversation
|
|
295
|
+
* comes from.
|
|
284
296
|
*/
|
|
285
|
-
create(orgId: string, accountId: string, agentId: string, pick
|
|
297
|
+
create(orgId: string, accountId: string, agentId: string, pick: NewConversationDevice | undefined, opts: NewConversationOptions): Promise<ConversationView>;
|
|
286
298
|
/**
|
|
287
299
|
* Creates a conversation together with its first message (UI refresh B, create on first send):
|
|
288
300
|
* the conversation row and the `user_message` are written in one transaction, keyed by the
|
|
@@ -291,12 +303,12 @@ export declare class ConversationsService {
|
|
|
291
303
|
* `agent.not_found` and nothing is written. Queues a run afterwards, like `postUserMessage`.
|
|
292
304
|
* `events` are every event the create wrote, in seq order from seq 1 (a `model_switched` before
|
|
293
305
|
* the message when the send switched), so a client can seed the new conversation's log whole.
|
|
294
|
-
* `internal
|
|
295
|
-
* `conversations.scheduled_task_id`, and its zone override.
|
|
306
|
+
* `internal`: the conversation's source and, for kernel callers only, a scheduled task's marker,
|
|
307
|
+
* which also sets `conversations.scheduled_task_id`, and its zone override.
|
|
296
308
|
*/
|
|
297
309
|
createWithMessage(orgId: string, accountId: string, agentId: string, input: UserMessageInput & {
|
|
298
310
|
clientMessageId: string;
|
|
299
|
-
}, pick
|
|
311
|
+
}, pick: NewConversationDevice | undefined, internal: InternalMessageOptions & NewConversationOptions): Promise<{
|
|
300
312
|
conversation: ConversationView;
|
|
301
313
|
event: ConversationEvent;
|
|
302
314
|
events: ConversationEvent[];
|
|
@@ -525,10 +537,43 @@ export declare class ConversationsService {
|
|
|
525
537
|
* 等人处理的运行没有 worker:直接取消等待并写终态。其余经 Redis 广播,运行所在的任意副本都会收到;
|
|
526
538
|
* 运行已是孤儿(没有任何 worker 在执行)时直接写终态。
|
|
527
539
|
*
|
|
528
|
-
* A root conversation's stop stops
|
|
529
|
-
*
|
|
530
|
-
*
|
|
531
|
-
*
|
|
540
|
+
* A root conversation's stop stops everything below it too (agent map spec §6, changed in
|
|
541
|
+
* acceptance 2026-11-13: after a person stops, the conversation stays stopped). In order:
|
|
542
|
+
* 1. `tree_stopped_at` is set on the root under its row lock: by the stopped run's terminal
|
|
543
|
+
* transaction (a waiting, queued or orphaned run), before the stop is asked for (a run a
|
|
544
|
+
* worker holds, which is active until then), or on its own (nothing of the root ran). From
|
|
545
|
+
* then on no sub-agent delivery wakes the root (`wakeDecisionIn`) and no
|
|
546
|
+
* sub-agent of the tree starts a run by a wake or a parent's message (`RunQueue.childWakeIn`),
|
|
547
|
+
* until the root next starts a run (a person's message, the queue, a background Bash task's
|
|
548
|
+
* finish), which clears it. A child that finishes just before or after the stop, `completed`
|
|
549
|
+
* or `aborted`, thus never wakes the root; its delivery stays in the log and reaches the
|
|
550
|
+
* model with the next run;
|
|
551
|
+
* 2. (with 1) the root's own active run stops as before ({@link abortRootRun});
|
|
552
|
+
* 3. every active sub-agent run of the tree stops (`RunControl.stopTree`, as the agent map's stop
|
|
553
|
+
* and the deletion do), with the background tasks of every sub-agent. A sub-agent the root's
|
|
554
|
+
* run dispatched after that read is stopped when the root run ends
|
|
555
|
+
* (`Waits.stopDescendantsOfStopped`).
|
|
556
|
+
* The root's own background Bash tasks keep running: they are the conversation's tasks, stopped
|
|
557
|
+
* with `TaskStop` or the task list. `run.not_active` when neither the root nor its tree had an
|
|
558
|
+
* active run (the stop still holds the tree's wakes); else the root's run id (its latest when only
|
|
559
|
+
* sub-agents ran).
|
|
560
|
+
*
|
|
561
|
+
* A sub-agent's conversation (its owner, from the agent map) stops that sub-agent with every
|
|
562
|
+
* descendant (`RunControl.stopTree`); `run.not_active` when nothing in that subtree runs, else
|
|
563
|
+
* the sub-agent's latest run id. Its `aborted` finish never wakes its parent.
|
|
532
564
|
*/
|
|
533
565
|
abort(orgId: string, conversationId: string, accountId: string): Promise<string>;
|
|
566
|
+
/**
|
|
567
|
+
* Marks a person's stop of the root's tree (`tree_stopped_at`, see {@link abort}); committed
|
|
568
|
+
* before any stop, so every wake decided after it (under the root's row lock) sees it.
|
|
569
|
+
*/
|
|
570
|
+
private holdTree;
|
|
571
|
+
/**
|
|
572
|
+
* Stops a root conversation's own active run (queued / running / waiting) and returns its id;
|
|
573
|
+
* null when none is active. Its sub-agents are the caller's. The tree's hold
|
|
574
|
+
* (`tree_stopped_at`) is written by the stopped run's terminal transaction (`RunFinish.finishIn`
|
|
575
|
+
* for a run a person stopped), in the lock order every stop and decision takes (conversation,
|
|
576
|
+
* decided row, run); a run a worker holds is marked before its stop is asked for as well.
|
|
577
|
+
*/
|
|
578
|
+
private abortRootRun;
|
|
534
579
|
}
|
|
@@ -177,9 +177,10 @@ let ConversationsService = class ConversationsService {
|
|
|
177
177
|
* Creates an empty conversation bound to its device (`pick`, see
|
|
178
178
|
* {@link ConversationDevices.forNew}); an agent the account cannot use →
|
|
179
179
|
* `agent.not_found`, a device it may not use → `device.not_found` /
|
|
180
|
-
* `device.unavailable`, and nothing is created.
|
|
180
|
+
* `device.unavailable`, and nothing is created. `opts.source` records where the conversation
|
|
181
|
+
* comes from.
|
|
181
182
|
*/
|
|
182
|
-
async create(orgId, accountId, agentId, pick = {}) {
|
|
183
|
+
async create(orgId, accountId, agentId, pick = {}, opts) {
|
|
183
184
|
await this.agents.get(orgId, agentId, accountId);
|
|
184
185
|
const deviceId = await this.devices.forNew(orgId, accountId, pick);
|
|
185
186
|
const row = await this.db
|
|
@@ -190,6 +191,7 @@ let ConversationsService = class ConversationsService {
|
|
|
190
191
|
agent_id: agentId,
|
|
191
192
|
account_id: accountId,
|
|
192
193
|
device_id: deviceId,
|
|
194
|
+
source: opts.source,
|
|
193
195
|
})
|
|
194
196
|
.returning(COLUMNS)
|
|
195
197
|
.executeTakeFirstOrThrow();
|
|
@@ -203,10 +205,10 @@ let ConversationsService = class ConversationsService {
|
|
|
203
205
|
* `agent.not_found` and nothing is written. Queues a run afterwards, like `postUserMessage`.
|
|
204
206
|
* `events` are every event the create wrote, in seq order from seq 1 (a `model_switched` before
|
|
205
207
|
* the message when the send switched), so a client can seed the new conversation's log whole.
|
|
206
|
-
* `internal
|
|
207
|
-
* `conversations.scheduled_task_id`, and its zone override.
|
|
208
|
+
* `internal`: the conversation's source and, for kernel callers only, a scheduled task's marker,
|
|
209
|
+
* which also sets `conversations.scheduled_task_id`, and its zone override.
|
|
208
210
|
*/
|
|
209
|
-
async createWithMessage(orgId, accountId, agentId, input, pick = {}, internal
|
|
211
|
+
async createWithMessage(orgId, accountId, agentId, input, pick = {}, internal) {
|
|
210
212
|
const agent = await this.agents.get(orgId, agentId, accountId);
|
|
211
213
|
const prior = await this.findCreated(orgId, accountId, agentId, input.clientMessageId);
|
|
212
214
|
if (prior)
|
|
@@ -242,6 +244,7 @@ let ConversationsService = class ConversationsService {
|
|
|
242
244
|
device_id: conv.deviceId,
|
|
243
245
|
create_request_id: input.clientMessageId,
|
|
244
246
|
scheduled_task_id: internal.schedule?.taskId ?? null,
|
|
247
|
+
source: internal.source,
|
|
245
248
|
})
|
|
246
249
|
.onConflict((oc) => oc
|
|
247
250
|
.columns(["org_id", "account_id", "create_request_id"])
|
|
@@ -339,13 +342,11 @@ let ConversationsService = class ConversationsService {
|
|
|
339
342
|
* log, runs and trajectory stay (super admins still see them).
|
|
340
343
|
*/
|
|
341
344
|
async remove(orgId, conversationId, accountId) {
|
|
342
|
-
await this.writable(orgId, conversationId, accountId);
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
if (!(e instanceof ApiError && e.code === "run.not_active"))
|
|
348
|
-
throw e;
|
|
345
|
+
const conv = await this.writable(orgId, conversationId, accountId);
|
|
346
|
+
// the root's own run only: the tree stops below, after the mark (a sub-agent's conversation
|
|
347
|
+
// is read-only, so `writable` refused it)
|
|
348
|
+
if (conv.parentConversationId === null) {
|
|
349
|
+
await this.abortRootRun(conversationId, accountId);
|
|
349
350
|
}
|
|
350
351
|
await this.markDeleted(orgId, conversationId, accountId);
|
|
351
352
|
// after the mark, so no finish or delivery of the stops below can start a run on the deleted
|
|
@@ -978,25 +979,77 @@ let ConversationsService = class ConversationsService {
|
|
|
978
979
|
* 等人处理的运行没有 worker:直接取消等待并写终态。其余经 Redis 广播,运行所在的任意副本都会收到;
|
|
979
980
|
* 运行已是孤儿(没有任何 worker 在执行)时直接写终态。
|
|
980
981
|
*
|
|
981
|
-
* A root conversation's stop stops
|
|
982
|
-
*
|
|
983
|
-
*
|
|
984
|
-
*
|
|
982
|
+
* A root conversation's stop stops everything below it too (agent map spec §6, changed in
|
|
983
|
+
* acceptance 2026-11-13: after a person stops, the conversation stays stopped). In order:
|
|
984
|
+
* 1. `tree_stopped_at` is set on the root under its row lock: by the stopped run's terminal
|
|
985
|
+
* transaction (a waiting, queued or orphaned run), before the stop is asked for (a run a
|
|
986
|
+
* worker holds, which is active until then), or on its own (nothing of the root ran). From
|
|
987
|
+
* then on no sub-agent delivery wakes the root (`wakeDecisionIn`) and no
|
|
988
|
+
* sub-agent of the tree starts a run by a wake or a parent's message (`RunQueue.childWakeIn`),
|
|
989
|
+
* until the root next starts a run (a person's message, the queue, a background Bash task's
|
|
990
|
+
* finish), which clears it. A child that finishes just before or after the stop, `completed`
|
|
991
|
+
* or `aborted`, thus never wakes the root; its delivery stays in the log and reaches the
|
|
992
|
+
* model with the next run;
|
|
993
|
+
* 2. (with 1) the root's own active run stops as before ({@link abortRootRun});
|
|
994
|
+
* 3. every active sub-agent run of the tree stops (`RunControl.stopTree`, as the agent map's stop
|
|
995
|
+
* and the deletion do), with the background tasks of every sub-agent. A sub-agent the root's
|
|
996
|
+
* run dispatched after that read is stopped when the root run ends
|
|
997
|
+
* (`Waits.stopDescendantsOfStopped`).
|
|
998
|
+
* The root's own background Bash tasks keep running: they are the conversation's tasks, stopped
|
|
999
|
+
* with `TaskStop` or the task list. `run.not_active` when neither the root nor its tree had an
|
|
1000
|
+
* active run (the stop still holds the tree's wakes); else the root's run id (its latest when only
|
|
1001
|
+
* sub-agents ran).
|
|
1002
|
+
*
|
|
1003
|
+
* A sub-agent's conversation (its owner, from the agent map) stops that sub-agent with every
|
|
1004
|
+
* descendant (`RunControl.stopTree`); `run.not_active` when nothing in that subtree runs, else
|
|
1005
|
+
* the sub-agent's latest run id. Its `aborted` finish never wakes its parent.
|
|
985
1006
|
*/
|
|
986
1007
|
async abort(orgId, conversationId, accountId) {
|
|
987
1008
|
const conv = await this.get(orgId, conversationId, accountId);
|
|
1009
|
+
const latest = () => this.db
|
|
1010
|
+
.selectFrom("runs")
|
|
1011
|
+
.select("id")
|
|
1012
|
+
.where("conversation_id", "=", conversationId)
|
|
1013
|
+
.orderBy("created_at", "desc")
|
|
1014
|
+
.executeTakeFirst();
|
|
988
1015
|
if (conv.parentConversationId !== null) {
|
|
989
|
-
const
|
|
990
|
-
.selectFrom("runs")
|
|
991
|
-
.select("id")
|
|
992
|
-
.where("conversation_id", "=", conversationId)
|
|
993
|
-
.orderBy("created_at", "desc")
|
|
994
|
-
.executeTakeFirst();
|
|
1016
|
+
const last = await latest();
|
|
995
1017
|
const n = await this.runs.stopTree(conversationId, accountId);
|
|
996
|
-
if (n === 0 || !
|
|
1018
|
+
if (n === 0 || !last)
|
|
997
1019
|
throw new ApiError("run.not_active", "No active run to abort");
|
|
998
|
-
return
|
|
1020
|
+
return last.id;
|
|
999
1021
|
}
|
|
1022
|
+
const stopped = await this.abortRootRun(conversationId, accountId);
|
|
1023
|
+
// nothing of the root's ran: the hold is the stop's own write
|
|
1024
|
+
if (stopped === null)
|
|
1025
|
+
await this.holdTree(conversationId);
|
|
1026
|
+
const n = await this.runs.stopTree(conversationId, accountId, { includeSelf: false });
|
|
1027
|
+
if (stopped !== null)
|
|
1028
|
+
return stopped;
|
|
1029
|
+
const last = await latest();
|
|
1030
|
+
if (n === 0 || !last)
|
|
1031
|
+
throw new ApiError("run.not_active", "No active run to abort");
|
|
1032
|
+
return last.id;
|
|
1033
|
+
}
|
|
1034
|
+
/**
|
|
1035
|
+
* Marks a person's stop of the root's tree (`tree_stopped_at`, see {@link abort}); committed
|
|
1036
|
+
* before any stop, so every wake decided after it (under the root's row lock) sees it.
|
|
1037
|
+
*/
|
|
1038
|
+
async holdTree(conversationId) {
|
|
1039
|
+
await this.db
|
|
1040
|
+
.updateTable("conversations")
|
|
1041
|
+
.set({ tree_stopped_at: sql `now()` })
|
|
1042
|
+
.where("id", "=", conversationId)
|
|
1043
|
+
.execute();
|
|
1044
|
+
}
|
|
1045
|
+
/**
|
|
1046
|
+
* Stops a root conversation's own active run (queued / running / waiting) and returns its id;
|
|
1047
|
+
* null when none is active. Its sub-agents are the caller's. The tree's hold
|
|
1048
|
+
* (`tree_stopped_at`) is written by the stopped run's terminal transaction (`RunFinish.finishIn`
|
|
1049
|
+
* for a run a person stopped), in the lock order every stop and decision takes (conversation,
|
|
1050
|
+
* decided row, run); a run a worker holds is marked before its stop is asked for as well.
|
|
1051
|
+
*/
|
|
1052
|
+
async abortRootRun(conversationId, accountId) {
|
|
1000
1053
|
const run = await this.db
|
|
1001
1054
|
.selectFrom("runs")
|
|
1002
1055
|
.select(["id", "status"])
|
|
@@ -1005,7 +1058,7 @@ let ConversationsService = class ConversationsService {
|
|
|
1005
1058
|
.orderBy("created_at", "desc")
|
|
1006
1059
|
.executeTakeFirst();
|
|
1007
1060
|
if (!run)
|
|
1008
|
-
|
|
1061
|
+
return null;
|
|
1009
1062
|
const runs = this.runs;
|
|
1010
1063
|
// 等待中:直接结束;没成功(刚被续跑、已回到 queued)则按下面的常规路径中止。
|
|
1011
1064
|
// the stop's terminal transaction owed the ensure-run backstop; the direct call starts what
|
|
@@ -1014,11 +1067,24 @@ let ConversationsService = class ConversationsService {
|
|
|
1014
1067
|
await runs.ensureRunAfterCommit(conversationId);
|
|
1015
1068
|
return run.id;
|
|
1016
1069
|
}
|
|
1070
|
+
// a queued run no worker has started yet is ended in PG (ruling R27), never left to the stop
|
|
1071
|
+
// marker's TTL: the marker first, so a worker that takes it meanwhile stops too
|
|
1072
|
+
if (run.status === "queued") {
|
|
1073
|
+
await runs.requestAbort(run.id, accountId);
|
|
1074
|
+
if (await runs.abortQueued(conversationId, run.id, accountId)) {
|
|
1075
|
+
await runs.ensureRunAfterCommit(conversationId);
|
|
1076
|
+
}
|
|
1077
|
+
return run.id;
|
|
1078
|
+
}
|
|
1017
1079
|
// 孤儿运行没有 worker 在听中止广播:直接写成 aborted,之后再看有没有等待中的消息要起新一轮。
|
|
1018
1080
|
if (await runs.abortIfOrphaned(conversationId, run.id, accountId)) {
|
|
1019
1081
|
await runs.ensureRunAfterCommit(conversationId);
|
|
1020
1082
|
return run.id;
|
|
1021
1083
|
}
|
|
1084
|
+
// a run a worker holds ends later: the hold is written now, before the stop is asked for,
|
|
1085
|
+
// while the run is still active (no wake can start the root before it), and again by its
|
|
1086
|
+
// terminal transaction
|
|
1087
|
+
await this.holdTree(conversationId);
|
|
1022
1088
|
await runs.requestAbort(run.id, accountId);
|
|
1023
1089
|
return run.id;
|
|
1024
1090
|
}
|
|
@@ -1041,6 +1107,9 @@ __decorate([
|
|
|
1041
1107
|
__decorate([
|
|
1042
1108
|
Transactional({ propagation: "mandatory" })
|
|
1043
1109
|
], ConversationsService.prototype, "bindIn", null);
|
|
1110
|
+
__decorate([
|
|
1111
|
+
Transactional()
|
|
1112
|
+
], ConversationsService.prototype, "holdTree", null);
|
|
1044
1113
|
ConversationsService = __decorate([
|
|
1045
1114
|
Injectable(),
|
|
1046
1115
|
__param(0, Inject(KernelDb)),
|
|
@@ -200,6 +200,20 @@ export declare const RunContextSchema: z.ZodObject<{
|
|
|
200
200
|
person: z.ZodString;
|
|
201
201
|
}, z.core.$strip>>;
|
|
202
202
|
}, z.core.$strip>;
|
|
203
|
+
/**
|
|
204
|
+
* The most UTF-8 bytes a `plugin_context` event's text may hold (memory spec §6.1): the providers'
|
|
205
|
+
* limit, so the log takes every text a provider may return.
|
|
206
|
+
*/
|
|
207
|
+
export declare const PLUGIN_CONTEXT_MAX_BYTES: number;
|
|
208
|
+
/**
|
|
209
|
+
* `plugin_context` (memory spec §6.1): the text a `context@1` provider added at a run start. Empty
|
|
210
|
+
* text means the provider has nothing (any more); `hash` is the sha256 (hex) of `text`.
|
|
211
|
+
*/
|
|
212
|
+
export declare const PluginContextSchema: z.ZodObject<{
|
|
213
|
+
plugin: z.ZodString;
|
|
214
|
+
text: z.ZodString;
|
|
215
|
+
hash: z.ZodString;
|
|
216
|
+
}, z.core.$strip>;
|
|
203
217
|
/**
|
|
204
218
|
* 会话事件(`type` + `payload`)的判别联合。事件日志只追加,写入前用它校验 payload;
|
|
205
219
|
* 读出时按写入时的形状原样返回。
|
|
@@ -547,6 +561,13 @@ export declare const ConversationEventSchema: z.ZodDiscriminatedUnion<[z.ZodObje
|
|
|
547
561
|
person: z.ZodString;
|
|
548
562
|
}, z.core.$strip>>;
|
|
549
563
|
}, z.core.$strip>;
|
|
564
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
565
|
+
type: z.ZodLiteral<"plugin_context">;
|
|
566
|
+
payload: z.ZodObject<{
|
|
567
|
+
plugin: z.ZodString;
|
|
568
|
+
text: z.ZodString;
|
|
569
|
+
hash: z.ZodString;
|
|
570
|
+
}, z.core.$strip>;
|
|
550
571
|
}, z.core.$strip>, z.ZodObject<{
|
|
551
572
|
type: z.ZodLiteral<"tools_changed">;
|
|
552
573
|
payload: z.ZodObject<{
|
|
@@ -716,6 +737,12 @@ export declare const ConversationEventSchema: z.ZodDiscriminatedUnion<[z.ZodObje
|
|
|
716
737
|
byAccountId: z.ZodNullable<z.ZodString>;
|
|
717
738
|
reason: z.ZodOptional<z.ZodString>;
|
|
718
739
|
}, z.core.$strip>;
|
|
740
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
741
|
+
type: z.ZodLiteral<"approval_updated">;
|
|
742
|
+
payload: z.ZodObject<{
|
|
743
|
+
approvalId: z.ZodString;
|
|
744
|
+
callId: z.ZodString;
|
|
745
|
+
}, z.core.$strip>;
|
|
719
746
|
}, z.core.$strip>, z.ZodObject<{
|
|
720
747
|
type: z.ZodLiteral<"question_asked">;
|
|
721
748
|
payload: z.ZodObject<{
|