@capybara-hq/kernel 0.1.0-rc.23 → 0.1.0-rc.24
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/bootstrap.json +25 -25
- package/dist/admin/admin.module.d.ts +2 -1
- package/dist/admin/admin.module.js +7 -3
- package/dist/admin/outbox-dead.controller.d.ts +23 -0
- package/dist/admin/outbox-dead.controller.js +62 -0
- package/dist/admin/outbox-dead.schemas.d.ts +8 -0
- package/dist/admin/outbox-dead.schemas.js +18 -0
- package/dist/admin/outbox-dead.service.d.ts +66 -0
- package/dist/admin/outbox-dead.service.js +179 -0
- package/dist/admin/overview.service.d.ts +40 -5
- package/dist/admin/overview.service.js +41 -7
- package/dist/agents/from-template.js +1 -1
- package/dist/background/finish.d.ts +5 -2
- package/dist/background/finish.js +14 -3
- package/dist/background/launch.d.ts +2 -1
- package/dist/background/launch.js +5 -2
- package/dist/background/store.d.ts +8 -0
- package/dist/background/store.js +24 -0
- package/dist/catalog/admin-catalog.controller.d.ts +21 -2
- package/dist/catalog/admin-catalog.controller.js +45 -6
- package/dist/catalog/admin-catalog.schemas.d.ts +5 -0
- package/dist/catalog/admin-catalog.schemas.js +8 -0
- package/dist/catalog/catalog.module.d.ts +2 -1
- package/dist/catalog/catalog.module.js +5 -1
- package/dist/catalog/marketplaces.service.d.ts +1 -1
- package/dist/catalog/platform-import-update.d.ts +50 -0
- package/dist/catalog/platform-import-update.js +116 -0
- package/dist/catalog/plugin-import.d.ts +36 -1
- package/dist/catalog/plugin-import.js +23 -1
- package/dist/catalog/plugin-upgrade.d.ts +18 -0
- package/dist/catalog/plugin-upgrade.js +19 -0
- package/dist/channels/channels.module.d.ts +5 -2
- package/dist/channels/channels.module.js +9 -2
- package/dist/channels/deliver-job.d.ts +25 -0
- package/dist/channels/deliver-job.js +25 -0
- package/dist/channels/inbound.d.ts +1 -1
- package/dist/channels/inbound.js +2 -15
- package/dist/channels/jobs.d.ts +28 -18
- package/dist/channels/jobs.js +63 -26
- package/dist/config/schema.d.ts +0 -3
- package/dist/config/schema.js +11 -20
- package/dist/conversations/agent-tree.d.ts +16 -0
- package/dist/conversations/agent-tree.js +36 -0
- package/dist/conversations/conversations.service.d.ts +44 -14
- package/dist/conversations/conversations.service.js +52 -38
- package/dist/conversations/event-log.d.ts +7 -10
- package/dist/conversations/event-log.js +24 -15
- package/dist/conversations/events.js +2 -1
- package/dist/conversations/queue.service.d.ts +8 -3
- package/dist/conversations/queue.service.js +37 -13
- package/dist/conversations/titles.d.ts +21 -8
- package/dist/conversations/titles.js +42 -15
- package/dist/db/kernel-schema.d.ts +26 -8
- package/dist/devices/devices.service.d.ts +12 -3
- package/dist/devices/devices.service.js +18 -3
- package/dist/errors.d.ts +11 -0
- package/dist/errors.js +11 -0
- package/dist/evals/run-replay.js +14 -8
- package/dist/extensions/extensions.module.js +3 -0
- package/dist/extensions/lifecycle.d.ts +6 -1
- package/dist/extensions/lifecycle.js +9 -2
- package/dist/extensions/usage-subscribers.d.ts +43 -0
- package/dist/extensions/usage-subscribers.js +108 -0
- package/dist/http/api.module.js +2 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/infra/cli/kernel-cli.module.js +5 -0
- package/dist/infra/events/kernel-events.d.ts +9 -35
- package/dist/infra/outbox/outbox-relay.module.d.ts +6 -0
- package/dist/infra/outbox/outbox-relay.module.js +18 -0
- package/dist/infra/outbox/outbox.dispatcher.d.ts +103 -0
- package/dist/infra/outbox/outbox.dispatcher.js +310 -0
- package/dist/infra/outbox/outbox.module.d.ts +24 -0
- package/dist/infra/outbox/outbox.module.js +50 -0
- package/dist/infra/outbox/outbox.relay.d.ts +31 -0
- package/dist/infra/outbox/outbox.relay.js +76 -0
- package/dist/infra/outbox/outbox.service.d.ts +51 -0
- package/dist/infra/outbox/outbox.service.js +131 -0
- package/dist/infra/outbox/registry.d.ts +120 -0
- package/dist/infra/outbox/registry.js +108 -0
- package/dist/infra/queue/queue-names.d.ts +8 -0
- package/dist/infra/queue/queue-names.js +16 -0
- package/dist/kernel.module.js +3 -0
- package/dist/local-devices/device-policy.service.d.ts +4 -0
- package/dist/local-devices/device-policy.service.js +61 -3
- package/dist/local-devices/machines.d.ts +13 -5
- package/dist/local-devices/machines.js +45 -7
- package/dist/local-devices/policy-summary.d.ts +44 -1
- package/dist/local-devices/policy-summary.js +69 -0
- package/dist/models/embeddings.d.ts +9 -9
- package/dist/models/embeddings.js +11 -18
- package/dist/models/models.module.js +3 -0
- package/dist/orgs/orgs.service.js +7 -0
- package/dist/overrides.d.ts +20 -3
- package/dist/overrides.js +10 -2
- package/dist/platform/exclusions.js +5 -0
- package/dist/platform/ops/admin.ops.js +18 -2
- package/dist/platform/ops/catalog-admin.ops.d.ts +3 -1
- package/dist/platform/ops/catalog-admin.ops.js +32 -4
- package/dist/platform/ops/outbox.ops.d.ts +21 -0
- package/dist/platform/ops/outbox.ops.js +125 -0
- package/dist/platform/ops/schedules.ops.js +40 -1
- package/dist/platform/platform-ops.module.d.ts +2 -1
- package/dist/platform/platform-ops.module.js +3 -0
- package/dist/platform/route-schemas.js +3 -0
- package/dist/roles/worker.module.js +9 -4
- package/dist/runtime/langgraph-loop.js +13 -5
- package/dist/runtime/limits.d.ts +15 -0
- package/dist/runtime/limits.js +25 -0
- package/dist/runtime/loop.d.ts +8 -3
- package/dist/runtime/loop.js +2 -1
- package/dist/runtime/maintenance.d.ts +4 -2
- package/dist/runtime/maintenance.js +17 -2
- package/dist/runtime/model-call.d.ts +14 -16
- package/dist/runtime/model-call.js +14 -24
- package/dist/runtime/queue-lost-jobs.d.ts +24 -0
- package/dist/runtime/queue-lost-jobs.js +51 -0
- package/dist/runtime/run-control.d.ts +42 -5
- package/dist/runtime/run-control.js +66 -12
- package/dist/runtime/run-finish.d.ts +39 -12
- package/dist/runtime/run-finish.js +97 -14
- package/dist/runtime/run-jobs.d.ts +95 -13
- package/dist/runtime/run-jobs.js +199 -28
- package/dist/runtime/run-orphan-check.job.d.ts +142 -0
- package/dist/runtime/run-orphan-check.job.js +339 -0
- package/dist/runtime/run-processors.d.ts +16 -9
- package/dist/runtime/run-processors.js +1 -1
- package/dist/runtime/run-queue.d.ts +64 -55
- package/dist/runtime/run-queue.js +147 -284
- package/dist/runtime/run-steps.d.ts +1 -2
- package/dist/runtime/run-steps.js +8 -11
- package/dist/runtime/run-worker.d.ts +6 -17
- package/dist/runtime/run-worker.js +32 -34
- package/dist/runtime/runtime.module.d.ts +2 -3
- package/dist/runtime/runtime.module.js +5 -6
- package/dist/runtime/step-log.d.ts +16 -4
- package/dist/runtime/step-log.js +26 -6
- package/dist/runtime/stream.controller.d.ts +2 -4
- package/dist/runtime/stream.controller.js +5 -11
- package/dist/runtime/subagents/child.js +1 -1
- package/dist/runtime/subagents/definition.d.ts +2 -1
- package/dist/runtime/subagents/definition.js +4 -2
- package/dist/runtime/subagents/deliveries.d.ts +6 -3
- package/dist/runtime/subagents/deliveries.js +16 -7
- package/dist/runtime/subagents/dispatch.d.ts +12 -12
- package/dist/runtime/subagents/dispatch.js +13 -38
- package/dist/runtime/subagents/messages.d.ts +5 -1
- package/dist/runtime/subagents/messages.js +11 -3
- package/dist/runtime/subagents/skill-fork.d.ts +3 -3
- package/dist/runtime/subagents/skill-fork.js +5 -5
- package/dist/runtime/subagents/subagents.service.js +1 -1
- package/dist/runtime/subagents/tree-jobs.d.ts +61 -0
- package/dist/runtime/subagents/tree-jobs.js +86 -0
- package/dist/runtime/usage-report-jobs.d.ts +23 -48
- package/dist/runtime/usage-report-jobs.js +67 -119
- package/dist/runtime/usage-report.d.ts +28 -77
- package/dist/runtime/usage-report.js +35 -213
- package/dist/runtime/waits.d.ts +48 -198
- package/dist/runtime/waits.js +103 -805
- package/dist/sandbox/sandbox.service.d.ts +17 -4
- package/dist/sandbox/sandbox.service.js +55 -14
- package/dist/schedules/calendar.d.ts +134 -0
- package/dist/schedules/calendar.js +410 -0
- package/dist/schedules/fire.d.ts +1 -3
- package/dist/schedules/fire.js +5 -10
- package/dist/schedules/run-tracker.d.ts +0 -5
- package/dist/schedules/run-tracker.js +0 -13
- package/dist/schedules/scan.job.js +15 -20
- package/dist/schedules/schedule-math.d.ts +42 -0
- package/dist/schedules/schedule-math.js +52 -0
- package/dist/schedules/scheduled-tasks.service.d.ts +16 -0
- package/dist/schedules/scheduled-tasks.service.js +53 -0
- package/dist/schedules/schedules.controller.d.ts +5 -1
- package/dist/schedules/schedules.controller.js +15 -1
- package/dist/schedules/schedules.schemas.d.ts +35 -0
- package/dist/schedules/schedules.schemas.js +27 -0
- package/dist/schedules/store.d.ts +5 -0
- package/dist/schedules/store.js +20 -0
- package/dist/tools/approvals.service.d.ts +26 -7
- package/dist/tools/approvals.service.js +88 -10
- package/dist/tools/builtin-names.d.ts +5 -3
- package/dist/tools/builtin-names.js +12 -4
- package/dist/tools/builtins/builtin-tools.d.ts +1 -2
- package/dist/tools/builtins/builtin-tools.js +21 -17
- package/dist/tools/builtins/sandbox/bash.js +9 -3
- package/dist/tools/builtins/sandbox/common.d.ts +18 -1
- package/dist/tools/builtins/sandbox/common.js +14 -1
- package/dist/tools/builtins/sandbox/index.d.ts +2 -1
- package/dist/tools/builtins/sandbox/index.js +4 -1
- package/dist/tools/builtins/sandbox/task-list.d.ts +12 -0
- package/dist/tools/builtins/sandbox/task-list.js +130 -0
- package/dist/tools/builtins/sandbox/task-output.d.ts +8 -1
- package/dist/tools/builtins/sandbox/task-output.js +21 -3
- package/dist/tools/builtins/web/extract.d.ts +2 -4
- package/dist/tools/builtins/web/extract.js +2 -5
- package/dist/tools/pipeline.d.ts +1 -1
- package/dist/tools/pipeline.js +1 -1
- package/dist/tools/questions.service.d.ts +15 -6
- package/dist/tools/questions.service.js +20 -8
- package/dist/tools/registry.d.ts +4 -2
- package/dist/tools/registry.js +4 -3
- package/dist/tools/tools.module.js +4 -3
- package/package.json +3 -3
- package/src/migrations/0118_device_policy_ops.sql +6 -0
- package/src/migrations/0119_outbox.sql +22 -0
- package/src/migrations/0120_drop_usage_report_sweep.sql +7 -0
|
@@ -3,6 +3,7 @@ import type { EventPayload } from "../../conversations/events.js";
|
|
|
3
3
|
import { KernelDb } from "../../infra/database/kernel-db.js";
|
|
4
4
|
import { TransactionHost } from "../../infra/database/transaction-host.js";
|
|
5
5
|
import { KernelEvents } from "../../infra/events/kernel-events.js";
|
|
6
|
+
import { OutboxService } from "../../infra/outbox/outbox.service.js";
|
|
6
7
|
import type { KernelLogger } from "../../log.js";
|
|
7
8
|
import { type DispatchRow } from "./child.js";
|
|
8
9
|
/** What a caller of {@link SubagentDeliveries.deliverIn} gives; the rest comes from the dispatch. */
|
|
@@ -17,7 +18,7 @@ export declare const finishedDeliveryIdemKey: (childRunId: string) => string;
|
|
|
17
18
|
*
|
|
18
19
|
* A background child's run reaching a terminal state ({@link childFinished}) writes the
|
|
19
20
|
* display-only `subagent_finished` and a `finished` delivery in one transaction, both idempotent by
|
|
20
|
-
* the child run, so of two finishers (the worker
|
|
21
|
+
* the child run, so of two finishers (the worker, a stop, the `deliver-to-parent` job, the `run-orphan-check`) exactly one writes
|
|
21
22
|
* them. A run that handed back a report and then completed writes no delivery (acceptance
|
|
22
23
|
* 2026-11-07): the report already told the parent everything, and a content-free "completed" line
|
|
23
24
|
* only woke an idle parent for nothing and pushed the report away from the end of its request. Fork invocations (phase D, `for_skill`) do not deliver: their parent run waits on the child
|
|
@@ -29,7 +30,8 @@ export declare class SubagentDeliveries {
|
|
|
29
30
|
private readonly events;
|
|
30
31
|
private readonly log;
|
|
31
32
|
readonly txHost: TransactionHost;
|
|
32
|
-
|
|
33
|
+
private readonly outbox;
|
|
34
|
+
constructor(kdb: KernelDb, eventLog: EventLog, events: KernelEvents, log: Pick<KernelLogger, "warn">, txHost: TransactionHost, outbox: Pick<OutboxService, "add">);
|
|
33
35
|
/** The context's handle (the transaction of a `@Transactional` method, else the pool). */
|
|
34
36
|
private get db();
|
|
35
37
|
/**
|
|
@@ -38,7 +40,8 @@ export declare class SubagentDeliveries {
|
|
|
38
40
|
* the child run's (a wake runs as that person, §7). Idempotent by `opts.idemKey` (a finish's
|
|
39
41
|
* default is {@link finishedDeliveryIdemKey}; a report or a note must name its own): under the
|
|
40
42
|
* parent conversation's row lock, an existing delivery returns null and writes nothing. After
|
|
41
|
-
* the commit: the log's notify and `subagent.delivered
|
|
43
|
+
* the commit: the log's notify and `subagent.delivered`; the parent's ensure-run backstop is
|
|
44
|
+
* owed in the transaction. Returns the new event's seq.
|
|
42
45
|
*/
|
|
43
46
|
deliverIn(row: DispatchRow, d: DeliveryInput, opts?: {
|
|
44
47
|
idemKey?: string;
|
|
@@ -15,8 +15,9 @@ import { KernelDb } from "../../infra/database/kernel-db.js";
|
|
|
15
15
|
import { TransactionHost } from "../../infra/database/transaction-host.js";
|
|
16
16
|
import { Transactional } from "../../infra/database/transactional.decorator.js";
|
|
17
17
|
import { KernelEvents } from "../../infra/events/kernel-events.js";
|
|
18
|
+
import { OutboxService } from "../../infra/outbox/outbox.service.js";
|
|
18
19
|
import { KERNEL_LOGGER } from "../../infra/tokens.js";
|
|
19
|
-
import { TERMINAL_STATUSES } from "../run-
|
|
20
|
+
import { oweEnsureRun, TERMINAL_STATUSES } from "../run-jobs.js";
|
|
20
21
|
import { childResult, deliveryRowOfChildRun, finishedEvent } from "./child.js";
|
|
21
22
|
import { dispatchedIdemKey } from "./dispatch.js";
|
|
22
23
|
/** Idempotency key of a child run's finished delivery (one per child run). */
|
|
@@ -31,7 +32,7 @@ const DELIVERY_TEXT_MAX = 200_000;
|
|
|
31
32
|
*
|
|
32
33
|
* A background child's run reaching a terminal state ({@link childFinished}) writes the
|
|
33
34
|
* display-only `subagent_finished` and a `finished` delivery in one transaction, both idempotent by
|
|
34
|
-
* the child run, so of two finishers (the worker
|
|
35
|
+
* the child run, so of two finishers (the worker, a stop, the `deliver-to-parent` job, the `run-orphan-check`) exactly one writes
|
|
35
36
|
* them. A run that handed back a report and then completed writes no delivery (acceptance
|
|
36
37
|
* 2026-11-07): the report already told the parent everything, and a content-free "completed" line
|
|
37
38
|
* only woke an idle parent for nothing and pushed the report away from the end of its request. Fork invocations (phase D, `for_skill`) do not deliver: their parent run waits on the child
|
|
@@ -43,12 +44,14 @@ let SubagentDeliveries = class SubagentDeliveries {
|
|
|
43
44
|
events;
|
|
44
45
|
log;
|
|
45
46
|
txHost;
|
|
46
|
-
|
|
47
|
+
outbox;
|
|
48
|
+
constructor(kdb, eventLog, events, log, txHost, outbox) {
|
|
47
49
|
this.kdb = kdb;
|
|
48
50
|
this.eventLog = eventLog;
|
|
49
51
|
this.events = events;
|
|
50
52
|
this.log = log;
|
|
51
53
|
this.txHost = txHost;
|
|
54
|
+
this.outbox = outbox;
|
|
52
55
|
}
|
|
53
56
|
/** The context's handle (the transaction of a `@Transactional` method, else the pool). */
|
|
54
57
|
get db() {
|
|
@@ -60,7 +63,8 @@ let SubagentDeliveries = class SubagentDeliveries {
|
|
|
60
63
|
* the child run's (a wake runs as that person, §7). Idempotent by `opts.idemKey` (a finish's
|
|
61
64
|
* default is {@link finishedDeliveryIdemKey}; a report or a note must name its own): under the
|
|
62
65
|
* parent conversation's row lock, an existing delivery returns null and writes nothing. After
|
|
63
|
-
* the commit: the log's notify and `subagent.delivered
|
|
66
|
+
* the commit: the log's notify and `subagent.delivered`; the parent's ensure-run backstop is
|
|
67
|
+
* owed in the transaction. Returns the new event's seq.
|
|
64
68
|
*/
|
|
65
69
|
async deliverIn(row, d, opts = {}) {
|
|
66
70
|
const idemKey = opts.idemKey ?? (d.kind === "finished" ? finishedDeliveryIdemKey(row.childRunId) : undefined);
|
|
@@ -68,9 +72,9 @@ let SubagentDeliveries = class SubagentDeliveries {
|
|
|
68
72
|
throw new Error(`a ${d.kind} delivery needs its idempotency key`);
|
|
69
73
|
const t = this.db;
|
|
70
74
|
const parent = row.parentConversationId;
|
|
71
|
-
await t
|
|
75
|
+
const conv = await t
|
|
72
76
|
.selectFrom("conversations")
|
|
73
|
-
.select("
|
|
77
|
+
.select("org_id")
|
|
74
78
|
.where("id", "=", parent)
|
|
75
79
|
.forUpdate()
|
|
76
80
|
.executeTakeFirst();
|
|
@@ -103,6 +107,10 @@ let SubagentDeliveries = class SubagentDeliveries {
|
|
|
103
107
|
]);
|
|
104
108
|
if (!event)
|
|
105
109
|
return null;
|
|
110
|
+
// the parent's wake is owed with the delivery: the wake after the commit (`BackgroundWake`)
|
|
111
|
+
// may never happen (transactional outbox spec §6)
|
|
112
|
+
if (conv)
|
|
113
|
+
await oweEnsureRun(this.outbox, parent, event.seq, conv.org_id);
|
|
106
114
|
afterCommit(async () => {
|
|
107
115
|
await this.eventLog.notify(parent, event.seq);
|
|
108
116
|
await this.events.emit("subagent.delivered", { conversationId: parent });
|
|
@@ -223,6 +231,7 @@ SubagentDeliveries = __decorate([
|
|
|
223
231
|
__param(1, Inject(EventLog)),
|
|
224
232
|
__param(2, Inject(KernelEvents)),
|
|
225
233
|
__param(3, Inject(KERNEL_LOGGER)),
|
|
226
|
-
__param(4, Inject(TransactionHost))
|
|
234
|
+
__param(4, Inject(TransactionHost)),
|
|
235
|
+
__param(5, Inject(OutboxService))
|
|
227
236
|
], SubagentDeliveries);
|
|
228
237
|
export { SubagentDeliveries };
|
|
@@ -18,8 +18,11 @@ export interface DispatchDeps {
|
|
|
18
18
|
purposes: Pick<ModelPurposesService, "resolve">;
|
|
19
19
|
registry: Pick<ProviderRegistry, "resolve">;
|
|
20
20
|
config: Pick<KernelConfigService, "get">;
|
|
21
|
-
/**
|
|
22
|
-
|
|
21
|
+
/**
|
|
22
|
+
* Owes a child run's job in the dispatch's transaction (`RunJobs.owe`: on the sub-agent run
|
|
23
|
+
* queue, `kind` is always `subagent`); false when the job id is already owed.
|
|
24
|
+
*/
|
|
25
|
+
owe: (data: RunJobData, jobId: string, kind: ConversationKind, orgId: string) => Promise<boolean>;
|
|
23
26
|
log: Pick<Logger, "error" | "warn">;
|
|
24
27
|
/**
|
|
25
28
|
* Runs {@link commitDispatchIn} in a transaction of its own (`SubagentsService.commitDispatch`,
|
|
@@ -121,9 +124,9 @@ export interface DispatchPlan {
|
|
|
121
124
|
* dispatches of one run serialize) inserts the child conversation, appends `subagent_started` and
|
|
122
125
|
* the task message, inserts the child run (`queued`) and the `subagent_dispatches` row, appends
|
|
123
126
|
* `subagent_dispatched` and the call's `tool_result` ({@link agentStartedText}: the sub-agent runs
|
|
124
|
-
* in the background) to the parent and audits `subagent.dispatched` and `tool.called`.
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
+
* in the background) to the parent and audits `subagent.dispatched` and `tool.called`. The child
|
|
128
|
+
* run's job is owed in the same transaction (transactional outbox spec §6), so the committed
|
|
129
|
+
* dispatch always starts its child. A fork invocation (`forSkill`) writes no `tool_result`: its run waits on
|
|
127
130
|
* the child (`advanceSkillForks`).
|
|
128
131
|
*
|
|
129
132
|
* Refusals (§4.5) start nothing: a call from a run at `subagents.maxDepth` (agent map spec §4.1:
|
|
@@ -188,7 +191,8 @@ export declare const sentMessageIdemKey: (runId: string, callId: string) => stri
|
|
|
188
191
|
* row, then the parent run row (the order every terminal path uses),
|
|
189
192
|
* runs `child` (writes the child conversation's first events and returns the task message, or
|
|
190
193
|
* null to refuse), then inserts the child run and the dispatch row, appends `subagent_dispatched`
|
|
191
|
-
* and the audit row
|
|
194
|
+
* and the audit row, and owes the child run's job (transactional outbox spec §6). After commit:
|
|
195
|
+
* notifies both conversations.
|
|
192
196
|
* Returns null when `child` refused.
|
|
193
197
|
*
|
|
194
198
|
* Under the parent run's row lock it first re-checks what the pipeline checked without it: a call
|
|
@@ -205,10 +209,6 @@ export type DispatchCommit = {
|
|
|
205
209
|
kind: "existing";
|
|
206
210
|
childRunId: string;
|
|
207
211
|
childConversationId: string;
|
|
208
|
-
requeue: {
|
|
209
|
-
conversationId: string;
|
|
210
|
-
seq: number;
|
|
211
|
-
} | null;
|
|
212
212
|
/** The call's logged result (null for a fork dispatch) and its seq, to notify. */
|
|
213
213
|
result: {
|
|
214
214
|
content: string;
|
|
@@ -233,7 +233,7 @@ export type DispatchCommit = {
|
|
|
233
233
|
* {@link commit}'s transaction (run by `SubagentsService.commitDispatch`, `d.db` its handle).
|
|
234
234
|
* Null when `child` refused.
|
|
235
235
|
*/
|
|
236
|
-
export declare function commitDispatchIn(d: Pick<DispatchDeps, "db" | "eventLog" | "config">, a: DispatchArgs, plan: DispatchPlan, child: ChildWriter): Promise<DispatchCommit | null>;
|
|
237
|
-
export declare function commit(d: Pick<DispatchDeps, "commitDispatch" | "
|
|
236
|
+
export declare function commitDispatchIn(d: Pick<DispatchDeps, "db" | "eventLog" | "config" | "owe">, a: DispatchArgs, plan: DispatchPlan, child: ChildWriter): Promise<DispatchCommit | null>;
|
|
237
|
+
export declare function commit(d: Pick<DispatchDeps, "commitDispatch" | "eventLog">, a: DispatchArgs, plan: DispatchPlan,
|
|
238
238
|
/** Writes the child's events; `checked` is the org agent version the in-lock gates passed. */
|
|
239
239
|
child: ChildWriter): Promise<DispatchOutcome | null>;
|
|
@@ -31,9 +31,9 @@ export const dispatchedIdemKey = (runId, callId) => `subagent_dispatched:${runId
|
|
|
31
31
|
* dispatches of one run serialize) inserts the child conversation, appends `subagent_started` and
|
|
32
32
|
* the task message, inserts the child run (`queued`) and the `subagent_dispatches` row, appends
|
|
33
33
|
* `subagent_dispatched` and the call's `tool_result` ({@link agentStartedText}: the sub-agent runs
|
|
34
|
-
* in the background) to the parent and audits `subagent.dispatched` and `tool.called`.
|
|
35
|
-
*
|
|
36
|
-
*
|
|
34
|
+
* in the background) to the parent and audits `subagent.dispatched` and `tool.called`. The child
|
|
35
|
+
* run's job is owed in the same transaction (transactional outbox spec §6), so the committed
|
|
36
|
+
* dispatch always starts its child. A fork invocation (`forSkill`) writes no `tool_result`: its run waits on
|
|
37
37
|
* the child (`advanceSkillForks`).
|
|
38
38
|
*
|
|
39
39
|
* Refusals (§4.5) start nothing: a call from a run at `subagents.maxDepth` (agent map spec §4.1:
|
|
@@ -226,7 +226,7 @@ const isActiveStatus = (status) => ACTIVE_RUN_STATUSES.includes(status);
|
|
|
226
226
|
* delivered (agent map spec, rc upgrade: a foreground-era dispatch whose rc finisher wrote
|
|
227
227
|
* `subagent_finished` and resumed the parent): the child's report (`childResult`), with the
|
|
228
228
|
* parent's `subagent_finished` appended in this transaction when it is missing (idempotent by its
|
|
229
|
-
* key), so the
|
|
229
|
+
* key), so the `deliver-to-parent` job never delivers it a second time. Null when the parent already
|
|
230
230
|
* has a delivery for the run (it carries the result) or the run has no terminal event yet.
|
|
231
231
|
*/
|
|
232
232
|
async function rcFinishedResult(d, row) {
|
|
@@ -272,7 +272,7 @@ export async function commitDispatchIn(d, a, plan, child) {
|
|
|
272
272
|
const existing = await t
|
|
273
273
|
.selectFrom("subagent_dispatches as sd")
|
|
274
274
|
.leftJoin("runs as cr", "cr.id", "sd.child_run_id")
|
|
275
|
-
.select(["sd.child_run_id", "sd.child_conversation_id", "cr.status"
|
|
275
|
+
.select(["sd.child_run_id", "sd.child_conversation_id", "cr.status"])
|
|
276
276
|
.where("sd.parent_run_id", "=", ctx.runId)
|
|
277
277
|
.where("sd.parent_call_id", "=", callId)
|
|
278
278
|
.executeTakeFirst();
|
|
@@ -284,10 +284,10 @@ export async function commitDispatchIn(d, a, plan, child) {
|
|
|
284
284
|
let result = null;
|
|
285
285
|
if (a.forSkill === undefined) {
|
|
286
286
|
// an rc child that already ended under the foreground wait, whose rc finisher wrote
|
|
287
|
-
// `subagent_finished` and resumed this run (no delivery;
|
|
288
|
-
//
|
|
287
|
+
// `subagent_finished` and resumed this run (no delivery; a run with `subagent_finished` is
|
|
288
|
+
// never delivered): its report is this call's result, as the rc gave it, or it is lost.
|
|
289
289
|
// Only while the call has no result: a call that has one (a background dispatch's second
|
|
290
|
-
// holder) keeps it, and its child's finish stays to its delivery or
|
|
290
|
+
// holder) keeps it, and its child's finish stays to its delivery (or its `deliver-to-parent` job).
|
|
291
291
|
const resultKey = resultEvent(ctx, callId, AGENT_TOOL, "").idemKey;
|
|
292
292
|
const finished = a.sent === undefined &&
|
|
293
293
|
existing.status !== null &&
|
|
@@ -312,13 +312,6 @@ export async function commitDispatchIn(d, a, plan, child) {
|
|
|
312
312
|
childRunId: existing.child_run_id,
|
|
313
313
|
childConversationId: existing.child_conversation_id,
|
|
314
314
|
result,
|
|
315
|
-
// still queued: the first holder may have died before its enqueue (review M4)
|
|
316
|
-
requeue: existing.status === "queued" && existing.trigger_seq !== null
|
|
317
|
-
? {
|
|
318
|
-
conversationId: existing.child_conversation_id,
|
|
319
|
-
seq: Number(existing.trigger_seq),
|
|
320
|
-
}
|
|
321
|
-
: null,
|
|
322
315
|
};
|
|
323
316
|
}
|
|
324
317
|
if (parent?.status !== "running")
|
|
@@ -384,6 +377,9 @@ export async function commitDispatchIn(d, a, plan, child) {
|
|
|
384
377
|
for_skill: a.forSkill !== undefined,
|
|
385
378
|
})
|
|
386
379
|
.execute();
|
|
380
|
+
// the child run's job is owed with its row (transactional outbox spec §6): a queued child
|
|
381
|
+
// always has its job, whatever happens after the commit
|
|
382
|
+
await d.owe({ runId: childRunId, conversationId: plan.childConversationId }, runJobId(plan.childConversationId, task.seq), "subagent", ctx.orgId);
|
|
387
383
|
// an org agent runs on its own model (§5.4): a `model` on the call was ignored
|
|
388
384
|
const ownModel = plan.source === "agent" && input.model !== undefined;
|
|
389
385
|
const content = a.sent?.content ?? agentStartedText(plan.childConversationId, { ownModel });
|
|
@@ -475,19 +471,7 @@ child) {
|
|
|
475
471
|
if (out === null)
|
|
476
472
|
return null;
|
|
477
473
|
if (out.kind === "existing") {
|
|
478
|
-
//
|
|
479
|
-
// it while that job exists), so a holder that died before its enqueue does not leave the
|
|
480
|
-
// child to the wait sweep
|
|
481
|
-
if (out.requeue !== null) {
|
|
482
|
-
const jobId = runJobId(out.requeue.conversationId, out.requeue.seq);
|
|
483
|
-
await d
|
|
484
|
-
.enqueue({ runId: out.childRunId, conversationId: out.requeue.conversationId }, jobId, "subagent")
|
|
485
|
-
.catch((err) => d.log.error("cannot enqueue sub-agent run; the sweep retries", {
|
|
486
|
-
childRunId: out.childRunId,
|
|
487
|
-
jobId,
|
|
488
|
-
err,
|
|
489
|
-
}));
|
|
490
|
-
}
|
|
474
|
+
// the child's job was owed with the first dispatch (a still queued child has it)
|
|
491
475
|
if (out.result !== null)
|
|
492
476
|
await d.eventLog.notify(ctx.conversationId, out.result.seq);
|
|
493
477
|
return {
|
|
@@ -507,18 +491,9 @@ child) {
|
|
|
507
491
|
return out.outcome;
|
|
508
492
|
await d.eventLog.notify(plan.childConversationId, out.childSeq);
|
|
509
493
|
await d.eventLog.notify(ctx.conversationId, out.parentSeq);
|
|
510
|
-
const childRunId = out.childRunId;
|
|
511
|
-
const jobId = runJobId(plan.childConversationId, out.childSeq);
|
|
512
|
-
try {
|
|
513
|
-
await d.enqueue({ runId: childRunId, conversationId: plan.childConversationId }, jobId, "subagent");
|
|
514
|
-
}
|
|
515
|
-
catch (err) {
|
|
516
|
-
// the dispatch is committed: the wait sweep re-enqueues the queued child run
|
|
517
|
-
d.log.error("cannot enqueue sub-agent run; the sweep retries", { childRunId, jobId, err });
|
|
518
|
-
}
|
|
519
494
|
return {
|
|
520
495
|
kind: "dispatched",
|
|
521
|
-
childRunId,
|
|
496
|
+
childRunId: out.childRunId,
|
|
522
497
|
childConversationId: plan.childConversationId,
|
|
523
498
|
content: out.content,
|
|
524
499
|
};
|
|
@@ -50,12 +50,16 @@ export declare class SubagentMessages {
|
|
|
50
50
|
private readonly kdb;
|
|
51
51
|
private readonly eventLog;
|
|
52
52
|
private readonly config;
|
|
53
|
+
/** Owes a restarted sub-agent's run job in the dispatch's transaction. */
|
|
54
|
+
private readonly jobs;
|
|
53
55
|
private readonly deliveries;
|
|
54
56
|
readonly txHost: TransactionHost;
|
|
55
57
|
private readonly hooks?;
|
|
56
58
|
/** What the dispatch transaction of a restart needs (`commit`). */
|
|
57
59
|
private readonly deps;
|
|
58
|
-
constructor(kdb: KernelDb, eventLog: EventLog, config: KernelConfigService,
|
|
60
|
+
constructor(kdb: KernelDb, eventLog: EventLog, config: KernelConfigService,
|
|
61
|
+
/** Owes a restarted sub-agent's run job in the dispatch's transaction. */
|
|
62
|
+
jobs: Pick<RunJobs, "owe">, log: KernelLogger, deliveries: SubagentDeliveries, txHost: TransactionHost, hooks?: SubagentMessagesHooks | undefined);
|
|
59
63
|
/** The restart's dispatch writes in a transaction of their own (see `commitDispatchIn`). */
|
|
60
64
|
private commitDispatch;
|
|
61
65
|
/** The context's handle (the transaction of a `@Transactional` method, else the pool). */
|
|
@@ -56,28 +56,36 @@ let SubagentMessages = class SubagentMessages {
|
|
|
56
56
|
kdb;
|
|
57
57
|
eventLog;
|
|
58
58
|
config;
|
|
59
|
+
jobs;
|
|
59
60
|
deliveries;
|
|
60
61
|
txHost;
|
|
61
62
|
hooks;
|
|
62
63
|
/** What the dispatch transaction of a restart needs (`commit`). */
|
|
63
64
|
deps;
|
|
64
|
-
constructor(kdb, eventLog, config,
|
|
65
|
+
constructor(kdb, eventLog, config,
|
|
66
|
+
/** Owes a restarted sub-agent's run job in the dispatch's transaction. */
|
|
67
|
+
jobs, log, deliveries, txHost, hooks) {
|
|
65
68
|
this.kdb = kdb;
|
|
66
69
|
this.eventLog = eventLog;
|
|
67
70
|
this.config = config;
|
|
71
|
+
this.jobs = jobs;
|
|
68
72
|
this.deliveries = deliveries;
|
|
69
73
|
this.txHost = txHost;
|
|
70
74
|
this.hooks = hooks;
|
|
71
75
|
this.deps = {
|
|
72
76
|
eventLog,
|
|
73
|
-
enqueue: (data, jobId, kind) => jobs.enqueue(data, jobId, kind),
|
|
74
77
|
log,
|
|
75
78
|
commitDispatch: (a, plan, child) => this.commitDispatch(a, plan, child),
|
|
76
79
|
};
|
|
77
80
|
}
|
|
78
81
|
/** The restart's dispatch writes in a transaction of their own (see `commitDispatchIn`). */
|
|
79
82
|
commitDispatch(a, plan, child) {
|
|
80
|
-
return commitDispatchIn({
|
|
83
|
+
return commitDispatchIn({
|
|
84
|
+
db: this.db,
|
|
85
|
+
eventLog: this.eventLog,
|
|
86
|
+
config: this.config,
|
|
87
|
+
owe: (data, jobId, kind, orgId) => this.jobs.owe(data, jobId, kind, orgId),
|
|
88
|
+
}, a, plan, child);
|
|
81
89
|
}
|
|
82
90
|
/** The context's handle (the transaction of a `@Transactional` method, else the pool). */
|
|
83
91
|
get db() {
|
|
@@ -11,8 +11,8 @@ import { type DispatchDeps } from "./dispatch.js";
|
|
|
11
11
|
* through phase C's `dispatchSubagent` before any model call and waits on it (`subagents` wait
|
|
12
12
|
* `sa-<runId>-0`; the dispatch row has `for_skill`, agent map spec §5.4: a fork stays in the
|
|
13
13
|
* foreground while `Agent` calls run in the background). When the child has ended, `prepare` appends the model-visible
|
|
14
|
-
* `skill_fork_result` and the parent's model answers. Every terminal path of the run
|
|
15
|
-
*
|
|
14
|
+
* `skill_fork_result` and the parent's model answers. Every terminal path of the run
|
|
15
|
+
* (`RunFinish.finishIn`) pairs a fork invocation without a report ({@link pairStoppedForks}), so the log never holds
|
|
16
16
|
* a fork lead whose report is missing before the next model call.
|
|
17
17
|
*/
|
|
18
18
|
/** The dispatch's parent call id (no `:`; there is no tool call). */
|
|
@@ -108,7 +108,7 @@ export declare const recordedSkillForks: (eventLog: Pick<EventLog, "append" | "f
|
|
|
108
108
|
payload: unknown;
|
|
109
109
|
}[]) => SkillForks;
|
|
110
110
|
/**
|
|
111
|
-
* In the transaction of a run's terminal path (
|
|
111
|
+
* In the transaction of a run's terminal path (`RunFinish.finishIn`): the run's fork invocation
|
|
112
112
|
* (its trigger message, by the run boundary) without a report gets one now: the ended child's
|
|
113
113
|
* result (and the parent's missing `subagent_finished`), {@link AGENT_STOPPED_WITH_RUN_TEXT} for a
|
|
114
114
|
* child still active (the caller stops it after its commit, as for `Agent` calls), or
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ApiError } from "../../errors.js";
|
|
2
2
|
import { AGENT_STOPPED_WITH_RUN_TEXT } from "../../tools/builtins/agent.js";
|
|
3
3
|
import { subagentsWait } from "../loop.js";
|
|
4
|
-
import { TERMINAL_STATUSES } from "../run-
|
|
4
|
+
import { TERMINAL_STATUSES } from "../run-jobs.js";
|
|
5
5
|
import { childResult, dispatchOfCall, finishedEvent } from "./child.js";
|
|
6
6
|
import { dispatchSubagent } from "./dispatch.js";
|
|
7
7
|
/**
|
|
@@ -10,8 +10,8 @@ import { dispatchSubagent } from "./dispatch.js";
|
|
|
10
10
|
* through phase C's `dispatchSubagent` before any model call and waits on it (`subagents` wait
|
|
11
11
|
* `sa-<runId>-0`; the dispatch row has `for_skill`, agent map spec §5.4: a fork stays in the
|
|
12
12
|
* foreground while `Agent` calls run in the background). When the child has ended, `prepare` appends the model-visible
|
|
13
|
-
* `skill_fork_result` and the parent's model answers. Every terminal path of the run
|
|
14
|
-
*
|
|
13
|
+
* `skill_fork_result` and the parent's model answers. Every terminal path of the run
|
|
14
|
+
* (`RunFinish.finishIn`) pairs a fork invocation without a report ({@link pairStoppedForks}), so the log never holds
|
|
15
15
|
* a fork lead whose report is missing before the next model call.
|
|
16
16
|
*/
|
|
17
17
|
/** The dispatch's parent call id (no `:`; there is no tool call). */
|
|
@@ -126,7 +126,7 @@ export async function advanceSkillForks(d, a) {
|
|
|
126
126
|
agent: ctx.agent,
|
|
127
127
|
accountId: ctx.triggerAccountId,
|
|
128
128
|
signal: ctx.signal,
|
|
129
|
-
deadline: ctx.limits.deadline,
|
|
129
|
+
...(ctx.limits.deadline !== undefined && { deadline: ctx.limits.deadline }),
|
|
130
130
|
...(ctx.conversationKind !== undefined &&
|
|
131
131
|
ctx.conversationKind !== "eval" && { conversationKind: ctx.conversationKind }),
|
|
132
132
|
},
|
|
@@ -188,7 +188,7 @@ export const recordedSkillForks = (eventLog, recorded) => ({
|
|
|
188
188
|
},
|
|
189
189
|
});
|
|
190
190
|
/**
|
|
191
|
-
* In the transaction of a run's terminal path (
|
|
191
|
+
* In the transaction of a run's terminal path (`RunFinish.finishIn`): the run's fork invocation
|
|
192
192
|
* (its trigger message, by the run boundary) without a report gets one now: the ended child's
|
|
193
193
|
* result (and the parent's missing `subagent_finished`), {@link AGENT_STOPPED_WITH_RUN_TEXT} for a
|
|
194
194
|
* child still active (the caller stops it after its commit, as for `Agent` calls), or
|
|
@@ -53,7 +53,7 @@ let SubagentsService = class SubagentsService {
|
|
|
53
53
|
purposes,
|
|
54
54
|
registry,
|
|
55
55
|
config,
|
|
56
|
-
|
|
56
|
+
owe: (data, jobId, kind, orgId) => jobs.owe(data, jobId, kind, orgId),
|
|
57
57
|
log,
|
|
58
58
|
invoker,
|
|
59
59
|
commitDispatch: (a, plan, child) => this.commitDispatch(a, plan, child),
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { OutboxService } from "../../infra/outbox/outbox.service.js";
|
|
2
|
+
import type { Waits } from "../waits.js";
|
|
3
|
+
/**
|
|
4
|
+
* The stops a transaction owes a sub-agent tree (transactional outbox spec §6 "Sub-agents"), on
|
|
5
|
+
* the maintenance queue. Two shapes:
|
|
6
|
+
* - `{ runId }`: what a run's terminal transaction owes its children: its active fork invocation
|
|
7
|
+
* child stops (`Waits.stopChildren`, sub-agents spec §12), and a sub-agent run a person stopped
|
|
8
|
+
* stops its descendants (`Waits.stopDescendantsOfStopped`, agent map spec §6);
|
|
9
|
+
* - `{ conversationId, byAccountId }`: a deleted root's whole tree below it stops
|
|
10
|
+
* (`Waits.stopTree` without the root, agent map spec §10).
|
|
11
|
+
*
|
|
12
|
+
* The caller still stops them right after its commit; the job is the backstop for a process that
|
|
13
|
+
* died in between, {@link TREE_JOB_BACKSTOP_MS} later.
|
|
14
|
+
*/
|
|
15
|
+
export declare const STOP_TREE_JOB = "stop-tree";
|
|
16
|
+
/**
|
|
17
|
+
* A finished sub-agent run's delivery to its parent (`Waits.childFinished`: a background child's
|
|
18
|
+
* `subagent_finished` and `finished` delivery, a fork child's `subagent_finished` and its parent
|
|
19
|
+
* run's resume), owed by the child run's terminal transaction. The delivery is not written in that
|
|
20
|
+
* transaction: it holds the child's conversation and run rows, and the parent's conversation row
|
|
21
|
+
* comes before the child's in the dispatch's lock order (parent conversation, parent run, budget
|
|
22
|
+
* run, child conversation), so taking it there could deadlock with a follow-up dispatch. The
|
|
23
|
+
* caller delivers right after its commit; the job is the backstop, {@link TREE_JOB_BACKSTOP_MS}
|
|
24
|
+
* later.
|
|
25
|
+
*/
|
|
26
|
+
export declare const DELIVER_TO_PARENT_JOB = "deliver-to-parent";
|
|
27
|
+
/** How long after its transaction a tree job fires (a backstop of the direct call, not a second). */
|
|
28
|
+
export declare const TREE_JOB_BACKSTOP_MS = 30000;
|
|
29
|
+
/** The `stop-tree` job's data. */
|
|
30
|
+
export type StopTreeJobData = {
|
|
31
|
+
runId: string;
|
|
32
|
+
} | {
|
|
33
|
+
conversationId: string;
|
|
34
|
+
byAccountId: string | null;
|
|
35
|
+
};
|
|
36
|
+
/** The `deliver-to-parent` job's data. */
|
|
37
|
+
export interface DeliverToParentJobData {
|
|
38
|
+
childRunId: string;
|
|
39
|
+
}
|
|
40
|
+
/** The `stop-tree` job id of a finished run's children (no `:`). */
|
|
41
|
+
export declare const stopChildrenJobId: (runId: string) => string;
|
|
42
|
+
/** The `stop-tree` job id of a deleted root's tree (a conversation is deleted once; no `:`). */
|
|
43
|
+
export declare const stopDeletedTreeJobId: (conversationId: string) => string;
|
|
44
|
+
/** The `deliver-to-parent` job id: one per child run (no `:`). */
|
|
45
|
+
export declare const deliverToParentJobId: (childRunId: string) => string;
|
|
46
|
+
/** Owes the stops of `runId`'s children in the caller's transaction (throws outside one). */
|
|
47
|
+
export declare function oweStopChildren(outbox: Pick<OutboxService, "add">, runId: string, orgId: string): Promise<boolean>;
|
|
48
|
+
/** Owes the stop of a deleted root's tree in the caller's transaction (throws outside one). */
|
|
49
|
+
export declare function oweStopDeletedTree(outbox: Pick<OutboxService, "add">, conversationId: string, byAccountId: string | null, orgId: string): Promise<boolean>;
|
|
50
|
+
/** Owes a finished child run's delivery to its parent in the caller's transaction. */
|
|
51
|
+
export declare function oweDeliverToParent(outbox: Pick<OutboxService, "add">, childRunId: string, orgId: string): Promise<boolean>;
|
|
52
|
+
/**
|
|
53
|
+
* The `stop-tree` job (`MaintenanceProcessor`): every stop reads the active runs and stops only
|
|
54
|
+
* those, so a run the direct call already stopped is not stopped again.
|
|
55
|
+
*/
|
|
56
|
+
export declare function processStopTree(waits: Pick<Waits, "stopChildren" | "stopDescendantsOfStopped" | "stopTree">, data: StopTreeJobData): Promise<void>;
|
|
57
|
+
/**
|
|
58
|
+
* The `deliver-to-parent` job (`MaintenanceProcessor`): `Waits.childFinished` writes the delivery
|
|
59
|
+
* once by the child run (idempotency keys under the parent conversation's row lock).
|
|
60
|
+
*/
|
|
61
|
+
export declare function processDeliverToParent(waits: Pick<Waits, "childFinished">, data: DeliverToParentJobData): Promise<void>;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { MAINTENANCE_QUEUE_NAME } from "../maintenance-queue.js";
|
|
2
|
+
/**
|
|
3
|
+
* The stops a transaction owes a sub-agent tree (transactional outbox spec §6 "Sub-agents"), on
|
|
4
|
+
* the maintenance queue. Two shapes:
|
|
5
|
+
* - `{ runId }`: what a run's terminal transaction owes its children: its active fork invocation
|
|
6
|
+
* child stops (`Waits.stopChildren`, sub-agents spec §12), and a sub-agent run a person stopped
|
|
7
|
+
* stops its descendants (`Waits.stopDescendantsOfStopped`, agent map spec §6);
|
|
8
|
+
* - `{ conversationId, byAccountId }`: a deleted root's whole tree below it stops
|
|
9
|
+
* (`Waits.stopTree` without the root, agent map spec §10).
|
|
10
|
+
*
|
|
11
|
+
* The caller still stops them right after its commit; the job is the backstop for a process that
|
|
12
|
+
* died in between, {@link TREE_JOB_BACKSTOP_MS} later.
|
|
13
|
+
*/
|
|
14
|
+
export const STOP_TREE_JOB = "stop-tree";
|
|
15
|
+
/**
|
|
16
|
+
* A finished sub-agent run's delivery to its parent (`Waits.childFinished`: a background child's
|
|
17
|
+
* `subagent_finished` and `finished` delivery, a fork child's `subagent_finished` and its parent
|
|
18
|
+
* run's resume), owed by the child run's terminal transaction. The delivery is not written in that
|
|
19
|
+
* transaction: it holds the child's conversation and run rows, and the parent's conversation row
|
|
20
|
+
* comes before the child's in the dispatch's lock order (parent conversation, parent run, budget
|
|
21
|
+
* run, child conversation), so taking it there could deadlock with a follow-up dispatch. The
|
|
22
|
+
* caller delivers right after its commit; the job is the backstop, {@link TREE_JOB_BACKSTOP_MS}
|
|
23
|
+
* later.
|
|
24
|
+
*/
|
|
25
|
+
export const DELIVER_TO_PARENT_JOB = "deliver-to-parent";
|
|
26
|
+
/** How long after its transaction a tree job fires (a backstop of the direct call, not a second). */
|
|
27
|
+
export const TREE_JOB_BACKSTOP_MS = 30_000;
|
|
28
|
+
/** The `stop-tree` job id of a finished run's children (no `:`). */
|
|
29
|
+
export const stopChildrenJobId = (runId) => `stop-tree-run-${runId}`;
|
|
30
|
+
/** The `stop-tree` job id of a deleted root's tree (a conversation is deleted once; no `:`). */
|
|
31
|
+
export const stopDeletedTreeJobId = (conversationId) => `stop-tree-deleted-${conversationId}`;
|
|
32
|
+
/** The `deliver-to-parent` job id: one per child run (no `:`). */
|
|
33
|
+
export const deliverToParentJobId = (childRunId) => `deliver-to-parent-${childRunId}`;
|
|
34
|
+
const backstopAt = () => new Date(Date.now() + TREE_JOB_BACKSTOP_MS);
|
|
35
|
+
/** Owes the stops of `runId`'s children in the caller's transaction (throws outside one). */
|
|
36
|
+
export function oweStopChildren(outbox, runId, orgId) {
|
|
37
|
+
return outbox.add({
|
|
38
|
+
queue: MAINTENANCE_QUEUE_NAME,
|
|
39
|
+
name: STOP_TREE_JOB,
|
|
40
|
+
jobId: stopChildrenJobId(runId),
|
|
41
|
+
data: { runId },
|
|
42
|
+
runAt: backstopAt(),
|
|
43
|
+
orgId,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
/** Owes the stop of a deleted root's tree in the caller's transaction (throws outside one). */
|
|
47
|
+
export function oweStopDeletedTree(outbox, conversationId, byAccountId, orgId) {
|
|
48
|
+
return outbox.add({
|
|
49
|
+
queue: MAINTENANCE_QUEUE_NAME,
|
|
50
|
+
name: STOP_TREE_JOB,
|
|
51
|
+
jobId: stopDeletedTreeJobId(conversationId),
|
|
52
|
+
data: { conversationId, byAccountId },
|
|
53
|
+
runAt: backstopAt(),
|
|
54
|
+
orgId,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
/** Owes a finished child run's delivery to its parent in the caller's transaction. */
|
|
58
|
+
export function oweDeliverToParent(outbox, childRunId, orgId) {
|
|
59
|
+
return outbox.add({
|
|
60
|
+
queue: MAINTENANCE_QUEUE_NAME,
|
|
61
|
+
name: DELIVER_TO_PARENT_JOB,
|
|
62
|
+
jobId: deliverToParentJobId(childRunId),
|
|
63
|
+
data: { childRunId },
|
|
64
|
+
runAt: backstopAt(),
|
|
65
|
+
orgId,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* The `stop-tree` job (`MaintenanceProcessor`): every stop reads the active runs and stops only
|
|
70
|
+
* those, so a run the direct call already stopped is not stopped again.
|
|
71
|
+
*/
|
|
72
|
+
export async function processStopTree(waits, data) {
|
|
73
|
+
if ("runId" in data) {
|
|
74
|
+
await waits.stopChildren(data.runId, null);
|
|
75
|
+
await waits.stopDescendantsOfStopped(data.runId);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
await waits.stopTree(data.conversationId, data.byAccountId, { includeSelf: false });
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* The `deliver-to-parent` job (`MaintenanceProcessor`): `Waits.childFinished` writes the delivery
|
|
82
|
+
* once by the child run (idempotency keys under the parent conversation's row lock).
|
|
83
|
+
*/
|
|
84
|
+
export async function processDeliverToParent(waits, data) {
|
|
85
|
+
await waits.childFinished(data.childRunId);
|
|
86
|
+
}
|