@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
|
@@ -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);
|
|
@@ -94,23 +94,31 @@ 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>;
|
|
100
102
|
/**
|
|
101
103
|
* Stores the local policy summary a connected program pushed after its hello (a `policy` frame:
|
|
102
104
|
* its policy file changed on the machine, its localhost proxy guard flipped, a new tier
|
|
103
105
|
* ceiling), read and audited as the hello's ({@link recordHello}): an unreadable summary keeps
|
|
104
|
-
* its isolation alone (`rawPolicy`)
|
|
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`.
|
|
105
108
|
* The next sandbox call then records a new `device_paths` event when the facts the model is told
|
|
106
109
|
* changed. A revoked machine is left alone.
|
|
107
110
|
*/
|
|
108
111
|
recordPolicy(machineId: string, summary: DeviceLocalPolicySummary | undefined, rawPolicy?: unknown): Promise<void>;
|
|
109
112
|
/**
|
|
110
113
|
* The summary to store for one a program reported (hello or `policy` frame): the parsed one,
|
|
111
|
-
* else its raw isolation alone, else null.
|
|
112
|
-
*
|
|
113
|
-
*
|
|
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.
|
|
114
122
|
*/
|
|
115
123
|
private reportedPolicy;
|
|
116
124
|
/** Marks the machine seen (a ping); writes at most once every 60 seconds. */
|
|
@@ -20,7 +20,7 @@ import { KERNEL_BUS, KERNEL_LOGGER } from "../infra/tokens.js";
|
|
|
20
20
|
import { LOCAL_PROVIDER } from "../sandbox/providers.js";
|
|
21
21
|
import { deviceNotFound } from "../sandbox/sandbox.service.js";
|
|
22
22
|
import { deviceRevokedTopic } from "./keys.js";
|
|
23
|
-
import { deviceFeaturesOf, isolationOf, localPolicyOf } from "./policy-summary.js";
|
|
23
|
+
import { deviceFeaturesOf, isolationOf, localPolicyDiff, localPolicyOf, opExplains, pendingPolicyOpsOf, } from "./policy-summary.js";
|
|
24
24
|
import { LocalDeviceRelay } from "./relay.js";
|
|
25
25
|
/** The machine credential's prefix (`cap_dev_` plus 32 random bytes, base64url). */
|
|
26
26
|
export const MACHINE_CREDENTIAL_PREFIX = "cap_dev_";
|
|
@@ -162,7 +162,9 @@ let MachinesService = class MachinesService {
|
|
|
162
162
|
* alone, read from `rawPolicy` (the frame's raw `policy`; {@link isolationOf}: anything but
|
|
163
163
|
* `sandbox` is `none`), so it never hides that commands run without a sandbox. A reported
|
|
164
164
|
* isolation that differs from the stored one (none stored counts as `sandbox`) is audited
|
|
165
|
-
* `device.isolation_reported` (sandbox dev tiers spec §10.4), once per change
|
|
165
|
+
* `device.isolation_reported` (sandbox dev tiers spec §10.4), once per change; a change of what
|
|
166
|
+
* the owner sets, made on the machine while it was away, is audited `device.policy_changed`
|
|
167
|
+
* ({@link reportedPolicy}).
|
|
166
168
|
*/
|
|
167
169
|
async recordHello(machineId, hello, rawPolicy = hello.policy) {
|
|
168
170
|
const self = await this.db
|
|
@@ -177,6 +179,7 @@ let MachinesService = class MachinesService {
|
|
|
177
179
|
"roots",
|
|
178
180
|
"created_at",
|
|
179
181
|
"local_policy",
|
|
182
|
+
"pending_policy_ops",
|
|
180
183
|
])
|
|
181
184
|
.where("id", "=", machineId)
|
|
182
185
|
.where("revoked_at", "is", null)
|
|
@@ -229,14 +232,15 @@ let MachinesService = class MachinesService {
|
|
|
229
232
|
* Stores the local policy summary a connected program pushed after its hello (a `policy` frame:
|
|
230
233
|
* its policy file changed on the machine, its localhost proxy guard flipped, a new tier
|
|
231
234
|
* ceiling), read and audited as the hello's ({@link recordHello}): an unreadable summary keeps
|
|
232
|
-
* its isolation alone (`rawPolicy`)
|
|
235
|
+
* its isolation alone (`rawPolicy`); a change the owner made on the machine is audited
|
|
236
|
+
* `device.policy_changed` (`via: "device"`), else a changed isolation `device.isolation_reported`.
|
|
233
237
|
* The next sandbox call then records a new `device_paths` event when the facts the model is told
|
|
234
238
|
* changed. A revoked machine is left alone.
|
|
235
239
|
*/
|
|
236
240
|
async recordPolicy(machineId, summary, rawPolicy = summary) {
|
|
237
241
|
const self = await this.db
|
|
238
242
|
.selectFrom("device_machines")
|
|
239
|
-
.select(["device_id", "org_id", "account_id", "local_policy"])
|
|
243
|
+
.select(["device_id", "org_id", "account_id", "local_policy", "pending_policy_ops"])
|
|
240
244
|
.where("id", "=", machineId)
|
|
241
245
|
.where("revoked_at", "is", null)
|
|
242
246
|
.forUpdate()
|
|
@@ -253,12 +257,46 @@ let MachinesService = class MachinesService {
|
|
|
253
257
|
}
|
|
254
258
|
/**
|
|
255
259
|
* The summary to store for one a program reported (hello or `policy` frame): the parsed one,
|
|
256
|
-
* else its raw isolation alone, else null.
|
|
257
|
-
*
|
|
258
|
-
*
|
|
260
|
+
* else its raw isolation alone, else null. When both the stored and the reported summary are
|
|
261
|
+
* readable and what the owner sets changed ({@link localPolicyDiff}) by no `policy` op the
|
|
262
|
+
* platform sent ({@link opExplains}: its answer may come after the machine's own report), the
|
|
263
|
+
* owner changed it on the machine (`capybara-device policy ...`): audited
|
|
264
|
+
* `device.policy_changed` with `via: "device"`, the isolation with it, so the device page lists
|
|
265
|
+
* it beside the platform's changes; one an op explains is not audited here (the op's answer
|
|
266
|
+
* is). Otherwise a reported isolation that differs from the stored one (none stored counts as
|
|
267
|
+
* `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.
|
|
259
268
|
*/
|
|
260
269
|
async reportedPolicy(machineId, self, reported, rawPolicy) {
|
|
261
270
|
const summary = localPolicyOf(reported);
|
|
271
|
+
const prev = localPolicyOf(self.local_policy);
|
|
272
|
+
const diff = summary !== null && prev !== null ? localPolicyDiff(prev, summary) : null;
|
|
273
|
+
if (diff !== null) {
|
|
274
|
+
const now = Date.now();
|
|
275
|
+
const byOp = pendingPolicyOpsOf(self.pending_policy_ops).some((op) => op.until > now && opExplains(op, diff));
|
|
276
|
+
// the platform's op: its answer lists it (the isolation with it)
|
|
277
|
+
if (byOp)
|
|
278
|
+
return summary;
|
|
279
|
+
await audit(this.db.db, {
|
|
280
|
+
actorAccountId: self.account_id,
|
|
281
|
+
orgId: self.org_id,
|
|
282
|
+
action: "device.policy_changed",
|
|
283
|
+
target: self.device_id,
|
|
284
|
+
detail: {
|
|
285
|
+
add: diff.add,
|
|
286
|
+
remove: diff.remove,
|
|
287
|
+
...(diff.tier !== undefined && { tier: diff.tier.to, tierFrom: diff.tier.from }),
|
|
288
|
+
...(diff.localhost !== undefined && { localhost: diff.localhost }),
|
|
289
|
+
...(diff.isolation !== undefined && { isolation: diff.isolation }),
|
|
290
|
+
...(diff.remoteGrants !== undefined && { remoteGrants: diff.remoteGrants }),
|
|
291
|
+
by: self.account_id,
|
|
292
|
+
via: "device",
|
|
293
|
+
machineId,
|
|
294
|
+
approvalId: null,
|
|
295
|
+
confirmed: true,
|
|
296
|
+
},
|
|
297
|
+
});
|
|
298
|
+
return summary;
|
|
299
|
+
}
|
|
262
300
|
const isolation = summary?.isolation ?? isolationOf(rawPolicy);
|
|
263
301
|
const before = isolationOf(self.local_policy) ?? "sandbox";
|
|
264
302
|
if (isolation !== null && isolation !== before) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type DeviceFeature, type DeviceLocalPolicySummary } from "@capybara-hq/plugin-sdk";
|
|
1
|
+
import { type DeviceFeature, type DeviceLocalPolicySummary, type SandboxTier } from "@capybara-hq/plugin-sdk";
|
|
2
2
|
/**
|
|
3
3
|
* The known features in `v` (a hello's `features` or the stored column), in a fixed order and
|
|
4
4
|
* deduped; anything else (unknown names, a non-array) is dropped.
|
|
@@ -18,3 +18,46 @@ export declare function localPolicyOf(v: unknown): DeviceLocalPolicySummary | nu
|
|
|
18
18
|
* there is none.
|
|
19
19
|
*/
|
|
20
20
|
export declare function isolationOf(v: unknown): "sandbox" | "none" | null;
|
|
21
|
+
/**
|
|
22
|
+
* What changed between two local policy summaries, in the owner's terms (the device page's change
|
|
23
|
+
* list, sandbox dev tiers spec §10.4): hosts added to and removed from the allowlist, the chosen
|
|
24
|
+
* tier (`null`: follows the org's default), the localhost access, the isolation and whether the
|
|
25
|
+
* machine accepts the platform's changes. Facts the owner does not set (the effective tier, unix
|
|
26
|
+
* sockets) are left out. Each field is present only when it changed.
|
|
27
|
+
*/
|
|
28
|
+
export interface LocalPolicyDiff {
|
|
29
|
+
add: string[];
|
|
30
|
+
remove: string[];
|
|
31
|
+
tier?: {
|
|
32
|
+
from: SandboxTier | null;
|
|
33
|
+
to: SandboxTier | null;
|
|
34
|
+
};
|
|
35
|
+
localhost?: DeviceLocalPolicySummary["localhost"];
|
|
36
|
+
isolation?: DeviceLocalPolicySummary["isolation"];
|
|
37
|
+
remoteGrants?: DeviceLocalPolicySummary["remoteGrants"];
|
|
38
|
+
}
|
|
39
|
+
/** The {@link LocalPolicyDiff} from `prev` to `next`; null when nothing the owner sets changed. */
|
|
40
|
+
export declare function localPolicyDiff(prev: DeviceLocalPolicySummary, next: DeviceLocalPolicySummary): LocalPolicyDiff | null;
|
|
41
|
+
/**
|
|
42
|
+
* A `policy` op the platform sent to a machine whose answer may not be recorded yet (kept on the
|
|
43
|
+
* machine's row, `pending_policy_ops`): the change asked for, and until when (epoch ms) a summary
|
|
44
|
+
* the machine reports by itself may still be that op's.
|
|
45
|
+
*/
|
|
46
|
+
export interface PendingPolicyOp {
|
|
47
|
+
id: string;
|
|
48
|
+
add: string[];
|
|
49
|
+
remove: string[];
|
|
50
|
+
/** Present when the op set the tier (null: follow the org's default). */
|
|
51
|
+
tier?: SandboxTier | null;
|
|
52
|
+
isolation?: "sandbox";
|
|
53
|
+
until: number;
|
|
54
|
+
}
|
|
55
|
+
/** The pending ops stored on a machine row (malformed entries dropped). */
|
|
56
|
+
export declare function pendingPolicyOpsOf(v: unknown): PendingPolicyOp[];
|
|
57
|
+
/**
|
|
58
|
+
* Whether `diff` is what the platform's `op` did (its answer, or the machine's own report of it
|
|
59
|
+
* before the answer was recorded): hosts within the op's, the tier the op set, and only what a
|
|
60
|
+
* tier change moves with it (the isolation back to `sandbox`, the localhost access). The device
|
|
61
|
+
* page then lists the change once, as the platform's.
|
|
62
|
+
*/
|
|
63
|
+
export declare function opExplains(op: PendingPolicyOp, diff: LocalPolicyDiff): boolean;
|
|
@@ -61,3 +61,72 @@ export function isolationOf(v) {
|
|
|
61
61
|
}
|
|
62
62
|
return "none";
|
|
63
63
|
}
|
|
64
|
+
/** The {@link LocalPolicyDiff} from `prev` to `next`; null when nothing the owner sets changed. */
|
|
65
|
+
export function localPolicyDiff(prev, next) {
|
|
66
|
+
const before = new Set(prev.allowedDomains);
|
|
67
|
+
const after = new Set(next.allowedDomains);
|
|
68
|
+
const d = {
|
|
69
|
+
add: [...after].filter((h) => !before.has(h)).sort(),
|
|
70
|
+
remove: [...before].filter((h) => !after.has(h)).sort(),
|
|
71
|
+
};
|
|
72
|
+
const fromTier = prev.tier ?? null;
|
|
73
|
+
const toTier = next.tier ?? null;
|
|
74
|
+
if (fromTier !== toTier)
|
|
75
|
+
d.tier = { from: fromTier, to: toTier };
|
|
76
|
+
if (prev.localhost !== next.localhost)
|
|
77
|
+
d.localhost = next.localhost;
|
|
78
|
+
if (prev.isolation !== next.isolation)
|
|
79
|
+
d.isolation = next.isolation;
|
|
80
|
+
if (prev.remoteGrants !== next.remoteGrants)
|
|
81
|
+
d.remoteGrants = next.remoteGrants;
|
|
82
|
+
const changed = d.add.length > 0 ||
|
|
83
|
+
d.remove.length > 0 ||
|
|
84
|
+
d.tier !== undefined ||
|
|
85
|
+
d.localhost !== undefined ||
|
|
86
|
+
d.isolation !== undefined ||
|
|
87
|
+
d.remoteGrants !== undefined;
|
|
88
|
+
return changed ? d : null;
|
|
89
|
+
}
|
|
90
|
+
/** The pending ops stored on a machine row (malformed entries dropped). */
|
|
91
|
+
export function pendingPolicyOpsOf(v) {
|
|
92
|
+
if (!Array.isArray(v))
|
|
93
|
+
return [];
|
|
94
|
+
const hosts = (x) => Array.isArray(x) ? x.filter((h) => typeof h === "string") : [];
|
|
95
|
+
const out = [];
|
|
96
|
+
for (const e of v) {
|
|
97
|
+
if (typeof e !== "object" || e === null)
|
|
98
|
+
continue;
|
|
99
|
+
const o = e;
|
|
100
|
+
if (typeof o.id !== "string" || typeof o.until !== "number")
|
|
101
|
+
continue;
|
|
102
|
+
out.push({
|
|
103
|
+
id: o.id,
|
|
104
|
+
add: hosts(o.add),
|
|
105
|
+
remove: hosts(o.remove),
|
|
106
|
+
...("tier" in o && { tier: isSandboxTier(o.tier) ? o.tier : null }),
|
|
107
|
+
...(o.isolation === "sandbox" && { isolation: "sandbox" }),
|
|
108
|
+
until: o.until,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
return out;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Whether `diff` is what the platform's `op` did (its answer, or the machine's own report of it
|
|
115
|
+
* before the answer was recorded): hosts within the op's, the tier the op set, and only what a
|
|
116
|
+
* tier change moves with it (the isolation back to `sandbox`, the localhost access). The device
|
|
117
|
+
* page then lists the change once, as the platform's.
|
|
118
|
+
*/
|
|
119
|
+
export function opExplains(op, diff) {
|
|
120
|
+
if (!diff.add.every((h) => op.add.includes(h)))
|
|
121
|
+
return false;
|
|
122
|
+
if (!diff.remove.every((h) => op.remove.includes(h)))
|
|
123
|
+
return false;
|
|
124
|
+
if (diff.tier !== undefined && (op.tier === undefined || diff.tier.to !== op.tier))
|
|
125
|
+
return false;
|
|
126
|
+
if (diff.isolation !== undefined && op.isolation === undefined && op.tier === undefined) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
if (diff.localhost !== undefined && op.tier === undefined)
|
|
130
|
+
return false;
|
|
131
|
+
return diff.remoteGrants === undefined;
|
|
132
|
+
}
|
|
@@ -2,9 +2,9 @@ import { type ExtensionEmbedRequest, type ExtensionEmbedResult, type ExtensionMo
|
|
|
2
2
|
import { KernelConfigService } from "../infra/config/kernel-config.service.js";
|
|
3
3
|
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
4
4
|
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
5
|
-
import { KernelEvents } from "../infra/events/kernel-events.js";
|
|
6
5
|
import { type HookRunner } from "../runtime/hook-calls.js";
|
|
7
6
|
import { ProviderRegistry } from "../runtime/providers.js";
|
|
7
|
+
import { UsageReportJobs } from "../runtime/usage-report-jobs.js";
|
|
8
8
|
import { ModelPurposesService } from "./purposes.js";
|
|
9
9
|
/** An extension's `permissions.models` (what it declared, as approved in its manifest). */
|
|
10
10
|
export type DeclaredModelPurposes = readonly NonNullable<ExtensionPermissions["models"]>[number][];
|
|
@@ -29,9 +29,9 @@ export declare const EMBED_MAX_INPUTS = 2048;
|
|
|
29
29
|
* limited by `models.embedTimeoutMs` (`ModelError("timeout")`), checks each result ({@link validateEmbedResult}: a
|
|
30
30
|
* wrong count, dimension or a non-finite number is a `ModelError` of kind `server`), and
|
|
31
31
|
* concatenates the vectors; no retries (an extension retries itself if it wants to);
|
|
32
|
-
* - writes one `model_usage` row (the sum of the batches' input tokens) and
|
|
33
|
-
*
|
|
34
|
-
*
|
|
32
|
+
* - writes one `model_usage` row (the sum of the batches' input tokens) and, in the same
|
|
33
|
+
* transaction, owes its `usage.report` delivery while some plugin subscribes (`UsageReportJobs`,
|
|
34
|
+
* transactional outbox spec §8.1). A call that fails after the
|
|
35
35
|
* provider answered some batches (an invalid result, a later batch failing) still records their
|
|
36
36
|
* input tokens before the error reaches the extension; one that fails before any answer records
|
|
37
37
|
* nothing.
|
|
@@ -42,14 +42,14 @@ export declare class ModelEmbeddings {
|
|
|
42
42
|
readonly txHost: TransactionHost;
|
|
43
43
|
private readonly purposes;
|
|
44
44
|
private readonly providers;
|
|
45
|
-
/**
|
|
46
|
-
private readonly
|
|
45
|
+
/** The row's `usage.report` delivery, owed with it. */
|
|
46
|
+
private readonly usage;
|
|
47
47
|
private readonly log;
|
|
48
48
|
/** `models.embedTimeoutMs` (hot). */
|
|
49
49
|
private readonly config;
|
|
50
50
|
constructor(kdb: KernelDb, txHost: TransactionHost, purposes: Pick<ModelPurposesService, "resolve">, providers: Pick<ProviderRegistry, "resolve">,
|
|
51
|
-
/**
|
|
52
|
-
|
|
51
|
+
/** The row's `usage.report` delivery, owed with it. */
|
|
52
|
+
usage: Pick<UsageReportJobs, "oweModelUsage">, log: Pick<Logger, "warn">,
|
|
53
53
|
/** `models.embedTimeoutMs` (hot). */
|
|
54
54
|
config: Pick<KernelConfigService, "get">);
|
|
55
55
|
/** Refuses a purpose `plugin` did not declare with kind `embedding`, or an org it is off in. */
|
|
@@ -77,6 +77,6 @@ export declare class ModelEmbeddings {
|
|
|
77
77
|
orgId: string;
|
|
78
78
|
purpose: string;
|
|
79
79
|
}): Promise<ExtensionModelTarget | null>;
|
|
80
|
-
/** One `model_usage` row
|
|
80
|
+
/** One `model_usage` row with its owed `usage.report` delivery. */
|
|
81
81
|
private record;
|
|
82
82
|
}
|
|
@@ -13,15 +13,14 @@ import { ApiError } from "../errors.js";
|
|
|
13
13
|
import { isExtensionEnabled } from "../extensions/enabled.js";
|
|
14
14
|
import { audit } from "../identity/audit.js";
|
|
15
15
|
import { KernelConfigService } from "../infra/config/kernel-config.service.js";
|
|
16
|
-
import { afterCommit } from "../infra/database/after-commit.js";
|
|
17
16
|
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
18
17
|
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
19
18
|
import { Transactional } from "../infra/database/transactional.decorator.js";
|
|
20
|
-
import { KernelEvents } from "../infra/events/kernel-events.js";
|
|
21
19
|
import { KERNEL_LOGGER } from "../infra/tokens.js";
|
|
22
20
|
import { embeddingDeniedAudit } from "../runtime/hook-calls.js";
|
|
23
21
|
import { modelDeniedText } from "../runtime/model-call.js";
|
|
24
22
|
import { ProviderRegistry } from "../runtime/providers.js";
|
|
23
|
+
import { UsageReportJobs } from "../runtime/usage-report-jobs.js";
|
|
25
24
|
import { ModelPurposesService } from "./purposes.js";
|
|
26
25
|
/** Most texts one `ctx.models.embed` call may carry (the kernel splits them by `maxBatch`). */
|
|
27
26
|
export const EMBED_MAX_INPUTS = 2048;
|
|
@@ -44,9 +43,9 @@ export const EMBED_MAX_INPUTS = 2048;
|
|
|
44
43
|
* limited by `models.embedTimeoutMs` (`ModelError("timeout")`), checks each result ({@link validateEmbedResult}: a
|
|
45
44
|
* wrong count, dimension or a non-finite number is a `ModelError` of kind `server`), and
|
|
46
45
|
* concatenates the vectors; no retries (an extension retries itself if it wants to);
|
|
47
|
-
* - writes one `model_usage` row (the sum of the batches' input tokens) and
|
|
48
|
-
*
|
|
49
|
-
*
|
|
46
|
+
* - writes one `model_usage` row (the sum of the batches' input tokens) and, in the same
|
|
47
|
+
* transaction, owes its `usage.report` delivery while some plugin subscribes (`UsageReportJobs`,
|
|
48
|
+
* transactional outbox spec §8.1). A call that fails after the
|
|
50
49
|
* provider answered some batches (an invalid result, a later batch failing) still records their
|
|
51
50
|
* input tokens before the error reaches the extension; one that fails before any answer records
|
|
52
51
|
* nothing.
|
|
@@ -57,19 +56,19 @@ let ModelEmbeddings = class ModelEmbeddings {
|
|
|
57
56
|
txHost;
|
|
58
57
|
purposes;
|
|
59
58
|
providers;
|
|
60
|
-
|
|
59
|
+
usage;
|
|
61
60
|
log;
|
|
62
61
|
config;
|
|
63
62
|
constructor(kdb, txHost, purposes, providers,
|
|
64
|
-
/**
|
|
65
|
-
|
|
63
|
+
/** The row's `usage.report` delivery, owed with it. */
|
|
64
|
+
usage, log,
|
|
66
65
|
/** `models.embedTimeoutMs` (hot). */
|
|
67
66
|
config) {
|
|
68
67
|
this.kdb = kdb;
|
|
69
68
|
this.txHost = txHost;
|
|
70
69
|
this.purposes = purposes;
|
|
71
70
|
this.providers = providers;
|
|
72
|
-
this.
|
|
71
|
+
this.usage = usage;
|
|
73
72
|
this.log = log;
|
|
74
73
|
this.config = config;
|
|
75
74
|
}
|
|
@@ -243,7 +242,7 @@ let ModelEmbeddings = class ModelEmbeddings {
|
|
|
243
242
|
throw e;
|
|
244
243
|
}
|
|
245
244
|
}
|
|
246
|
-
/** One `model_usage` row
|
|
245
|
+
/** One `model_usage` row with its owed `usage.report` delivery. */
|
|
247
246
|
async record(plugin, orgId, purpose, model, inputTokens) {
|
|
248
247
|
const row = await this.kdb.db
|
|
249
248
|
.insertInto("model_usage")
|
|
@@ -257,13 +256,7 @@ let ModelEmbeddings = class ModelEmbeddings {
|
|
|
257
256
|
})
|
|
258
257
|
.returning("id")
|
|
259
258
|
.executeTakeFirstOrThrow();
|
|
260
|
-
|
|
261
|
-
// caught up by the usage sweep
|
|
262
|
-
afterCommit(() => {
|
|
263
|
-
void this.events
|
|
264
|
-
.emit("model_usage.recorded", { id: row.id })
|
|
265
|
-
.catch((err) => this.log.warn("model usage event failed", { usageId: row.id, err }));
|
|
266
|
-
});
|
|
259
|
+
await this.usage.oweModelUsage(row.id, orgId);
|
|
267
260
|
}
|
|
268
261
|
};
|
|
269
262
|
__decorate([
|
|
@@ -275,7 +268,7 @@ ModelEmbeddings = __decorate([
|
|
|
275
268
|
__param(1, Inject(TransactionHost)),
|
|
276
269
|
__param(2, Inject(ModelPurposesService)),
|
|
277
270
|
__param(3, Inject(ProviderRegistry)),
|
|
278
|
-
__param(4, Inject(
|
|
271
|
+
__param(4, Inject(UsageReportJobs)),
|
|
279
272
|
__param(5, Inject(KERNEL_LOGGER)),
|
|
280
273
|
__param(6, Inject(KernelConfigService))
|
|
281
274
|
], ModelEmbeddings);
|
|
@@ -9,6 +9,7 @@ import { ExtensionRegistryModule } from "../extensions/registry.js";
|
|
|
9
9
|
import { IdentityModule } from "../identity/identity.module.js";
|
|
10
10
|
import { OrgLockModule } from "../orgs/org-lock.js";
|
|
11
11
|
import { ProvidersModule } from "../runtime/providers.js";
|
|
12
|
+
import { UsageReportJobsModule } from "../runtime/usage-report-jobs.js";
|
|
12
13
|
import { ModelEmbeddings } from "./embeddings.js";
|
|
13
14
|
import { ModelEnablementModule } from "./enablement.js";
|
|
14
15
|
import { PricesService } from "./prices.js";
|
|
@@ -28,6 +29,8 @@ ModelsModule = __decorate([
|
|
|
28
29
|
OrgLockModule,
|
|
29
30
|
ProvidersModule,
|
|
30
31
|
ModelEnablementModule,
|
|
32
|
+
// an embed call's `usage.report` delivery, owed with its `model_usage` row (no queue)
|
|
33
|
+
UsageReportJobsModule,
|
|
31
34
|
],
|
|
32
35
|
providers: [PricesService, ModelPurposesService, ModelEmbeddings],
|
|
33
36
|
exports: [PricesService, ModelPurposesService, ModelEnablementModule, ModelEmbeddings],
|
|
@@ -611,7 +611,7 @@ let DirectorySync = class DirectorySync {
|
|
|
611
611
|
if (row?.source !== source)
|
|
612
612
|
return undefined;
|
|
613
613
|
const result = await this.offboarding.offboard(orgId, accountId, "directory");
|
|
614
|
-
// the markers
|
|
614
|
+
// the revocation markers and events follow the root commit (hints; none after a rollback)
|
|
615
615
|
afterCommit(() => this.offboarding.afterOffboard(result));
|
|
616
616
|
return result;
|
|
617
617
|
}
|
|
@@ -151,7 +151,7 @@ let MembersService = class MembersService {
|
|
|
151
151
|
}
|
|
152
152
|
const actor = { actorAccountId, ...(actorTokenId !== undefined && { actorTokenId }) };
|
|
153
153
|
const res = await this.offboarding.offboard(orgId, targetId, "removed", actor);
|
|
154
|
-
// the markers
|
|
154
|
+
// the revocation markers and events follow the root commit (hints; none after a rollback)
|
|
155
155
|
afterCommit(() => this.offboarding.afterOffboard(res));
|
|
156
156
|
await audit(this.db.db, {
|
|
157
157
|
...actor,
|