@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
|
@@ -54,6 +54,35 @@ export const OUTBOX_JOBS = {
|
|
|
54
54
|
removeOnFail: true,
|
|
55
55
|
},
|
|
56
56
|
},
|
|
57
|
+
// `orgs/offboarding.ts` RUN_STOP_JOB: owed by the offboard transaction for each of the departed
|
|
58
|
+
// member's active runs in the org, under `runStopJobId` (spec §8.1)
|
|
59
|
+
"run-stop": {
|
|
60
|
+
queue: "cap-kernel-maintenance",
|
|
61
|
+
idempotent: "stopping a finished run is a no-op",
|
|
62
|
+
processedIn: "packages/kernel/src/runtime/maintenance.ts",
|
|
63
|
+
// retried while PG or Redis is briefly unavailable; removed once done; a job that failed every
|
|
64
|
+
// attempt is kept a week (no sweep stops the run after it: the failure must stay visible)
|
|
65
|
+
opts: {
|
|
66
|
+
attempts: 5,
|
|
67
|
+
backoff: { type: "exponential", delay: 5_000 },
|
|
68
|
+
removeOnComplete: true,
|
|
69
|
+
removeOnFail: { age: 7 * 86_400, count: 1_000 },
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
// `sandbox/jobs.ts` SANDBOX_DESTROY_FOR_JOB: owed by the transaction that soft-deletes a device
|
|
73
|
+
// (its deletion, offboarding), under `deviceDestroyJobId` (spec §8.1)
|
|
74
|
+
"sandbox-destroy-for": {
|
|
75
|
+
queue: "cap-kernel-maintenance",
|
|
76
|
+
idempotent: "destroying a destroyed device is a no-op",
|
|
77
|
+
processedIn: "packages/kernel/src/runtime/maintenance.ts",
|
|
78
|
+
// the options the direct enqueue had: retried while the provider is down
|
|
79
|
+
opts: {
|
|
80
|
+
attempts: 5,
|
|
81
|
+
backoff: { type: "exponential", delay: 30_000 },
|
|
82
|
+
removeOnComplete: true,
|
|
83
|
+
removeOnFail: 100,
|
|
84
|
+
},
|
|
85
|
+
},
|
|
57
86
|
// `runtime/subagents/tree-jobs.ts` DELIVER_TO_PARENT_JOB: owed by a sub-agent run's terminal
|
|
58
87
|
// transaction, under `deliverToParentJobId` (spec §6)
|
|
59
88
|
"deliver-to-parent": {
|
|
@@ -101,6 +130,160 @@ export const OUTBOX_JOBS = {
|
|
|
101
130
|
removeOnFail: true,
|
|
102
131
|
},
|
|
103
132
|
},
|
|
133
|
+
// `channels/deliver-job.ts` RESUME_JOB: owed by the update that lets a connection deliver again
|
|
134
|
+
// (re-enabled, or credentials rotated while active), under `resumeJobId` (one per update)
|
|
135
|
+
"channel-resume": {
|
|
136
|
+
queue: "cap-kernel-channels",
|
|
137
|
+
idempotent: "the catch-up re-reads which of the connection's conversations lag their log and adds their delivery jobs, and a delivery resumes from the delivered seq under a per-conversation lock, so a second catch-up sends nothing twice",
|
|
138
|
+
processedIn: "packages/kernel/src/channels/channels.module.ts",
|
|
139
|
+
// retried while PG or Redis is briefly unavailable; behind inbound jobs like a delivery;
|
|
140
|
+
// removed once done
|
|
141
|
+
opts: {
|
|
142
|
+
attempts: 5,
|
|
143
|
+
backoff: { type: "exponential", delay: 5_000 },
|
|
144
|
+
priority: 10,
|
|
145
|
+
removeOnComplete: true,
|
|
146
|
+
removeOnFail: true,
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
// `catalog/scope-sync.ts` SCOPE_ITEM_JOB: owed by every transaction that changes an item's
|
|
150
|
+
// scope, disabled flag, list, owner or platform version, under `scopeItemJobId` (one per scope
|
|
151
|
+
// version), and by a sync deferred on a replica that had not loaded the extension yet, under
|
|
152
|
+
// `scopeRetryJobId` with `runAt` (spec §8.1)
|
|
153
|
+
"scope-item": {
|
|
154
|
+
queue: "cap-kernel-plugin-scope",
|
|
155
|
+
idempotent: "the sync converges each (item, org) from the scope, switches and installs it reads under the org's lock, and the installer re-checks under the org row lock, so a second run changes nothing",
|
|
156
|
+
processedIn: "packages/kernel/src/catalog/scope-sync-workers.ts",
|
|
157
|
+
// one attempt (a pair's failure is recorded, not retried); removed once done, so the next
|
|
158
|
+
// add of the same id is not swallowed
|
|
159
|
+
opts: { attempts: 1, removeOnComplete: true, removeOnFail: true },
|
|
160
|
+
},
|
|
161
|
+
// `catalog/scope-jobs.ts` SCOPE_BACKSTOP_JOB: owed by a confirmation's commit (an import, a
|
|
162
|
+
// permissions confirm, an org upload's first version) with `runAt` 5 min on, under
|
|
163
|
+
// `scopeBackstopJobId`, so a crash before the post-deploy add still converges (ruling R24)
|
|
164
|
+
"scope-item-backstop": {
|
|
165
|
+
queue: "cap-kernel-plugin-scope",
|
|
166
|
+
idempotent: "the sync converges each (item, org) from what it reads, as a scope-item job does, so a run after the immediate job changes nothing",
|
|
167
|
+
processedIn: "packages/kernel/src/catalog/scope-sync-workers.ts",
|
|
168
|
+
// retried while the item's extension is not enabled on the deployment yet (the job throws):
|
|
169
|
+
// 1, 2, 4, 8 min; removed once done
|
|
170
|
+
opts: {
|
|
171
|
+
attempts: 5,
|
|
172
|
+
backoff: { type: "exponential", delay: 60_000 },
|
|
173
|
+
removeOnComplete: true,
|
|
174
|
+
removeOnFail: true,
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
// `catalog/scope-sync.ts` SCOPE_ORG_JOB: owed by an org's creation, under `scopeOrgJobId`
|
|
178
|
+
"scope-org": {
|
|
179
|
+
queue: "cap-kernel-plugin-scope",
|
|
180
|
+
idempotent: "the sync converges each (item, org) of the org from what it reads, so a second run changes nothing",
|
|
181
|
+
processedIn: "packages/kernel/src/catalog/scope-sync-workers.ts",
|
|
182
|
+
opts: { attempts: 1, removeOnComplete: true, removeOnFail: true },
|
|
183
|
+
},
|
|
184
|
+
// `catalog/job-ids.ts` OWNED_PLUGIN_CLEANUP_JOB: owed by the removal of an org-owned plugin
|
|
185
|
+
// (and by a confirmation that took out the extension of an item removed meanwhile), under
|
|
186
|
+
// `ownedPluginCleanupJobId` (spec §8.1)
|
|
187
|
+
"owned-plugin-cleanup": {
|
|
188
|
+
queue: "cap-kernel-maintenance",
|
|
189
|
+
idempotent: "the cleanup takes the extension out only while no live item uses it, and a missing install or a released claim changes nothing",
|
|
190
|
+
processedIn: "packages/kernel/src/runtime/maintenance.ts",
|
|
191
|
+
// a few retries while PG is briefly unavailable; removed once done
|
|
192
|
+
opts: {
|
|
193
|
+
attempts: 3,
|
|
194
|
+
backoff: { type: "exponential", delay: 5_000 },
|
|
195
|
+
removeOnComplete: true,
|
|
196
|
+
removeOnFail: true,
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
// `schedules/job-ids.ts` SCHED_FIRE_JOB: owed by the transaction that writes a pending task run
|
|
200
|
+
// (the scan's tick, a person's "run now"), under `schedFireJobId` (spec §8.1)
|
|
201
|
+
"sched-fire": {
|
|
202
|
+
queue: "cap-kernel-maintenance",
|
|
203
|
+
idempotent: "a fire acts only on a pending task run, under its row lock; a retried start reuses what the first one made",
|
|
204
|
+
processedIn: "packages/kernel/src/runtime/maintenance.ts",
|
|
205
|
+
// a few retries while PG is briefly unavailable; removed once done
|
|
206
|
+
opts: {
|
|
207
|
+
attempts: 3,
|
|
208
|
+
backoff: { type: "exponential", delay: 2_000 },
|
|
209
|
+
removeOnComplete: true,
|
|
210
|
+
removeOnFail: true,
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
// `schedules/job-ids.ts` SCHED_SETTLE_JOB: the claimed check, owed by the fire job's claim (the
|
|
214
|
+
// transaction that writes `fired_at`) with `runAt` 10 min on, under `schedSettleJobId` (a run
|
|
215
|
+
// still in flight owes the next check an hour on); the unfired check, owed with the task run
|
|
216
|
+
// (scan tick, "run now") under `schedSettleUnfiredJobId` (ruling R26)
|
|
217
|
+
"sched-settle": {
|
|
218
|
+
queue: "cap-kernel-maintenance",
|
|
219
|
+
idempotent: "a check acts only on a task run still pending (claimed for the claimed check, unclaimed for the unfired one), under the task and run row locks, so a run claimed, started, ended or skipped since is left as it is",
|
|
220
|
+
processedIn: "packages/kernel/src/runtime/maintenance.ts",
|
|
221
|
+
// retried while PG is briefly unavailable; removed once done
|
|
222
|
+
opts: {
|
|
223
|
+
attempts: 5,
|
|
224
|
+
backoff: { type: "exponential", delay: 5_000 },
|
|
225
|
+
removeOnComplete: true,
|
|
226
|
+
removeOnFail: true,
|
|
227
|
+
},
|
|
228
|
+
},
|
|
229
|
+
// `local-devices/policy-op-jobs.ts` DEVICE_OP_RESOLVE_JOB: owed by a device policy op's intent
|
|
230
|
+
// transaction under `deviceOpResolveJobId` with `runAt` = when the op can no longer reach the
|
|
231
|
+
// machine, and by a hello that reported the op's outcome under `deviceOpReportedJobId` (spec §7)
|
|
232
|
+
"device-op-resolve": {
|
|
233
|
+
queue: "cap-kernel-maintenance",
|
|
234
|
+
idempotent: "resolving an op whose entry is gone is a no-op (the entry is read under the machine row's lock, and under the network ask's lock for an op that answers one, so the ask is decided at most once); the resolver only reads the device, never sends a change",
|
|
235
|
+
processedIn: "packages/kernel/src/runtime/maintenance.ts",
|
|
236
|
+
// retried while PG or the device is briefly unreachable; removed once done
|
|
237
|
+
opts: {
|
|
238
|
+
attempts: 5,
|
|
239
|
+
backoff: { type: "exponential", delay: 5_000 },
|
|
240
|
+
removeOnComplete: true,
|
|
241
|
+
removeOnFail: true,
|
|
242
|
+
},
|
|
243
|
+
},
|
|
244
|
+
// `background/wake-rules.ts` BG_WAKE_JOB: owed by the `ensureRun` transaction that finds a
|
|
245
|
+
// conversation's wake inside its wake window, under `bgWakeJobId`, with `runAt` = the window's
|
|
246
|
+
// end (background Bash spec §3)
|
|
247
|
+
"bg-wake": {
|
|
248
|
+
queue: "cap-kernel-maintenance",
|
|
249
|
+
idempotent: "the wake only calls ensureRun, which decides from the conversation's state under its row lock, so a wake with nothing unconsumed or an active run starts nothing",
|
|
250
|
+
processedIn: "packages/kernel/src/runtime/maintenance.ts",
|
|
251
|
+
// the options the direct enqueue had (the queue's default retries): removed once done, so a
|
|
252
|
+
// later window's wake is never deduped away (a job that fired early owes its next retry id)
|
|
253
|
+
opts: { removeOnComplete: true, removeOnFail: true },
|
|
254
|
+
},
|
|
255
|
+
// `evals/jobs.ts` EVAL_CASE_JOB: owed by an eval run's start transaction (the run row and its
|
|
256
|
+
// pending results), one per case, under `evalJobId`
|
|
257
|
+
"eval-case": {
|
|
258
|
+
queue: "cap-kernel-evals",
|
|
259
|
+
idempotent: "a case job replays only the case's unfinished results (and pending judgements) of a still-active run, each under a fresh claim, so a second job writes no result twice",
|
|
260
|
+
processedIn: "packages/kernel/src/evals/evals.module.ts",
|
|
261
|
+
// the options the direct enqueue had: transient failures outside the handler (PG, Redis)
|
|
262
|
+
// retried with backoff; removed once done, failed ones kept (by count)
|
|
263
|
+
opts: {
|
|
264
|
+
attempts: 3,
|
|
265
|
+
backoff: { type: "exponential", delay: 1000 },
|
|
266
|
+
removeOnComplete: true,
|
|
267
|
+
removeOnFail: 1000,
|
|
268
|
+
},
|
|
269
|
+
},
|
|
270
|
+
// `extensions/lifecycle-hooks.ts` LIFECYCLE_JOB: owed by the transaction that removes a member,
|
|
271
|
+
// deletes an agent or deletes an org, once per subscribing plugin, under `lifecycleJobId`
|
|
272
|
+
// (memory plugin spec §6.3)
|
|
273
|
+
lifecycle: {
|
|
274
|
+
queue: "cap-kernel-lifecycle",
|
|
275
|
+
idempotent: "lifecycle handlers must be idempotent (the SDK contract): they delete what the plugin holds for the removed member, agent or org, and deleting it again is a no-op",
|
|
276
|
+
processedIn: "packages/kernel/src/extensions/lifecycle-hooks.ts",
|
|
277
|
+
// retried while the plugin is not loaded on the replica that took the job (a rolling deploy)
|
|
278
|
+
// or its handler fails: 15 attempts from 2 s, doubling (about 18 h in all); removed once done;
|
|
279
|
+
// a job that failed every attempt is kept a week (the failure must stay visible)
|
|
280
|
+
opts: {
|
|
281
|
+
attempts: 15,
|
|
282
|
+
backoff: { type: "exponential", delay: 2_000 },
|
|
283
|
+
removeOnComplete: true,
|
|
284
|
+
removeOnFail: { age: 7 * 86_400, count: 1_000 },
|
|
285
|
+
},
|
|
286
|
+
},
|
|
104
287
|
};
|
|
105
288
|
/** The registry's BullMQ options of a job name (none for a name it does not list). */
|
|
106
289
|
export function outboxJobOptions(name) {
|
|
@@ -4,5 +4,5 @@
|
|
|
4
4
|
* (admin/health.service.ts) lists the same names from the feature constants;
|
|
5
5
|
* test/infra/queue-names.test.ts keeps the two equal.
|
|
6
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"];
|
|
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", "cap-kernel-lifecycle"];
|
|
8
8
|
export type KernelQueueName = (typeof KERNEL_QUEUES)[number];
|
|
@@ -1,11 +1,24 @@
|
|
|
1
|
-
import { type DeviceFeature, type DeviceLocalPolicySummary, type DevicePolicyArgs } from "@capybara-hq/plugin-sdk";
|
|
1
|
+
import { type DeviceFeature, type DeviceLocalPolicySummary, type DevicePolicyArgs, type DevicePolicyStatus } from "@capybara-hq/plugin-sdk";
|
|
2
|
+
import { KernelConfigService } from "../infra/config/kernel-config.service.js";
|
|
2
3
|
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
3
4
|
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
5
|
+
import { OutboxService } from "../infra/outbox/outbox.service.js";
|
|
4
6
|
import type { KernelLogger } from "../log.js";
|
|
5
7
|
import { MachinesService } from "./machines.js";
|
|
6
8
|
import { LocalDeviceRelay } from "./relay.js";
|
|
7
9
|
/** How long the machine may take to apply a policy change. */
|
|
8
10
|
export declare const POLICY_DEADLINE_MS = 30000;
|
|
11
|
+
/** How long an op left `unknown` waits for the machine's hello to report it (from its intent). */
|
|
12
|
+
export declare const UNKNOWN_OP_MS: number;
|
|
13
|
+
/** A policy change as callers ask for it: the op id and the source are added here. */
|
|
14
|
+
type PolicyChange = Omit<DevicePolicyArgs, "source" | "opId">;
|
|
15
|
+
/**
|
|
16
|
+
* Test seam (`KernelOverride`): called by {@link DevicePolicyService.send} right after the `policy`
|
|
17
|
+
* op's call returned, before its outcome is recorded (by `apply` or a network ask's decision);
|
|
18
|
+
* throwing stands for a crash between the call and the resolve transaction.
|
|
19
|
+
* The kernel never provides it.
|
|
20
|
+
*/
|
|
21
|
+
export declare const DEVICE_POLICY_AFTER_CALL: unique symbol;
|
|
9
22
|
/** What became of a policy change. */
|
|
10
23
|
export type PolicyChangeOutcome = {
|
|
11
24
|
applied: true;
|
|
@@ -15,55 +28,147 @@ export type PolicyChangeOutcome = {
|
|
|
15
28
|
reason: "offline" | "refused" | "unsupported";
|
|
16
29
|
message: string;
|
|
17
30
|
};
|
|
31
|
+
/**
|
|
32
|
+
* An op whose intent is recorded ({@link DevicePolicyService.begin}): the call sends `args` to
|
|
33
|
+
* the device's machine.
|
|
34
|
+
*/
|
|
35
|
+
export interface PolicyIntent {
|
|
36
|
+
orgId: string;
|
|
37
|
+
deviceId: string;
|
|
38
|
+
machineId: string;
|
|
39
|
+
opId: string;
|
|
40
|
+
args: DevicePolicyArgs;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* What became of a sent op, as recorded by {@link DevicePolicyService.settleIn}: `applied` (with
|
|
44
|
+
* the policy the machine wrote, when it said), `refused` (the machine's message), `not_received`
|
|
45
|
+
* (the machine never got it and never will), `unknown` (no answer).
|
|
46
|
+
*/
|
|
47
|
+
export type PolicyOpResult = {
|
|
48
|
+
outcome: "applied";
|
|
49
|
+
policy?: DeviceLocalPolicySummary;
|
|
50
|
+
} | {
|
|
51
|
+
outcome: "refused";
|
|
52
|
+
message: string;
|
|
53
|
+
} | {
|
|
54
|
+
outcome: "not_received";
|
|
55
|
+
} | {
|
|
56
|
+
outcome: "interrupted";
|
|
57
|
+
} | {
|
|
58
|
+
outcome: "unknown";
|
|
59
|
+
};
|
|
60
|
+
/** What a machine says of an op id ({@link DevicePolicyService.outcomeOf}). */
|
|
61
|
+
export type PolicyOpStatus = DevicePolicyStatus | {
|
|
62
|
+
outcome: "offline";
|
|
63
|
+
};
|
|
18
64
|
/**
|
|
19
65
|
* Changes a local device's policy on its machine (network ask-to-allow spec §5.2; sandbox dev
|
|
20
66
|
* tiers spec §7.3: its hosts, its `tier`, and `isolation` back to `sandbox`): the kernel→device
|
|
21
|
-
* `policy` op over the {@link LocalDeviceRelay}. The machine is the authority: it
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* audits as `device.policy_changed`.
|
|
67
|
+
* `policy` op over the {@link LocalDeviceRelay}. The machine is the authority: it refuses
|
|
68
|
+
* widening unless its owner allows remote grants, validates every host and answers with the
|
|
69
|
+
* policy as written, which the kernel then keeps on the active machine's row (§5.4).
|
|
25
70
|
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
71
|
+
* Intent first (transactional outbox spec §7): {@link begin} writes the intent in a transaction
|
|
72
|
+
* before anything is sent (the entry on the machine row's `pending_policy_ops`, the audit
|
|
73
|
+
* `device.policy_requested` and the `device-op-resolve` job owed for when the op can no longer
|
|
74
|
+
* reach the machine); the call sends the op with its id (no transaction open); {@link settleIn}
|
|
75
|
+
* records the outcome: `device.policy_changed` (applied), `device.policy_failed` (refused, never
|
|
76
|
+
* received) or `device.policy_unknown` (no answer: the entry stays, `unknown`, for the machine's
|
|
77
|
+
* next hello or the resolver). A crash between the call and the resolve transaction leaves the
|
|
78
|
+
* intent and its owed job, whose resolver asks the machine what became of the op (never sends it
|
|
79
|
+
* again). Callers decide who may change a device's policy (the device owner, in a signed-in
|
|
80
|
+
* session); this service checks only that the device is a live device of the org.
|
|
30
81
|
*/
|
|
31
82
|
export declare class DevicePolicyService {
|
|
32
83
|
private readonly db;
|
|
33
84
|
readonly txHost: TransactionHost;
|
|
34
85
|
private readonly relay;
|
|
35
86
|
private readonly machines;
|
|
87
|
+
private readonly outbox;
|
|
88
|
+
private readonly config;
|
|
36
89
|
private readonly log;
|
|
37
|
-
|
|
90
|
+
private readonly afterCall;
|
|
91
|
+
constructor(db: KernelDb, txHost: TransactionHost, relay: Pick<LocalDeviceRelay, "call" | "holderLive">, machines: Pick<MachinesService, "activeFor">, outbox: Pick<OutboxService, "add">, config: Pick<KernelConfigService, "get">, log: Pick<KernelLogger, "warn">, afterCall?: (opId: string) => void);
|
|
38
92
|
/**
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
* the `
|
|
42
|
-
* is `device.not_found`.
|
|
93
|
+
* Changes the policy: {@link begin}, the call, then the outcome recorded in its own transaction
|
|
94
|
+
* (no transaction may be open). Never throws for an offline machine, a refusal or a program
|
|
95
|
+
* without the `policyOpId` feature (nothing is sent to it); a device that is not a live device
|
|
96
|
+
* of `orgId` is `device.not_found`.
|
|
43
97
|
*/
|
|
44
98
|
apply(input: {
|
|
45
99
|
orgId: string;
|
|
46
100
|
deviceId: string;
|
|
47
|
-
change:
|
|
101
|
+
change: PolicyChange;
|
|
48
102
|
source: DevicePolicyArgs["source"];
|
|
49
103
|
actorTokenId?: string;
|
|
50
104
|
}): Promise<PolicyChangeOutcome>;
|
|
105
|
+
/**
|
|
106
|
+
* Checks the device and its machine, then records the intent in its own transaction: the entry
|
|
107
|
+
* (`sent`) on the machine's row, the audit `device.policy_requested` and the `device-op-resolve`
|
|
108
|
+
* job owed for when the op can no longer reach the machine. No transaction may be open (the
|
|
109
|
+
* call follows). Returns the intent, or why nothing will be sent (offline, unsupported).
|
|
110
|
+
*/
|
|
111
|
+
begin(input: {
|
|
112
|
+
orgId: string;
|
|
113
|
+
deviceId: string;
|
|
114
|
+
change: PolicyChange;
|
|
115
|
+
source: DevicePolicyArgs["source"];
|
|
116
|
+
actorTokenId?: string;
|
|
117
|
+
}): Promise<{
|
|
118
|
+
intent: PolicyIntent;
|
|
119
|
+
} | {
|
|
120
|
+
outcome: PolicyChangeOutcome;
|
|
121
|
+
}>;
|
|
122
|
+
/**
|
|
123
|
+
* Sends the intent's `policy` op (no transaction may be open) and classifies the answer:
|
|
124
|
+
* applied; refused (the machine's local policy refused it: its message); anything else is
|
|
125
|
+
* `unknown`, left to the resolver's `policyStatus`: no answer, and an answer the kernel cannot
|
|
126
|
+
* trust to mean "not applied" (a result it cannot read, the machine's own error). `notFound`:
|
|
127
|
+
* the device was deleted meanwhile (recorded as unknown; the caller throws `device.not_found`).
|
|
128
|
+
*/
|
|
129
|
+
send(intent: PolicyIntent): Promise<{
|
|
130
|
+
result: PolicyOpResult;
|
|
131
|
+
outcome: PolicyChangeOutcome;
|
|
132
|
+
notFound?: true;
|
|
133
|
+
}>;
|
|
134
|
+
/** The `policy` op's call and its classification ({@link send}). */
|
|
135
|
+
private call;
|
|
136
|
+
/**
|
|
137
|
+
* What the machine says became of the op `opId` (a read-only `policyStatus`), or `offline` when
|
|
138
|
+
* no replica holds its connection or it does not answer. Never sends the change.
|
|
139
|
+
*/
|
|
140
|
+
outcomeOf(deviceId: string, opId: string): Promise<PolicyOpStatus>;
|
|
141
|
+
/**
|
|
142
|
+
* Records what became of the op `opId` of machine `machineId`, in the caller's transaction (the
|
|
143
|
+
* machine's row locked): `applied` → the policy the machine wrote (when given) kept on the row,
|
|
144
|
+
* the entry removed, `device.policy_changed` (`late` when the op was left `unknown`); `refused`,
|
|
145
|
+
* `not_received` or `interrupted` → the entry removed, `device.policy_failed` (`reason` the
|
|
146
|
+
* outcome); `unknown` → the entry kept, `unknown` until a day after its intent,
|
|
147
|
+
* `device.policy_unknown` (once; ignored once a hello reported the op). Returns the outcome for
|
|
148
|
+
* the caller, or null when the op has no live entry (resolved already, or expired: a no-op).
|
|
149
|
+
*/
|
|
150
|
+
settleIn(ref: {
|
|
151
|
+
machineId: string;
|
|
152
|
+
opId: string;
|
|
153
|
+
}, result: PolicyOpResult): Promise<PolicyChangeOutcome | null>;
|
|
51
154
|
/** The active machine's features and policy summary (null when no machine is connected). */
|
|
52
155
|
summary(deviceId: string): Promise<{
|
|
53
156
|
features: DeviceFeature[];
|
|
54
157
|
policy: DeviceLocalPolicySummary | null;
|
|
55
158
|
} | 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;
|
|
60
159
|
/**
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
|
|
160
|
+
* How long after its intent an op can no longer reach the machine: the call's deadline, the
|
|
161
|
+
* reconnect grace and the request list's margin (the relay's list TTL), plus 5 s. A machine
|
|
162
|
+
* that does not know the op id by then never will.
|
|
163
|
+
*/
|
|
164
|
+
resolveDelayMs(): number;
|
|
165
|
+
/** {@link settleIn} in its own transaction (the {@link apply} path). */
|
|
166
|
+
private settle;
|
|
167
|
+
/**
|
|
168
|
+
* The intent transaction: locks the machine's row (still the device's active machine), appends
|
|
169
|
+
* the entry (dropping the expired ones), audits `device.policy_requested` and owes the
|
|
170
|
+
* `device-op-resolve` job. False when the machine was revoked meanwhile (nothing written).
|
|
65
171
|
*/
|
|
66
|
-
private
|
|
67
|
-
/** The outcome of a failed op: offline, or the machine's refusal; a deleted device is thrown. */
|
|
68
|
-
private notApplied;
|
|
172
|
+
private recordIntent;
|
|
69
173
|
}
|
|
174
|
+
export {};
|