@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
|
@@ -34,12 +34,10 @@ const ADMIN_CONSOLE_PENDING = "Review it in the admin console (plugins, pending
|
|
|
34
34
|
const requestName = (r) => r.item?.name ?? r.entry?.name ?? r.agent?.name ?? r.kind;
|
|
35
35
|
const requester = (r) => r.requestedByName ?? "a deleted account";
|
|
36
36
|
/** The name an org notice is about. */
|
|
37
|
-
const orgNoticeName = (n) => n.
|
|
37
|
+
const orgNoticeName = (n) => n.detail.name;
|
|
38
38
|
/** An org notice in one English sentence, as the inbox tells it. */
|
|
39
39
|
function orgNoticeSummary(n) {
|
|
40
40
|
switch (n.kind) {
|
|
41
|
-
case "skill_renamed":
|
|
42
|
-
return `Skill "${n.skillRenamed.from}" was renamed to "${n.skillRenamed.to}": a platform skill on in this org has its name; agents keep it.`;
|
|
43
41
|
case "item_shared": {
|
|
44
42
|
const d = n.detail;
|
|
45
43
|
return `${d.ref.kind} ${d.name} was shared with ${d.org === "all_orgs" ? "every org" : d.org.name}.`;
|
|
@@ -182,18 +180,6 @@ function platformRow(i) {
|
|
|
182
180
|
next: "Open the plugin in the admin console to see the error and retry.",
|
|
183
181
|
};
|
|
184
182
|
}
|
|
185
|
-
case "platform_migration": {
|
|
186
|
-
const r = i.run;
|
|
187
|
-
const failed = r.itemsFailed.slice(0, 5).join(", ");
|
|
188
|
-
return {
|
|
189
|
-
...base,
|
|
190
|
-
name: "platform components migration",
|
|
191
|
-
state: r.stuck ? "stuck" : "failed",
|
|
192
|
-
summary: r.stuck
|
|
193
|
-
? "The platform components migration run has not finished for over an hour; its worker stopped. The next run starts at the next worker start or day."
|
|
194
|
-
: `The platform components migration could not write ${r.itemsFailed.length} catalog item(s): ${failed}${r.itemsFailed.length > 5 ? ", …" : ""}. The next run tries them again.`,
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
183
|
case "ownership_transferred":
|
|
198
184
|
return {
|
|
199
185
|
...base,
|
|
@@ -243,9 +229,7 @@ let InboxOperations = class InboxOperations {
|
|
|
243
229
|
if (row === undefined)
|
|
244
230
|
return null;
|
|
245
231
|
const base = { id: row.id, createdAt: row.created_at.toISOString() };
|
|
246
|
-
return
|
|
247
|
-
? { ...base, kind: row.kind, skillRenamed: row.detail }
|
|
248
|
-
: { ...base, kind: row.kind, detail: row.detail });
|
|
232
|
+
return { ...base, kind: row.kind, detail: row.detail };
|
|
249
233
|
}
|
|
250
234
|
/** A platform notice by id (dismissed or not), or null. */
|
|
251
235
|
async platformNotice(id) {
|
|
@@ -292,7 +276,7 @@ let InboxOperations = class InboxOperations {
|
|
|
292
276
|
}),
|
|
293
277
|
defineOperation({
|
|
294
278
|
id: "inbox.dismiss-notice",
|
|
295
|
-
description: "Dismiss an org notice from the inbox for every org admin (org admins): the id of a notice row from inbox.list (types
|
|
279
|
+
description: "Dismiss an org notice from the inbox for every org admin (org admins): the id of a notice row from inbox.list (types item_shared, shared_item_updated, upload_confirmed, upload_rejected). Approvals and requests are not dismissed here: they are decided on the web.",
|
|
296
280
|
input: NoticeInput,
|
|
297
281
|
example: NOTICE_EXAMPLE,
|
|
298
282
|
authz: { orgAction: "catalog.decide" },
|
|
@@ -323,7 +307,7 @@ let InboxOperations = class InboxOperations {
|
|
|
323
307
|
}),
|
|
324
308
|
defineOperation({
|
|
325
309
|
id: "inbox.platform-list",
|
|
326
|
-
description: "List the platform inbox (super admin), newest first, one compact row each (type, what, state, summary, next step): orgs' requests to the platform, plugins waiting for a permission confirmation, this org's uploads waiting for you, failed scope syncs
|
|
310
|
+
description: "List the platform inbox (super admin), newest first, one compact row each (type, what, state, summary, next step): orgs' requests to the platform, plugins waiting for a permission confirmation, this org's uploads waiting for you, failed scope syncs and ownership notices. Only the newest 100 items are read (truncated: more wait); listed is how many of them are shown, otherOrgItems how many are items of plugins other orgs own (only counted). total counts everything waiting on the platform, other orgs' items included, as the admin console does; byType splits total by type (everything waiting, not the listed page). Nothing is decided here: requests and confirmations are done in the admin console. Pages by 50: pass the returned next as params.cursor.",
|
|
327
311
|
input: z.object({ cursor: Cursor.optional() }).strict(),
|
|
328
312
|
example: {},
|
|
329
313
|
authz: { superAdmin: true },
|
|
@@ -32,6 +32,7 @@ export declare class PlatformOperationsProvider implements OnModuleInit {
|
|
|
32
32
|
* default-on spec §8): every tool the org offers that the definition does not turn off, builtins
|
|
33
33
|
* by name, extension tools as `plugin__<plugin>__<tool>`, connector tools as
|
|
34
34
|
* `mcp__<slug>__<tool>`; the ones that cannot load now are listed with the registry's reason.
|
|
35
|
+
* Listed for the source of the conversation the operation runs in (memory plugin spec §6.2).
|
|
35
36
|
*/
|
|
36
37
|
private toolKeys;
|
|
37
38
|
onModuleInit(): void;
|
|
@@ -10,6 +10,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
|
10
10
|
import { Inject, Injectable } from "@nestjs/common";
|
|
11
11
|
import { z } from "zod";
|
|
12
12
|
import { AgentsService } from "../../agents/agents.service.js";
|
|
13
|
+
import { conversationSourceOf } from "../../conversations/source.js";
|
|
13
14
|
import { AccountsService } from "../../identity/accounts.js";
|
|
14
15
|
import { PreferencesSchema } from "../../identity/me.schemas.js";
|
|
15
16
|
import { KernelDb } from "../../infra/database/kernel-db.js";
|
|
@@ -81,12 +82,14 @@ let PlatformOperationsProvider = class PlatformOperationsProvider {
|
|
|
81
82
|
* default-on spec §8): every tool the org offers that the definition does not turn off, builtins
|
|
82
83
|
* by name, extension tools as `plugin__<plugin>__<tool>`, connector tools as
|
|
83
84
|
* `mcp__<slug>__<tool>`; the ones that cannot load now are listed with the registry's reason.
|
|
85
|
+
* Listed for the source of the conversation the operation runs in (memory plugin spec §6.2).
|
|
84
86
|
*/
|
|
85
|
-
async toolKeys(
|
|
86
|
-
const resolved = await this.tools.forAgent(orgId, {
|
|
87
|
+
async toolKeys(ctx, d) {
|
|
88
|
+
const resolved = await this.tools.forAgent(ctx.orgId, {
|
|
87
89
|
disabledTools: d.disabledTools ?? [],
|
|
88
90
|
subagentTools: d.subagentTools,
|
|
89
91
|
subagents: d.subagents,
|
|
92
|
+
source: await conversationSourceOf(this.db.db, ctx.conversationId),
|
|
90
93
|
});
|
|
91
94
|
return {
|
|
92
95
|
keys: [...new Set(resolved.tools.map((t) => t.key))],
|
|
@@ -142,7 +145,7 @@ let PlatformOperationsProvider = class PlatformOperationsProvider {
|
|
|
142
145
|
const agent = await this.agents.get(ctx.orgId, ctx.agentId, ctx.actorAccountId);
|
|
143
146
|
const d = agent.definition;
|
|
144
147
|
const [{ keys: toolKeys, unavailable }, skills] = await Promise.all([
|
|
145
|
-
this.toolKeys(ctx
|
|
148
|
+
this.toolKeys(ctx, d),
|
|
146
149
|
this.skillNames(ctx, d),
|
|
147
150
|
]);
|
|
148
151
|
const sandbox = toolKeys.some((k) => SANDBOX_TOOL_NAMES.includes(k));
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { PluginScopeSyncProcessor } from "../catalog/scope-sync.js";
|
|
1
|
+
import { PluginScopeSyncProcessor } from "../catalog/scope-sync-workers.js";
|
|
2
2
|
import { ChannelsProcessor } from "../channels/channels.module.js";
|
|
3
3
|
import { ChannelOutbound } from "../channels/outbound.js";
|
|
4
4
|
import { TitleQueue } from "../conversations/titles.js";
|
|
5
5
|
import { EvalsProcessor } from "../evals/evals.module.js";
|
|
6
6
|
import { EvalWorker } from "../evals/worker.js";
|
|
7
|
+
import { LifecycleHooksProcessor } from "../extensions/lifecycle-hooks.js";
|
|
7
8
|
import { KernelConfigService } from "../infra/config/kernel-config.service.js";
|
|
8
9
|
import { LockedIntervalScheduler } from "../infra/schedule/locked-interval.js";
|
|
9
10
|
import { ScheduledRuns } from "../infra/schedule/scheduled-runs.js";
|
|
@@ -15,8 +16,8 @@ import { UsageReporter } from "../runtime/usage-report.js";
|
|
|
15
16
|
/**
|
|
16
17
|
* The worker role's parts, driven by `KernelLifecycle` at fixed points of its start order and stop
|
|
17
18
|
* phases (so they stay ordered after the extension load and before the extension host closes): the
|
|
18
|
-
* queue consumers (runs, sub-agent runs, evals, maintenance, the plugin scope sync,
|
|
19
|
-
* deliveries, `usage.report`, titles) and the schedulers of the `@LockedCron` / `@LockedInterval` jobs. Provided only by
|
|
19
|
+
* queue consumers (runs, sub-agent runs, evals, maintenance, the plugin scope sync, lifecycle
|
|
20
|
+
* hook deliveries, channel deliveries, `usage.report`, titles) and the schedulers of the `@LockedCron` / `@LockedInterval` jobs. Provided only by
|
|
20
21
|
* `WorkerModule`, so a process without the worker role has none of them.
|
|
21
22
|
*/
|
|
22
23
|
export declare class WorkerRole {
|
|
@@ -36,13 +37,14 @@ export declare class WorkerRole {
|
|
|
36
37
|
private readonly evalsProcessor;
|
|
37
38
|
private readonly maintenanceProcessor;
|
|
38
39
|
private readonly scopeProcessor;
|
|
40
|
+
private readonly lifecycleProcessor;
|
|
39
41
|
/** The startup config (the roles). */
|
|
40
42
|
private readonly config;
|
|
41
43
|
/** The run worker was started: the runs phase drains it. */
|
|
42
44
|
private runsStarted;
|
|
43
45
|
/** The eval worker was started: the runs phase drains it. */
|
|
44
46
|
private evalsStarted;
|
|
45
|
-
constructor(config: KernelConfigService, log: KernelLogger, intervals: LockedIntervalScheduler, scheduledRuns: ScheduledRuns, runWorker: RunWorker, evalWorker: EvalWorker, usageReport: UsageReporter, titles: TitleQueue, channelOutbound: ChannelOutbound, runsProcessor: RunsProcessor, subagentRunsProcessor: SubagentRunsProcessor, usageProcessor: UsageReportProcessor, titleProcessor: TitleProcessor, channelsProcessor: ChannelsProcessor, evalsProcessor: EvalsProcessor, maintenanceProcessor: MaintenanceProcessor, scopeProcessor: PluginScopeSyncProcessor);
|
|
47
|
+
constructor(config: KernelConfigService, log: KernelLogger, intervals: LockedIntervalScheduler, scheduledRuns: ScheduledRuns, runWorker: RunWorker, evalWorker: EvalWorker, usageReport: UsageReporter, titles: TitleQueue, channelOutbound: ChannelOutbound, runsProcessor: RunsProcessor, subagentRunsProcessor: SubagentRunsProcessor, usageProcessor: UsageReportProcessor, titleProcessor: TitleProcessor, channelsProcessor: ChannelsProcessor, evalsProcessor: EvalsProcessor, maintenanceProcessor: MaintenanceProcessor, scopeProcessor: PluginScopeSyncProcessor, lifecycleProcessor: LifecycleHooksProcessor);
|
|
46
48
|
/**
|
|
47
49
|
* Starts the queue consumers (after the extension load), then opens the schedules' start gate:
|
|
48
50
|
* the cron and interval timers were armed at application bootstrap, but every tick before this
|
|
@@ -60,6 +62,9 @@ export declare class WorkerRole {
|
|
|
60
62
|
drainRuns(budgetMs: number): Promise<void> | undefined;
|
|
61
63
|
/** The eval cases in progress stop (undefined: never started). */
|
|
62
64
|
drainEvals(budgetMs: number): Promise<void> | undefined;
|
|
63
|
-
/**
|
|
65
|
+
/**
|
|
66
|
+
* Closes the maintenance queue's worker, the plugin scope sync's (it stops between pairs) and
|
|
67
|
+
* the lifecycle hook deliveries' (calls in flight are aborted and retried).
|
|
68
|
+
*/
|
|
64
69
|
closeMaintenance(): Promise<void>;
|
|
65
70
|
}
|
|
@@ -8,12 +8,13 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
|
8
8
|
return function (target, key) { decorator(target, key, paramIndex); }
|
|
9
9
|
};
|
|
10
10
|
import { Inject, Injectable } from "@nestjs/common";
|
|
11
|
-
import { PluginScopeSyncProcessor } from "../catalog/scope-sync.js";
|
|
11
|
+
import { PluginScopeSyncProcessor } from "../catalog/scope-sync-workers.js";
|
|
12
12
|
import { ChannelsProcessor } from "../channels/channels.module.js";
|
|
13
13
|
import { ChannelOutbound } from "../channels/outbound.js";
|
|
14
14
|
import { TitleQueue } from "../conversations/titles.js";
|
|
15
15
|
import { EvalsProcessor } from "../evals/evals.module.js";
|
|
16
16
|
import { EvalWorker } from "../evals/worker.js";
|
|
17
|
+
import { LifecycleHooksProcessor } from "../extensions/lifecycle-hooks.js";
|
|
17
18
|
import { KernelConfigService } from "../infra/config/kernel-config.service.js";
|
|
18
19
|
import { LockedIntervalScheduler } from "../infra/schedule/locked-interval.js";
|
|
19
20
|
import { ScheduledRuns } from "../infra/schedule/scheduled-runs.js";
|
|
@@ -25,8 +26,8 @@ import { UsageReporter } from "../runtime/usage-report.js";
|
|
|
25
26
|
/**
|
|
26
27
|
* The worker role's parts, driven by `KernelLifecycle` at fixed points of its start order and stop
|
|
27
28
|
* phases (so they stay ordered after the extension load and before the extension host closes): the
|
|
28
|
-
* queue consumers (runs, sub-agent runs, evals, maintenance, the plugin scope sync,
|
|
29
|
-
* deliveries, `usage.report`, titles) and the schedulers of the `@LockedCron` / `@LockedInterval` jobs. Provided only by
|
|
29
|
+
* queue consumers (runs, sub-agent runs, evals, maintenance, the plugin scope sync, lifecycle
|
|
30
|
+
* hook deliveries, channel deliveries, `usage.report`, titles) and the schedulers of the `@LockedCron` / `@LockedInterval` jobs. Provided only by
|
|
30
31
|
* `WorkerModule`, so a process without the worker role has none of them.
|
|
31
32
|
*/
|
|
32
33
|
let WorkerRole = class WorkerRole {
|
|
@@ -46,13 +47,14 @@ let WorkerRole = class WorkerRole {
|
|
|
46
47
|
evalsProcessor;
|
|
47
48
|
maintenanceProcessor;
|
|
48
49
|
scopeProcessor;
|
|
50
|
+
lifecycleProcessor;
|
|
49
51
|
/** The startup config (the roles). */
|
|
50
52
|
config;
|
|
51
53
|
/** The run worker was started: the runs phase drains it. */
|
|
52
54
|
runsStarted = false;
|
|
53
55
|
/** The eval worker was started: the runs phase drains it. */
|
|
54
56
|
evalsStarted = false;
|
|
55
|
-
constructor(config, log, intervals, scheduledRuns, runWorker, evalWorker, usageReport, titles, channelOutbound, runsProcessor, subagentRunsProcessor, usageProcessor, titleProcessor, channelsProcessor, evalsProcessor, maintenanceProcessor, scopeProcessor) {
|
|
57
|
+
constructor(config, log, intervals, scheduledRuns, runWorker, evalWorker, usageReport, titles, channelOutbound, runsProcessor, subagentRunsProcessor, usageProcessor, titleProcessor, channelsProcessor, evalsProcessor, maintenanceProcessor, scopeProcessor, lifecycleProcessor) {
|
|
56
58
|
this.log = log;
|
|
57
59
|
this.intervals = intervals;
|
|
58
60
|
this.scheduledRuns = scheduledRuns;
|
|
@@ -69,6 +71,7 @@ let WorkerRole = class WorkerRole {
|
|
|
69
71
|
this.evalsProcessor = evalsProcessor;
|
|
70
72
|
this.maintenanceProcessor = maintenanceProcessor;
|
|
71
73
|
this.scopeProcessor = scopeProcessor;
|
|
74
|
+
this.lifecycleProcessor = lifecycleProcessor;
|
|
72
75
|
this.config = config.get();
|
|
73
76
|
}
|
|
74
77
|
/**
|
|
@@ -96,6 +99,7 @@ let WorkerRole = class WorkerRole {
|
|
|
96
99
|
checkpoint();
|
|
97
100
|
this.maintenanceProcessor.start();
|
|
98
101
|
this.scopeProcessor.start();
|
|
102
|
+
this.lifecycleProcessor.start();
|
|
99
103
|
this.channelOutbound.startWorker(this.channelsProcessor.worker);
|
|
100
104
|
this.usageReport.startWorker(this.usageProcessor.worker);
|
|
101
105
|
this.titles.startWorker(this.titleProcessor.worker);
|
|
@@ -105,8 +109,6 @@ let WorkerRole = class WorkerRole {
|
|
|
105
109
|
// The gate opens: from here on ticks run; the runOnStart jobs run once now, in the background
|
|
106
110
|
this.scheduledRuns.open();
|
|
107
111
|
this.intervals.runStartJobs();
|
|
108
|
-
// Every plugin's scope converges once per worker start (a failed or lost sync is retried)
|
|
109
|
-
this.scopeProcessor.resync();
|
|
110
112
|
}
|
|
111
113
|
/**
|
|
112
114
|
* Closes the start gate and stops the schedulers (no new tick; the runs in progress get the stop
|
|
@@ -123,9 +125,16 @@ let WorkerRole = class WorkerRole {
|
|
|
123
125
|
drainEvals(budgetMs) {
|
|
124
126
|
return this.evalsStarted ? this.evalWorker.close(budgetMs) : undefined;
|
|
125
127
|
}
|
|
126
|
-
/**
|
|
128
|
+
/**
|
|
129
|
+
* Closes the maintenance queue's worker, the plugin scope sync's (it stops between pairs) and
|
|
130
|
+
* the lifecycle hook deliveries' (calls in flight are aborted and retried).
|
|
131
|
+
*/
|
|
127
132
|
async closeMaintenance() {
|
|
128
|
-
await Promise.all([
|
|
133
|
+
await Promise.all([
|
|
134
|
+
this.maintenanceProcessor.worker.close(),
|
|
135
|
+
this.scopeProcessor.close(),
|
|
136
|
+
this.lifecycleProcessor.close(),
|
|
137
|
+
]);
|
|
129
138
|
}
|
|
130
139
|
};
|
|
131
140
|
WorkerRole = __decorate([
|
|
@@ -146,6 +155,7 @@ WorkerRole = __decorate([
|
|
|
146
155
|
__param(13, Inject(ChannelsProcessor)),
|
|
147
156
|
__param(14, Inject(EvalsProcessor)),
|
|
148
157
|
__param(15, Inject(MaintenanceProcessor)),
|
|
149
|
-
__param(16, Inject(PluginScopeSyncProcessor))
|
|
158
|
+
__param(16, Inject(PluginScopeSyncProcessor)),
|
|
159
|
+
__param(17, Inject(LifecycleHooksProcessor))
|
|
150
160
|
], WorkerRole);
|
|
151
161
|
export { WorkerRole };
|
|
@@ -10,7 +10,6 @@ import { BackgroundModule } from "../background/background.module.js";
|
|
|
10
10
|
import { BackgroundTaskPollJob } from "../background/poll.job.js";
|
|
11
11
|
import { CatalogModule } from "../catalog/catalog.module.js";
|
|
12
12
|
import { CatalogJobs } from "../catalog/jobs.js";
|
|
13
|
-
import { PlatformMigration } from "../catalog/platform-migration.js";
|
|
14
13
|
import { PluginScopeWorkersModule } from "../catalog/scope-sync-workers.js";
|
|
15
14
|
import { ChannelsModule, ChannelWorkersModule } from "../channels/channels.module.js";
|
|
16
15
|
import { GatewayCheckJob } from "../channels/supervisor.js";
|
|
@@ -24,11 +23,11 @@ import { LegacySandboxCleanupJob } from "../devices/legacy-cleanup.job.js";
|
|
|
24
23
|
import { EvalWorkersModule } from "../evals/evals.module.js";
|
|
25
24
|
import { ExtensionsModule } from "../extensions/extensions.module.js";
|
|
26
25
|
import { PluginsReconcileJob } from "../extensions/lifecycle.js";
|
|
26
|
+
import { LifecycleHookWorkersModule } from "../extensions/lifecycle-hooks.js";
|
|
27
27
|
import { InitialPasswordCleanupJob } from "../identity/initial-passwords.job.js";
|
|
28
28
|
import { OutboxRelayModule } from "../infra/outbox/outbox-relay.module.js";
|
|
29
29
|
import { KernelScheduleModule } from "../infra/schedule/schedule.module.js";
|
|
30
30
|
import { DirectoryScanJob } from "../orgs/directory-sync.js";
|
|
31
|
-
import { OffboardSweepJob } from "../orgs/offboarding.js";
|
|
32
31
|
import { OrgLockModule } from "../orgs/org-lock.js";
|
|
33
32
|
import { OrgsModule } from "../orgs/orgs.module.js";
|
|
34
33
|
import { MaintenanceModule } from "../runtime/maintenance.js";
|
|
@@ -69,6 +68,7 @@ let WorkerModule = WorkerModule_1 = class WorkerModule {
|
|
|
69
68
|
ChannelWorkersModule.forRoot(config),
|
|
70
69
|
MaintenanceModule,
|
|
71
70
|
PluginScopeWorkersModule,
|
|
71
|
+
LifecycleHookWorkersModule,
|
|
72
72
|
// the domains the fixed-period jobs call
|
|
73
73
|
RuntimeModule,
|
|
74
74
|
RunJobsModule,
|
|
@@ -100,9 +100,7 @@ let WorkerModule = WorkerModule_1 = class WorkerModule {
|
|
|
100
100
|
InitialPasswordCleanupJob,
|
|
101
101
|
ClientReportsCron,
|
|
102
102
|
CatalogJobs,
|
|
103
|
-
PlatformMigration,
|
|
104
103
|
DirectoryScanJob,
|
|
105
|
-
OffboardSweepJob,
|
|
106
104
|
PluginsReconcileJob,
|
|
107
105
|
GatewayCheckJob,
|
|
108
106
|
WorkerRole,
|
|
@@ -237,6 +237,9 @@ export declare function summaryText(summary: string): string;
|
|
|
237
237
|
* summary: the latest one follows the summary) /
|
|
238
238
|
* `run_context` (`role: "system"` at its position, `runContextText`; covered by the summary: the
|
|
239
239
|
* current context follows the summary whole; a user message's sender line by `senderLines`) /
|
|
240
|
+
* `plugin_context` (`role: "system"` at its position, `pluginContextText`, nothing for empty
|
|
241
|
+
* text; covered by the summary: the latest non-empty one per plugin, in plugin-name order,
|
|
242
|
+
* follows the summary after `device_paths`) /
|
|
240
243
|
* `background_task_finished`(`role: "system"`, the text of
|
|
241
244
|
* {@link renderBackgroundFinished}, placed like a user_message)/ `subagent_delivery` (likewise,
|
|
242
245
|
* the text of {@link renderSubagentDelivery});`tool_result` 事件还原为紧随其 assistant 消息之后的一条 user 消息
|
|
@@ -6,6 +6,7 @@ import { catalogText, isAlwaysLoaded } from "../../tools/builtins/tool-search.js
|
|
|
6
6
|
import { renderSubagentDelivery } from "../subagents/render.js";
|
|
7
7
|
import { devicePathsAt, devicePathsText } from "./device-paths.js";
|
|
8
8
|
import { LEGACY_CATALOG_HEADER, LEGACY_SKILL_TEXTS, usesLegacyToolNames } from "./legacy-text.js";
|
|
9
|
+
import { pluginContextsAfterSummary, pluginContextText } from "./plugin-context.js";
|
|
9
10
|
import { currentRunContext, runContextEvents, runContextFullText, runContextText, scheduleHeader, senderLines, } from "./run-context.js";
|
|
10
11
|
import { invokedSkillNames, invokedSkillsText, SKILL_TEXTS, skillsCatalogEvents, snapshotChangeText, snapshotCurrentText, snapshotSystemText, systemSkillsCatalog, } from "./skills.js";
|
|
11
12
|
import { toolsChangedText } from "./tool-changes.js";
|
|
@@ -341,6 +342,9 @@ export function summaryText(summary) {
|
|
|
341
342
|
* summary: the latest one follows the summary) /
|
|
342
343
|
* `run_context` (`role: "system"` at its position, `runContextText`; covered by the summary: the
|
|
343
344
|
* current context follows the summary whole; a user message's sender line by `senderLines`) /
|
|
345
|
+
* `plugin_context` (`role: "system"` at its position, `pluginContextText`, nothing for empty
|
|
346
|
+
* text; covered by the summary: the latest non-empty one per plugin, in plugin-name order,
|
|
347
|
+
* follows the summary after `device_paths`) /
|
|
344
348
|
* `background_task_finished`(`role: "system"`, the text of
|
|
345
349
|
* {@link renderBackgroundFinished}, placed like a user_message)/ `subagent_delivery` (likewise,
|
|
346
350
|
* the text of {@link renderSubagentDelivery});`tool_result` 事件还原为紧随其 assistant 消息之后的一条 user 消息
|
|
@@ -561,6 +565,10 @@ export function buildModelRequest(input) {
|
|
|
561
565
|
if (paths) {
|
|
562
566
|
messages.push({ role: "system", content: devicePathsText(paths.payload) });
|
|
563
567
|
}
|
|
568
|
+
// memory spec §6.1: each plugin's context in effect at the boundary follows the summary again
|
|
569
|
+
for (const text of pluginContextsAfterSummary(events, after)) {
|
|
570
|
+
messages.push({ role: "system", content: text });
|
|
571
|
+
}
|
|
564
572
|
// 被摘要覆盖的变更提醒不再发送:摘要时生效的目录与 system 里的不同 → 紧随摘要补上完整的当前目录
|
|
565
573
|
const covered = skillSnaps.filter((e) => e.seq <= after).at(-1);
|
|
566
574
|
// (phase B: plus the current pinned skills and tool-name map when they differ from system's)
|
|
@@ -639,6 +647,13 @@ export function buildModelRequest(input) {
|
|
|
639
647
|
case "device_paths":
|
|
640
648
|
messages.push({ role: "system", content: devicePathsText(e.payload) });
|
|
641
649
|
break;
|
|
650
|
+
// a context@1 provider's text (memory spec §6.1); empty text (nothing any more) renders nothing
|
|
651
|
+
case "plugin_context": {
|
|
652
|
+
const text = pluginContextText(e.payload);
|
|
653
|
+
if (text !== "")
|
|
654
|
+
messages.push({ role: "system", content: text });
|
|
655
|
+
break;
|
|
656
|
+
}
|
|
642
657
|
// hook_denied is not model-visible: the model sees only the denied tool call's tool_result
|
|
643
658
|
// (and nothing for a denied model request), so request building skips it
|
|
644
659
|
case "hook_denied":
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ConversationEvent, EventPayload } from "../../conversations/events.js";
|
|
2
|
+
/**
|
|
3
|
+
* The `plugin_context` event (memory spec §6.1): the text a `context@1` provider added to the
|
|
4
|
+
* conversation at a run start, written only when it changed. Model-visible: rendered at its
|
|
5
|
+
* position as a system message; after a summary that covers it, the latest non-empty one per
|
|
6
|
+
* plugin follows the summary again. Its text is in the log, so it needs no `DriftReason`.
|
|
7
|
+
*/
|
|
8
|
+
/** The payload of a `plugin_context` event. */
|
|
9
|
+
export type PluginContextPayload = EventPayload<"plugin_context">;
|
|
10
|
+
/** The idempotency key of a run's `plugin_context` event for `plugin`. */
|
|
11
|
+
export declare const pluginContextIdemKey: (runId: string, plugin: string) => string;
|
|
12
|
+
/** The system message of a `plugin_context` event (empty text renders nothing: ""). */
|
|
13
|
+
export declare function pluginContextText(p: PluginContextPayload): string;
|
|
14
|
+
/** The latest `plugin_context` payload of each plugin at or before `uptoSeq`. */
|
|
15
|
+
export declare function latestPluginContexts(events: readonly ConversationEvent[], uptoSeq: number): Map<string, PluginContextPayload>;
|
|
16
|
+
/**
|
|
17
|
+
* The system messages that follow a summary covering up to `after`: the latest non-empty
|
|
18
|
+
* `plugin_context` of each plugin, in plugin-name order.
|
|
19
|
+
*/
|
|
20
|
+
export declare function pluginContextsAfterSummary(events: readonly ConversationEvent[], after: number): string[];
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** The idempotency key of a run's `plugin_context` event for `plugin`. */
|
|
2
|
+
export const pluginContextIdemKey = (runId, plugin) => `plugin_context:${runId}:${plugin}`;
|
|
3
|
+
/** The system message of a `plugin_context` event (empty text renders nothing: ""). */
|
|
4
|
+
export function pluginContextText(p) {
|
|
5
|
+
if (p.text === "")
|
|
6
|
+
return "";
|
|
7
|
+
return `<plugin-context plugin="${p.plugin}">\n${p.text}\n</plugin-context>`;
|
|
8
|
+
}
|
|
9
|
+
/** The latest `plugin_context` payload of each plugin at or before `uptoSeq`. */
|
|
10
|
+
export function latestPluginContexts(events, uptoSeq) {
|
|
11
|
+
const latest = new Map();
|
|
12
|
+
for (const e of events) {
|
|
13
|
+
if (e.type !== "plugin_context" || e.seq > uptoSeq)
|
|
14
|
+
continue;
|
|
15
|
+
const prev = latest.get(e.payload.plugin);
|
|
16
|
+
if (!prev || e.seq > prev.seq)
|
|
17
|
+
latest.set(e.payload.plugin, { seq: e.seq, payload: e.payload });
|
|
18
|
+
}
|
|
19
|
+
return new Map([...latest].map(([plugin, v]) => [plugin, v.payload]));
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The system messages that follow a summary covering up to `after`: the latest non-empty
|
|
23
|
+
* `plugin_context` of each plugin, in plugin-name order.
|
|
24
|
+
*/
|
|
25
|
+
export function pluginContextsAfterSummary(events, after) {
|
|
26
|
+
return [...latestPluginContexts(events, after)]
|
|
27
|
+
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
|
|
28
|
+
.map(([, p]) => pluginContextText(p))
|
|
29
|
+
.filter((text) => text !== "");
|
|
30
|
+
}
|
|
@@ -148,6 +148,7 @@ export class LangGraphLoop {
|
|
|
148
148
|
mediaCache,
|
|
149
149
|
agentId: ctx.agentId,
|
|
150
150
|
accountId: ctx.triggerAccountId,
|
|
151
|
+
source: ctx.source,
|
|
151
152
|
...(ctx.conversationKind !== undefined && { conversationKind: ctx.conversationKind }),
|
|
152
153
|
...(ctx.parent !== undefined && { parentRunId: ctx.parent.runId, parent: ctx.parent }),
|
|
153
154
|
...(ctx.subagent === true && { subagent: true }),
|
|
@@ -221,6 +222,9 @@ export class LangGraphLoop {
|
|
|
221
222
|
agentVersion: ctx.agentVersion,
|
|
222
223
|
agent: ctx.agent,
|
|
223
224
|
accountId: ctx.triggerAccountId,
|
|
225
|
+
source: ctx.source,
|
|
226
|
+
ownerAccountId: ctx.ownerAccountId,
|
|
227
|
+
role: ctx.role,
|
|
224
228
|
signal: ctx.signal,
|
|
225
229
|
...(ctx.limits.deadline !== undefined && { deadline: ctx.limits.deadline }),
|
|
226
230
|
...(ctx.conversationKind !== undefined && { conversationKind: ctx.conversationKind }),
|
package/dist/runtime/loop.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import type { ConversationSource } from "@capybara-hq/plugin-sdk";
|
|
1
2
|
import type { AgentDefinition } from "../agents/definition.js";
|
|
2
3
|
import type { KernelConfig } from "../config/schema.js";
|
|
3
4
|
import type { AbsorbableEvent } from "../conversations/events.js";
|
|
5
|
+
import type { OrgRole } from "../orgs/roles.js";
|
|
4
6
|
import type { StreamSink } from "./model-call.js";
|
|
5
7
|
/** 一次运行交给 loop 的上下文(spec 4.4)。 */
|
|
6
8
|
export interface LoopContext {
|
|
@@ -12,6 +14,12 @@ export interface LoopContext {
|
|
|
12
14
|
agentId: string;
|
|
13
15
|
/** 发起本次运行的人(对话发起人):工具调用用其个人凭据,审计记为 actor。 */
|
|
14
16
|
triggerAccountId: string;
|
|
17
|
+
/** The conversation's source, read once per run (memory plugin spec §6.2). */
|
|
18
|
+
source: ConversationSource;
|
|
19
|
+
/** The conversation's owner (`conversations.account_id`); plugin tool handlers receive it. */
|
|
20
|
+
ownerAccountId: string;
|
|
21
|
+
/** The trigger account's org role when the run starts; plugin tool handlers receive it. */
|
|
22
|
+
role: OrgRole;
|
|
15
23
|
agent: AgentDefinition;
|
|
16
24
|
agentVersion: number;
|
|
17
25
|
/** 用户中止或到达 deadline 时触发:进行中的模型调用立即停下。 */
|
|
@@ -4,8 +4,10 @@ import { OwnedPluginCleanup } from "../catalog/owned-plugin-cleanup.js";
|
|
|
4
4
|
import { ScheduledRuns } from "../infra/schedule/scheduled-runs.js";
|
|
5
5
|
import type { KernelLogger } from "../log.js";
|
|
6
6
|
import { DirectorySync } from "../orgs/directory-sync.js";
|
|
7
|
+
import { Offboarding } from "../orgs/offboarding.js";
|
|
7
8
|
import { SandboxService } from "../sandbox/sandbox.service.js";
|
|
8
9
|
import { ScheduledTaskFirer } from "../schedules/fire.js";
|
|
10
|
+
import { DeviceOpResolver } from "../tools/device-op-resolver.js";
|
|
9
11
|
import { ToolDrift } from "../tools/drift.js";
|
|
10
12
|
import { RunQueue } from "./run-queue.js";
|
|
11
13
|
import { Waits } from "./waits.js";
|
|
@@ -16,27 +18,30 @@ export declare const MAINTENANCE_CONCURRENCY = 5;
|
|
|
16
18
|
* The one consumer of `cap-kernel-maintenance` (decision R11 of the Nest kernel spec): dispatches
|
|
17
19
|
* each one-off job by name to its domain (`wait-timeout` → {@link Waits}, `sandbox-destroy-for` and
|
|
18
20
|
* `sandbox-reset-files` → {@link SandboxService}, `drift-connector` → {@link ToolDrift}, `directory-sync` →
|
|
19
|
-
* {@link DirectorySync}, `bg-wake` and `ensure-run` → {@link RunQueue.ensureRun}, `stop-tree` →
|
|
21
|
+
* {@link DirectorySync}, `run-stop` → {@link Offboarding.stopRun}, `bg-wake` and `ensure-run` → {@link RunQueue.ensureRun}, `stop-tree` →
|
|
20
22
|
* {@link processStopTree} and `deliver-to-parent` → {@link processDeliverToParent} (both in
|
|
21
23
|
* runtime/subagents/tree-jobs.ts, over {@link Waits}), `owned-plugin-cleanup` →
|
|
22
|
-
* {@link OwnedPluginCleanup}, `sched-fire` → {@link ScheduledTaskFirer}
|
|
24
|
+
* {@link OwnedPluginCleanup}, `sched-fire` and `sched-settle` → {@link ScheduledTaskFirer}, `device-op-resolve` →
|
|
25
|
+
* {@link DeviceOpResolver}). The name of a periodic job (a `@LockedCron` / `@LockedInterval` job,
|
|
23
26
|
* such as `sandbox-suspend-idle`) runs that job's body once ({@link ScheduledRuns.runNow}), as the
|
|
24
27
|
* maintenance handlers did when those jobs were schedulers on this queue: an operator's or a
|
|
25
28
|
* test's one-off run, not a second schedule (decision R23 of the Nest kernel spec). Any other
|
|
26
|
-
* name
|
|
29
|
+
* name fails the job (BullMQ retries it; in a rolling deploy a newer replica knows it). The producers are the domains' own (same names, ids and options).
|
|
27
30
|
*/
|
|
28
31
|
export declare class MaintenanceProcessor extends WorkerHost<Worker> {
|
|
29
32
|
private readonly waits;
|
|
30
33
|
private readonly sandbox;
|
|
31
34
|
private readonly drift;
|
|
32
35
|
private readonly directory;
|
|
36
|
+
private readonly offboarding;
|
|
33
37
|
private readonly runs;
|
|
34
38
|
private readonly ownedCleanup;
|
|
35
39
|
private readonly scheduledTasks;
|
|
40
|
+
private readonly deviceOps;
|
|
36
41
|
private readonly scheduled;
|
|
37
42
|
private readonly log;
|
|
38
43
|
private readonly queue;
|
|
39
|
-
constructor(waits: Pick<Waits, "processTimeout" | "childFinished" | "stopChildren" | "stopDescendantsOfStopped" | "stopTree">, sandbox: Pick<SandboxService, "destroyDevice" | "resetFileSystem">, drift: Pick<ToolDrift, "check">, directory: Pick<DirectorySync, "runSyncJob">, runs: Pick<RunQueue, "ensureRun">, ownedCleanup: Pick<OwnedPluginCleanup, "run">, scheduledTasks: Pick<ScheduledTaskFirer, "fire">, scheduled: Pick<ScheduledRuns, "runNow">, log: Pick<KernelLogger, "error" | "warn">, queue: Pick<Queue, "removeJobScheduler">);
|
|
44
|
+
constructor(waits: Pick<Waits, "processTimeout" | "childFinished" | "stopChildren" | "stopDescendantsOfStopped" | "stopTree">, sandbox: Pick<SandboxService, "destroyDevice" | "resetFileSystem">, drift: Pick<ToolDrift, "check">, directory: Pick<DirectorySync, "runSyncJob">, offboarding: Pick<Offboarding, "stopRun">, runs: Pick<RunQueue, "ensureRun">, ownedCleanup: Pick<OwnedPluginCleanup, "run">, scheduledTasks: Pick<ScheduledTaskFirer, "fire" | "settle">, deviceOps: Pick<DeviceOpResolver, "resolve">, scheduled: Pick<ScheduledRuns, "runNow">, log: Pick<KernelLogger, "error" | "warn">, queue: Pick<Queue, "removeJobScheduler">);
|
|
40
45
|
/**
|
|
41
46
|
* Removes the retired job schedulers (`RETIRED_SCHEDULER_IDS`, runtime/retired-schedulers.ts): every fixed-period
|
|
42
47
|
* maintenance job is a `@LockedCron` / `@LockedInterval` now. Idempotent; called once when a
|
|
@@ -19,13 +19,16 @@ import { runJob } from "../infra/queue/run-job.js";
|
|
|
19
19
|
import { KernelScheduleModule } from "../infra/schedule/schedule.module.js";
|
|
20
20
|
import { ScheduledRuns } from "../infra/schedule/scheduled-runs.js";
|
|
21
21
|
import { KERNEL_LOGGER } from "../infra/tokens.js";
|
|
22
|
+
import { DEVICE_OP_RESOLVE_JOB, } from "../local-devices/policy-op-jobs.js";
|
|
22
23
|
import { DIRECTORY_SYNC_JOB, DirectorySync } from "../orgs/directory-sync.js";
|
|
24
|
+
import { Offboarding, RUN_STOP_JOB } from "../orgs/offboarding.js";
|
|
23
25
|
import { OrgsModule } from "../orgs/orgs.module.js";
|
|
24
26
|
import { runSandboxDestroyFor, runSandboxResetFiles, SANDBOX_DESTROY_FOR_JOB, SANDBOX_RESET_FILES_JOB, } from "../sandbox/jobs.js";
|
|
25
27
|
import { SandboxModule } from "../sandbox/sandbox.module.js";
|
|
26
28
|
import { SandboxService } from "../sandbox/sandbox.service.js";
|
|
27
29
|
import { ScheduledTaskFireModule, ScheduledTaskFirer } from "../schedules/fire.js";
|
|
28
|
-
import { SCHED_FIRE_JOB } from "../schedules/job-ids.js";
|
|
30
|
+
import { SCHED_FIRE_JOB, SCHED_SETTLE_JOB, } from "../schedules/job-ids.js";
|
|
31
|
+
import { DeviceOpResolver } from "../tools/device-op-resolver.js";
|
|
29
32
|
import { ToolDrift } from "../tools/drift.js";
|
|
30
33
|
import { DRIFT_CONNECTOR_JOB } from "../tools/job-ids.js";
|
|
31
34
|
import { ToolsModule } from "../tools/tools.module.js";
|
|
@@ -42,35 +45,40 @@ export const MAINTENANCE_CONCURRENCY = 5;
|
|
|
42
45
|
* The one consumer of `cap-kernel-maintenance` (decision R11 of the Nest kernel spec): dispatches
|
|
43
46
|
* each one-off job by name to its domain (`wait-timeout` → {@link Waits}, `sandbox-destroy-for` and
|
|
44
47
|
* `sandbox-reset-files` → {@link SandboxService}, `drift-connector` → {@link ToolDrift}, `directory-sync` →
|
|
45
|
-
* {@link DirectorySync}, `bg-wake` and `ensure-run` → {@link RunQueue.ensureRun}, `stop-tree` →
|
|
48
|
+
* {@link DirectorySync}, `run-stop` → {@link Offboarding.stopRun}, `bg-wake` and `ensure-run` → {@link RunQueue.ensureRun}, `stop-tree` →
|
|
46
49
|
* {@link processStopTree} and `deliver-to-parent` → {@link processDeliverToParent} (both in
|
|
47
50
|
* runtime/subagents/tree-jobs.ts, over {@link Waits}), `owned-plugin-cleanup` →
|
|
48
|
-
* {@link OwnedPluginCleanup}, `sched-fire` → {@link ScheduledTaskFirer}
|
|
51
|
+
* {@link OwnedPluginCleanup}, `sched-fire` and `sched-settle` → {@link ScheduledTaskFirer}, `device-op-resolve` →
|
|
52
|
+
* {@link DeviceOpResolver}). The name of a periodic job (a `@LockedCron` / `@LockedInterval` job,
|
|
49
53
|
* such as `sandbox-suspend-idle`) runs that job's body once ({@link ScheduledRuns.runNow}), as the
|
|
50
54
|
* maintenance handlers did when those jobs were schedulers on this queue: an operator's or a
|
|
51
55
|
* test's one-off run, not a second schedule (decision R23 of the Nest kernel spec). Any other
|
|
52
|
-
* name
|
|
56
|
+
* name fails the job (BullMQ retries it; in a rolling deploy a newer replica knows it). The producers are the domains' own (same names, ids and options).
|
|
53
57
|
*/
|
|
54
58
|
let MaintenanceProcessor = class MaintenanceProcessor extends WorkerHost {
|
|
55
59
|
waits;
|
|
56
60
|
sandbox;
|
|
57
61
|
drift;
|
|
58
62
|
directory;
|
|
63
|
+
offboarding;
|
|
59
64
|
runs;
|
|
60
65
|
ownedCleanup;
|
|
61
66
|
scheduledTasks;
|
|
67
|
+
deviceOps;
|
|
62
68
|
scheduled;
|
|
63
69
|
log;
|
|
64
70
|
queue;
|
|
65
|
-
constructor(waits, sandbox, drift, directory, runs, ownedCleanup, scheduledTasks, scheduled, log, queue) {
|
|
71
|
+
constructor(waits, sandbox, drift, directory, offboarding, runs, ownedCleanup, scheduledTasks, deviceOps, scheduled, log, queue) {
|
|
66
72
|
super();
|
|
67
73
|
this.waits = waits;
|
|
68
74
|
this.sandbox = sandbox;
|
|
69
75
|
this.drift = drift;
|
|
70
76
|
this.directory = directory;
|
|
77
|
+
this.offboarding = offboarding;
|
|
71
78
|
this.runs = runs;
|
|
72
79
|
this.ownedCleanup = ownedCleanup;
|
|
73
80
|
this.scheduledTasks = scheduledTasks;
|
|
81
|
+
this.deviceOps = deviceOps;
|
|
74
82
|
this.scheduled = scheduled;
|
|
75
83
|
this.log = log;
|
|
76
84
|
this.queue = queue;
|
|
@@ -109,6 +117,23 @@ let MaintenanceProcessor = class MaintenanceProcessor extends WorkerHost {
|
|
|
109
117
|
case DIRECTORY_SYNC_JOB:
|
|
110
118
|
await this.directory.runSyncJob(job.data);
|
|
111
119
|
return;
|
|
120
|
+
// a departed member's run stop, owed by the offboarding (transactional outbox spec §8.1)
|
|
121
|
+
case RUN_STOP_JOB:
|
|
122
|
+
try {
|
|
123
|
+
await this.offboarding.stopRun(job.data);
|
|
124
|
+
}
|
|
125
|
+
catch (err) {
|
|
126
|
+
// nothing stops the run after its last attempt: say so (the failed job is kept)
|
|
127
|
+
if (job.attemptsMade + 1 >= (job.opts.attempts ?? 1)) {
|
|
128
|
+
this.log.error("a departed member's run could not be stopped", {
|
|
129
|
+
jobId: job.id,
|
|
130
|
+
runId: job.data.runId,
|
|
131
|
+
err,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
throw err;
|
|
135
|
+
}
|
|
136
|
+
return;
|
|
112
137
|
// the end of a conversation's wake window (background Bash spec §3)
|
|
113
138
|
case BG_WAKE_JOB: {
|
|
114
139
|
// fired before the window ended (clock skew): it reschedules under its next retry id
|
|
@@ -136,10 +161,22 @@ let MaintenanceProcessor = class MaintenanceProcessor extends WorkerHost {
|
|
|
136
161
|
case SCHED_FIRE_JOB:
|
|
137
162
|
await this.scheduledTasks.fire(job.data.taskRunId);
|
|
138
163
|
return;
|
|
164
|
+
// a task run's settle check, claimed or unfired (ruling R26)
|
|
165
|
+
case SCHED_SETTLE_JOB:
|
|
166
|
+
await this.scheduledTasks.settle(job.data);
|
|
167
|
+
return;
|
|
168
|
+
// a device policy op whose outcome was not recorded (transactional outbox spec §7)
|
|
169
|
+
case DEVICE_OP_RESOLVE_JOB:
|
|
170
|
+
await this.deviceOps.resolve(job.data);
|
|
171
|
+
return;
|
|
139
172
|
default:
|
|
140
173
|
if (await this.scheduled.runNow(job.name))
|
|
141
174
|
return;
|
|
142
|
-
|
|
175
|
+
// never completed unrun: in a rolling deploy an old worker meets a job a newer replica
|
|
176
|
+
// owed (its outbox row is gone once added); failing it leaves it to BullMQ's retries, so a
|
|
177
|
+
// newer replica can take it within the job's attempts (old workers still go first, see
|
|
178
|
+
// docs/deploy/upgrade.md)
|
|
179
|
+
throw new Error(`unknown maintenance job "${job.name}"`);
|
|
143
180
|
}
|
|
144
181
|
}
|
|
145
182
|
failed(job, err) {
|
|
@@ -161,12 +198,14 @@ MaintenanceProcessor = __decorate([
|
|
|
161
198
|
__param(1, Inject(SandboxService)),
|
|
162
199
|
__param(2, Inject(ToolDrift)),
|
|
163
200
|
__param(3, Inject(DirectorySync)),
|
|
164
|
-
__param(4, Inject(
|
|
165
|
-
__param(5, Inject(
|
|
166
|
-
__param(6, Inject(
|
|
167
|
-
__param(7, Inject(
|
|
168
|
-
__param(8, Inject(
|
|
169
|
-
__param(9, Inject(
|
|
201
|
+
__param(4, Inject(Offboarding)),
|
|
202
|
+
__param(5, Inject(RunQueue)),
|
|
203
|
+
__param(6, Inject(OwnedPluginCleanup)),
|
|
204
|
+
__param(7, Inject(ScheduledTaskFirer)),
|
|
205
|
+
__param(8, Inject(DeviceOpResolver)),
|
|
206
|
+
__param(9, Inject(ScheduledRuns)),
|
|
207
|
+
__param(10, Inject(KERNEL_LOGGER)),
|
|
208
|
+
__param(11, Inject(getQueueToken(MAINTENANCE_QUEUE_NAME)))
|
|
170
209
|
], MaintenanceProcessor);
|
|
171
210
|
export { MaintenanceProcessor };
|
|
172
211
|
/**
|
|
@@ -186,7 +225,7 @@ MaintenanceModule = __decorate([
|
|
|
186
225
|
SandboxModule,
|
|
187
226
|
ToolsModule,
|
|
188
227
|
OrgsModule,
|
|
189
|
-
// the removed org plugins' cleanup
|
|
228
|
+
// the removed org plugins' cleanup job (worker-only through this module)
|
|
190
229
|
OwnedPluginCleanupModule,
|
|
191
230
|
// the scheduled task fire job and the scanner (worker-only through this module)
|
|
192
231
|
ScheduledTaskFireModule,
|