@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
|
@@ -25,11 +25,13 @@ import { KernelEvents } from "../infra/events/kernel-events.js";
|
|
|
25
25
|
import { KERNEL_BUS, KERNEL_LOGGER } from "../infra/tokens.js";
|
|
26
26
|
import { SkillsCatalogService } from "../skills/catalog.js";
|
|
27
27
|
import { normalizeStoredDefinitionIn } from "../tools/legacy-data.js";
|
|
28
|
+
import { ToolRegistry } from "../tools/registry.js";
|
|
28
29
|
import { RunAbort } from "./abort.js";
|
|
29
30
|
import { KyselyCheckpointSaver } from "./checkpointer.js";
|
|
30
31
|
import { ConversationLock } from "./conversation-lock.js";
|
|
31
32
|
import { runDeadline, tightestLimit } from "./limits.js";
|
|
32
33
|
import { RunError } from "./model-call.js";
|
|
34
|
+
import { PluginContextService } from "./plugin-context.service.js";
|
|
33
35
|
import { ProviderListError, ProviderRegistry } from "./providers.js";
|
|
34
36
|
import { ORG_QUOTA_LEASE_MS, orgRunLimit, RUN_QUOTA } from "./quota.js";
|
|
35
37
|
import { RunContextService } from "./run-context.service.js";
|
|
@@ -64,6 +66,15 @@ async function requeue(job, token, delayMs) {
|
|
|
64
66
|
await job.moveToDelayed(Date.now() + delayMs, token);
|
|
65
67
|
throw new DelayedError();
|
|
66
68
|
}
|
|
69
|
+
/** An account's display name ("" when it is gone). */
|
|
70
|
+
async function accountName(db, accountId) {
|
|
71
|
+
const row = await db
|
|
72
|
+
.selectFrom("accounts")
|
|
73
|
+
.select("display_name")
|
|
74
|
+
.where("id", "=", accountId)
|
|
75
|
+
.executeTakeFirst();
|
|
76
|
+
return row?.display_name ?? "";
|
|
77
|
+
}
|
|
67
78
|
/**
|
|
68
79
|
* 运行 worker(只在含 `worker` 角色的进程消费):领取 `cap-kernel-runs` 与 `cap-kernel-subagent-runs` 的任务
|
|
69
80
|
* (两个 `@Processor`,见 run-processors.ts),取会话锁与组织配额,调用 AgentLoop,按结果写终态事件并推进
|
|
@@ -87,6 +98,8 @@ let RunWorker = class RunWorker {
|
|
|
87
98
|
models;
|
|
88
99
|
skillsCatalog;
|
|
89
100
|
runContext;
|
|
101
|
+
pluginContext;
|
|
102
|
+
tools;
|
|
90
103
|
finisher;
|
|
91
104
|
txHost;
|
|
92
105
|
drafts;
|
|
@@ -114,7 +127,11 @@ let RunWorker = class RunWorker {
|
|
|
114
127
|
/** Run start's skills catalog snapshot (reads the pinned skills' bodies, phase B). */
|
|
115
128
|
skillsCatalog,
|
|
116
129
|
/** Run start's run context (run context spec §5): who the agent is, the day, the person. */
|
|
117
|
-
runContext,
|
|
130
|
+
runContext,
|
|
131
|
+
/** Run start's plugin context (memory plugin spec §6.1): the `context@1` providers' text. */
|
|
132
|
+
pluginContext,
|
|
133
|
+
/** The run's resolved tools, for the plugin context's `tools` (chat runs with providers). */
|
|
134
|
+
tools, finisher, txHost,
|
|
118
135
|
/** The steps' live drafts this process writes for the runs it executes. */
|
|
119
136
|
drafts, hooks) {
|
|
120
137
|
this.kdb = kdb;
|
|
@@ -134,6 +151,8 @@ let RunWorker = class RunWorker {
|
|
|
134
151
|
this.models = models;
|
|
135
152
|
this.skillsCatalog = skillsCatalog;
|
|
136
153
|
this.runContext = runContext;
|
|
154
|
+
this.pluginContext = pluginContext;
|
|
155
|
+
this.tools = tools;
|
|
137
156
|
this.finisher = finisher;
|
|
138
157
|
this.txHost = txHost;
|
|
139
158
|
this.drafts = drafts;
|
|
@@ -190,8 +209,10 @@ let RunWorker = class RunWorker {
|
|
|
190
209
|
/**
|
|
191
210
|
* After any terminal commit of a run that is not a sub-agent's (sub-agents spec §12): stops its
|
|
192
211
|
* active fork invocation child on its behalf (no account); background `Agent` children keep
|
|
193
|
-
* running (agent map spec §5.4)
|
|
194
|
-
*
|
|
212
|
+
* running (agent map spec §5.4), unless a person stopped the run: then the sub-agents of its
|
|
213
|
+
* windows stop too, also one it dispatched after the stop read the tree (agent map spec §6,
|
|
214
|
+
* changed in acceptance 2026-11-13). The terminal transaction owed the same stops
|
|
215
|
+
* (`stop-tree`), the backstop when this call never happens.
|
|
195
216
|
*/
|
|
196
217
|
async stopChildren(runId) {
|
|
197
218
|
try {
|
|
@@ -200,6 +221,12 @@ let RunWorker = class RunWorker {
|
|
|
200
221
|
catch (err) {
|
|
201
222
|
this.log.error("cannot stop the sub-agents of a finished run", { runId, err });
|
|
202
223
|
}
|
|
224
|
+
try {
|
|
225
|
+
await this.waits.stopDescendantsOfStopped(runId);
|
|
226
|
+
}
|
|
227
|
+
catch (err) {
|
|
228
|
+
this.log.error("cannot stop the sub-agents of a stopped run", { runId, err });
|
|
229
|
+
}
|
|
203
230
|
}
|
|
204
231
|
/**
|
|
205
232
|
* After a sub-agent run's terminal state committed (sub-agents spec §7.3): reports it to the
|
|
@@ -379,9 +406,10 @@ let RunWorker = class RunWorker {
|
|
|
379
406
|
// 运行在等人处理(例如暂停后本任务被 stalled 机制重新投递):不执行,续跑由处理 / 超时发起的新任务负责。
|
|
380
407
|
if (run.status === "waiting")
|
|
381
408
|
return;
|
|
409
|
+
await this.hooks?.afterRunRead?.(runId, conversationId);
|
|
382
410
|
const conv = await this.db
|
|
383
411
|
.selectFrom("conversations")
|
|
384
|
-
.select(["org_id", "agent_id", "kind", "subagent_depth"])
|
|
412
|
+
.select(["org_id", "agent_id", "kind", "subagent_depth", "source", "account_id"])
|
|
385
413
|
.where("id", "=", conversationId)
|
|
386
414
|
.executeTakeFirst();
|
|
387
415
|
if (!conv)
|
|
@@ -448,8 +476,6 @@ let RunWorker = class RunWorker {
|
|
|
448
476
|
delayMs = QUOTA_FULL_DELAY_MS;
|
|
449
477
|
}
|
|
450
478
|
else {
|
|
451
|
-
// 运行开始执行(含续跑、stalled 后接手):通知旁路(渠道出站投递据此把输出投到 IM),失败不影响运行
|
|
452
|
-
void this.events.emit("run.started", { conversationId, runId });
|
|
453
479
|
const r = await this.execute(run, { id: conversationId, ...conv }, state).catch(async (err) => {
|
|
454
480
|
if (state.abandoned)
|
|
455
481
|
return "abandoned";
|
|
@@ -574,6 +600,14 @@ let RunWorker = class RunWorker {
|
|
|
574
600
|
saved = await normalizeStoredDefinitionIn(this.db, conv.org_id, version.definition);
|
|
575
601
|
agent = saved;
|
|
576
602
|
}
|
|
603
|
+
// the trigger account's role, read once per run for the plugin context and plugin tool
|
|
604
|
+
// handlers (memory plugin spec §6.2); an account no longer in the org counts as a guest
|
|
605
|
+
const role = (await this.db
|
|
606
|
+
.selectFrom("memberships")
|
|
607
|
+
.select("role")
|
|
608
|
+
.where("org_id", "=", conv.org_id)
|
|
609
|
+
.where("account_id", "=", run.trigger_account_id)
|
|
610
|
+
.executeTakeFirst())?.role ?? "guest";
|
|
577
611
|
let startedAt = run.started_at;
|
|
578
612
|
// The model / effort switch in effect (UI refresh B): the latest `model_switched` before this
|
|
579
613
|
// run's `run_started`, read under the conversation lock when the run starts, so a replay
|
|
@@ -585,25 +619,36 @@ let RunWorker = class RunWorker {
|
|
|
585
619
|
}
|
|
586
620
|
if (run.status === "queued" || startedAt === null) {
|
|
587
621
|
if (!existingStart) {
|
|
622
|
+
await this.hooks?.beforeRunStarted?.(runId, conversationId);
|
|
588
623
|
if (saved !== undefined)
|
|
589
624
|
await this.fallBackIfUnavailable(conv.org_id, conversationId, runId, saved);
|
|
590
625
|
const started = await this.appendRunStarted(conversationId, run, agent, saved);
|
|
626
|
+
// the status was read before the conversation lock: a stop that ended the run meanwhile
|
|
627
|
+
// (a queued run's direct abort, an orphan abort) wins, and the run never starts
|
|
628
|
+
if (started.terminal)
|
|
629
|
+
return "finished";
|
|
591
630
|
agent = started.agent;
|
|
592
631
|
const toSeq = started.toSeq;
|
|
593
632
|
if (toSeq !== undefined)
|
|
594
633
|
await this.eventLog.notify(conversationId, toSeq);
|
|
595
634
|
}
|
|
596
|
-
//
|
|
597
|
-
|
|
635
|
+
// Skills catalog snapshot (ruling M17): written before the run is running, and again on a
|
|
636
|
+
// crash retry (deduped by its idem key). Both records re-read the run under the conversation
|
|
637
|
+
// and run row locks in their append transactions: a stop that ended it meanwhile (a retried
|
|
638
|
+
// start reads `queued` before any lock) wins, nothing is appended after its end, and the run
|
|
639
|
+
// never starts.
|
|
640
|
+
const catalogged = await this.skillsCatalog.record({
|
|
598
641
|
orgId: conv.org_id,
|
|
599
642
|
agentId: conv.agent_id,
|
|
600
643
|
conversationId,
|
|
601
644
|
runId,
|
|
602
645
|
agent,
|
|
603
646
|
}, { log: this.log });
|
|
647
|
+
if (!catalogged)
|
|
648
|
+
return "finished";
|
|
604
649
|
// run context (spec §5): after the catalog, before the run is running, so before any
|
|
605
650
|
// compaction and model call; a retried run start finds its event or no change
|
|
606
|
-
await this.runContext.record({
|
|
651
|
+
const contexted = await this.runContext.record({
|
|
607
652
|
orgId: conv.org_id,
|
|
608
653
|
conversationId,
|
|
609
654
|
runId,
|
|
@@ -617,14 +662,47 @@ let RunWorker = class RunWorker {
|
|
|
617
662
|
},
|
|
618
663
|
}),
|
|
619
664
|
});
|
|
665
|
+
if (!contexted)
|
|
666
|
+
return "finished";
|
|
667
|
+
// plugin context (memory plugin spec §6.1): after the run context, still before the run is
|
|
668
|
+
// running; the providers are asked outside any transaction, a chat run only, so a stop may
|
|
669
|
+
// end the run meanwhile: its append transaction re-reads the run under the locks
|
|
670
|
+
const pluginContexted = await this.pluginContext.record({
|
|
671
|
+
orgId: conv.org_id,
|
|
672
|
+
conversationId,
|
|
673
|
+
runId,
|
|
674
|
+
agentId: conv.agent_id,
|
|
675
|
+
agentName: agent.name,
|
|
676
|
+
ownerAccountId: conv.account_id,
|
|
677
|
+
// only a chat run asks the providers
|
|
678
|
+
ownerName: conv.kind === "chat" ? await accountName(this.db, conv.account_id) : "",
|
|
679
|
+
triggerAccountId: run.trigger_account_id,
|
|
680
|
+
source: conv.source,
|
|
681
|
+
role,
|
|
682
|
+
kind: conv.kind,
|
|
683
|
+
toolKeysByPlugin: () => this.pluginToolKeys(conv, agent),
|
|
684
|
+
// the run's own abort watch starts with its execution below: until then, the shutdown
|
|
685
|
+
signal: state.shutdown.signal,
|
|
686
|
+
});
|
|
687
|
+
if (!pluginContexted)
|
|
688
|
+
return "finished";
|
|
689
|
+
await this.hooks?.beforeRunning?.(runId, conversationId);
|
|
690
|
+
// never over a terminal status: a run finished meanwhile stays finished
|
|
620
691
|
const updated = await this.db
|
|
621
692
|
.updateTable("runs")
|
|
622
693
|
.set({ status: "running", started_at: sql `COALESCE(started_at, now())` })
|
|
623
694
|
.where("id", "=", runId)
|
|
695
|
+
.where("status", "in", ["queued", "running"])
|
|
624
696
|
.returning("started_at")
|
|
625
|
-
.
|
|
697
|
+
.executeTakeFirst();
|
|
698
|
+
if (updated === undefined)
|
|
699
|
+
return "finished";
|
|
626
700
|
startedAt = updated.started_at;
|
|
627
701
|
}
|
|
702
|
+
// the run executes (a first start, a resume, a stalled takeover): a hint to the side paths (the
|
|
703
|
+
// channel outbound delivers its output to IM), after the start went through, never for a run a
|
|
704
|
+
// stop ended first; a failure does not affect the run
|
|
705
|
+
void this.events.emit("run.started", { conversationId, runId });
|
|
628
706
|
// 截止时间以首次开始为准:停机后在其他 worker 续跑也不重新计时;等人处理的时长(waited_ms)不计入。
|
|
629
707
|
// `runtime.maxRunSeconds` 0: no deadline, no timer (the run ends when the model stops or is stopped)
|
|
630
708
|
const deadline = runDeadline(startedAt.getTime() + Number(run.waited_ms), runtime.maxRunSeconds);
|
|
@@ -688,6 +766,9 @@ let RunWorker = class RunWorker {
|
|
|
688
766
|
runId,
|
|
689
767
|
agentId: conv.agent_id,
|
|
690
768
|
triggerAccountId: run.trigger_account_id,
|
|
769
|
+
source: conv.source,
|
|
770
|
+
ownerAccountId: conv.account_id,
|
|
771
|
+
role,
|
|
691
772
|
agent,
|
|
692
773
|
agentVersion: run.agent_version,
|
|
693
774
|
signal: ac.signal,
|
|
@@ -788,6 +869,28 @@ let RunWorker = class RunWorker {
|
|
|
788
869
|
// a completed run enqueues the conversation's title job (never throws)
|
|
789
870
|
void this.titles.maybeEnqueue(conversationId);
|
|
790
871
|
}
|
|
872
|
+
/**
|
|
873
|
+
* The run's extension tool keys by plugin, resolved as a chat run's first step lists them
|
|
874
|
+
* (disabled tools, org switches, the conversation's source): a provider's `tools`.
|
|
875
|
+
*/
|
|
876
|
+
async pluginToolKeys(conv, agent) {
|
|
877
|
+
const { tools } = await this.tools.forAgent(conv.org_id, {
|
|
878
|
+
disabledTools: agent.disabledTools,
|
|
879
|
+
subagentTools: agent.subagentTools,
|
|
880
|
+
subagents: agent.subagents,
|
|
881
|
+
sandboxConversationId: conv.id,
|
|
882
|
+
source: conv.source,
|
|
883
|
+
});
|
|
884
|
+
const out = new Map();
|
|
885
|
+
for (const t of tools) {
|
|
886
|
+
if (t.source.kind !== "plugin")
|
|
887
|
+
continue;
|
|
888
|
+
const keys = out.get(t.source.plugin) ?? [];
|
|
889
|
+
keys.push(t.key);
|
|
890
|
+
out.set(t.source.plugin, keys);
|
|
891
|
+
}
|
|
892
|
+
return out;
|
|
893
|
+
}
|
|
791
894
|
/**
|
|
792
895
|
* When the conversation was switched to a model that no longer resolves (provider disabled,
|
|
793
896
|
* model removed), writes `model_switched { model: null, effort: null, reason: "unavailable" }`
|
|
@@ -820,6 +923,26 @@ let RunWorker = class RunWorker {
|
|
|
820
923
|
await this.eventLog.notify(conversationId, ev.seq);
|
|
821
924
|
await this.eventLog.publishChanged(conversationId, "selection");
|
|
822
925
|
}
|
|
926
|
+
/**
|
|
927
|
+
* Whether the run is gone or terminal, read in the caller's transaction under the locks
|
|
928
|
+
* (conversation, then run, as `RunFinish.finishIn`): the job read its status before taking the
|
|
929
|
+
* conversation lock, and a stop may have ended it since.
|
|
930
|
+
*/
|
|
931
|
+
async endedUnderLocks(conversationId, runId) {
|
|
932
|
+
await this.db
|
|
933
|
+
.selectFrom("conversations")
|
|
934
|
+
.select("id")
|
|
935
|
+
.where("id", "=", conversationId)
|
|
936
|
+
.forUpdate()
|
|
937
|
+
.execute();
|
|
938
|
+
const row = await this.db
|
|
939
|
+
.selectFrom("runs")
|
|
940
|
+
.select("status")
|
|
941
|
+
.where("id", "=", runId)
|
|
942
|
+
.forUpdate()
|
|
943
|
+
.executeTakeFirst();
|
|
944
|
+
return !row || TERMINAL_STATUSES.has(row.status);
|
|
945
|
+
}
|
|
823
946
|
/**
|
|
824
947
|
* `run_started` under the conversation row lock, with the model / effort switch in effect then
|
|
825
948
|
* (a chat run's `saved` version under the conversation's selection): what the run uses.
|
|
@@ -828,12 +951,9 @@ let RunWorker = class RunWorker {
|
|
|
828
951
|
/** The definition so far (a sub-agent's frozen one is final). */
|
|
829
952
|
current, saved) {
|
|
830
953
|
const t = this.db;
|
|
831
|
-
await
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
.where("id", "=", conversationId)
|
|
835
|
-
.forUpdate()
|
|
836
|
-
.execute();
|
|
954
|
+
if (await this.endedUnderLocks(conversationId, run.id)) {
|
|
955
|
+
return { toSeq: undefined, agent: current, terminal: true };
|
|
956
|
+
}
|
|
837
957
|
const agent = saved !== undefined
|
|
838
958
|
? effectiveDefinition(saved, await readSelection(t, conversationId))
|
|
839
959
|
: current;
|
|
@@ -951,10 +1071,12 @@ RunWorker = __decorate([
|
|
|
951
1071
|
__param(16, Inject(ProviderRegistry)),
|
|
952
1072
|
__param(17, Inject(SkillsCatalogService)),
|
|
953
1073
|
__param(18, Inject(RunContextService)),
|
|
954
|
-
__param(19, Inject(
|
|
955
|
-
__param(20, Inject(
|
|
956
|
-
__param(21, Inject(
|
|
957
|
-
__param(22,
|
|
958
|
-
__param(
|
|
1074
|
+
__param(19, Inject(PluginContextService)),
|
|
1075
|
+
__param(20, Inject(ToolRegistry)),
|
|
1076
|
+
__param(21, Inject(RunFinish)),
|
|
1077
|
+
__param(22, Inject(TransactionHost)),
|
|
1078
|
+
__param(23, Inject(RunDrafts)),
|
|
1079
|
+
__param(24, Optional()),
|
|
1080
|
+
__param(24, Inject(RUN_HOOKS))
|
|
959
1081
|
], RunWorker);
|
|
960
1082
|
export { RunWorker };
|
|
@@ -23,6 +23,7 @@ import { RunAbort } from "./abort.js";
|
|
|
23
23
|
import { KyselyCheckpointSaver } from "./checkpointer.js";
|
|
24
24
|
import { ConversationLock } from "./conversation-lock.js";
|
|
25
25
|
import { MAINTENANCE_QUEUE_NAME } from "./maintenance-queue.js";
|
|
26
|
+
import { PluginContextService } from "./plugin-context.service.js";
|
|
26
27
|
import { ProvidersModule } from "./providers.js";
|
|
27
28
|
import { EVAL_QUOTA, EVAL_QUOTA_PREFIX, OrgQuota, RUN_QUOTA, RUN_QUOTA_PREFIX } from "./quota.js";
|
|
28
29
|
import { RunContextService } from "./run-context.service.js";
|
|
@@ -83,6 +84,7 @@ RuntimeModule = __decorate([
|
|
|
83
84
|
AgentLoops,
|
|
84
85
|
RunSteps,
|
|
85
86
|
RunContextService,
|
|
87
|
+
PluginContextService,
|
|
86
88
|
RunWorker,
|
|
87
89
|
// a finished background task wakes an idle conversation (background Bash spec §3)
|
|
88
90
|
BackgroundWake,
|
|
@@ -9,10 +9,10 @@ import { UsageReportJobs } from "./usage-report-jobs.js";
|
|
|
9
9
|
export type StepAuditEntry = Parameters<typeof audit>[1];
|
|
10
10
|
/**
|
|
11
11
|
* The writes of a run step that go to the event log together with another row, each in a
|
|
12
|
-
* transaction of its own (the caller notifies the log after the commit): a model call with the
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
12
|
+
* transaction of its own (the caller notifies the log after the commit): a model call with the tool
|
|
13
|
+
* definitions it sent, a denial with its audit row, usage events with their owed `usage.report`
|
|
14
|
+
* deliveries (transactional outbox spec §8.1), and a sub-agent's usage counted on its parent run.
|
|
15
|
+
* The step functions (`model-call.ts`, `hook-calls.ts`) reach it through their deps.
|
|
16
16
|
*/
|
|
17
17
|
export declare class StepLog {
|
|
18
18
|
private readonly kdb;
|
|
@@ -35,7 +35,8 @@ export declare class StepLog {
|
|
|
35
35
|
* the whole tree counts on one run): `counter.budgetRunId`, the run's own window
|
|
36
36
|
* (`SubagentParent.budgetRunId`), else the parent run's ({@link budgetRunOf}). A usage event
|
|
37
37
|
* already in the log (an idempotent hit after a crash) adds nothing, so each call counts exactly
|
|
38
|
-
* once; the new usage events' `usage.report` deliveries are owed in the same transaction. Returns
|
|
38
|
+
* once; the new usage events' `usage.report` deliveries are owed in the same transaction. Returns
|
|
39
|
+
* what was written (hits included) and the highest new seq (null: nothing new).
|
|
39
40
|
*/
|
|
40
41
|
appendCounted(conversationId: string, counter: {
|
|
41
42
|
parentRunId: string;
|
package/dist/runtime/step-log.js
CHANGED
|
@@ -20,10 +20,10 @@ import { budgetRunOf } from "./subagents/limits.js";
|
|
|
20
20
|
import { UsageReportJobs, UsageReportJobsModule } from "./usage-report-jobs.js";
|
|
21
21
|
/**
|
|
22
22
|
* The writes of a run step that go to the event log together with another row, each in a
|
|
23
|
-
* transaction of its own (the caller notifies the log after the commit): a model call with the
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
23
|
+
* transaction of its own (the caller notifies the log after the commit): a model call with the tool
|
|
24
|
+
* definitions it sent, a denial with its audit row, usage events with their owed `usage.report`
|
|
25
|
+
* deliveries (transactional outbox spec §8.1), and a sub-agent's usage counted on its parent run.
|
|
26
|
+
* The step functions (`model-call.ts`, `hook-calls.ts`) reach it through their deps.
|
|
27
27
|
*/
|
|
28
28
|
let StepLog = class StepLog {
|
|
29
29
|
kdb;
|
|
@@ -53,7 +53,8 @@ let StepLog = class StepLog {
|
|
|
53
53
|
* the whole tree counts on one run): `counter.budgetRunId`, the run's own window
|
|
54
54
|
* (`SubagentParent.budgetRunId`), else the parent run's ({@link budgetRunOf}). A usage event
|
|
55
55
|
* already in the log (an idempotent hit after a crash) adds nothing, so each call counts exactly
|
|
56
|
-
* once; the new usage events' `usage.report` deliveries are owed in the same transaction. Returns
|
|
56
|
+
* once; the new usage events' `usage.report` deliveries are owed in the same transaction. Returns
|
|
57
|
+
* what was written (hits included) and the highest new seq (null: nothing new).
|
|
57
58
|
*/
|
|
58
59
|
async appendCounted(conversationId, counter, events) {
|
|
59
60
|
const t = this.kdb.db;
|
|
@@ -121,6 +121,7 @@ export async function dispatchSubagent(d, a) {
|
|
|
121
121
|
parent_conversation_id: ctx.conversationId,
|
|
122
122
|
subagent_type: entry.type,
|
|
123
123
|
subagent_depth: childDepth,
|
|
124
|
+
source: "subagent",
|
|
124
125
|
})
|
|
125
126
|
.execute();
|
|
126
127
|
const [, task] = await d.eventLog.appendIn(childId, [
|
|
@@ -125,6 +125,9 @@ export async function advanceSkillForks(d, a) {
|
|
|
125
125
|
agentVersion: ctx.agentVersion,
|
|
126
126
|
agent: ctx.agent,
|
|
127
127
|
accountId: ctx.triggerAccountId,
|
|
128
|
+
source: ctx.source,
|
|
129
|
+
ownerAccountId: ctx.ownerAccountId,
|
|
130
|
+
role: ctx.role,
|
|
128
131
|
signal: ctx.signal,
|
|
129
132
|
...(ctx.limits.deadline !== undefined && { deadline: ctx.limits.deadline }),
|
|
130
133
|
...(ctx.conversationKind !== undefined &&
|
|
@@ -58,7 +58,9 @@ export declare function narrowPending(stored: readonly string[] | undefined, fai
|
|
|
58
58
|
* `model_usage` row and owes job `usage-model-<rowId>` with it; the payload
|
|
59
59
|
* (`purpose: "embedding"`, `eventId` = the row id, the calling `extension`, no conversation fields)
|
|
60
60
|
* is read from the row when the job runs, and the row's `reported_at` is set once nothing is left
|
|
61
|
-
* to deliver; a job that gives up after its last attempt sets `report_failed_at`.
|
|
61
|
+
* to deliver; a job that gives up after its last attempt sets `report_failed_at`. A row with
|
|
62
|
+
* `reported_at IS NULL` is not necessarily pending: no job is owed when no enabled extension
|
|
63
|
+
* subscribed to `usage.report` at the time of the call, and such a row is never reported.
|
|
62
64
|
*/
|
|
63
65
|
export declare class UsageReporter {
|
|
64
66
|
private readonly kdb;
|
|
@@ -113,7 +113,9 @@ export function narrowPending(stored, failed) {
|
|
|
113
113
|
* `model_usage` row and owes job `usage-model-<rowId>` with it; the payload
|
|
114
114
|
* (`purpose: "embedding"`, `eventId` = the row id, the calling `extension`, no conversation fields)
|
|
115
115
|
* is read from the row when the job runs, and the row's `reported_at` is set once nothing is left
|
|
116
|
-
* to deliver; a job that gives up after its last attempt sets `report_failed_at`.
|
|
116
|
+
* to deliver; a job that gives up after its last attempt sets `report_failed_at`. A row with
|
|
117
|
+
* `reported_at IS NULL` is not necessarily pending: no job is owed when no enabled extension
|
|
118
|
+
* subscribed to `usage.report` at the time of the call, and such a row is never reported.
|
|
117
119
|
*/
|
|
118
120
|
let UsageReporter = class UsageReporter {
|
|
119
121
|
kdb;
|
|
@@ -359,7 +361,7 @@ let UsageReporter = class UsageReporter {
|
|
|
359
361
|
/** One delivery job (the `@Processor` of {@link USAGE_REPORT_QUEUE} calls it). */
|
|
360
362
|
async process(j, token) {
|
|
361
363
|
const payload = await this.payloadOfJob(j.data);
|
|
362
|
-
// conversation
|
|
364
|
+
// conversation deleted meanwhile: nothing to report
|
|
363
365
|
if (payload === null)
|
|
364
366
|
return;
|
|
365
367
|
const subscribers = await this.subscribers(payload.orgId);
|
package/dist/runtime/waits.d.ts
CHANGED
|
@@ -230,6 +230,13 @@ export declare class Waits implements OnApplicationShutdown {
|
|
|
230
230
|
* background tasks of every covered sub-agent conversation stop, whether it was running or idle.
|
|
231
231
|
* Returns the number of runs asked to stop.
|
|
232
232
|
*
|
|
233
|
+
* `windowsUpTo` (a root run's id; only after that root run a person stopped ended,
|
|
234
|
+
* {@link stopDescendantsOfStopped}) narrows the stop to the runs of the budget windows opened up
|
|
235
|
+
* to that run (`runs.budget_run_id`, agent map spec §6): a run of a window a later root run
|
|
236
|
+
* opened (the person's next message, started meanwhile) is never stopped by the late call, and
|
|
237
|
+
* the background task sweep is left out (a stopped child's own end stops its tasks,
|
|
238
|
+
* {@link childFinished}).
|
|
239
|
+
*
|
|
233
240
|
* Lock order: nothing is held across runs. Each run's stop is its own transaction (or none), and
|
|
234
241
|
* one that writes the terminal state locks that run's conversation row, then its run row, the
|
|
235
242
|
* order of `StepLog.appendCounted` (conversation row, then the root's budget run row) and of
|
|
@@ -238,14 +245,17 @@ export declare class Waits implements OnApplicationShutdown {
|
|
|
238
245
|
*/
|
|
239
246
|
stopTree(conversationId: string, byAccountId: string | null, opts?: {
|
|
240
247
|
includeSelf?: boolean;
|
|
248
|
+
windowsUpTo?: string;
|
|
241
249
|
}): Promise<number>;
|
|
242
250
|
/**
|
|
243
|
-
* After a
|
|
244
|
-
*
|
|
245
|
-
*
|
|
246
|
-
*
|
|
247
|
-
*
|
|
248
|
-
*
|
|
251
|
+
* After a run's terminal commit (its worker): a run a person stopped (`run_aborted` with an
|
|
252
|
+
* account and no `detail`; {@link stopTree} or a person's stop of the root) stops the active runs
|
|
253
|
+
* below its conversation as well, so a descendant it dispatched after the tree stop read the tree
|
|
254
|
+
* still stops (dispatch needs its run `running`, so after its terminal commit the set is final).
|
|
255
|
+
* A root's run stops only the runs of the budget windows up to it (`windowsUpTo`): the person's
|
|
256
|
+
* next message may already have started a new run with sub-agents of its own (agent map spec §6,
|
|
257
|
+
* changed in acceptance 2026-11-13). Any other end leaves its children running (§5.4). Returns
|
|
258
|
+
* the number of runs asked to stop.
|
|
249
259
|
*/
|
|
250
260
|
stopDescendantsOfStopped(runId: string): Promise<number>;
|
|
251
261
|
/**
|
package/dist/runtime/waits.js
CHANGED
|
@@ -607,6 +607,13 @@ let Waits = class Waits {
|
|
|
607
607
|
* background tasks of every covered sub-agent conversation stop, whether it was running or idle.
|
|
608
608
|
* Returns the number of runs asked to stop.
|
|
609
609
|
*
|
|
610
|
+
* `windowsUpTo` (a root run's id; only after that root run a person stopped ended,
|
|
611
|
+
* {@link stopDescendantsOfStopped}) narrows the stop to the runs of the budget windows opened up
|
|
612
|
+
* to that run (`runs.budget_run_id`, agent map spec §6): a run of a window a later root run
|
|
613
|
+
* opened (the person's next message, started meanwhile) is never stopped by the late call, and
|
|
614
|
+
* the background task sweep is left out (a stopped child's own end stops its tasks,
|
|
615
|
+
* {@link childFinished}).
|
|
616
|
+
*
|
|
610
617
|
* Lock order: nothing is held across runs. Each run's stop is its own transaction (or none), and
|
|
611
618
|
* one that writes the terminal state locks that run's conversation row, then its run row, the
|
|
612
619
|
* order of `StepLog.appendCounted` (conversation row, then the root's budget run row) and of
|
|
@@ -619,12 +626,22 @@ let Waits = class Waits {
|
|
|
619
626
|
if (ids.length === 0)
|
|
620
627
|
return 0;
|
|
621
628
|
const level = new Map(tree.map((c) => [c.id, c.level]));
|
|
622
|
-
|
|
629
|
+
let q = this.db
|
|
623
630
|
.selectFrom("runs")
|
|
624
631
|
.select(["id", "conversation_id", "status"])
|
|
625
632
|
.where("conversation_id", "in", ids)
|
|
626
|
-
.where("status", "in", ACTIVE_RUN_STATUSES)
|
|
627
|
-
|
|
633
|
+
.where("status", "in", ACTIVE_RUN_STATUSES);
|
|
634
|
+
const upTo = opts.windowsUpTo;
|
|
635
|
+
if (upTo !== undefined) {
|
|
636
|
+
// a window is opened by a root run without a budget run of its own (`budgetWindowOf`)
|
|
637
|
+
q = q.where("budget_run_id", "in", (eb) => eb
|
|
638
|
+
.selectFrom("runs as w")
|
|
639
|
+
.select("w.id")
|
|
640
|
+
.where("w.conversation_id", "=", conversationId)
|
|
641
|
+
.where("w.budget_run_id", "is", null)
|
|
642
|
+
.where("w.created_at", "<=", (eb2) => eb2.selectFrom("runs as s").select("s.created_at").where("s.id", "=", upTo)));
|
|
643
|
+
}
|
|
644
|
+
const runs = (await q.execute()).sort((a, b) => (level.get(a.conversation_id) ?? 0) - (level.get(b.conversation_id) ?? 0));
|
|
628
645
|
for (const r of runs) {
|
|
629
646
|
try {
|
|
630
647
|
// a stopped wait's card updates are owed by its transaction's appends (outbox spec §8.1)
|
|
@@ -639,34 +656,39 @@ let Waits = class Waits {
|
|
|
639
656
|
// all of each conversation's tasks, as a stopped child's run end does (`childFinished`), with
|
|
640
657
|
// which this is idempotent (only running tasks are stopped; `stoppedBy: "subagent_end"`
|
|
641
658
|
// never wakes). A root conversation is skipped (`stopSubagentTasks`); never throws.
|
|
642
|
-
|
|
659
|
+
if (upTo === undefined) {
|
|
660
|
+
await Promise.all(ids.map((id) => this.background.stopSubagentTasks(id)));
|
|
661
|
+
}
|
|
643
662
|
return runs.length;
|
|
644
663
|
}
|
|
645
664
|
/**
|
|
646
|
-
* After a
|
|
647
|
-
*
|
|
648
|
-
*
|
|
649
|
-
*
|
|
650
|
-
*
|
|
651
|
-
*
|
|
665
|
+
* After a run's terminal commit (its worker): a run a person stopped (`run_aborted` with an
|
|
666
|
+
* account and no `detail`; {@link stopTree} or a person's stop of the root) stops the active runs
|
|
667
|
+
* below its conversation as well, so a descendant it dispatched after the tree stop read the tree
|
|
668
|
+
* still stops (dispatch needs its run `running`, so after its terminal commit the set is final).
|
|
669
|
+
* A root's run stops only the runs of the budget windows up to it (`windowsUpTo`): the person's
|
|
670
|
+
* next message may already have started a new run with sub-agents of its own (agent map spec §6,
|
|
671
|
+
* changed in acceptance 2026-11-13). Any other end leaves its children running (§5.4). Returns
|
|
672
|
+
* the number of runs asked to stop.
|
|
652
673
|
*/
|
|
653
674
|
async stopDescendantsOfStopped(runId) {
|
|
654
675
|
const ev = await this.db
|
|
655
676
|
.selectFrom("runs as r")
|
|
656
677
|
.innerJoin("conversation_events as e", (j) => j.onRef("e.conversation_id", "=", "r.conversation_id").onRef("e.run_id", "=", "r.id"))
|
|
657
678
|
.innerJoin("conversations as c", "c.id", "r.conversation_id")
|
|
658
|
-
.select(["r.conversation_id", "e.payload"])
|
|
679
|
+
.select(["r.conversation_id", "c.kind", "e.payload"])
|
|
659
680
|
.where("r.id", "=", runId)
|
|
660
|
-
|
|
661
|
-
// reaching here (a redelivered job, the failure path) stops nothing
|
|
662
|
-
.where("c.kind", "=", "subagent")
|
|
681
|
+
.where("c.kind", "in", ["chat", "subagent"])
|
|
663
682
|
.where("r.status", "=", "aborted")
|
|
664
683
|
.where("e.type", "=", "run_aborted")
|
|
665
684
|
.executeTakeFirst();
|
|
666
685
|
const p = ev?.payload;
|
|
667
686
|
if (!ev || typeof p?.byAccountId !== "string" || p.detail !== undefined)
|
|
668
687
|
return 0;
|
|
669
|
-
return this.stopTree(ev.conversation_id, p.byAccountId, {
|
|
688
|
+
return this.stopTree(ev.conversation_id, p.byAccountId, {
|
|
689
|
+
includeSelf: false,
|
|
690
|
+
...(ev.kind === "chat" && { windowsUpTo: runId }),
|
|
691
|
+
});
|
|
670
692
|
}
|
|
671
693
|
/**
|
|
672
694
|
* Stops one active sub-agent run: a `waiting` one through its own {@link abortWaiting}, a
|
package/dist/sandbox/jobs.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Job, Queue } from "bullmq";
|
|
2
2
|
import { LockService } from "../infra/lock/lock.service.js";
|
|
3
|
+
import type { OutboxService } from "../infra/outbox/outbox.service.js";
|
|
3
4
|
import type { KernelLogger } from "../log.js";
|
|
4
5
|
import { SandboxService } from "./sandbox.service.js";
|
|
5
6
|
/** The idle suspension sweep (every minute, once per tick across replicas). */
|
|
@@ -7,10 +8,25 @@ export declare const SANDBOX_SUSPEND_JOB = "sandbox-suspend-idle";
|
|
|
7
8
|
/** The inactivity destruction sweep (hourly, once per tick across replicas). */
|
|
8
9
|
export declare const SANDBOX_DESTROY_JOB = "sandbox-destroy-inactive";
|
|
9
10
|
/**
|
|
10
|
-
* One-off job: destroy
|
|
11
|
-
*
|
|
11
|
+
* One-off job: destroy devices now, instance and file system (data `{ deviceIds }`). Owed through
|
|
12
|
+
* the outbox by the transaction that soft-deletes the device (a device's deletion, offboarding),
|
|
13
|
+
* one device per job under {@link deviceDestroyJobId} ({@link oweDeviceDestroy}).
|
|
12
14
|
*/
|
|
13
15
|
export declare const SANDBOX_DESTROY_FOR_JOB = "sandbox-destroy-for";
|
|
16
|
+
/** The data of a {@link SANDBOX_DESTROY_FOR_JOB} job. */
|
|
17
|
+
export interface SandboxDestroyForData {
|
|
18
|
+
deviceIds: string[];
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The deterministic id of a deleted device's destroy job (no `:`): a device is deleted once, so
|
|
22
|
+
* its destruction is owed once.
|
|
23
|
+
*/
|
|
24
|
+
export declare const deviceDestroyJobId: (deviceId: string) => string;
|
|
25
|
+
/**
|
|
26
|
+
* Owes the destruction of a device in the caller's transaction, the one that soft-deleted it
|
|
27
|
+
* (transactional outbox spec §8.1; throws outside a transaction).
|
|
28
|
+
*/
|
|
29
|
+
export declare function oweDeviceDestroy(outbox: Pick<OutboxService, "add">, deviceId: string, orgId: string): Promise<boolean>;
|
|
14
30
|
/**
|
|
15
31
|
* One-off job: start a device over with empty files (`resetFileSystem`; data
|
|
16
32
|
* `{ deviceId }`, job id {@link resetFilesJobId}).
|
|
@@ -22,18 +38,12 @@ export declare const SANDBOX_RESET_FILES_JOB = "sandbox-reset-files";
|
|
|
22
38
|
*/
|
|
23
39
|
export declare const resetFilesJobId: (deviceId: string) => string;
|
|
24
40
|
/**
|
|
25
|
-
* The producer of
|
|
26
|
-
*
|
|
27
|
-
* {@link SANDBOX_RESET_FILES_JOB}; `MaintenanceProcessor` consumes them.
|
|
41
|
+
* The producer of {@link SANDBOX_RESET_FILES_JOB} (destroys are owed through the outbox,
|
|
42
|
+
* {@link oweDeviceDestroy}); `MaintenanceProcessor` consumes both.
|
|
28
43
|
*/
|
|
29
44
|
export declare class SandboxJobs {
|
|
30
45
|
private readonly queue;
|
|
31
46
|
constructor(queue: Pick<Queue, "add">);
|
|
32
|
-
/**
|
|
33
|
-
* Enqueues the destruction of these devices: a failed batch is retried whole (destroying
|
|
34
|
-
* one already destroyed does nothing).
|
|
35
|
-
*/
|
|
36
|
-
destroy(deviceIds: string[]): Promise<void>;
|
|
37
47
|
/**
|
|
38
48
|
* Enqueues the reset of a device's files (see {@link resetFilesJobId}): retried on failure
|
|
39
49
|
* (the exclusive lease not obtained in time, the provider failing); a job that failed all its
|