@capybara-hq/kernel 0.1.0-rc.23 → 0.1.0-rc.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bootstrap.json +25 -25
- package/dist/admin/admin.module.d.ts +2 -1
- package/dist/admin/admin.module.js +9 -3
- package/dist/admin/health.service.d.ts +2 -2
- package/dist/admin/health.service.js +6 -2
- package/dist/admin/outbox-dead.controller.d.ts +23 -0
- package/dist/admin/outbox-dead.controller.js +62 -0
- package/dist/admin/outbox-dead.schemas.d.ts +8 -0
- package/dist/admin/outbox-dead.schemas.js +18 -0
- package/dist/admin/outbox-dead.service.d.ts +66 -0
- package/dist/admin/outbox-dead.service.js +179 -0
- package/dist/admin/overview.service.d.ts +40 -5
- package/dist/admin/overview.service.js +41 -7
- package/dist/agents/agents.module.js +3 -0
- package/dist/agents/agents.service.d.ts +6 -1
- package/dist/agents/agents.service.js +11 -2
- package/dist/agents/from-template.js +1 -1
- package/dist/background/finish.d.ts +5 -2
- package/dist/background/finish.js +14 -3
- package/dist/background/launch.d.ts +2 -1
- package/dist/background/launch.js +5 -2
- package/dist/background/store.d.ts +8 -0
- package/dist/background/store.js +24 -0
- package/dist/catalog/admin-catalog.controller.d.ts +21 -2
- package/dist/catalog/admin-catalog.controller.js +45 -6
- package/dist/catalog/admin-catalog.schemas.d.ts +5 -0
- package/dist/catalog/admin-catalog.schemas.js +8 -0
- package/dist/catalog/catalog-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.d.ts +2 -1
- package/dist/catalog/catalog.module.js +9 -4
- 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/platform-import-update.d.ts +50 -0
- package/dist/catalog/platform-import-update.js +116 -0
- package/dist/catalog/plugin-import.d.ts +64 -26
- package/dist/catalog/plugin-import.js +113 -77
- package/dist/catalog/plugin-upgrade.d.ts +22 -4
- package/dist/catalog/plugin-upgrade.js +26 -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 +5 -2
- package/dist/channels/channels.module.js +9 -2
- package/dist/channels/deliver-job.d.ts +25 -0
- package/dist/channels/deliver-job.js +25 -0
- package/dist/channels/inbound.d.ts +1 -1
- package/dist/channels/inbound.js +4 -17
- package/dist/channels/jobs.d.ts +28 -18
- package/dist/channels/jobs.js +63 -26
- package/dist/channels/threads.d.ts +4 -3
- package/dist/channels/threads.js +4 -3
- package/dist/config/schema.d.ts +0 -3
- package/dist/config/schema.js +11 -20
- package/dist/conversations/agent-tree.d.ts +16 -0
- package/dist/conversations/agent-tree.js +36 -0
- package/dist/conversations/conversations.controller.js +4 -2
- package/dist/conversations/conversations.service.d.ts +61 -19
- package/dist/conversations/conversations.service.js +69 -43
- package/dist/conversations/event-log.d.ts +7 -10
- package/dist/conversations/event-log.js +24 -15
- package/dist/conversations/events.d.ts +21 -0
- package/dist/conversations/events.js +26 -2
- package/dist/conversations/queue.service.d.ts +8 -3
- package/dist/conversations/queue.service.js +37 -13
- package/dist/conversations/source.d.ts +10 -0
- package/dist/conversations/source.js +17 -0
- package/dist/conversations/titles.d.ts +21 -8
- package/dist/conversations/titles.js +42 -15
- package/dist/conversations/tree.d.ts +10 -0
- package/dist/conversations/tree.js +35 -0
- package/dist/db/kernel-schema.d.ts +35 -22
- package/dist/devices/devices.service.d.ts +23 -11
- package/dist/devices/devices.service.js +34 -26
- package/dist/errors.d.ts +11 -0
- package/dist/errors.js +11 -0
- package/dist/evals/run-replay.d.ts +13 -2
- package/dist/evals/run-replay.js +42 -9
- package/dist/evals/snapshot.d.ts +7 -1
- package/dist/evals/snapshot.js +2 -1
- package/dist/extensions/extensions.module.js +3 -0
- 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/lifecycle.d.ts +6 -1
- package/dist/extensions/lifecycle.js +9 -2
- 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/extensions/usage-subscribers.d.ts +43 -0
- package/dist/extensions/usage-subscribers.js +108 -0
- package/dist/http/api.module.js +2 -0
- package/dist/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/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/infra/cli/kernel-cli.module.js +5 -0
- package/dist/infra/events/kernel-events.d.ts +9 -35
- package/dist/infra/outbox/outbox-relay.module.d.ts +6 -0
- package/dist/infra/outbox/outbox-relay.module.js +18 -0
- package/dist/infra/outbox/outbox.dispatcher.d.ts +103 -0
- package/dist/infra/outbox/outbox.dispatcher.js +310 -0
- package/dist/infra/outbox/outbox.module.d.ts +24 -0
- package/dist/infra/outbox/outbox.module.js +50 -0
- package/dist/infra/outbox/outbox.relay.d.ts +31 -0
- package/dist/infra/outbox/outbox.relay.js +76 -0
- package/dist/infra/outbox/outbox.service.d.ts +51 -0
- package/dist/infra/outbox/outbox.service.js +131 -0
- package/dist/infra/outbox/registry.d.ts +244 -0
- package/dist/infra/outbox/registry.js +234 -0
- package/dist/infra/queue/queue-names.d.ts +8 -0
- package/dist/infra/queue/queue-names.js +17 -0
- package/dist/kernel.module.js +3 -0
- package/dist/local-devices/device-policy.service.d.ts +4 -0
- package/dist/local-devices/device-policy.service.js +61 -3
- package/dist/local-devices/machines.d.ts +13 -5
- package/dist/local-devices/machines.js +45 -7
- package/dist/local-devices/policy-summary.d.ts +44 -1
- package/dist/local-devices/policy-summary.js +69 -0
- package/dist/models/embeddings.d.ts +9 -9
- package/dist/models/embeddings.js +11 -18
- package/dist/models/models.module.js +3 -0
- package/dist/orgs/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 +22 -6
- package/dist/overrides.d.ts +23 -3
- package/dist/overrides.js +12 -2
- package/dist/platform/exclusions.js +5 -0
- package/dist/platform/ops/admin.ops.js +18 -2
- package/dist/platform/ops/catalog-admin.ops.d.ts +3 -1
- package/dist/platform/ops/catalog-admin.ops.js +32 -4
- package/dist/platform/ops/inbox.ops.js +4 -20
- package/dist/platform/ops/outbox.ops.d.ts +21 -0
- package/dist/platform/ops/outbox.ops.js +125 -0
- package/dist/platform/ops/platform.ops.d.ts +1 -0
- package/dist/platform/ops/platform.ops.js +6 -3
- package/dist/platform/ops/schedules.ops.js +40 -1
- package/dist/platform/platform-ops.module.d.ts +2 -1
- package/dist/platform/platform-ops.module.js +3 -0
- package/dist/platform/route-schemas.js +3 -0
- package/dist/roles/worker-role.d.ts +10 -5
- package/dist/roles/worker-role.js +19 -9
- package/dist/roles/worker.module.js +11 -8
- 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 +17 -5
- package/dist/runtime/limits.d.ts +15 -0
- package/dist/runtime/limits.js +25 -0
- package/dist/runtime/loop.d.ts +16 -3
- package/dist/runtime/loop.js +2 -1
- package/dist/runtime/maintenance.d.ts +7 -3
- package/dist/runtime/maintenance.js +52 -12
- package/dist/runtime/model-call.d.ts +20 -17
- package/dist/runtime/model-call.js +15 -24
- package/dist/runtime/plugin-context.service.d.ts +69 -0
- package/dist/runtime/plugin-context.service.js +208 -0
- package/dist/runtime/queue-lost-jobs.d.ts +24 -0
- package/dist/runtime/queue-lost-jobs.js +51 -0
- package/dist/runtime/run-control.d.ts +47 -5
- package/dist/runtime/run-control.js +79 -12
- package/dist/runtime/run-finish.d.ts +39 -12
- package/dist/runtime/run-finish.js +97 -14
- package/dist/runtime/run-jobs.d.ts +95 -13
- package/dist/runtime/run-jobs.js +199 -28
- package/dist/runtime/run-orphan-check.job.d.ts +142 -0
- package/dist/runtime/run-orphan-check.job.js +339 -0
- package/dist/runtime/run-processors.d.ts +16 -9
- package/dist/runtime/run-processors.js +1 -1
- package/dist/runtime/run-queue.d.ts +75 -53
- package/dist/runtime/run-queue.js +189 -263
- package/dist/runtime/run-steps.d.ts +1 -2
- package/dist/runtime/run-steps.js +8 -11
- package/dist/runtime/run-worker.d.ts +26 -16
- package/dist/runtime/run-worker.js +132 -37
- package/dist/runtime/runtime.module.d.ts +2 -3
- package/dist/runtime/runtime.module.js +7 -6
- package/dist/runtime/step-log.d.ts +16 -4
- package/dist/runtime/step-log.js +26 -6
- package/dist/runtime/stream.controller.d.ts +2 -4
- package/dist/runtime/stream.controller.js +5 -11
- package/dist/runtime/subagents/child.js +1 -1
- package/dist/runtime/subagents/definition.d.ts +2 -1
- package/dist/runtime/subagents/definition.js +4 -2
- package/dist/runtime/subagents/deliveries.d.ts +6 -3
- package/dist/runtime/subagents/deliveries.js +16 -7
- package/dist/runtime/subagents/dispatch.d.ts +12 -12
- package/dist/runtime/subagents/dispatch.js +14 -38
- package/dist/runtime/subagents/messages.d.ts +5 -1
- package/dist/runtime/subagents/messages.js +11 -3
- package/dist/runtime/subagents/skill-fork.d.ts +3 -3
- package/dist/runtime/subagents/skill-fork.js +8 -5
- package/dist/runtime/subagents/subagents.service.js +1 -1
- package/dist/runtime/subagents/tree-jobs.d.ts +61 -0
- package/dist/runtime/subagents/tree-jobs.js +86 -0
- package/dist/runtime/usage-report-jobs.d.ts +23 -48
- package/dist/runtime/usage-report-jobs.js +67 -119
- package/dist/runtime/usage-report.d.ts +28 -77
- package/dist/runtime/usage-report.js +35 -213
- package/dist/runtime/waits.d.ts +48 -198
- package/dist/runtime/waits.js +103 -805
- package/dist/sandbox/jobs.d.ts +20 -10
- package/dist/sandbox/jobs.js +23 -17
- package/dist/sandbox/sandbox.service.d.ts +17 -4
- package/dist/sandbox/sandbox.service.js +55 -14
- package/dist/schedules/calendar.d.ts +134 -0
- package/dist/schedules/calendar.js +410 -0
- package/dist/schedules/fire.d.ts +26 -7
- package/dist/schedules/fire.js +91 -22
- package/dist/schedules/job-ids.d.ts +66 -17
- package/dist/schedules/job-ids.js +81 -13
- package/dist/schedules/run-tracker.d.ts +0 -5
- package/dist/schedules/run-tracker.js +2 -14
- package/dist/schedules/scan.job.d.ts +13 -32
- package/dist/schedules/scan.job.js +50 -164
- package/dist/schedules/schedule-math.d.ts +42 -0
- package/dist/schedules/schedule-math.js +52 -0
- package/dist/schedules/scheduled-tasks.service.d.ts +22 -6
- package/dist/schedules/scheduled-tasks.service.js +70 -14
- package/dist/schedules/schedules.controller.d.ts +5 -1
- package/dist/schedules/schedules.controller.js +15 -1
- package/dist/schedules/schedules.module.js +0 -4
- package/dist/schedules/schedules.schemas.d.ts +35 -0
- package/dist/schedules/schedules.schemas.js +27 -0
- package/dist/schedules/store.d.ts +5 -0
- package/dist/schedules/store.js +20 -0
- package/dist/skills/changes.controller.d.ts +1 -1
- package/dist/skills/changes.controller.js +1 -1
- package/dist/tools/approvals.service.d.ts +41 -11
- package/dist/tools/approvals.service.js +108 -14
- package/dist/tools/builtin-names.d.ts +5 -3
- package/dist/tools/builtin-names.js +12 -4
- package/dist/tools/builtins/builtin-tools.d.ts +1 -2
- package/dist/tools/builtins/builtin-tools.js +21 -17
- package/dist/tools/builtins/sandbox/bash.js +9 -3
- package/dist/tools/builtins/sandbox/common.d.ts +18 -1
- package/dist/tools/builtins/sandbox/common.js +14 -1
- package/dist/tools/builtins/sandbox/index.d.ts +2 -1
- package/dist/tools/builtins/sandbox/index.js +4 -1
- package/dist/tools/builtins/sandbox/task-list.d.ts +12 -0
- package/dist/tools/builtins/sandbox/task-list.js +130 -0
- package/dist/tools/builtins/sandbox/task-output.d.ts +8 -1
- package/dist/tools/builtins/sandbox/task-output.js +21 -3
- package/dist/tools/builtins/web/extract.d.ts +2 -4
- package/dist/tools/builtins/web/extract.js +2 -5
- package/dist/tools/pipeline.d.ts +12 -3
- package/dist/tools/pipeline.js +6 -1
- package/dist/tools/questions.service.d.ts +30 -10
- package/dist/tools/questions.service.js +41 -27
- package/dist/tools/registry.d.ts +12 -3
- package/dist/tools/registry.js +13 -3
- package/dist/tools/tools.module.js +4 -3
- package/package.json +3 -3
- package/src/migrations/0118_device_policy_ops.sql +6 -0
- package/src/migrations/0119_outbox.sql +22 -0
- package/src/migrations/0120_drop_usage_report_sweep.sql +7 -0
- 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/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
|
@@ -15,18 +15,17 @@ import { ApiError } from "../errors.js";
|
|
|
15
15
|
import { ExtensionDisplayNames } from "../extensions/display-name.js";
|
|
16
16
|
import { audit } from "../identity/audit.js";
|
|
17
17
|
import { KernelConfigService } from "../infra/config/kernel-config.service.js";
|
|
18
|
-
import { afterCommit } from "../infra/database/after-commit.js";
|
|
19
18
|
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
20
19
|
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
21
20
|
import { Transactional } from "../infra/database/transactional.decorator.js";
|
|
22
21
|
import { LockService } from "../infra/lock/lock.service.js";
|
|
23
|
-
import {
|
|
22
|
+
import { OutboxService } from "../infra/outbox/outbox.service.js";
|
|
24
23
|
import { DevicePolicyService } from "../local-devices/device-policy.service.js";
|
|
25
24
|
import { deviceLeaseKey } from "../local-devices/keys.js";
|
|
26
25
|
import { MachinesService } from "../local-devices/machines.js";
|
|
27
26
|
import { deviceFeaturesOf, localPolicyOf } from "../local-devices/policy-summary.js";
|
|
28
27
|
import { readOrgSettings } from "../orgs/settings.service.js";
|
|
29
|
-
import { SandboxJobs } from "../sandbox/jobs.js";
|
|
28
|
+
import { oweDeviceDestroy, SandboxJobs } from "../sandbox/jobs.js";
|
|
30
29
|
import { isInternalProvider, LOCAL_PROVIDER, SandboxProviders } from "../sandbox/providers.js";
|
|
31
30
|
import { deviceNotFound, SandboxService } from "../sandbox/sandbox.service.js";
|
|
32
31
|
import { effectiveSandboxPolicy, readDeploymentSandboxPolicy, } from "../sandbox/tiers.js";
|
|
@@ -43,8 +42,8 @@ const nameTaken = () => new ApiError("device.name_taken", "You already have a de
|
|
|
43
42
|
* Personal devices (spec 2026-11-07-environments-design §4.2 items 7, 12, 13; §6; §9):
|
|
44
43
|
* cloud ones on the deployment's sandbox provider and `device` ones on a connected machine; a
|
|
45
44
|
* person's devices in an org, visible and usable only by their owner (anyone else gets
|
|
46
|
-
* `device.not_found`). Deleting one soft-deletes it and
|
|
47
|
-
* `sandbox-destroy-for` job
|
|
45
|
+
* `device.not_found`). Deleting one soft-deletes it and owes the destruction of its instance and
|
|
46
|
+
* files (a `sandbox-destroy-for` job) in the same transaction.
|
|
48
47
|
*/
|
|
49
48
|
let DevicesService = class DevicesService {
|
|
50
49
|
db;
|
|
@@ -53,13 +52,15 @@ let DevicesService = class DevicesService {
|
|
|
53
52
|
providers;
|
|
54
53
|
sandbox;
|
|
55
54
|
sandboxJobs;
|
|
55
|
+
outbox;
|
|
56
56
|
access;
|
|
57
57
|
displayNames;
|
|
58
|
-
log;
|
|
59
58
|
locks;
|
|
60
59
|
machines;
|
|
61
60
|
policies;
|
|
62
|
-
constructor(db, txHost, config, providers, sandbox, sandboxJobs,
|
|
61
|
+
constructor(db, txHost, config, providers, sandbox, sandboxJobs,
|
|
62
|
+
/** Owes a deleted device's destruction in the delete's transaction. */
|
|
63
|
+
outbox, access, displayNames,
|
|
63
64
|
/** Reads the device relay's lease (`online`). */
|
|
64
65
|
locks, machines, policies) {
|
|
65
66
|
this.db = db;
|
|
@@ -68,9 +69,9 @@ let DevicesService = class DevicesService {
|
|
|
68
69
|
this.providers = providers;
|
|
69
70
|
this.sandbox = sandbox;
|
|
70
71
|
this.sandboxJobs = sandboxJobs;
|
|
72
|
+
this.outbox = outbox;
|
|
71
73
|
this.access = access;
|
|
72
74
|
this.displayNames = displayNames;
|
|
73
|
-
this.log = log;
|
|
74
75
|
this.locks = locks;
|
|
75
76
|
this.machines = machines;
|
|
76
77
|
this.policies = policies;
|
|
@@ -388,9 +389,9 @@ let DevicesService = class DevicesService {
|
|
|
388
389
|
});
|
|
389
390
|
}
|
|
390
391
|
/**
|
|
391
|
-
* Deletes one of the person's live devices: soft delete, its IM default row cleared, and
|
|
392
|
-
*
|
|
393
|
-
* destroys the instance and the files). Conversations bound to it keep pointing at it; their
|
|
392
|
+
* Deletes one of the person's live devices: soft delete, its IM default row cleared, and a
|
|
393
|
+
* `sandbox-destroy-for` job owed in the same transaction (`device-destroy-<deviceId>`; the
|
|
394
|
+
* worker waits for running commands, then destroys the instance and the files). Conversations bound to it keep pointing at it; their
|
|
394
395
|
* sandbox tools then fail with `device.not_found`. A local device's machine is
|
|
395
396
|
* revoked in the same transaction; after the commit it is told `stop { reason: "deleted" }`
|
|
396
397
|
* (while connected) and its connection is closed.
|
|
@@ -417,15 +418,7 @@ let DevicesService = class DevicesService {
|
|
|
417
418
|
target: id,
|
|
418
419
|
detail: { name: before.name, isDefault: before.is_default, ...auditDetail },
|
|
419
420
|
});
|
|
420
|
-
|
|
421
|
-
try {
|
|
422
|
-
await this.sandboxJobs.destroy([id]);
|
|
423
|
-
}
|
|
424
|
-
catch (err) {
|
|
425
|
-
// the hourly destruction sweep destroys deleted devices too
|
|
426
|
-
this.log.warn("cannot schedule device destruction", { deviceId: id, err });
|
|
427
|
-
}
|
|
428
|
-
});
|
|
421
|
+
await oweDeviceDestroy(this.outbox, id, orgId);
|
|
429
422
|
}
|
|
430
423
|
/**
|
|
431
424
|
* Disconnects the machine of one of the person's own local devices (it must sign in
|
|
@@ -1021,9 +1014,9 @@ DevicesService = __decorate([
|
|
|
1021
1014
|
__param(3, Inject(SandboxProviders)),
|
|
1022
1015
|
__param(4, Inject(SandboxService)),
|
|
1023
1016
|
__param(5, Inject(SandboxJobs)),
|
|
1024
|
-
__param(6, Inject(
|
|
1025
|
-
__param(7, Inject(
|
|
1026
|
-
__param(8, Inject(
|
|
1017
|
+
__param(6, Inject(OutboxService)),
|
|
1018
|
+
__param(7, Inject(DeviceAccess)),
|
|
1019
|
+
__param(8, Inject(ExtensionDisplayNames)),
|
|
1027
1020
|
__param(9, Inject(LockService)),
|
|
1028
1021
|
__param(10, Inject(MachinesService)),
|
|
1029
1022
|
__param(11, Inject(DevicePolicyService))
|
|
@@ -1055,7 +1048,16 @@ const tierOrNull = (v) => v === "strict" || v === "dev" || v === "open" ? v : nu
|
|
|
1055
1048
|
function sandboxChangeOf(action, at, d, by) {
|
|
1056
1049
|
const hosts = (v) => Array.isArray(v) ? v.filter((h) => typeof h === "string") : [];
|
|
1057
1050
|
const isolation = d.isolation === "sandbox" || d.isolation === "none" ? d.isolation : null;
|
|
1058
|
-
const base = {
|
|
1051
|
+
const base = {
|
|
1052
|
+
at: at.toISOString(),
|
|
1053
|
+
add: [],
|
|
1054
|
+
remove: [],
|
|
1055
|
+
tier: null,
|
|
1056
|
+
isolation: null,
|
|
1057
|
+
localhost: null,
|
|
1058
|
+
remoteGrants: null,
|
|
1059
|
+
by,
|
|
1060
|
+
};
|
|
1059
1061
|
if (action === "device.tier_changed") {
|
|
1060
1062
|
return { ...base, kind: "tier", tier: { to: tierOrNull(d.to) }, via: "web", confirmed: true };
|
|
1061
1063
|
}
|
|
@@ -1068,9 +1070,15 @@ function sandboxChangeOf(action, at, d, by) {
|
|
|
1068
1070
|
add: hosts(d.add),
|
|
1069
1071
|
remove: hosts(d.remove),
|
|
1070
1072
|
// the key is there only when the change set the tier (null: follow the org's default)
|
|
1071
|
-
tier: "tier" in d
|
|
1073
|
+
tier: "tier" in d
|
|
1074
|
+
? { ...("tierFrom" in d && { from: tierOrNull(d.tierFrom) }), to: tierOrNull(d.tier) }
|
|
1075
|
+
: null,
|
|
1072
1076
|
isolation,
|
|
1073
|
-
|
|
1077
|
+
localhost: d.localhost === "on" || d.localhost === "off-proxy-detected" || d.localhost === "off"
|
|
1078
|
+
? d.localhost
|
|
1079
|
+
: null,
|
|
1080
|
+
remoteGrants: d.remoteGrants === "owner" || d.remoteGrants === "off" ? d.remoteGrants : null,
|
|
1081
|
+
via: d.via === "im" ? "im" : d.via === "device" ? "device" : "web",
|
|
1074
1082
|
confirmed: d.confirmed === true,
|
|
1075
1083
|
};
|
|
1076
1084
|
}
|
package/dist/errors.d.ts
CHANGED
|
@@ -225,10 +225,17 @@ export declare const ERROR_CODES: {
|
|
|
225
225
|
readonly "plugin.upgrade_stale": 409;
|
|
226
226
|
readonly "plugin.upgrade_target_invalid": 409;
|
|
227
227
|
readonly "plugin.upgrade_failed": 502;
|
|
228
|
+
/**
|
|
229
|
+
* The platform's import and update imported the version but the update failed: the platform
|
|
230
|
+
* stays on its version, the imported version is in the catalog (retry with the plain update).
|
|
231
|
+
*/
|
|
232
|
+
readonly "plugin.imported_not_updated": 409;
|
|
228
233
|
readonly "setup.already_completed": 409;
|
|
229
234
|
readonly "setup.token_invalid": 403;
|
|
230
235
|
/** The admin overview's seven-day usage aggregate ran past its statement timeout (spec §5.5). */
|
|
231
236
|
readonly "admin.usage_unavailable": 503;
|
|
237
|
+
/** No dead outbox row has this id (delivered, discarded, retried, or never dead). */
|
|
238
|
+
readonly "outbox.not_found": 404;
|
|
232
239
|
readonly "agent.not_found": 404;
|
|
233
240
|
readonly "agent.version_conflict": 409;
|
|
234
241
|
/** The profile was changed since the client read it (`expectedUpdatedAt` is stale). */
|
|
@@ -319,6 +326,10 @@ export declare const ERROR_CODES: {
|
|
|
319
326
|
readonly "attachment.too_many_pending": 429;
|
|
320
327
|
readonly "sandbox.unavailable": 503;
|
|
321
328
|
readonly "sandbox.busy": 409;
|
|
329
|
+
/** A cloud sandbox provider refused the device's tier (its config refuses open egress). */
|
|
330
|
+
readonly "sandbox.tier_refused": 409;
|
|
331
|
+
/** A cloud sandbox provider failed to create a device's sandbox for another reason. */
|
|
332
|
+
readonly "sandbox.create_failed": 502;
|
|
322
333
|
/** A sandbox file path the kernel reads is not absolute or not under the sandbox roots. */
|
|
323
334
|
readonly "sandbox.file_invalid_path": 422;
|
|
324
335
|
/** No file at the sandbox path the kernel reads. */
|
package/dist/errors.js
CHANGED
|
@@ -225,10 +225,17 @@ export const ERROR_CODES = {
|
|
|
225
225
|
"plugin.upgrade_stale": 409,
|
|
226
226
|
"plugin.upgrade_target_invalid": 409,
|
|
227
227
|
"plugin.upgrade_failed": 502,
|
|
228
|
+
/**
|
|
229
|
+
* The platform's import and update imported the version but the update failed: the platform
|
|
230
|
+
* stays on its version, the imported version is in the catalog (retry with the plain update).
|
|
231
|
+
*/
|
|
232
|
+
"plugin.imported_not_updated": 409,
|
|
228
233
|
"setup.already_completed": 409,
|
|
229
234
|
"setup.token_invalid": 403,
|
|
230
235
|
/** The admin overview's seven-day usage aggregate ran past its statement timeout (spec §5.5). */
|
|
231
236
|
"admin.usage_unavailable": 503,
|
|
237
|
+
/** No dead outbox row has this id (delivered, discarded, retried, or never dead). */
|
|
238
|
+
"outbox.not_found": 404,
|
|
232
239
|
"agent.not_found": 404,
|
|
233
240
|
"agent.version_conflict": 409,
|
|
234
241
|
/** The profile was changed since the client read it (`expectedUpdatedAt` is stale). */
|
|
@@ -319,6 +326,10 @@ export const ERROR_CODES = {
|
|
|
319
326
|
"attachment.too_many_pending": 429,
|
|
320
327
|
"sandbox.unavailable": 503,
|
|
321
328
|
"sandbox.busy": 409,
|
|
329
|
+
/** A cloud sandbox provider refused the device's tier (its config refuses open egress). */
|
|
330
|
+
"sandbox.tier_refused": 409,
|
|
331
|
+
/** A cloud sandbox provider failed to create a device's sandbox for another reason. */
|
|
332
|
+
"sandbox.create_failed": 502,
|
|
322
333
|
/** A sandbox file path the kernel reads is not absolute or not under the sandbox roots. */
|
|
323
334
|
"sandbox.file_invalid_path": 422,
|
|
324
335
|
/** No file at the sandbox path the kernel reads. */
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ContentBlock, type Logger, type ModelUsage, type StopReason } from "@capybara-hq/plugin-sdk";
|
|
1
|
+
import { type ContentBlock, type ConversationSource, type Logger, type ModelUsage, type StopReason } from "@capybara-hq/plugin-sdk";
|
|
2
2
|
import { type ModelRef } from "../agents/definition.js";
|
|
3
3
|
import { EventLog } from "../conversations/event-log.js";
|
|
4
4
|
import type { KernelSchemaDb } from "../db/kernel-schema.js";
|
|
@@ -98,6 +98,8 @@ export interface ReplaySeed {
|
|
|
98
98
|
evalRunId: string;
|
|
99
99
|
orgId: string;
|
|
100
100
|
agentId: string;
|
|
101
|
+
/** The original conversation's source: the replay offers the tools of this source. */
|
|
102
|
+
source: ConversationSource;
|
|
101
103
|
accountId: string;
|
|
102
104
|
conversationId: string;
|
|
103
105
|
runId: string;
|
|
@@ -121,11 +123,20 @@ export interface ReplaySeed {
|
|
|
121
123
|
runContext: {
|
|
122
124
|
payload: unknown;
|
|
123
125
|
} | undefined;
|
|
126
|
+
/**
|
|
127
|
+
* The original run's `plugin_context` events (memory spec §6.1), in log order, copied as is:
|
|
128
|
+
* replays never call context providers.
|
|
129
|
+
*/
|
|
130
|
+
pluginContexts: {
|
|
131
|
+
payload: {
|
|
132
|
+
plugin: string;
|
|
133
|
+
};
|
|
134
|
+
}[];
|
|
124
135
|
}
|
|
125
136
|
/**
|
|
126
137
|
* The seed of a full-run replay (C6): the `kind = 'eval'` conversation with the events before the
|
|
127
138
|
* original run at their original seqs, the run row and its `run_started` (and the original
|
|
128
|
-
* `skills_catalog`), in one transaction of its own.
|
|
139
|
+
* `skills_catalog`, `run_context` and `plugin_context` events), in one transaction of its own.
|
|
129
140
|
*/
|
|
130
141
|
export declare class EvalReplaySeeder {
|
|
131
142
|
private readonly db;
|
package/dist/evals/run-replay.js
CHANGED
|
@@ -18,7 +18,9 @@ import { ApiError } from "../errors.js";
|
|
|
18
18
|
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
19
19
|
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
20
20
|
import { Transactional } from "../infra/database/transactional.decorator.js";
|
|
21
|
+
import { pluginContextIdemKey } from "../runtime/context/plugin-context.js";
|
|
21
22
|
import { estimateTokens } from "../runtime/context/tokens.js";
|
|
23
|
+
import { runDeadline, tightestLimit } from "../runtime/limits.js";
|
|
22
24
|
import { RunError } from "../runtime/model-call.js";
|
|
23
25
|
import { runContextIdemKey } from "../runtime/run-context.service.js";
|
|
24
26
|
import { recordedSkillForks } from "../runtime/subagents/skill-fork.js";
|
|
@@ -35,7 +37,7 @@ export class ReplayClaimLost extends Error {
|
|
|
35
37
|
/**
|
|
36
38
|
* The seed of a full-run replay (C6): the `kind = 'eval'` conversation with the events before the
|
|
37
39
|
* original run at their original seqs, the run row and its `run_started` (and the original
|
|
38
|
-
* `skills_catalog`), in one transaction of its own.
|
|
40
|
+
* `skills_catalog`, `run_context` and `plugin_context` events), in one transaction of its own.
|
|
39
41
|
*/
|
|
40
42
|
let EvalReplaySeeder = class EvalReplaySeeder {
|
|
41
43
|
db;
|
|
@@ -73,6 +75,7 @@ let EvalReplaySeeder = class EvalReplaySeeder {
|
|
|
73
75
|
consumed_seq: p.consumed,
|
|
74
76
|
kind: "eval",
|
|
75
77
|
eval_run_id: evalRunId,
|
|
78
|
+
source: p.source,
|
|
76
79
|
})
|
|
77
80
|
.execute();
|
|
78
81
|
for (let i = 0; i < p.seeds.length; i += SEED_BATCH) {
|
|
@@ -132,6 +135,12 @@ let EvalReplaySeeder = class EvalReplaySeeder {
|
|
|
132
135
|
},
|
|
133
136
|
]
|
|
134
137
|
: []),
|
|
138
|
+
...p.pluginContexts.map((c) => ({
|
|
139
|
+
type: "plugin_context",
|
|
140
|
+
runId,
|
|
141
|
+
idemKey: pluginContextIdemKey(runId, c.payload.plugin),
|
|
142
|
+
payload: c.payload,
|
|
143
|
+
})),
|
|
135
144
|
]);
|
|
136
145
|
if (onSeeded && !(await onSeeded(conversationId)))
|
|
137
146
|
throw new ReplayClaimLost();
|
|
@@ -332,6 +341,9 @@ export async function replayRun(d, s, candidate, evalRunId, signal) {
|
|
|
332
341
|
const trigger = replayTrigger(start.payload, seeds, consumed);
|
|
333
342
|
const catalog = s.events.find((e) => e.runId === s.runId && e.type === "skills_catalog" && e.seq > R);
|
|
334
343
|
const runContext = s.events.find((e) => e.runId === s.runId && e.type === "run_context" && e.seq > R);
|
|
344
|
+
const pluginContexts = s.events
|
|
345
|
+
.filter((e) => e.runId === s.runId && e.type === "plugin_context" && e.seq > R)
|
|
346
|
+
.map((e) => ({ payload: e.payload }));
|
|
335
347
|
// a sub-agent's case (spec §15.3) runs the effective definition frozen in its subagent_started
|
|
336
348
|
// (the request renders that event's preamble and instructions; its tools come from it too), with
|
|
337
349
|
// only the candidate's model and params, and the logged step limit
|
|
@@ -347,12 +359,16 @@ export async function replayRun(d, s, candidate, evalRunId, signal) {
|
|
|
347
359
|
const forkRun = triggerEvent?.type === "user_message" &&
|
|
348
360
|
triggerEvent.payload.skill !== undefined &&
|
|
349
361
|
triggerEvent.payload.dispatch !== undefined;
|
|
362
|
+
// the original conversation's source (recorded in the case); a case taken before it was
|
|
363
|
+
// recorded replays as its run did by default: a web conversation, or a sub-agent's for a child
|
|
364
|
+
const source = s.source ?? (subagent === undefined ? "web" : "subagent");
|
|
350
365
|
const conversationId = randomUUID();
|
|
351
366
|
const runId = randomUUID();
|
|
352
367
|
await d.seeder.seed({
|
|
353
368
|
evalRunId,
|
|
354
369
|
orgId: s.orgId,
|
|
355
370
|
agentId: s.agentId,
|
|
371
|
+
source,
|
|
356
372
|
accountId,
|
|
357
373
|
conversationId,
|
|
358
374
|
runId,
|
|
@@ -364,6 +380,7 @@ export async function replayRun(d, s, candidate, evalRunId, signal) {
|
|
|
364
380
|
model: agent.model,
|
|
365
381
|
catalog,
|
|
366
382
|
runContext,
|
|
383
|
+
pluginContexts,
|
|
367
384
|
}, d.onSeeded);
|
|
368
385
|
await d.hooks?.afterRunSeeded?.(evalRunId, conversationId);
|
|
369
386
|
// 停止:取消 / 越限 / 熔断 / 失去认领 / 截止时间 → 中止 loop(进行中的请求立即停下,loop 以 aborted 返回)
|
|
@@ -526,16 +543,28 @@ export async function replayRun(d, s, candidate, evalRunId, signal) {
|
|
|
526
543
|
return out;
|
|
527
544
|
};
|
|
528
545
|
const runtime = d.config.get().runtime;
|
|
529
|
-
|
|
546
|
+
// 0 in the config or the logged child limit = no limit there
|
|
547
|
+
const maxSteps = tightestLimit(subagent?.maxSteps, runtime.maxSteps);
|
|
548
|
+
// `runtime.maxRunSeconds` 0: no deadline and no timer
|
|
549
|
+
const deadline = runDeadline(Date.now(), runtime.maxRunSeconds);
|
|
530
550
|
let deadlineHit = false;
|
|
531
|
-
const timer =
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
551
|
+
const timer = deadline === undefined
|
|
552
|
+
? undefined
|
|
553
|
+
: setTimeout(() => {
|
|
554
|
+
deadlineHit = true;
|
|
555
|
+
stopper.abort();
|
|
556
|
+
}, Math.max(0, deadline - Date.now()));
|
|
557
|
+
timer?.unref();
|
|
536
558
|
// a fork invocation's report is the recorded one: a replay never dispatches a sub-agent (A5)
|
|
537
559
|
const skillForks = recordedSkillForks(eventLog, s.events.filter((e) => e.runId === s.runId));
|
|
538
560
|
const loop = d.loop({ toolPipeline: pipeline, registry, skillForks, ...(media && { media }) });
|
|
561
|
+
// a replay never executes a tool, so the role only completes the context
|
|
562
|
+
const membership = await db
|
|
563
|
+
.selectFrom("memberships")
|
|
564
|
+
.select("role")
|
|
565
|
+
.where("org_id", "=", s.orgId)
|
|
566
|
+
.where("account_id", "=", accountId)
|
|
567
|
+
.executeTakeFirst();
|
|
539
568
|
let outcome;
|
|
540
569
|
try {
|
|
541
570
|
outcome = await loop.run({
|
|
@@ -544,6 +573,10 @@ export async function replayRun(d, s, candidate, evalRunId, signal) {
|
|
|
544
573
|
runId,
|
|
545
574
|
agentId: s.agentId,
|
|
546
575
|
triggerAccountId: accountId,
|
|
576
|
+
// the seeded conversation's source (the original's): its tools are the original's
|
|
577
|
+
source,
|
|
578
|
+
ownerAccountId: accountId,
|
|
579
|
+
role: membership?.role ?? "guest",
|
|
547
580
|
agent,
|
|
548
581
|
agentVersion: candidate.agentVersion,
|
|
549
582
|
signal: loopSignal,
|
|
@@ -551,8 +584,8 @@ export async function replayRun(d, s, candidate, evalRunId, signal) {
|
|
|
551
584
|
sink: () => { },
|
|
552
585
|
pendingAbsorbable,
|
|
553
586
|
limits: {
|
|
554
|
-
maxSteps
|
|
555
|
-
deadline,
|
|
587
|
+
...(maxSteps !== undefined && { maxSteps }),
|
|
588
|
+
...(deadline !== undefined && { deadline }),
|
|
556
589
|
},
|
|
557
590
|
// replays never fire plugin hooks (the loop deps omit them too, P-18)
|
|
558
591
|
conversationKind: "eval",
|
package/dist/evals/snapshot.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ToolSpec } from "@capybara-hq/plugin-sdk";
|
|
1
|
+
import type { ConversationSource, ToolSpec } from "@capybara-hq/plugin-sdk";
|
|
2
2
|
import { type AgentDefinition } from "../agents/definition.js";
|
|
3
3
|
import type { ConversationEvent, ConversationEventType } from "../conversations/events.js";
|
|
4
4
|
import type { KernelSchemaDb } from "../db/kernel-schema.js";
|
|
@@ -21,6 +21,12 @@ export interface CaseSnapshot {
|
|
|
21
21
|
runId: string;
|
|
22
22
|
agentId: string;
|
|
23
23
|
agentVersion: number;
|
|
24
|
+
/**
|
|
25
|
+
* The original conversation's source (memory plugin spec §6.2): a full-run replay offers the
|
|
26
|
+
* tools of this source. Cases taken before it was recorded have none (replayed as `web`, or as
|
|
27
|
+
* `subagent` for a child case).
|
|
28
|
+
*/
|
|
29
|
+
source?: ConversationSource;
|
|
24
30
|
/** seq ≤ 该运行终态事件的 seq,按 seq 升序。 */
|
|
25
31
|
events: SnapshotEvent[];
|
|
26
32
|
/** 事件里各次运行(`run_started`)与本运行引用的 agent 版本 → 定义。 */
|
package/dist/evals/snapshot.js
CHANGED
|
@@ -17,7 +17,7 @@ const PAGE = 1000;
|
|
|
17
17
|
export async function takeSnapshot(db, orgId, conversationId, runId, maxBytes) {
|
|
18
18
|
const conv = await db
|
|
19
19
|
.selectFrom("conversations")
|
|
20
|
-
.select(["agent_id", "kind"])
|
|
20
|
+
.select(["agent_id", "kind", "source"])
|
|
21
21
|
.where("id", "=", conversationId)
|
|
22
22
|
.where("org_id", "=", orgId)
|
|
23
23
|
.executeTakeFirst();
|
|
@@ -126,6 +126,7 @@ export async function takeSnapshot(db, orgId, conversationId, runId, maxBytes) {
|
|
|
126
126
|
runId,
|
|
127
127
|
agentId: conv.agent_id,
|
|
128
128
|
agentVersion: run.agent_version,
|
|
129
|
+
source: conv.source,
|
|
129
130
|
events,
|
|
130
131
|
definitions,
|
|
131
132
|
toolSpecs,
|
|
@@ -28,6 +28,7 @@ import { SingleProviderGuard } from "./single-provider.js";
|
|
|
28
28
|
import { ReplicaStatusStore } from "./sources.js";
|
|
29
29
|
import { ExtensionStateModule } from "./state.module.js";
|
|
30
30
|
import { ExtensionAvailability } from "./unavailable.js";
|
|
31
|
+
import { UsageSubscribersModule } from "./usage-subscribers.js";
|
|
31
32
|
/**
|
|
32
33
|
* The extensions domain: the extension host (it builds each `ctx` from the injected kernel
|
|
33
34
|
* services), the package store and install records (the desired state the host converges to, one
|
|
@@ -48,6 +49,8 @@ ExtensionsModule = __decorate([
|
|
|
48
49
|
ExtensionStateModule,
|
|
49
50
|
// ctx.models: the embeddings broker (models domain, below the host through ExtensionStateModule)
|
|
50
51
|
ModelsModule,
|
|
52
|
+
// the lifecycle clears the `usage.report` subscriber cache on PLUGINS_CHANGED
|
|
53
|
+
UsageSubscribersModule,
|
|
51
54
|
],
|
|
52
55
|
providers: [
|
|
53
56
|
PluginInstallLock,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type AvailableHook, type HookHandler, type HookOptions, type HookPayloads, type Logger, type PolicyHook } from "@capybara-hq/plugin-sdk";
|
|
1
|
+
import { type AvailableHook, type HookHandler, type HookOptions, type HookPayloads, type LIFECYCLE_HOOKS, type Logger, type PolicyHook } from "@capybara-hq/plugin-sdk";
|
|
2
2
|
import type { KernelConfig } from "../config/schema.js";
|
|
3
3
|
import type { LockService } from "../infra/lock/lock.service.js";
|
|
4
4
|
import { type EffectiveConfig } from "./effective-config.js";
|
|
@@ -63,7 +63,16 @@ export interface HookSource {
|
|
|
63
63
|
readonly name: string;
|
|
64
64
|
readonly hooks: readonly RegisteredHook[];
|
|
65
65
|
readonly inflight: InflightTracker;
|
|
66
|
+
/** The instance's platform deployment config (what lifecycle hooks are called with). */
|
|
67
|
+
readonly config?: unknown;
|
|
66
68
|
}
|
|
69
|
+
/** A lifecycle mount: an observe-only notice that a member, an agent or an org was removed. */
|
|
70
|
+
export type LifecycleHook = (typeof LIFECYCLE_HOOKS)[number];
|
|
71
|
+
/**
|
|
72
|
+
* Mounts delivered by a queue (at least once, by plugin) instead of {@link HookRegistry.run}:
|
|
73
|
+
* `usage.report` and the lifecycle mounts.
|
|
74
|
+
*/
|
|
75
|
+
export type QueuedHook = "usage.report" | LifecycleHook;
|
|
67
76
|
/** Error text that never throws itself (a hostile `message` getter or `toString`). */
|
|
68
77
|
export declare function errMsg(e: unknown): string;
|
|
69
78
|
/**
|
|
@@ -166,8 +175,18 @@ export declare class HookRegistry {
|
|
|
166
175
|
plugin: string;
|
|
167
176
|
handler: HookHandler<"usage.report">;
|
|
168
177
|
}[];
|
|
169
|
-
/**
|
|
170
|
-
|
|
178
|
+
/**
|
|
179
|
+
* Lifecycle handlers by plugin name (delivered by the lifecycle queue, `lifecycle-hooks.ts`, not
|
|
180
|
+
* by {@link run}). Each call runs through the instance's `inflight.run` with the plugin's platform
|
|
181
|
+
* deployment config, never an org's copy: cleanup goes out whether or not the org's effective
|
|
182
|
+
* config is valid (or the org still exists).
|
|
183
|
+
*/
|
|
184
|
+
lifecycleHandlers<K extends LifecycleHook>(mount: K): {
|
|
185
|
+
plugin: string;
|
|
186
|
+
handler: HookHandler<K>;
|
|
187
|
+
}[];
|
|
188
|
+
/** Runs the hooks of a mount point (see the class comment); queue-delivered mounts excluded. */
|
|
189
|
+
run<K extends Exclude<AvailableHook, QueuedHook>>(mount: K, payload: HookPayloads[K], signal: AbortSignal): Promise<HookOutcome>;
|
|
171
190
|
/**
|
|
172
191
|
* The M-1 denial: `missing` (sorted) are enabled policy plugins that are not loaded on this replica.
|
|
173
192
|
* The model-visible reason is generic; the detail and a throttled warning name the plugin for the
|
package/dist/extensions/hooks.js
CHANGED
|
@@ -189,7 +189,21 @@ export class HookRegistry {
|
|
|
189
189
|
}),
|
|
190
190
|
}));
|
|
191
191
|
}
|
|
192
|
-
/**
|
|
192
|
+
/**
|
|
193
|
+
* Lifecycle handlers by plugin name (delivered by the lifecycle queue, `lifecycle-hooks.ts`, not
|
|
194
|
+
* by {@link run}). Each call runs through the instance's `inflight.run` with the plugin's platform
|
|
195
|
+
* deployment config, never an org's copy: cleanup goes out whether or not the org's effective
|
|
196
|
+
* config is valid (or the org still exists).
|
|
197
|
+
*/
|
|
198
|
+
lifecycleHandlers(mount) {
|
|
199
|
+
return this.entries(mount).map(({ src, hook }) => ({
|
|
200
|
+
plugin: src.name,
|
|
201
|
+
handler: (payload, signal) => src.inflight.run(signal, async (s) => {
|
|
202
|
+
await hook.handler(payload, s, { config: src.config });
|
|
203
|
+
}),
|
|
204
|
+
}));
|
|
205
|
+
}
|
|
206
|
+
/** Runs the hooks of a mount point (see the class comment); queue-delivered mounts excluded. */
|
|
193
207
|
async run(mount, payload, signal) {
|
|
194
208
|
if (signal.aborted)
|
|
195
209
|
throw signal.reason;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { HookPayloads } from "@capybara-hq/plugin-sdk";
|
|
2
|
+
import { WorkerHost } from "@nestjs/bullmq";
|
|
3
|
+
import type { Job, Worker } from "bullmq";
|
|
4
|
+
import type { KernelSchemaDb } from "../db/kernel-schema.js";
|
|
5
|
+
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
6
|
+
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
7
|
+
import { OutboxService } from "../infra/outbox/outbox.service.js";
|
|
8
|
+
import type { KernelLogger } from "../log.js";
|
|
9
|
+
import { type LifecycleHook } from "./hooks.js";
|
|
10
|
+
import { ExtensionHost } from "./host.js";
|
|
11
|
+
/** Queue of lifecycle hook deliveries (no `:` in the name). */
|
|
12
|
+
export declare const LIFECYCLE_QUEUE = "cap-kernel-lifecycle";
|
|
13
|
+
/** The job name of every delivery (registered in `infra/outbox/registry.ts`). */
|
|
14
|
+
export declare const LIFECYCLE_JOB = "lifecycle";
|
|
15
|
+
/** One handler call's time limit. */
|
|
16
|
+
export declare const LIFECYCLE_HOOK_TIMEOUT_MS = 5000;
|
|
17
|
+
/** Job data: one mount's payload for one subscribing plugin. */
|
|
18
|
+
export interface LifecycleJob<K extends LifecycleHook = LifecycleHook> {
|
|
19
|
+
mount: K;
|
|
20
|
+
plugin: string;
|
|
21
|
+
payload: HookPayloads[K];
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The deterministic job id of one delivery: the mount, the plugin and the payload's ids (no `:`;
|
|
25
|
+
* kernel ids and plugin names carry none). Removing the same member, agent or org again while the
|
|
26
|
+
* first delivery is still owed is the same job.
|
|
27
|
+
*/
|
|
28
|
+
export declare function lifecycleJobId<K extends LifecycleHook>(mount: K, plugin: string, payload: HookPayloads[K]): string;
|
|
29
|
+
/**
|
|
30
|
+
* Plugins that subscribe to a lifecycle mount according to deployment state: the approved
|
|
31
|
+
* permission snapshot declares the mount in `permissions.hooks`, and that approved version is the
|
|
32
|
+
* one the deployment runs: an enabled install of that version, or a replica status row (not
|
|
33
|
+
* `unloaded`) that loaded or still serves it (an upgrade pending approval keeps serving the
|
|
34
|
+
* approved version; a config-declared plugin has only replica rows). A snapshot approved for
|
|
35
|
+
* another version than the one installed or served does not count. Org enablement does not count
|
|
36
|
+
* either: cleanup concerns data the plugin already holds (memory plugin spec §6.3). Sorted by name.
|
|
37
|
+
*/
|
|
38
|
+
export declare function queryLifecycleSubscribers(db: KernelSchemaDb, mount: LifecycleHook): Promise<string[]>;
|
|
39
|
+
/**
|
|
40
|
+
* Durable delivery of the lifecycle hooks `org.member_removed`, `agent.deleted` and `org.deleted`
|
|
41
|
+
* (memory plugin spec §6.3), so extensions reliably learn to delete the data they hold:
|
|
42
|
+
* - **Owe** ({@link recordIn}): in the transaction that removes the member, the agent or the org,
|
|
43
|
+
* one outbox job per subscribing plugin ({@link queryLifecycleSubscribers}) on
|
|
44
|
+
* {@link LIFECYCLE_QUEUE}, under {@link lifecycleJobId}, with no `org_id` (it is the deletion's
|
|
45
|
+
* own cleanup work, transactional outbox spec §9); a rollback owes nothing. The outbox delivers
|
|
46
|
+
* it to BullMQ after the commit (registry: attempts and backoff).
|
|
47
|
+
* - **Deliver** ({@link process}, worker role): the plugin's handlers loaded on this replica are
|
|
48
|
+
* called in turn (5 s each). Not loaded on this replica -> the job fails and is retried (another
|
|
49
|
+
* replica or a later attempt). Loaded without a handler -> dropped with a warning. Handlers get
|
|
50
|
+
* the plugin's platform deployment config (`call.config`), never an org's copy, so an org whose
|
|
51
|
+
* effective config is not valid still gets its cleanup. The last failed attempt logs an error.
|
|
52
|
+
* Handlers must be idempotent (a delivery may repeat when a replica dies after the call).
|
|
53
|
+
*/
|
|
54
|
+
export declare class LifecycleHooks {
|
|
55
|
+
private readonly kdb;
|
|
56
|
+
readonly txHost: TransactionHost;
|
|
57
|
+
private readonly outbox;
|
|
58
|
+
/** The loaded plugins' handlers (`hooks.handlers`) and whether a plugin is active here. */
|
|
59
|
+
private readonly host;
|
|
60
|
+
private readonly log;
|
|
61
|
+
constructor(kdb: KernelDb, txHost: TransactionHost, outbox: Pick<OutboxService, "add">,
|
|
62
|
+
/** The loaded plugins' handlers (`hooks.handlers`) and whether a plugin is active here. */
|
|
63
|
+
host: Pick<ExtensionHost, "hooks" | "current">, log: KernelLogger);
|
|
64
|
+
/**
|
|
65
|
+
* Owes one delivery per subscribing plugin in the caller's transaction (`mandatory`); returns
|
|
66
|
+
* the plugins.
|
|
67
|
+
*/
|
|
68
|
+
recordIn<K extends LifecycleHook>(mount: K, payload: HookPayloads[K]): Promise<string[]>;
|
|
69
|
+
/** One delivery job (the consumer {@link LifecycleHooksProcessor} calls it). */
|
|
70
|
+
process(data: LifecycleJob, stop: AbortSignal): Promise<void>;
|
|
71
|
+
/** One handler call, aborted after {@link LIFECYCLE_HOOK_TIMEOUT_MS} or when the replica stops. */
|
|
72
|
+
private call;
|
|
73
|
+
/** A job failed (the worker's `failed` event): a retry warns, the last attempt logs an error. */
|
|
74
|
+
onFailed(j: Job<LifecycleJob> | undefined, err: Error): void;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* The consumer of {@link LIFECYCLE_QUEUE}: {@link LifecycleHooks.process}. Worker role only
|
|
78
|
+
* (`LifecycleHookWorkersModule`); built with `autorun: false`, started by the worker role after the
|
|
79
|
+
* extensions loaded.
|
|
80
|
+
*/
|
|
81
|
+
export declare class LifecycleHooksProcessor extends WorkerHost<Worker<LifecycleJob>> {
|
|
82
|
+
private readonly hooks;
|
|
83
|
+
private readonly log;
|
|
84
|
+
private readonly stopping;
|
|
85
|
+
constructor(hooks: LifecycleHooks, log: Pick<KernelLogger, "error">);
|
|
86
|
+
process(job: Job<LifecycleJob>): Promise<void>;
|
|
87
|
+
/** Starts consuming (worker role, after the extensions loaded). */
|
|
88
|
+
start(): void;
|
|
89
|
+
/** Aborts the calls in flight and closes the worker. */
|
|
90
|
+
close(): Promise<void>;
|
|
91
|
+
failed(job: Job<LifecycleJob> | undefined, err: Error): void;
|
|
92
|
+
error(err: Error): void;
|
|
93
|
+
}
|
|
94
|
+
/** Lifecycle hook delivery: owing the jobs, and the queue the outbox adds them to, on every role. */
|
|
95
|
+
export declare class LifecycleHooksModule {
|
|
96
|
+
}
|
|
97
|
+
/** The lifecycle queue's consumer; imported only by the worker role (`WorkerModule`). */
|
|
98
|
+
export declare class LifecycleHookWorkersModule {
|
|
99
|
+
}
|