@capybara-hq/kernel 0.1.0-rc.25 → 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/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/background/wake-rules.d.ts +5 -0
- package/dist/background/wake-rules.js +16 -5
- package/dist/catalog/marketplaces.service.d.ts +1 -1
- 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/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/conversations/conversations.controller.d.ts +4 -2
- package/dist/conversations/conversations.controller.js +4 -2
- package/dist/conversations/conversations.service.d.ts +37 -4
- package/dist/conversations/conversations.service.js +77 -20
- package/dist/conversations/events.d.ts +6 -0
- package/dist/conversations/events.js +6 -0
- package/dist/db/kernel-schema.d.ts +10 -1
- package/dist/devices/devices.service.d.ts +14 -6
- package/dist/devices/devices.service.js +20 -7
- 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/runs.service.d.ts +5 -6
- package/dist/evals/runs.service.js +11 -33
- 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 +52 -0
- package/dist/infra/outbox/registry.js +57 -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/orgs.service.js +4 -1
- package/dist/overrides.d.ts +4 -0
- package/dist/overrides.js +2 -0
- package/dist/platform/ops/conversations.ops.js +1 -1
- package/dist/runtime/maintenance.d.ts +6 -3
- package/dist/runtime/maintenance.js +21 -7
- package/dist/runtime/plugin-context.service.d.ts +9 -3
- package/dist/runtime/plugin-context.service.js +17 -5
- package/dist/runtime/run-context.service.d.ts +7 -3
- package/dist/runtime/run-context.service.js +12 -2
- 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 +23 -31
- package/dist/runtime/run-queue.js +62 -64
- package/dist/runtime/run-status.d.ts +12 -0
- package/dist/runtime/run-status.js +21 -0
- package/dist/runtime/run-worker.d.ts +10 -2
- package/dist/runtime/run-worker.js +44 -19
- package/dist/runtime/step-log.d.ts +6 -5
- package/dist/runtime/step-log.js +6 -5
- 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/schedules/scan.job.d.ts +6 -6
- package/dist/schedules/scan.job.js +6 -6
- package/dist/skills/catalog.d.ts +7 -3
- package/dist/skills/catalog.js +12 -2
- package/dist/tools/approvals.service.d.ts +96 -19
- package/dist/tools/approvals.service.js +300 -67
- 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/tools.module.js +4 -0
- package/package.json +2 -2
- package/src/migrations/0125_conversation_tree_stopped_at.sql +5 -0
|
@@ -50,8 +50,9 @@ export declare class OutboxDeadService {
|
|
|
50
50
|
* the fast path after the commit; the relay delivers it otherwise. When the same (queue, job id)
|
|
51
51
|
* is owed by a pending row already, the dead row is deleted instead (`already_owed`). A pending
|
|
52
52
|
* row added between the check and the update fails the update on the unique index: the retry
|
|
53
|
-
* then runs once more and takes the `already_owed` branch
|
|
54
|
-
*
|
|
53
|
+
* then runs once more and takes the `already_owed` branch; a second conflict on that run (the
|
|
54
|
+
* job owed, delivered and owed again meanwhile) is `conflict` (409), the dead row left as it was.
|
|
55
|
+
* `outbox.not_found` (404) when no dead row has the id.
|
|
55
56
|
*/
|
|
56
57
|
retry(id: string, actor: WriteActor, detail?: Record<string, unknown>): Promise<{
|
|
57
58
|
outcome: OutboxRetryOutcome;
|
|
@@ -61,6 +62,9 @@ export declare class OutboxDeadService {
|
|
|
61
62
|
discard(id: string, actor: WriteActor, detail?: Record<string, unknown>): Promise<void>;
|
|
62
63
|
/** The dead row `id`, locked for the transaction; `outbox.not_found` when it is not dead. */
|
|
63
64
|
private lockDead;
|
|
64
|
-
/**
|
|
65
|
+
/**
|
|
66
|
+
* A platform-level audit event (no org: the outbox is the deployment's), naming the job. The
|
|
67
|
+
* caller's detail (`via`, `operation`) comes first, so it cannot override the row's fields.
|
|
68
|
+
*/
|
|
65
69
|
private audit;
|
|
66
70
|
}
|
|
@@ -89,8 +89,9 @@ let OutboxDeadService = class OutboxDeadService {
|
|
|
89
89
|
* the fast path after the commit; the relay delivers it otherwise. When the same (queue, job id)
|
|
90
90
|
* is owed by a pending row already, the dead row is deleted instead (`already_owed`). A pending
|
|
91
91
|
* row added between the check and the update fails the update on the unique index: the retry
|
|
92
|
-
* then runs once more and takes the `already_owed` branch
|
|
93
|
-
*
|
|
92
|
+
* then runs once more and takes the `already_owed` branch; a second conflict on that run (the
|
|
93
|
+
* job owed, delivered and owed again meanwhile) is `conflict` (409), the dead row left as it was.
|
|
94
|
+
* `outbox.not_found` (404) when no dead row has the id.
|
|
94
95
|
*/
|
|
95
96
|
async retry(id, actor, detail = {}) {
|
|
96
97
|
try {
|
|
@@ -99,7 +100,14 @@ let OutboxDeadService = class OutboxDeadService {
|
|
|
99
100
|
catch (e) {
|
|
100
101
|
if (!isPendingConflict(e))
|
|
101
102
|
throw e;
|
|
102
|
-
|
|
103
|
+
}
|
|
104
|
+
try {
|
|
105
|
+
return await this.retryOnce(id, actor, detail);
|
|
106
|
+
}
|
|
107
|
+
catch (e) {
|
|
108
|
+
if (!isPendingConflict(e))
|
|
109
|
+
throw e;
|
|
110
|
+
throw new ApiError("conflict", `The job of dead outbox row ${id} was owed again while it was retried; list the dead rows with GET /api/v1/admin/outbox/dead and retry or discard it again`);
|
|
103
111
|
}
|
|
104
112
|
}
|
|
105
113
|
async retryOnce(id, actor, detail) {
|
|
@@ -125,7 +133,7 @@ let OutboxDeadService = class OutboxDeadService {
|
|
|
125
133
|
outcome = "retried";
|
|
126
134
|
afterCommit(() => this.fastPath([id]));
|
|
127
135
|
}
|
|
128
|
-
await this.audit("outbox.retried", id, row, actor, {
|
|
136
|
+
await this.audit("outbox.retried", id, row, actor, { ...detail, outcome });
|
|
129
137
|
return { outcome };
|
|
130
138
|
}
|
|
131
139
|
/** Deletes a dead row: its job is never delivered. `outbox.not_found` (404) when none. */
|
|
@@ -147,7 +155,10 @@ let OutboxDeadService = class OutboxDeadService {
|
|
|
147
155
|
throw notFound(id);
|
|
148
156
|
return row;
|
|
149
157
|
}
|
|
150
|
-
/**
|
|
158
|
+
/**
|
|
159
|
+
* A platform-level audit event (no org: the outbox is the deployment's), naming the job. The
|
|
160
|
+
* caller's detail (`via`, `operation`) comes first, so it cannot override the row's fields.
|
|
161
|
+
*/
|
|
151
162
|
audit(action, id, row, actor, detail) {
|
|
152
163
|
return audit(this.db.db, {
|
|
153
164
|
actorAccountId: actor.accountId,
|
|
@@ -155,11 +166,11 @@ let OutboxDeadService = class OutboxDeadService {
|
|
|
155
166
|
action,
|
|
156
167
|
target: id,
|
|
157
168
|
detail: {
|
|
169
|
+
...detail,
|
|
158
170
|
queue: row.queue,
|
|
159
171
|
jobName: row.job_name,
|
|
160
172
|
jobId: row.job_id,
|
|
161
173
|
...(row.org_id !== null && { orgId: row.org_id }),
|
|
162
|
-
...detail,
|
|
163
174
|
},
|
|
164
175
|
});
|
|
165
176
|
}
|
|
@@ -107,11 +107,11 @@ export declare function extensionReason(e: Pick<AdminExtensionView, "status" | "
|
|
|
107
107
|
/** The problem of a provider in the platform view, or null (`org_configured` is not one). */
|
|
108
108
|
export declare function providerProblem(p: ProviderStatus): AttentionProviderProblem | null;
|
|
109
109
|
/**
|
|
110
|
-
* The admin overview (spec §4.1, §5.2): its numbers and the 待处理 lists, read live from the
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
* `unavailable`. Reads are not audited (decision D5) and not cached further
|
|
114
|
-
* or already Redis-cached).
|
|
110
|
+
* The admin overview (spec §4.1, §5.2): its numbers and the 待处理 lists, read live from the sources
|
|
111
|
+
* the admin pages read (`ExtensionAdmin`, the catalog, the provider registry's cached lists, the
|
|
112
|
+
* dead outbox rows, the run queue's lost-jobs canary). Each source is read on its own: a failing
|
|
113
|
+
* one is reported in `unavailable`. Reads are not audited (decision D5) and not cached further
|
|
114
|
+
* (every source is PG or already Redis-cached).
|
|
115
115
|
*/
|
|
116
116
|
export declare class AdminOverviewService {
|
|
117
117
|
private readonly extensions;
|
|
@@ -136,7 +136,7 @@ export declare class AdminOverviewService {
|
|
|
136
136
|
/**
|
|
137
137
|
* The extensions needing a look, but not one pending approval whose plugin waits in the platform
|
|
138
138
|
* inbox (a permission confirmation or an org upload: the inbox already lists and counts it). A
|
|
139
|
-
* config-declared extension has no catalog item and stays. Without the catalog
|
|
139
|
+
* config-declared extension has no catalog item and stays. Without the catalog none is left out.
|
|
140
140
|
*/
|
|
141
141
|
private extensionRows;
|
|
142
142
|
/** Every live catalog item, page by page (the catalog lists by name). */
|
|
@@ -53,11 +53,11 @@ export function providerProblem(p) {
|
|
|
53
53
|
return p.models.some((m) => m.kind === "chat" && m.disabled !== true) ? null : "no_chat_model";
|
|
54
54
|
}
|
|
55
55
|
/**
|
|
56
|
-
* The admin overview (spec §4.1, §5.2): its numbers and the 待处理 lists, read live from the
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
* `unavailable`. Reads are not audited (decision D5) and not cached further
|
|
60
|
-
* or already Redis-cached).
|
|
56
|
+
* The admin overview (spec §4.1, §5.2): its numbers and the 待处理 lists, read live from the sources
|
|
57
|
+
* the admin pages read (`ExtensionAdmin`, the catalog, the provider registry's cached lists, the
|
|
58
|
+
* dead outbox rows, the run queue's lost-jobs canary). Each source is read on its own: a failing
|
|
59
|
+
* one is reported in `unavailable`. Reads are not audited (decision D5) and not cached further
|
|
60
|
+
* (every source is PG or already Redis-cached).
|
|
61
61
|
*/
|
|
62
62
|
let AdminOverviewService = class AdminOverviewService {
|
|
63
63
|
extensions;
|
|
@@ -164,13 +164,13 @@ let AdminOverviewService = class AdminOverviewService {
|
|
|
164
164
|
/**
|
|
165
165
|
* The extensions needing a look, but not one pending approval whose plugin waits in the platform
|
|
166
166
|
* inbox (a permission confirmation or an org upload: the inbox already lists and counts it). A
|
|
167
|
-
* config-declared extension has no catalog item and stays. Without the catalog
|
|
167
|
+
* config-declared extension has no catalog item and stays. Without the catalog none is left out.
|
|
168
168
|
*/
|
|
169
169
|
async extensionRows(items) {
|
|
170
170
|
const [views, catalog] = await Promise.all([this.extensions.list(), items.catch(() => [])]);
|
|
171
171
|
const inInbox = new Set(catalog.filter((i) => i.pending).map((i) => i.name));
|
|
172
172
|
return views.flatMap((v) => {
|
|
173
|
-
// every status of extensionNeedsAction has a reason,
|
|
173
|
+
// every status of extensionNeedsAction has a reason, as has enabled with an invalid config
|
|
174
174
|
const reason = extensionReason(v);
|
|
175
175
|
if (reason === null)
|
|
176
176
|
return [];
|
|
@@ -54,6 +54,11 @@ export type WakeDecision = {
|
|
|
54
54
|
* - the wake setting is on (the org's override, else `deploymentOn`);
|
|
55
55
|
* - the root's queue is not paused and the root is not deleted (a paused root holds the wakes of
|
|
56
56
|
* the whole tree, agent map spec §5.3);
|
|
57
|
+
* - for a `subagent_delivery` to the root: the root's tree is not stopped (`tree_stopped_at`, set by
|
|
58
|
+
* a person's stop of the root and cleared by the root's next run; agent map spec §6, changed in
|
|
59
|
+
* acceptance 2026-11-13): no sub-agent's finish or report, whenever its child finished, wakes a
|
|
60
|
+
* root a person stopped, the delivery waits for the next run. A sub-agent of a stopped tree is
|
|
61
|
+
* never woken here (`RunQueue.childWakeIn` refuses it first);
|
|
57
62
|
* - the event's trigger account can still send to the root (`canSend`, which the caller binds to
|
|
58
63
|
* the root);
|
|
59
64
|
* - no other wake of this conversation started in the last {@link WAKE_WINDOW_MS} (else `later`).
|
|
@@ -38,6 +38,11 @@ const WAKE_EVENT_TYPES = ["background_task_finished", "subagent_delivery"];
|
|
|
38
38
|
* - the wake setting is on (the org's override, else `deploymentOn`);
|
|
39
39
|
* - the root's queue is not paused and the root is not deleted (a paused root holds the wakes of
|
|
40
40
|
* the whole tree, agent map spec §5.3);
|
|
41
|
+
* - for a `subagent_delivery` to the root: the root's tree is not stopped (`tree_stopped_at`, set by
|
|
42
|
+
* a person's stop of the root and cleared by the root's next run; agent map spec §6, changed in
|
|
43
|
+
* acceptance 2026-11-13): no sub-agent's finish or report, whenever its child finished, wakes a
|
|
44
|
+
* root a person stopped, the delivery waits for the next run. A sub-agent of a stopped tree is
|
|
45
|
+
* never woken here (`RunQueue.childWakeIn` refuses it first);
|
|
41
46
|
* - the event's trigger account can still send to the root (`canSend`, which the caller binds to
|
|
42
47
|
* the root);
|
|
43
48
|
* - no other wake of this conversation started in the last {@link WAKE_WINDOW_MS} (else `later`).
|
|
@@ -68,21 +73,27 @@ export async function wakeDecisionIn(t, conv, consumed, deploymentOn, canSend) {
|
|
|
68
73
|
.orderBy("e.seq", "desc")
|
|
69
74
|
.limit(CANDIDATES)
|
|
70
75
|
.execute();
|
|
71
|
-
|
|
72
|
-
.sort((a, b) => Number(b.seq) - Number(a.seq))
|
|
73
|
-
.slice(0, CANDIDATES);
|
|
74
|
-
if (candidates.length === 0)
|
|
76
|
+
if (finishes.length === 0 && deliveries.length === 0)
|
|
75
77
|
return null;
|
|
76
78
|
const org = (await readOrgSettings(t, conv.orgId)).wakeOnBackgroundTask;
|
|
77
79
|
if (!effectiveWakeOnBackgroundTask(deploymentOn, org))
|
|
78
80
|
return null;
|
|
81
|
+
// read under the root's row lock when `conv` is the root (`RunQueue.ensureRunIn` holds it), so a
|
|
82
|
+
// person's stop of the tree (which writes `tree_stopped_at` on that row) is seen by every wake
|
|
83
|
+
// decided after its commit
|
|
79
84
|
const root = await t
|
|
80
85
|
.selectFrom("conversations")
|
|
81
|
-
.select(["queue_paused", "deleted_at"])
|
|
86
|
+
.select(["queue_paused", "deleted_at", "tree_stopped_at"])
|
|
82
87
|
.where("id", "=", conv.rootId)
|
|
83
88
|
.executeTakeFirst();
|
|
84
89
|
if (!root || root.queue_paused || root.deleted_at !== null)
|
|
85
90
|
return null;
|
|
91
|
+
const treeStopped = root.tree_stopped_at !== null;
|
|
92
|
+
const candidates = [...finishes, ...(treeStopped ? [] : deliveries)]
|
|
93
|
+
.sort((a, b) => Number(b.seq) - Number(a.seq))
|
|
94
|
+
.slice(0, CANDIDATES);
|
|
95
|
+
if (candidates.length === 0)
|
|
96
|
+
return null;
|
|
86
97
|
let boundary;
|
|
87
98
|
for (const c of candidates) {
|
|
88
99
|
if (await canSend(c.account)) {
|
|
@@ -188,7 +188,7 @@ export declare function headsToResolve(entries: readonly MarketplaceEntry[], imp
|
|
|
188
188
|
/** The host versions (an entry's `minKernel` is judged against them). */
|
|
189
189
|
export declare const HOST_VERSIONS: {
|
|
190
190
|
readonly kernel: string;
|
|
191
|
-
readonly sdk: "0.1.0-rc.
|
|
191
|
+
readonly sdk: "0.1.0-rc.20";
|
|
192
192
|
};
|
|
193
193
|
/** Whether this deployment satisfies the entry's `minKernel`. */
|
|
194
194
|
export declare const entryCompatible: (e: Pick<MarketplaceEntry, "minKernel">) => boolean;
|
|
@@ -9,14 +9,15 @@ import { ChannelOutbound } from "./outbound.js";
|
|
|
9
9
|
* Channels on every role: the org's channel connections, inbound (dedupe and enqueue; the inbound
|
|
10
10
|
* jobs), the outbound delivery, the producer of the channel queue `cap-kernel-channels`, the
|
|
11
11
|
* anonymous guests. The roles' parts are the role modules': the queue's consumer
|
|
12
|
-
* ({@link ChannelWorkersModule}) and the gateway check job are the worker role's, the
|
|
13
|
-
* supervisor and the webhook inbound the gateway role's (`GatewayModule`).
|
|
12
|
+
* ({@link ChannelWorkersModule}) and the gateway check job are the worker role's, the
|
|
13
|
+
* long-connection supervisor and the webhook inbound the gateway role's (`GatewayModule`).
|
|
14
14
|
*/
|
|
15
15
|
export declare class ChannelsModule {
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
18
18
|
* The consumer of `cap-kernel-channels`: deliveries (`DELIVER_JOB`, owed through the outbox or
|
|
19
|
-
* hinted) and inbound jobs (`INBOUND_JOB`),
|
|
19
|
+
* hinted), a connection's catch-up (`RESUME_JOB`, owed) and inbound jobs (`INBOUND_JOB`), all
|
|
20
|
+
* handled by {@link ChannelOutbound}.
|
|
20
21
|
*/
|
|
21
22
|
export declare class ChannelsProcessor extends WorkerHost<Worker<ChannelJobData>> {
|
|
22
23
|
private readonly outbound;
|
|
@@ -32,11 +33,11 @@ export declare class ChannelsProcessor extends WorkerHost<Worker<ChannelJobData>
|
|
|
32
33
|
*/
|
|
33
34
|
export declare function channelsProcessorOptions(config: Pick<KernelConfig, "runtime" | "channels">): ProcessorWorkerOptions;
|
|
34
35
|
/**
|
|
35
|
-
* The consumer of the channel queue (concurrency `channels.deliveryConcurrency`, the run queue's
|
|
36
|
-
* lock and stalled detection, the delivery backoff). Imported only by the worker role
|
|
37
|
-
* (`WorkerModule`), so other roles get no processor and no BullMQ worker. It never starts by
|
|
38
|
-
* the worker role (`WorkerRole`) starts it after the extension load;
|
|
39
|
-
* on stop.
|
|
36
|
+
* The consumer of the channel queue (concurrency `channels.deliveryConcurrency`, the run queue's
|
|
37
|
+
* job lock and stalled detection, the delivery backoff). Imported only by the worker role
|
|
38
|
+
* (`WorkerModule`), so other roles get no processor and no BullMQ worker. It never starts by
|
|
39
|
+
* itself: the worker role (`WorkerRole`) starts it after the extension load;
|
|
40
|
+
* `ChannelOutbound.close` closes it on stop.
|
|
40
41
|
*/
|
|
41
42
|
export declare class ChannelWorkersModule {
|
|
42
43
|
static forRoot(config: KernelConfig): DynamicModule;
|
|
@@ -26,7 +26,7 @@ import { SchedulesModule } from "../schedules/schedules.module.js";
|
|
|
26
26
|
import { ToolsModule } from "../tools/tools.module.js";
|
|
27
27
|
import { ChannelConnections } from "./connections.service.js";
|
|
28
28
|
import { ChannelInbound } from "./inbound.js";
|
|
29
|
-
import { CHANNEL_QUEUE_NAME, ChannelJobs, DELIVER_JOB, INBOUND_JOB, } from "./jobs.js";
|
|
29
|
+
import { CHANNEL_QUEUE_NAME, ChannelJobs, DELIVER_JOB, INBOUND_JOB, RESUME_JOB, } from "./jobs.js";
|
|
30
30
|
import { ChannelOutbound, deliverBackoff } from "./outbound.js";
|
|
31
31
|
import { AnonymousGuestLock, AnonymousGuests } from "./threads.js";
|
|
32
32
|
/** The channel queue's registration (its producer here; the admin health reads its counts). */
|
|
@@ -35,8 +35,8 @@ const CHANNEL_QUEUE_MODULE = QueueModule.registerQueue(CHANNEL_QUEUE_NAME);
|
|
|
35
35
|
* Channels on every role: the org's channel connections, inbound (dedupe and enqueue; the inbound
|
|
36
36
|
* jobs), the outbound delivery, the producer of the channel queue `cap-kernel-channels`, the
|
|
37
37
|
* anonymous guests. The roles' parts are the role modules': the queue's consumer
|
|
38
|
-
* ({@link ChannelWorkersModule}) and the gateway check job are the worker role's, the
|
|
39
|
-
* supervisor and the webhook inbound the gateway role's (`GatewayModule`).
|
|
38
|
+
* ({@link ChannelWorkersModule}) and the gateway check job are the worker role's, the
|
|
39
|
+
* long-connection supervisor and the webhook inbound the gateway role's (`GatewayModule`).
|
|
40
40
|
*/
|
|
41
41
|
let ChannelsModule = class ChannelsModule {
|
|
42
42
|
};
|
|
@@ -77,7 +77,8 @@ ChannelsModule = __decorate([
|
|
|
77
77
|
export { ChannelsModule };
|
|
78
78
|
/**
|
|
79
79
|
* The consumer of `cap-kernel-channels`: deliveries (`DELIVER_JOB`, owed through the outbox or
|
|
80
|
-
* hinted) and inbound jobs (`INBOUND_JOB`),
|
|
80
|
+
* hinted), a connection's catch-up (`RESUME_JOB`, owed) and inbound jobs (`INBOUND_JOB`), all
|
|
81
|
+
* handled by {@link ChannelOutbound}.
|
|
81
82
|
*/
|
|
82
83
|
let ChannelsProcessor = class ChannelsProcessor extends WorkerHost {
|
|
83
84
|
outbound;
|
|
@@ -86,7 +87,7 @@ let ChannelsProcessor = class ChannelsProcessor extends WorkerHost {
|
|
|
86
87
|
this.outbound = outbound;
|
|
87
88
|
}
|
|
88
89
|
process(job, token) {
|
|
89
|
-
if (job.name !== DELIVER_JOB && job.name !== INBOUND_JOB) {
|
|
90
|
+
if (job.name !== DELIVER_JOB && job.name !== INBOUND_JOB && job.name !== RESUME_JOB) {
|
|
90
91
|
return Promise.reject(new UnrecoverableError(`unknown channel job "${job.name}"`));
|
|
91
92
|
}
|
|
92
93
|
return runJob(CHANNEL_QUEUE_NAME, job, () => this.outbound.process(job, token));
|
|
@@ -124,11 +125,11 @@ export function channelsProcessorOptions(config) {
|
|
|
124
125
|
};
|
|
125
126
|
}
|
|
126
127
|
/**
|
|
127
|
-
* The consumer of the channel queue (concurrency `channels.deliveryConcurrency`, the run queue's
|
|
128
|
-
* lock and stalled detection, the delivery backoff). Imported only by the worker role
|
|
129
|
-
* (`WorkerModule`), so other roles get no processor and no BullMQ worker. It never starts by
|
|
130
|
-
* the worker role (`WorkerRole`) starts it after the extension load;
|
|
131
|
-
* on stop.
|
|
128
|
+
* The consumer of the channel queue (concurrency `channels.deliveryConcurrency`, the run queue's
|
|
129
|
+
* job lock and stalled detection, the delivery backoff). Imported only by the worker role
|
|
130
|
+
* (`WorkerModule`), so other roles get no processor and no BullMQ worker. It never starts by
|
|
131
|
+
* itself: the worker role (`WorkerRole`) starts it after the extension load;
|
|
132
|
+
* `ChannelOutbound.close` closes it on stop.
|
|
132
133
|
*/
|
|
133
134
|
let ChannelWorkersModule = ChannelWorkersModule_1 = class ChannelWorkersModule {
|
|
134
135
|
static forRoot(config) {
|
|
@@ -123,7 +123,7 @@ export declare class ChannelConnections implements OnApplicationShutdown {
|
|
|
123
123
|
private readonly registry;
|
|
124
124
|
private readonly bus;
|
|
125
125
|
private readonly redis;
|
|
126
|
-
/** A connection that can deliver again
|
|
126
|
+
/** A connection that can deliver again owes its conversations' catch-up (`oweResume`). */
|
|
127
127
|
private readonly jobs;
|
|
128
128
|
/** `channels.webhookBaseUrl`: without it webhook mode is unavailable. */
|
|
129
129
|
private readonly config;
|
|
@@ -138,8 +138,8 @@ export declare class ChannelConnections implements OnApplicationShutdown {
|
|
|
138
138
|
constructor(db: KernelDb,
|
|
139
139
|
/** The org row lock (orgs/org-lock.ts): joins the transaction of the write. */
|
|
140
140
|
orgLock: Pick<OrgLock, "lockAndAuthorize">, keyring: Keyring, registry: Pick<ExtensionRegistry, "list">, bus: ExtensionContext["bus"], redis: Pick<Redis, "del">,
|
|
141
|
-
/** A connection that can deliver again
|
|
142
|
-
jobs: Pick<ChannelJobs, "
|
|
141
|
+
/** A connection that can deliver again owes its conversations' catch-up (`oweResume`). */
|
|
142
|
+
jobs: Pick<ChannelJobs, "oweResume">,
|
|
143
143
|
/** `channels.webhookBaseUrl`: without it webhook mode is unavailable. */
|
|
144
144
|
config: Pick<KernelConfigService, "get">, events: Pick<KernelEvents, "on">,
|
|
145
145
|
/** Secret values handled here (written or decrypted) are redacted from the logs (`addOrgSecrets`). */
|
|
@@ -154,7 +154,7 @@ let ChannelConnections = class ChannelConnections {
|
|
|
154
154
|
constructor(db,
|
|
155
155
|
/** The org row lock (orgs/org-lock.ts): joins the transaction of the write. */
|
|
156
156
|
orgLock, keyring, registry, bus, redis,
|
|
157
|
-
/** A connection that can deliver again
|
|
157
|
+
/** A connection that can deliver again owes its conversations' catch-up (`oweResume`). */
|
|
158
158
|
jobs,
|
|
159
159
|
/** `channels.webhookBaseUrl`: without it webhook mode is unavailable. */
|
|
160
160
|
config, events,
|
|
@@ -546,11 +546,6 @@ let ChannelConnections = class ChannelConnections {
|
|
|
546
546
|
}
|
|
547
547
|
// A mode switch also reconnects: the holder drops the long connection when going to webhook (F9)
|
|
548
548
|
await this.changed(id, rotated !== undefined || patch.status !== undefined || modeChanged);
|
|
549
|
-
if ((rotated || patch.status === "active") && row.status === "active") {
|
|
550
|
-
await this.jobs
|
|
551
|
-
.resumeConnection(id)
|
|
552
|
-
.catch((err) => this.log.warn("cannot resume channel deliveries", { err }));
|
|
553
|
-
}
|
|
554
549
|
return { channel: await this.viewOf(row), verifySkipped };
|
|
555
550
|
}
|
|
556
551
|
/** The rows of {@link update}, in their own transaction (the caller publishes after the commit). */
|
|
@@ -592,6 +587,11 @@ let ChannelConnections = class ChannelConnections {
|
|
|
592
587
|
.executeTakeFirst();
|
|
593
588
|
if (!updated)
|
|
594
589
|
throw notFound();
|
|
590
|
+
// can deliver again (re-enabled, or new credentials while active): the conversations that fell
|
|
591
|
+
// behind meanwhile are caught up by a job owed here (transactional outbox spec §3.1)
|
|
592
|
+
if ((rotated || patch.status === "active") && updated.status === "active") {
|
|
593
|
+
await this.jobs.oweResume(id, orgId, updated.updated_at);
|
|
594
|
+
}
|
|
595
595
|
await audit(this.db.db, {
|
|
596
596
|
actorAccountId,
|
|
597
597
|
...(actorTokenId !== undefined && { actorTokenId }),
|
|
@@ -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/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
|
/** 本副本没有加载连接的插件:推迟重试(不消耗重试次数),排队超过上限才放弃。 */
|
|
@@ -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
|
*/
|
|
@@ -456,8 +456,10 @@ let ConversationsController = class ConversationsController {
|
|
|
456
456
|
}
|
|
457
457
|
/**
|
|
458
458
|
* Stops the current run (the conversation's owner): 202 `{ runId }`; nothing active → 409
|
|
459
|
-
* `run.not_active`. On a root conversation
|
|
460
|
-
*
|
|
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
|
|
461
463
|
* sub-agent stops with every sub-agent below it, and `runId` is the sub-agent's latest run,
|
|
462
464
|
* which may already be finished when only its descendants were working (agent map spec §6).
|
|
463
465
|
*/
|
|
@@ -537,10 +537,43 @@ export declare class ConversationsService {
|
|
|
537
537
|
* 等人处理的运行没有 worker:直接取消等待并写终态。其余经 Redis 广播,运行所在的任意副本都会收到;
|
|
538
538
|
* 运行已是孤儿(没有任何 worker 在执行)时直接写终态。
|
|
539
539
|
*
|
|
540
|
-
* A root conversation's stop stops
|
|
541
|
-
*
|
|
542
|
-
*
|
|
543
|
-
*
|
|
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.
|
|
544
564
|
*/
|
|
545
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;
|
|
546
579
|
}
|