@capybara-hq/kernel 0.1.0-rc.22 → 0.1.0-rc.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bootstrap.json +25 -25
- package/dist/admin/admin.controller.d.ts +3 -3
- package/dist/admin/admin.controller.js +6 -2
- package/dist/admin/admin.module.d.ts +2 -1
- package/dist/admin/admin.module.js +7 -3
- package/dist/admin/admin.schemas.d.ts +13 -0
- package/dist/admin/outbox-dead.controller.d.ts +23 -0
- package/dist/admin/outbox-dead.controller.js +62 -0
- package/dist/admin/outbox-dead.schemas.d.ts +8 -0
- package/dist/admin/outbox-dead.schemas.js +18 -0
- package/dist/admin/outbox-dead.service.d.ts +66 -0
- package/dist/admin/outbox-dead.service.js +179 -0
- package/dist/admin/overview.service.d.ts +40 -5
- package/dist/admin/overview.service.js +41 -7
- package/dist/agents/from-template.js +1 -1
- package/dist/background/finish.d.ts +5 -2
- package/dist/background/finish.js +14 -3
- package/dist/background/launch.d.ts +44 -5
- package/dist/background/launch.js +92 -9
- package/dist/background/session.d.ts +14 -2
- package/dist/background/session.js +26 -4
- package/dist/background/status.d.ts +1 -1
- package/dist/background/status.js +3 -3
- package/dist/background/stop.d.ts +0 -2
- package/dist/background/stop.js +1 -21
- 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-content.d.ts +5 -0
- package/dist/catalog/catalog-content.js +1 -0
- package/dist/catalog/catalog.module.d.ts +2 -1
- package/dist/catalog/catalog.module.js +5 -1
- package/dist/catalog/marketplaces.service.d.ts +1 -1
- package/dist/catalog/platform-import-update.d.ts +50 -0
- package/dist/catalog/platform-import-update.js +116 -0
- package/dist/catalog/plugin-import.d.ts +36 -1
- package/dist/catalog/plugin-import.js +23 -1
- package/dist/catalog/plugin-upgrade.d.ts +18 -0
- package/dist/catalog/plugin-upgrade.js +19 -0
- package/dist/catalog/requests.service.js +2 -2
- package/dist/channels/channels.module.d.ts +5 -2
- package/dist/channels/channels.module.js +9 -2
- package/dist/channels/deliver-job.d.ts +25 -0
- package/dist/channels/deliver-job.js +25 -0
- package/dist/channels/inbound.d.ts +1 -1
- package/dist/channels/inbound.js +2 -15
- package/dist/channels/jobs.d.ts +28 -18
- package/dist/channels/jobs.js +63 -26
- package/dist/config/schema.d.ts +3 -6
- package/dist/config/schema.js +20 -27
- package/dist/conversations/agent-tree.d.ts +16 -0
- package/dist/conversations/agent-tree.js +36 -0
- package/dist/conversations/conversations.service.d.ts +44 -14
- package/dist/conversations/conversations.service.js +52 -38
- package/dist/conversations/event-log.d.ts +7 -10
- package/dist/conversations/event-log.js +24 -15
- package/dist/conversations/events.js +2 -1
- package/dist/conversations/queue.service.d.ts +8 -3
- package/dist/conversations/queue.service.js +37 -13
- package/dist/conversations/titles.d.ts +21 -8
- package/dist/conversations/titles.js +42 -15
- package/dist/db/kernel-schema.d.ts +26 -8
- package/dist/devices/devices.service.d.ts +12 -3
- package/dist/devices/devices.service.js +30 -9
- package/dist/errors.d.ts +15 -0
- package/dist/errors.js +15 -0
- package/dist/evals/run-replay.js +14 -8
- package/dist/extensions/extensions.module.js +3 -0
- package/dist/extensions/lifecycle.d.ts +6 -1
- package/dist/extensions/lifecycle.js +9 -2
- package/dist/extensions/usage-subscribers.d.ts +43 -0
- package/dist/extensions/usage-subscribers.js +108 -0
- package/dist/http/api.module.js +2 -0
- package/dist/identity/settings.d.ts +53 -3
- package/dist/identity/settings.js +92 -9
- 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/config/kernel-config.service.d.ts +7 -0
- package/dist/infra/config/kernel-config.service.js +20 -0
- package/dist/infra/events/kernel-events.d.ts +9 -35
- package/dist/infra/outbox/outbox-relay.module.d.ts +6 -0
- package/dist/infra/outbox/outbox-relay.module.js +18 -0
- package/dist/infra/outbox/outbox.dispatcher.d.ts +103 -0
- package/dist/infra/outbox/outbox.dispatcher.js +310 -0
- package/dist/infra/outbox/outbox.module.d.ts +24 -0
- package/dist/infra/outbox/outbox.module.js +50 -0
- package/dist/infra/outbox/outbox.relay.d.ts +31 -0
- package/dist/infra/outbox/outbox.relay.js +76 -0
- package/dist/infra/outbox/outbox.service.d.ts +51 -0
- package/dist/infra/outbox/outbox.service.js +131 -0
- package/dist/infra/outbox/registry.d.ts +120 -0
- package/dist/infra/outbox/registry.js +108 -0
- package/dist/infra/queue/queue-names.d.ts +8 -0
- package/dist/infra/queue/queue-names.js +16 -0
- package/dist/kernel.module.js +3 -0
- package/dist/local-devices/device-policy.service.d.ts +4 -0
- package/dist/local-devices/device-policy.service.js +61 -3
- package/dist/local-devices/local-device-gateway.d.ts +4 -2
- package/dist/local-devices/local-device-gateway.js +39 -14
- package/dist/local-devices/machines.d.ts +25 -1
- package/dist/local-devices/machines.js +91 -15
- package/dist/local-devices/policy-summary.d.ts +44 -1
- package/dist/local-devices/policy-summary.js +69 -0
- package/dist/models/embeddings.d.ts +9 -9
- package/dist/models/embeddings.js +11 -18
- package/dist/models/models.module.js +3 -0
- package/dist/orgs/orgs.service.js +7 -0
- package/dist/orgs/settings.controller.js +2 -2
- package/dist/orgs/settings.service.d.ts +11 -5
- package/dist/orgs/settings.service.js +28 -14
- package/dist/overrides.d.ts +20 -3
- package/dist/overrides.js +10 -2
- package/dist/platform/exclusions.js +5 -0
- package/dist/platform/ops/admin.ops.js +50 -9
- 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/org-settings.ops.js +3 -3
- package/dist/platform/ops/orgs.ops.js +1 -0
- 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.js +1 -1
- package/dist/platform/ops/schedules.ops.js +40 -1
- package/dist/platform/platform-ops.module.d.ts +2 -1
- package/dist/platform/platform-ops.module.js +3 -0
- package/dist/platform/route-schemas.js +3 -0
- package/dist/roles/worker.module.js +9 -4
- package/dist/runtime/langgraph-loop.js +13 -5
- package/dist/runtime/limits.d.ts +15 -0
- package/dist/runtime/limits.js +25 -0
- package/dist/runtime/loop.d.ts +8 -3
- package/dist/runtime/loop.js +2 -1
- package/dist/runtime/maintenance.d.ts +4 -2
- package/dist/runtime/maintenance.js +17 -2
- package/dist/runtime/model-call.d.ts +14 -16
- package/dist/runtime/model-call.js +14 -24
- package/dist/runtime/queue-lost-jobs.d.ts +24 -0
- package/dist/runtime/queue-lost-jobs.js +51 -0
- package/dist/runtime/run-control.d.ts +42 -5
- package/dist/runtime/run-control.js +66 -12
- package/dist/runtime/run-finish.d.ts +39 -12
- package/dist/runtime/run-finish.js +97 -14
- package/dist/runtime/run-jobs.d.ts +95 -13
- package/dist/runtime/run-jobs.js +199 -28
- package/dist/runtime/run-orphan-check.job.d.ts +142 -0
- package/dist/runtime/run-orphan-check.job.js +339 -0
- package/dist/runtime/run-processors.d.ts +16 -9
- package/dist/runtime/run-processors.js +1 -1
- package/dist/runtime/run-queue.d.ts +64 -55
- package/dist/runtime/run-queue.js +147 -284
- package/dist/runtime/run-steps.d.ts +1 -2
- package/dist/runtime/run-steps.js +8 -11
- package/dist/runtime/run-worker.d.ts +6 -17
- package/dist/runtime/run-worker.js +32 -34
- package/dist/runtime/runtime.module.d.ts +2 -3
- package/dist/runtime/runtime.module.js +5 -6
- package/dist/runtime/step-log.d.ts +16 -4
- package/dist/runtime/step-log.js +26 -6
- package/dist/runtime/stream.controller.d.ts +2 -4
- package/dist/runtime/stream.controller.js +5 -11
- package/dist/runtime/subagents/child.js +1 -1
- package/dist/runtime/subagents/definition.d.ts +2 -1
- package/dist/runtime/subagents/definition.js +4 -2
- package/dist/runtime/subagents/deliveries.d.ts +6 -3
- package/dist/runtime/subagents/deliveries.js +16 -7
- package/dist/runtime/subagents/dispatch.d.ts +12 -12
- package/dist/runtime/subagents/dispatch.js +13 -38
- package/dist/runtime/subagents/messages.d.ts +5 -1
- package/dist/runtime/subagents/messages.js +11 -3
- package/dist/runtime/subagents/skill-fork.d.ts +3 -3
- package/dist/runtime/subagents/skill-fork.js +5 -5
- package/dist/runtime/subagents/subagents.service.js +1 -1
- package/dist/runtime/subagents/tree-jobs.d.ts +61 -0
- package/dist/runtime/subagents/tree-jobs.js +86 -0
- package/dist/runtime/usage-report-jobs.d.ts +23 -48
- package/dist/runtime/usage-report-jobs.js +67 -119
- package/dist/runtime/usage-report.d.ts +28 -77
- package/dist/runtime/usage-report.js +35 -213
- package/dist/runtime/waits.d.ts +48 -198
- package/dist/runtime/waits.js +103 -805
- package/dist/sandbox/platform-tiers.d.ts +86 -0
- package/dist/sandbox/platform-tiers.js +97 -0
- package/dist/sandbox/sandbox.service.d.ts +18 -5
- package/dist/sandbox/sandbox.service.js +75 -20
- package/dist/sandbox/tiers.d.ts +14 -2
- package/dist/sandbox/tiers.js +8 -0
- package/dist/schedules/calendar.d.ts +134 -0
- package/dist/schedules/calendar.js +410 -0
- package/dist/schedules/fire.d.ts +1 -3
- package/dist/schedules/fire.js +5 -10
- package/dist/schedules/run-tracker.d.ts +0 -5
- package/dist/schedules/run-tracker.js +0 -13
- package/dist/schedules/scan.job.js +15 -20
- package/dist/schedules/schedule-math.d.ts +42 -0
- package/dist/schedules/schedule-math.js +52 -0
- package/dist/schedules/scheduled-tasks.service.d.ts +16 -0
- package/dist/schedules/scheduled-tasks.service.js +53 -0
- package/dist/schedules/schedules.controller.d.ts +5 -1
- package/dist/schedules/schedules.controller.js +15 -1
- package/dist/schedules/schedules.schemas.d.ts +35 -0
- package/dist/schedules/schedules.schemas.js +27 -0
- package/dist/schedules/store.d.ts +5 -0
- package/dist/schedules/store.js +20 -0
- package/dist/tools/approvals.service.d.ts +26 -7
- package/dist/tools/approvals.service.js +88 -10
- package/dist/tools/builtin-names.d.ts +5 -3
- package/dist/tools/builtin-names.js +12 -4
- package/dist/tools/builtins/builtin-tools.d.ts +1 -2
- package/dist/tools/builtins/builtin-tools.js +21 -17
- package/dist/tools/builtins/sandbox/bash.js +9 -3
- package/dist/tools/builtins/sandbox/common.d.ts +18 -1
- package/dist/tools/builtins/sandbox/common.js +14 -1
- package/dist/tools/builtins/sandbox/index.d.ts +2 -1
- package/dist/tools/builtins/sandbox/index.js +4 -1
- package/dist/tools/builtins/sandbox/task-list.d.ts +12 -0
- package/dist/tools/builtins/sandbox/task-list.js +130 -0
- package/dist/tools/builtins/sandbox/task-output.d.ts +8 -1
- package/dist/tools/builtins/sandbox/task-output.js +21 -3
- package/dist/tools/builtins/web/extract.d.ts +2 -4
- package/dist/tools/builtins/web/extract.js +2 -5
- package/dist/tools/pipeline.d.ts +1 -1
- package/dist/tools/pipeline.js +1 -1
- package/dist/tools/questions.service.d.ts +15 -6
- package/dist/tools/questions.service.js +20 -8
- package/dist/tools/registry.d.ts +4 -2
- package/dist/tools/registry.js +4 -3
- package/dist/tools/tools.module.js +4 -3
- package/package.json +3 -3
- package/src/migrations/0118_device_policy_ops.sql +6 -0
- package/src/migrations/0119_outbox.sql +22 -0
- package/src/migrations/0120_drop_usage_report_sweep.sql +7 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import type { JobsOptions } from "bullmq";
|
|
2
|
+
import type { KernelQueueName } from "../queue/queue-names.js";
|
|
3
|
+
/** What the registry says about one job name added through the outbox (spec §3.5, §5). */
|
|
4
|
+
export interface OutboxJobSpec {
|
|
5
|
+
/** The kernel queue the job is added to, or the queues when one job name serves several. */
|
|
6
|
+
queue: KernelQueueName | readonly KernelQueueName[];
|
|
7
|
+
/** One sentence: why a duplicate delivery of this job is harmless (state check, key, id). */
|
|
8
|
+
idempotent: string;
|
|
9
|
+
/** Repo-relative path of the file whose `@Processor` handles the job. */
|
|
10
|
+
processedIn: string;
|
|
11
|
+
/**
|
|
12
|
+
* BullMQ options the dispatcher adds the job with over the queue defaults
|
|
13
|
+
* (`QUEUE_JOB_DEFAULTS`); the job id and the delay always come from the row.
|
|
14
|
+
*/
|
|
15
|
+
opts?: Pick<JobsOptions, "attempts" | "backoff" | "priority" | "removeOnComplete" | "removeOnFail">;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Every job name added through the outbox. The task that introduces a job name adds its entry; a
|
|
19
|
+
* name without one does not type-check as an `OutboxJobName` (`add` does not check it at runtime).
|
|
20
|
+
*/
|
|
21
|
+
export declare const OUTBOX_JOBS: {
|
|
22
|
+
readonly run: {
|
|
23
|
+
readonly queue: readonly ["cap-kernel-runs", "cap-kernel-subagent-runs"];
|
|
24
|
+
readonly idempotent: "the run worker starts only a run in `queued` state for this job's runId; a finished run's job only repeats the idempotent post-run ensureRun, a waiting run's does nothing";
|
|
25
|
+
readonly processedIn: "packages/kernel/src/runtime/run-processors.ts";
|
|
26
|
+
readonly opts: {
|
|
27
|
+
readonly attempts: 1;
|
|
28
|
+
readonly removeOnComplete: true;
|
|
29
|
+
readonly removeOnFail: true;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
readonly "ensure-run": {
|
|
33
|
+
readonly queue: "cap-kernel-maintenance";
|
|
34
|
+
readonly idempotent: "ensureRun decides from the conversation's state; with nothing unconsumed it is a no-op";
|
|
35
|
+
readonly processedIn: "packages/kernel/src/runtime/maintenance.ts";
|
|
36
|
+
readonly opts: {
|
|
37
|
+
readonly removeOnComplete: true;
|
|
38
|
+
readonly removeOnFail: true;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
readonly "wait-timeout": {
|
|
42
|
+
readonly queue: "cap-kernel-maintenance";
|
|
43
|
+
readonly idempotent: "a timeout acts only on a still-pending wait (a row lock and status check) and a resume starts only a waiting run with nothing else pending";
|
|
44
|
+
readonly processedIn: "packages/kernel/src/runtime/maintenance.ts";
|
|
45
|
+
readonly opts: {
|
|
46
|
+
readonly attempts: 5;
|
|
47
|
+
readonly backoff: {
|
|
48
|
+
readonly type: "exponential";
|
|
49
|
+
readonly delay: 5000;
|
|
50
|
+
};
|
|
51
|
+
readonly removeOnComplete: true;
|
|
52
|
+
readonly removeOnFail: true;
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
readonly "stop-tree": {
|
|
56
|
+
readonly queue: "cap-kernel-maintenance";
|
|
57
|
+
readonly idempotent: "stopping a stopped run is a no-op";
|
|
58
|
+
readonly processedIn: "packages/kernel/src/runtime/maintenance.ts";
|
|
59
|
+
readonly opts: {
|
|
60
|
+
readonly attempts: 5;
|
|
61
|
+
readonly backoff: {
|
|
62
|
+
readonly type: "exponential";
|
|
63
|
+
readonly delay: 5000;
|
|
64
|
+
};
|
|
65
|
+
readonly removeOnComplete: true;
|
|
66
|
+
readonly removeOnFail: true;
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
readonly "deliver-to-parent": {
|
|
70
|
+
readonly queue: "cap-kernel-maintenance";
|
|
71
|
+
readonly idempotent: "the delivery's idempotency key is the child run id";
|
|
72
|
+
readonly processedIn: "packages/kernel/src/runtime/maintenance.ts";
|
|
73
|
+
readonly opts: {
|
|
74
|
+
readonly attempts: 5;
|
|
75
|
+
readonly backoff: {
|
|
76
|
+
readonly type: "exponential";
|
|
77
|
+
readonly delay: 5000;
|
|
78
|
+
};
|
|
79
|
+
readonly removeOnComplete: true;
|
|
80
|
+
readonly removeOnFail: true;
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
readonly "usage-report": {
|
|
84
|
+
readonly queue: "cap-kernel-usage-report";
|
|
85
|
+
readonly idempotent: "handlers are idempotent by eventId (the event's `<conversationId>/<seq>` or the model_usage row id), and the job's `pending` list keeps a retry from reaching plugins already served";
|
|
86
|
+
readonly processedIn: "packages/kernel/src/runtime/run-processors.ts";
|
|
87
|
+
readonly opts: {
|
|
88
|
+
readonly attempts: 6;
|
|
89
|
+
readonly backoff: {
|
|
90
|
+
readonly type: "exponential";
|
|
91
|
+
readonly delay: 2000;
|
|
92
|
+
};
|
|
93
|
+
readonly removeOnComplete: {
|
|
94
|
+
readonly age: number;
|
|
95
|
+
readonly count: 100000;
|
|
96
|
+
};
|
|
97
|
+
readonly removeOnFail: {
|
|
98
|
+
readonly age: number;
|
|
99
|
+
readonly count: 100000;
|
|
100
|
+
};
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
readonly deliver: {
|
|
104
|
+
readonly queue: "cap-kernel-channels";
|
|
105
|
+
readonly idempotent: "a delivery resumes from the conversation's delivered seq under a per-conversation lock and sends with idempotency keys, so a second job sends nothing twice";
|
|
106
|
+
readonly processedIn: "packages/kernel/src/channels/channels.module.ts";
|
|
107
|
+
readonly opts: {
|
|
108
|
+
readonly attempts: 8;
|
|
109
|
+
readonly backoff: {
|
|
110
|
+
readonly type: "deliver";
|
|
111
|
+
};
|
|
112
|
+
readonly priority: 10;
|
|
113
|
+
readonly removeOnComplete: true;
|
|
114
|
+
readonly removeOnFail: true;
|
|
115
|
+
};
|
|
116
|
+
};
|
|
117
|
+
};
|
|
118
|
+
export type OutboxJobName = keyof typeof OUTBOX_JOBS;
|
|
119
|
+
/** The registry's BullMQ options of a job name (none for a name it does not list). */
|
|
120
|
+
export declare function outboxJobOptions(name: string): OutboxJobSpec["opts"];
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every job name added through the outbox. The task that introduces a job name adds its entry; a
|
|
3
|
+
* name without one does not type-check as an `OutboxJobName` (`add` does not check it at runtime).
|
|
4
|
+
*/
|
|
5
|
+
export const OUTBOX_JOBS = {
|
|
6
|
+
// `runtime/run-jobs.ts` RUN_JOB: one name on both run queues (a sub-agent's runs go to the
|
|
7
|
+
// sub-agent queue), under the job ids `runJobId` builds and, for a waiting run's resume
|
|
8
|
+
// (`RunResume.resumeIn`), `resumeJobId`
|
|
9
|
+
run: {
|
|
10
|
+
queue: ["cap-kernel-runs", "cap-kernel-subagent-runs"],
|
|
11
|
+
idempotent: "the run worker starts only a run in `queued` state for this job's runId; a finished run's job only repeats the idempotent post-run ensureRun, a waiting run's does nothing",
|
|
12
|
+
processedIn: "packages/kernel/src/runtime/run-processors.ts",
|
|
13
|
+
// one attempt (the run's own paths handle failures); removed once done, so the next run of the
|
|
14
|
+
// same trigger can take the id again (the post-run ensureRun, R34)
|
|
15
|
+
opts: { attempts: 1, removeOnComplete: true, removeOnFail: true },
|
|
16
|
+
},
|
|
17
|
+
// `runtime/run-jobs.ts` ENSURE_RUN_JOB: the ensure-run backstop owed with every message, queue
|
|
18
|
+
// change, background task finish, sub-agent delivery and run end (spec §6), under the job ids
|
|
19
|
+
// `ensureRunJobId` builds
|
|
20
|
+
"ensure-run": {
|
|
21
|
+
queue: "cap-kernel-maintenance",
|
|
22
|
+
idempotent: "ensureRun decides from the conversation's state; with nothing unconsumed it is a no-op",
|
|
23
|
+
processedIn: "packages/kernel/src/runtime/maintenance.ts",
|
|
24
|
+
// removed once done (the queue defaults keep finished jobs), so a later write under the same
|
|
25
|
+
// seq (a queue change writes no event of its own) owes a job that is not deduped away
|
|
26
|
+
opts: { removeOnComplete: true, removeOnFail: true },
|
|
27
|
+
},
|
|
28
|
+
// `runtime/waits.ts` WAIT_TIMEOUT_JOB: owed with the suspend that sets a run `waiting`, under
|
|
29
|
+
// `waitTimeoutJobId`, with `runAt` = the wait's deadline (spec §6 "Resumes and timeouts")
|
|
30
|
+
"wait-timeout": {
|
|
31
|
+
queue: "cap-kernel-maintenance",
|
|
32
|
+
idempotent: "a timeout acts only on a still-pending wait (a row lock and status check) and a resume starts only a waiting run with nothing else pending",
|
|
33
|
+
processedIn: "packages/kernel/src/runtime/maintenance.ts",
|
|
34
|
+
// retried while PG is briefly unavailable; removed once done
|
|
35
|
+
opts: {
|
|
36
|
+
attempts: 5,
|
|
37
|
+
backoff: { type: "exponential", delay: 5_000 },
|
|
38
|
+
removeOnComplete: true,
|
|
39
|
+
removeOnFail: true,
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
// `runtime/subagents/tree-jobs.ts` STOP_TREE_JOB: owed by a run's terminal transaction that
|
|
43
|
+
// leaves an active fork child (or a person-stopped sub-agent's descendants), under
|
|
44
|
+
// `stopChildrenJobId`, and by a root's delete, under `stopDeletedTreeJobId` (spec §6)
|
|
45
|
+
"stop-tree": {
|
|
46
|
+
queue: "cap-kernel-maintenance",
|
|
47
|
+
idempotent: "stopping a stopped run is a no-op",
|
|
48
|
+
processedIn: "packages/kernel/src/runtime/maintenance.ts",
|
|
49
|
+
// retried while PG is briefly unavailable; removed once done
|
|
50
|
+
opts: {
|
|
51
|
+
attempts: 5,
|
|
52
|
+
backoff: { type: "exponential", delay: 5_000 },
|
|
53
|
+
removeOnComplete: true,
|
|
54
|
+
removeOnFail: true,
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
// `runtime/subagents/tree-jobs.ts` DELIVER_TO_PARENT_JOB: owed by a sub-agent run's terminal
|
|
58
|
+
// transaction, under `deliverToParentJobId` (spec §6)
|
|
59
|
+
"deliver-to-parent": {
|
|
60
|
+
queue: "cap-kernel-maintenance",
|
|
61
|
+
idempotent: "the delivery's idempotency key is the child run id",
|
|
62
|
+
processedIn: "packages/kernel/src/runtime/maintenance.ts",
|
|
63
|
+
opts: {
|
|
64
|
+
attempts: 5,
|
|
65
|
+
backoff: { type: "exponential", delay: 5_000 },
|
|
66
|
+
removeOnComplete: true,
|
|
67
|
+
removeOnFail: true,
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
// `runtime/usage-report-jobs.ts` USAGE_REPORT_JOB: owed with the usage it reports (a `usage`
|
|
71
|
+
// event or a `model_usage` row) while some plugin subscribes, under `usageReportJobId` /
|
|
72
|
+
// `modelUsageJobId` (spec §8.1)
|
|
73
|
+
"usage-report": {
|
|
74
|
+
queue: "cap-kernel-usage-report",
|
|
75
|
+
idempotent: "handlers are idempotent by eventId (the event's `<conversationId>/<seq>` or the model_usage row id), and the job's `pending` list keeps a retry from reaching plugins already served",
|
|
76
|
+
processedIn: "packages/kernel/src/runtime/run-processors.ts",
|
|
77
|
+
// first attempt plus 5 retries, 2 s doubling (F11); finished jobs kept a day so a re-add of the
|
|
78
|
+
// same id (a crash between the add and the row's delete) dedupes; failed ones a week; both
|
|
79
|
+
// capped by count (I-2: a long outage of a billing endpoint cannot grow Redis)
|
|
80
|
+
opts: {
|
|
81
|
+
attempts: 6,
|
|
82
|
+
backoff: { type: "exponential", delay: 2_000 },
|
|
83
|
+
removeOnComplete: { age: 24 * 3600, count: 100_000 },
|
|
84
|
+
removeOnFail: { age: 7 * 86_400, count: 100_000 },
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
// `channels/deliver-job.ts` DELIVER_JOB: owed by every event append of a conversation with a
|
|
88
|
+
// channel target (a reply, an approval decision, a question answer, a wait stop) and by binding
|
|
89
|
+
// a conversation to its target, under `deliverJobId` (spec §8.1)
|
|
90
|
+
deliver: {
|
|
91
|
+
queue: "cap-kernel-channels",
|
|
92
|
+
idempotent: "a delivery resumes from the conversation's delivered seq under a per-conversation lock and sends with idempotency keys, so a second job sends nothing twice",
|
|
93
|
+
processedIn: "packages/kernel/src/channels/channels.module.ts",
|
|
94
|
+
// the platform's retry-after, else exponential (the processor's `deliver` backoff strategy);
|
|
95
|
+
// behind inbound jobs; removed once done, so a later append's id is never deduped away
|
|
96
|
+
opts: {
|
|
97
|
+
attempts: 8,
|
|
98
|
+
backoff: { type: "deliver" },
|
|
99
|
+
priority: 10,
|
|
100
|
+
removeOnComplete: true,
|
|
101
|
+
removeOnFail: true,
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
/** The registry's BullMQ options of a job name (none for a name it does not list). */
|
|
106
|
+
export function outboxJobOptions(name) {
|
|
107
|
+
return OUTBOX_JOBS[name]?.opts;
|
|
108
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The kernel's own BullMQ queues, spelled as literals so infra code (the outbox registry and
|
|
3
|
+
* dispatcher) names a queue without importing feature modules. `KERNEL_QUEUE_NAMES`
|
|
4
|
+
* (admin/health.service.ts) lists the same names from the feature constants;
|
|
5
|
+
* test/infra/queue-names.test.ts keeps the two equal.
|
|
6
|
+
*/
|
|
7
|
+
export declare const KERNEL_QUEUES: readonly ["cap-kernel-runs", "cap-kernel-subagent-runs", "cap-kernel-channels", "cap-kernel-maintenance", "cap-kernel-plugin-scope", "cap-kernel-titles", "cap-kernel-usage-report", "cap-kernel-evals"];
|
|
8
|
+
export type KernelQueueName = (typeof KERNEL_QUEUES)[number];
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The kernel's own BullMQ queues, spelled as literals so infra code (the outbox registry and
|
|
3
|
+
* dispatcher) names a queue without importing feature modules. `KERNEL_QUEUE_NAMES`
|
|
4
|
+
* (admin/health.service.ts) lists the same names from the feature constants;
|
|
5
|
+
* test/infra/queue-names.test.ts keeps the two equal.
|
|
6
|
+
*/
|
|
7
|
+
export const KERNEL_QUEUES = [
|
|
8
|
+
"cap-kernel-runs",
|
|
9
|
+
"cap-kernel-subagent-runs",
|
|
10
|
+
"cap-kernel-channels",
|
|
11
|
+
"cap-kernel-maintenance",
|
|
12
|
+
"cap-kernel-plugin-scope",
|
|
13
|
+
"cap-kernel-titles",
|
|
14
|
+
"cap-kernel-usage-report",
|
|
15
|
+
"cap-kernel-evals",
|
|
16
|
+
];
|
package/dist/kernel.module.js
CHANGED
|
@@ -25,6 +25,7 @@ import { databaseCheck, HEALTH_CHECKS, redisCheck } from "./infra/health/health.
|
|
|
25
25
|
import { GracefulShutdownModule } from "./infra/lifecycle/graceful-shutdown.module.js";
|
|
26
26
|
import { LockModule } from "./infra/lock/lock.module.js";
|
|
27
27
|
import { LoggingModule } from "./infra/logging/logging.module.js";
|
|
28
|
+
import { OutboxModule } from "./infra/outbox/outbox.module.js";
|
|
28
29
|
import { PresenceModule } from "./infra/presence/presence.module.js";
|
|
29
30
|
import { QueueModule } from "./infra/queue/queue.module.js";
|
|
30
31
|
import { RedisModule } from "./infra/redis/redis.module.js";
|
|
@@ -95,6 +96,8 @@ let KernelModule = KernelModule_1 = class KernelModule {
|
|
|
95
96
|
ConfigModule.forRoot(config, options.env !== undefined ? { env: options.env } : {}),
|
|
96
97
|
GracefulShutdownModule,
|
|
97
98
|
DatabaseModule.forRoot(),
|
|
99
|
+
// owed jobs written in the caller's transaction (transactional outbox spec §3.2)
|
|
100
|
+
OutboxModule,
|
|
98
101
|
RedisModule,
|
|
99
102
|
LockModule,
|
|
100
103
|
CacheModule,
|
|
@@ -53,6 +53,10 @@ export declare class DevicePolicyService {
|
|
|
53
53
|
features: DeviceFeature[];
|
|
54
54
|
policy: DeviceLocalPolicySummary | null;
|
|
55
55
|
} | null>;
|
|
56
|
+
/** Adds `op` to the machine's pending ops, dropping the expired ones. */
|
|
57
|
+
private notePending;
|
|
58
|
+
/** Removes the pending op `opId` from the machine's row. */
|
|
59
|
+
private dropPending;
|
|
56
60
|
/**
|
|
57
61
|
* Keeps the policy the machine wrote on its row and audits the change (`confirmed`); with no
|
|
58
62
|
* policy (the answer never came) only audits the attempt, unconfirmed. The detail's `isolation`
|
|
@@ -7,8 +7,10 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
8
8
|
return function (target, key) { decorator(target, key, paramIndex); }
|
|
9
9
|
};
|
|
10
|
+
import { randomUUID } from "node:crypto";
|
|
10
11
|
import { ToolError, validateAllowHost, } from "@capybara-hq/plugin-sdk";
|
|
11
12
|
import { Inject, Injectable } from "@nestjs/common";
|
|
13
|
+
import { sql } from "kysely";
|
|
12
14
|
import { ApiError } from "../errors.js";
|
|
13
15
|
import { audit } from "../identity/audit.js";
|
|
14
16
|
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
@@ -21,6 +23,11 @@ import { isolationOf } from "./policy-summary.js";
|
|
|
21
23
|
import { LocalDeviceRelay, ON_MACHINE_PREFIX } from "./relay.js";
|
|
22
24
|
/** How long the machine may take to apply a policy change. */
|
|
23
25
|
export const POLICY_DEADLINE_MS = 30_000;
|
|
26
|
+
/**
|
|
27
|
+
* How long a sent op may still explain a summary the machine reports by itself: its deadline and
|
|
28
|
+
* the reconnect grace, with room for an answer that comes only with the next hello.
|
|
29
|
+
*/
|
|
30
|
+
const PENDING_OP_MS = POLICY_DEADLINE_MS + 5 * 60_000;
|
|
24
31
|
/** The prefix the relay puts before a machine's `policy`-kind refusal. */
|
|
25
32
|
const REFUSED_PREFIX = "Refused by the machine's local policy: ";
|
|
26
33
|
const OFFLINE_MESSAGE = "The device's machine is offline, so its policy was not changed. Start capybara-device on it.";
|
|
@@ -89,6 +96,17 @@ let DevicePolicyService = class DevicePolicyService {
|
|
|
89
96
|
return { applied: false, reason: "offline", message: OFFLINE_MESSAGE };
|
|
90
97
|
}
|
|
91
98
|
const args = { ...input.change, source: input.source };
|
|
99
|
+
// noted before it is sent: the machine may report the change by itself (a `policy` frame)
|
|
100
|
+
// before its answer is recorded, and the device page lists it once, as the platform's
|
|
101
|
+
const opId = randomUUID();
|
|
102
|
+
await this.notePending(machine.id, {
|
|
103
|
+
id: opId,
|
|
104
|
+
add: input.change.add ?? [],
|
|
105
|
+
remove: input.change.remove ?? [],
|
|
106
|
+
...(input.change.tier !== undefined && { tier: input.change.tier }),
|
|
107
|
+
...(input.change.isolation !== undefined && { isolation: input.change.isolation }),
|
|
108
|
+
until: Date.now() + PENDING_OP_MS,
|
|
109
|
+
});
|
|
92
110
|
let policy;
|
|
93
111
|
try {
|
|
94
112
|
({ policy } = await this.relay.call(input.deviceId, "policy", args, {
|
|
@@ -101,11 +119,15 @@ let DevicePolicyService = class DevicePolicyService {
|
|
|
101
119
|
if (e instanceof ApiError && e.code === "device.offline") {
|
|
102
120
|
// the op was sent and the wait ran out: the machine may still apply it, so the attempt is
|
|
103
121
|
// audited unconfirmed; its next hello reports the policy as it is
|
|
104
|
-
await this.record({ ...input, machineId: machine.id, policy: null });
|
|
122
|
+
await this.record({ ...input, machineId: machine.id, opId, policy: null });
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
// refused or failed: nothing changed, so it explains no later change
|
|
126
|
+
await this.dropPending(machine.id, opId);
|
|
105
127
|
}
|
|
106
128
|
return this.notApplied(input.deviceId, e);
|
|
107
129
|
}
|
|
108
|
-
await this.record({ ...input, machineId: machine.id, policy });
|
|
130
|
+
await this.record({ ...input, machineId: machine.id, opId, policy });
|
|
109
131
|
return { applied: true, policy };
|
|
110
132
|
}
|
|
111
133
|
/** The active machine's features and policy summary (null when no machine is connected). */
|
|
@@ -113,6 +135,27 @@ let DevicePolicyService = class DevicePolicyService {
|
|
|
113
135
|
const m = await this.machines.activeFor(deviceId);
|
|
114
136
|
return m === null ? null : { features: m.features, policy: m.localPolicy };
|
|
115
137
|
}
|
|
138
|
+
/** Adds `op` to the machine's pending ops, dropping the expired ones. */
|
|
139
|
+
async notePending(machineId, op) {
|
|
140
|
+
await this.db
|
|
141
|
+
.updateTable("device_machines")
|
|
142
|
+
.set({
|
|
143
|
+
pending_policy_ops: sql `(
|
|
144
|
+
SELECT coalesce(jsonb_agg(e), '[]'::jsonb) FROM jsonb_array_elements(pending_policy_ops) e
|
|
145
|
+
WHERE (e->>'until')::bigint > ${Date.now()}
|
|
146
|
+
) || jsonb_build_array(${JSON.stringify(op)}::jsonb)`,
|
|
147
|
+
})
|
|
148
|
+
.where("id", "=", machineId)
|
|
149
|
+
.execute();
|
|
150
|
+
}
|
|
151
|
+
/** Removes the pending op `opId` from the machine's row. */
|
|
152
|
+
async dropPending(machineId, opId) {
|
|
153
|
+
await this.db
|
|
154
|
+
.updateTable("device_machines")
|
|
155
|
+
.set({ pending_policy_ops: withoutOp(opId) })
|
|
156
|
+
.where("id", "=", machineId)
|
|
157
|
+
.execute();
|
|
158
|
+
}
|
|
116
159
|
/**
|
|
117
160
|
* Keeps the policy the machine wrote on its row and audits the change (`confirmed`); with no
|
|
118
161
|
* policy (the answer never came) only audits the attempt, unconfirmed. The detail's `isolation`
|
|
@@ -136,7 +179,11 @@ let DevicePolicyService = class DevicePolicyService {
|
|
|
136
179
|
isolationNow = input.policy.isolation;
|
|
137
180
|
await this.db
|
|
138
181
|
.updateTable("device_machines")
|
|
139
|
-
|
|
182
|
+
// answered: the op no longer explains a summary the machine reports
|
|
183
|
+
.set({
|
|
184
|
+
local_policy: JSON.stringify(input.policy),
|
|
185
|
+
pending_policy_ops: withoutOp(input.opId),
|
|
186
|
+
})
|
|
140
187
|
.where("id", "=", input.machineId)
|
|
141
188
|
.where("revoked_at", "is", null)
|
|
142
189
|
.execute();
|
|
@@ -186,6 +233,12 @@ let DevicePolicyService = class DevicePolicyService {
|
|
|
186
233
|
};
|
|
187
234
|
}
|
|
188
235
|
};
|
|
236
|
+
__decorate([
|
|
237
|
+
Transactional()
|
|
238
|
+
], DevicePolicyService.prototype, "notePending", null);
|
|
239
|
+
__decorate([
|
|
240
|
+
Transactional()
|
|
241
|
+
], DevicePolicyService.prototype, "dropPending", null);
|
|
189
242
|
__decorate([
|
|
190
243
|
Transactional()
|
|
191
244
|
], DevicePolicyService.prototype, "record", null);
|
|
@@ -198,6 +251,11 @@ DevicePolicyService = __decorate([
|
|
|
198
251
|
__param(4, Inject(KERNEL_LOGGER))
|
|
199
252
|
], DevicePolicyService);
|
|
200
253
|
export { DevicePolicyService };
|
|
254
|
+
/** The machine's pending ops without `opId` (an expired one left for the next note to drop). */
|
|
255
|
+
const withoutOp = (opId) => sql `(
|
|
256
|
+
SELECT coalesce(jsonb_agg(e), '[]'::jsonb) FROM jsonb_array_elements(pending_policy_ops) e
|
|
257
|
+
WHERE e->>'id' <> ${opId}
|
|
258
|
+
)`;
|
|
201
259
|
/**
|
|
202
260
|
* Hosts the platform may send: an approval's `add` takes exact hosts only (a card never grants a
|
|
203
261
|
* wildcard); the owner's own change on the device page (no approval) may add a wildcard (spec §7);
|
|
@@ -55,6 +55,8 @@ declare class DeviceConnection {
|
|
|
55
55
|
private chain;
|
|
56
56
|
/** Resolves once the lease is handed back (or was never held). */
|
|
57
57
|
private released;
|
|
58
|
+
/** The hello's record, then each policy frame's, one after another (never rejects). */
|
|
59
|
+
private recorded;
|
|
58
60
|
constructor(gw: LocalDeviceGateway, socket: WebSocket, identity: MachineIdentity, credential: string);
|
|
59
61
|
get deviceId(): string;
|
|
60
62
|
start(): void;
|
|
@@ -101,7 +103,7 @@ declare class DeviceConnection {
|
|
|
101
103
|
* sockets are this process's own; everything other replicas need is in Redis.
|
|
102
104
|
*/
|
|
103
105
|
export declare class LocalDeviceGateway {
|
|
104
|
-
readonly machines: Pick<MachinesService, "authenticate" | "recordHello" | "touch">;
|
|
106
|
+
readonly machines: Pick<MachinesService, "authenticate" | "recordHello" | "recordPolicy" | "touch">;
|
|
105
107
|
readonly locks: Pick<LockService, "hold">;
|
|
106
108
|
readonly bus: ExtensionContext["bus"];
|
|
107
109
|
readonly redis: Redis;
|
|
@@ -112,7 +114,7 @@ export declare class LocalDeviceGateway {
|
|
|
112
114
|
private stopping;
|
|
113
115
|
/** How often connections are pinged ({@link PING_INTERVAL_MS}; a test seam). */
|
|
114
116
|
pingIntervalMs: number;
|
|
115
|
-
constructor(machines: Pick<MachinesService, "authenticate" | "recordHello" | "touch">, locks: Pick<LockService, "hold">, bus: ExtensionContext["bus"], redis: Redis, config: KernelConfigService, log: Pick<KernelLogger, "info" | "warn">);
|
|
117
|
+
constructor(machines: Pick<MachinesService, "authenticate" | "recordHello" | "recordPolicy" | "touch">, locks: Pick<LockService, "hold">, bus: ExtensionContext["bus"], redis: Redis, config: KernelConfigService, log: Pick<KernelLogger, "info" | "warn">);
|
|
116
118
|
graceMs(): number;
|
|
117
119
|
leaseTtlMs(): number;
|
|
118
120
|
maxFrameBytes(): number;
|
|
@@ -114,6 +114,8 @@ class DeviceConnection {
|
|
|
114
114
|
chain = Promise.resolve();
|
|
115
115
|
/** Resolves once the lease is handed back (or was never held). */
|
|
116
116
|
released;
|
|
117
|
+
/** The hello's record, then each policy frame's, one after another (never rejects). */
|
|
118
|
+
recorded = Promise.resolve();
|
|
117
119
|
constructor(gw, socket, identity, credential) {
|
|
118
120
|
this.gw = gw;
|
|
119
121
|
this.socket = socket;
|
|
@@ -174,20 +176,43 @@ class DeviceConnection {
|
|
|
174
176
|
}
|
|
175
177
|
clearTimeout(this.helloTimer);
|
|
176
178
|
this.state = "claiming";
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
179
|
+
// the raw summary too: the frame parser drops one it cannot read whole, and its
|
|
180
|
+
// isolation must still reach the kernel
|
|
181
|
+
const rawPolicy = JSON.parse(text).policy;
|
|
182
|
+
// set before the first await: a policy frame that follows is recorded after the hello
|
|
183
|
+
this.recorded = (async () => {
|
|
184
|
+
if (!(await this.claim()))
|
|
185
|
+
return;
|
|
186
|
+
try {
|
|
187
|
+
await this.gw.machines.recordHello(this.identity.machineId, frame, rawPolicy);
|
|
188
|
+
}
|
|
189
|
+
catch (e) {
|
|
190
|
+
this.gw.log.warn("could not record a device hello", {
|
|
191
|
+
deviceId: this.deviceId,
|
|
192
|
+
error: errMsg(e),
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
})();
|
|
196
|
+
await this.recorded;
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
if (frame.type === "policy" && this.state !== "hello") {
|
|
200
|
+
// after the hello's record and the earlier policy frames, in the order they came
|
|
201
|
+
const rawPolicy = JSON.parse(text).policy;
|
|
202
|
+
this.recorded = this.recorded.then(async () => {
|
|
203
|
+
if (this.state === "closed")
|
|
204
|
+
return;
|
|
205
|
+
try {
|
|
206
|
+
await this.gw.machines.recordPolicy(this.identity.machineId, frame.policy, rawPolicy);
|
|
207
|
+
}
|
|
208
|
+
catch (e) {
|
|
209
|
+
this.gw.log.warn("could not record a device policy change", {
|
|
210
|
+
deviceId: this.deviceId,
|
|
211
|
+
error: errMsg(e),
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
await this.recorded;
|
|
191
216
|
return;
|
|
192
217
|
}
|
|
193
218
|
if (frame.type !== "res" || this.state === "hello") {
|
|
@@ -94,9 +94,33 @@ export declare class MachinesService {
|
|
|
94
94
|
* alone, read from `rawPolicy` (the frame's raw `policy`; {@link isolationOf}: anything but
|
|
95
95
|
* `sandbox` is `none`), so it never hides that commands run without a sandbox. A reported
|
|
96
96
|
* isolation that differs from the stored one (none stored counts as `sandbox`) is audited
|
|
97
|
-
* `device.isolation_reported` (sandbox dev tiers spec §10.4), once per change
|
|
97
|
+
* `device.isolation_reported` (sandbox dev tiers spec §10.4), once per change; a change of what
|
|
98
|
+
* the owner sets, made on the machine while it was away, is audited `device.policy_changed`
|
|
99
|
+
* ({@link reportedPolicy}).
|
|
98
100
|
*/
|
|
99
101
|
recordHello(machineId: string, hello: DeviceHello, rawPolicy?: unknown): Promise<void>;
|
|
102
|
+
/**
|
|
103
|
+
* Stores the local policy summary a connected program pushed after its hello (a `policy` frame:
|
|
104
|
+
* its policy file changed on the machine, its localhost proxy guard flipped, a new tier
|
|
105
|
+
* ceiling), read and audited as the hello's ({@link recordHello}): an unreadable summary keeps
|
|
106
|
+
* its isolation alone (`rawPolicy`); a change the owner made on the machine is audited
|
|
107
|
+
* `device.policy_changed` (`via: "device"`), else a changed isolation `device.isolation_reported`.
|
|
108
|
+
* The next sandbox call then records a new `device_paths` event when the facts the model is told
|
|
109
|
+
* changed. A revoked machine is left alone.
|
|
110
|
+
*/
|
|
111
|
+
recordPolicy(machineId: string, summary: DeviceLocalPolicySummary | undefined, rawPolicy?: unknown): Promise<void>;
|
|
112
|
+
/**
|
|
113
|
+
* The summary to store for one a program reported (hello or `policy` frame): the parsed one,
|
|
114
|
+
* else its raw isolation alone, else null. When both the stored and the reported summary are
|
|
115
|
+
* readable and what the owner sets changed ({@link localPolicyDiff}) by no `policy` op the
|
|
116
|
+
* platform sent ({@link opExplains}: its answer may come after the machine's own report), the
|
|
117
|
+
* owner changed it on the machine (`capybara-device policy ...`): audited
|
|
118
|
+
* `device.policy_changed` with `via: "device"`, the isolation with it, so the device page lists
|
|
119
|
+
* it beside the platform's changes; one an op explains is not audited here (the op's answer
|
|
120
|
+
* is). Otherwise a reported isolation that differs from the stored one (none stored counts as
|
|
121
|
+
* `sandbox`) is audited `device.isolation_reported`. A summary reported again is no change. Runs in the caller's transaction, with the machine's row locked.
|
|
122
|
+
*/
|
|
123
|
+
private reportedPolicy;
|
|
100
124
|
/** Marks the machine seen (a ping); writes at most once every 60 seconds. */
|
|
101
125
|
touch(machineId: string): Promise<void>;
|
|
102
126
|
/**
|