@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
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
8
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
9
|
+
};
|
|
10
|
+
import { OnWorkerEvent, Processor, WorkerHost } from "@nestjs/bullmq";
|
|
11
|
+
import { Inject, Injectable, Module } from "@nestjs/common";
|
|
12
|
+
import { sql } from "kysely";
|
|
13
|
+
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
14
|
+
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
15
|
+
import { Transactional } from "../infra/database/transactional.decorator.js";
|
|
16
|
+
import { OutboxService } from "../infra/outbox/outbox.service.js";
|
|
17
|
+
import { QueueModule } from "../infra/queue/queue.module.js";
|
|
18
|
+
import { runJob } from "../infra/queue/run-job.js";
|
|
19
|
+
import { KERNEL_LOGGER } from "../infra/tokens.js";
|
|
20
|
+
import { ExtensionsModule } from "./extensions.module.js";
|
|
21
|
+
import { deepFreeze, errMsg } from "./hooks.js";
|
|
22
|
+
import { ExtensionHost } from "./host.js";
|
|
23
|
+
/** Queue of lifecycle hook deliveries (no `:` in the name). */
|
|
24
|
+
export const LIFECYCLE_QUEUE = "cap-kernel-lifecycle";
|
|
25
|
+
/** The job name of every delivery (registered in `infra/outbox/registry.ts`). */
|
|
26
|
+
export const LIFECYCLE_JOB = "lifecycle";
|
|
27
|
+
/** One handler call's time limit. */
|
|
28
|
+
export const LIFECYCLE_HOOK_TIMEOUT_MS = 5000;
|
|
29
|
+
/** Concurrent deliveries per worker replica. */
|
|
30
|
+
const LIFECYCLE_CONCURRENCY = 4;
|
|
31
|
+
/**
|
|
32
|
+
* The deterministic job id of one delivery: the mount, the plugin and the payload's ids (no `:`;
|
|
33
|
+
* kernel ids and plugin names carry none). Removing the same member, agent or org again while the
|
|
34
|
+
* first delivery is still owed is the same job.
|
|
35
|
+
*/
|
|
36
|
+
export function lifecycleJobId(mount, plugin, payload) {
|
|
37
|
+
const ids = Object.entries(payload)
|
|
38
|
+
.sort(([a], [b]) => (a < b ? -1 : 1))
|
|
39
|
+
.map(([, v]) => String(v));
|
|
40
|
+
return ["lifecycle", mount.replaceAll(".", "-"), plugin, ...ids].join("-").replaceAll(":", "-");
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Plugins that subscribe to a lifecycle mount according to deployment state: the approved
|
|
44
|
+
* permission snapshot declares the mount in `permissions.hooks`, and that approved version is the
|
|
45
|
+
* one the deployment runs: an enabled install of that version, or a replica status row (not
|
|
46
|
+
* `unloaded`) that loaded or still serves it (an upgrade pending approval keeps serving the
|
|
47
|
+
* approved version; a config-declared plugin has only replica rows). A snapshot approved for
|
|
48
|
+
* another version than the one installed or served does not count. Org enablement does not count
|
|
49
|
+
* either: cleanup concerns data the plugin already holds (memory plugin spec §6.3). Sorted by name.
|
|
50
|
+
*/
|
|
51
|
+
export async function queryLifecycleSubscribers(db, mount) {
|
|
52
|
+
const rows = await db
|
|
53
|
+
.selectFrom("extension_permission_snapshots as s")
|
|
54
|
+
.select("s.plugin")
|
|
55
|
+
.distinct()
|
|
56
|
+
.where(sql `s.permissions -> 'permissions' -> 'hooks' ? ${mount}`)
|
|
57
|
+
.where((eb) => eb.or([
|
|
58
|
+
eb.exists(eb
|
|
59
|
+
.selectFrom("extension_installs as i")
|
|
60
|
+
.select(sql `1`.as("one"))
|
|
61
|
+
.whereRef("i.name", "=", "s.plugin")
|
|
62
|
+
.whereRef("i.version", "=", "s.version")
|
|
63
|
+
.where("i.desired_state", "=", "enabled")),
|
|
64
|
+
eb.exists(eb
|
|
65
|
+
.selectFrom("extension_replica_status as r")
|
|
66
|
+
.select(sql `1`.as("one"))
|
|
67
|
+
.whereRef("r.plugin", "=", "s.plugin")
|
|
68
|
+
.where("r.state", "<>", "unloaded")
|
|
69
|
+
.where((r) => r.or([
|
|
70
|
+
r("r.version", "=", r.ref("s.version")),
|
|
71
|
+
r("r.serving_version", "=", r.ref("s.version")),
|
|
72
|
+
]))),
|
|
73
|
+
]))
|
|
74
|
+
.orderBy("s.plugin")
|
|
75
|
+
.execute();
|
|
76
|
+
return rows.map((r) => r.plugin);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Durable delivery of the lifecycle hooks `org.member_removed`, `agent.deleted` and `org.deleted`
|
|
80
|
+
* (memory plugin spec §6.3), so extensions reliably learn to delete the data they hold:
|
|
81
|
+
* - **Owe** ({@link recordIn}): in the transaction that removes the member, the agent or the org,
|
|
82
|
+
* one outbox job per subscribing plugin ({@link queryLifecycleSubscribers}) on
|
|
83
|
+
* {@link LIFECYCLE_QUEUE}, under {@link lifecycleJobId}, with no `org_id` (it is the deletion's
|
|
84
|
+
* own cleanup work, transactional outbox spec §9); a rollback owes nothing. The outbox delivers
|
|
85
|
+
* it to BullMQ after the commit (registry: attempts and backoff).
|
|
86
|
+
* - **Deliver** ({@link process}, worker role): the plugin's handlers loaded on this replica are
|
|
87
|
+
* called in turn (5 s each). Not loaded on this replica -> the job fails and is retried (another
|
|
88
|
+
* replica or a later attempt). Loaded without a handler -> dropped with a warning. Handlers get
|
|
89
|
+
* the plugin's platform deployment config (`call.config`), never an org's copy, so an org whose
|
|
90
|
+
* effective config is not valid still gets its cleanup. The last failed attempt logs an error.
|
|
91
|
+
* Handlers must be idempotent (a delivery may repeat when a replica dies after the call).
|
|
92
|
+
*/
|
|
93
|
+
let LifecycleHooks = class LifecycleHooks {
|
|
94
|
+
kdb;
|
|
95
|
+
txHost;
|
|
96
|
+
outbox;
|
|
97
|
+
host;
|
|
98
|
+
log;
|
|
99
|
+
constructor(kdb, txHost, outbox,
|
|
100
|
+
/** The loaded plugins' handlers (`hooks.handlers`) and whether a plugin is active here. */
|
|
101
|
+
host, log) {
|
|
102
|
+
this.kdb = kdb;
|
|
103
|
+
this.txHost = txHost;
|
|
104
|
+
this.outbox = outbox;
|
|
105
|
+
this.host = host;
|
|
106
|
+
this.log = log;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Owes one delivery per subscribing plugin in the caller's transaction (`mandatory`); returns
|
|
110
|
+
* the plugins.
|
|
111
|
+
*/
|
|
112
|
+
async recordIn(mount, payload) {
|
|
113
|
+
const plugins = await queryLifecycleSubscribers(this.kdb.db, mount);
|
|
114
|
+
for (const plugin of plugins) {
|
|
115
|
+
const data = { mount, plugin, payload };
|
|
116
|
+
await this.outbox.add({
|
|
117
|
+
queue: LIFECYCLE_QUEUE,
|
|
118
|
+
name: LIFECYCLE_JOB,
|
|
119
|
+
jobId: lifecycleJobId(mount, plugin, payload),
|
|
120
|
+
data,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
return plugins;
|
|
124
|
+
}
|
|
125
|
+
/** One delivery job (the consumer {@link LifecycleHooksProcessor} calls it). */
|
|
126
|
+
async process(data, stop) {
|
|
127
|
+
const fields = { mount: data.mount, plugin: data.plugin };
|
|
128
|
+
const handlers = this.host.hooks
|
|
129
|
+
.lifecycleHandlers(data.mount)
|
|
130
|
+
.filter((h) => h.plugin === data.plugin)
|
|
131
|
+
.map((h) => h.handler);
|
|
132
|
+
if (handlers.length === 0) {
|
|
133
|
+
if (this.host.current(data.plugin) === undefined) {
|
|
134
|
+
// another replica or a later attempt may have it loaded
|
|
135
|
+
throw new Error(`plugin ${data.plugin} is not loaded on this replica`);
|
|
136
|
+
}
|
|
137
|
+
this.log.warn("lifecycle hook delivery dropped: the plugin registered no handler", fields);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
const payload = deepFreeze(structuredClone(data.payload));
|
|
141
|
+
for (const h of handlers)
|
|
142
|
+
await this.call(h, payload, stop);
|
|
143
|
+
}
|
|
144
|
+
/** One handler call, aborted after {@link LIFECYCLE_HOOK_TIMEOUT_MS} or when the replica stops. */
|
|
145
|
+
async call(h, payload, stop) {
|
|
146
|
+
const ac = new AbortController();
|
|
147
|
+
const onStop = () => ac.abort(new Error("replica stopping"));
|
|
148
|
+
if (stop.aborted)
|
|
149
|
+
onStop();
|
|
150
|
+
else
|
|
151
|
+
stop.addEventListener("abort", onStop, { once: true });
|
|
152
|
+
let timer;
|
|
153
|
+
try {
|
|
154
|
+
await Promise.race([
|
|
155
|
+
Promise.resolve().then(() => h(payload, ac.signal)),
|
|
156
|
+
new Promise((_, reject) => {
|
|
157
|
+
timer = setTimeout(() => {
|
|
158
|
+
const err = new Error(`timed out after ${LIFECYCLE_HOOK_TIMEOUT_MS} ms`);
|
|
159
|
+
ac.abort(err);
|
|
160
|
+
reject(err);
|
|
161
|
+
}, LIFECYCLE_HOOK_TIMEOUT_MS);
|
|
162
|
+
// already aborted (the replica was stopping before the call): no abort event will fire
|
|
163
|
+
if (ac.signal.aborted)
|
|
164
|
+
reject(ac.signal.reason);
|
|
165
|
+
else
|
|
166
|
+
ac.signal.addEventListener("abort", () => reject(ac.signal.reason), { once: true });
|
|
167
|
+
}),
|
|
168
|
+
]);
|
|
169
|
+
}
|
|
170
|
+
finally {
|
|
171
|
+
clearTimeout(timer);
|
|
172
|
+
stop.removeEventListener("abort", onStop);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/** A job failed (the worker's `failed` event): a retry warns, the last attempt logs an error. */
|
|
176
|
+
onFailed(j, err) {
|
|
177
|
+
if (!j)
|
|
178
|
+
return;
|
|
179
|
+
const fields = {
|
|
180
|
+
jobId: j.id,
|
|
181
|
+
mount: j.data.mount,
|
|
182
|
+
plugin: j.data.plugin,
|
|
183
|
+
attempt: j.attemptsMade,
|
|
184
|
+
error: errMsg(err),
|
|
185
|
+
};
|
|
186
|
+
if (j.attemptsMade >= (j.opts.attempts ?? 1)) {
|
|
187
|
+
this.log.error("lifecycle hook delivery failed on its last attempt; given up", fields);
|
|
188
|
+
}
|
|
189
|
+
else
|
|
190
|
+
this.log.warn("lifecycle hook delivery failed; retrying", fields);
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
__decorate([
|
|
194
|
+
Transactional({ propagation: "mandatory" })
|
|
195
|
+
], LifecycleHooks.prototype, "recordIn", null);
|
|
196
|
+
LifecycleHooks = __decorate([
|
|
197
|
+
Injectable(),
|
|
198
|
+
__param(0, Inject(KernelDb)),
|
|
199
|
+
__param(1, Inject(TransactionHost)),
|
|
200
|
+
__param(2, Inject(OutboxService)),
|
|
201
|
+
__param(3, Inject(ExtensionHost)),
|
|
202
|
+
__param(4, Inject(KERNEL_LOGGER))
|
|
203
|
+
], LifecycleHooks);
|
|
204
|
+
export { LifecycleHooks };
|
|
205
|
+
/**
|
|
206
|
+
* The consumer of {@link LIFECYCLE_QUEUE}: {@link LifecycleHooks.process}. Worker role only
|
|
207
|
+
* (`LifecycleHookWorkersModule`); built with `autorun: false`, started by the worker role after the
|
|
208
|
+
* extensions loaded.
|
|
209
|
+
*/
|
|
210
|
+
let LifecycleHooksProcessor = class LifecycleHooksProcessor extends WorkerHost {
|
|
211
|
+
hooks;
|
|
212
|
+
log;
|
|
213
|
+
stopping = new AbortController();
|
|
214
|
+
constructor(hooks, log) {
|
|
215
|
+
super();
|
|
216
|
+
this.hooks = hooks;
|
|
217
|
+
this.log = log;
|
|
218
|
+
}
|
|
219
|
+
process(job) {
|
|
220
|
+
return runJob(LIFECYCLE_QUEUE, job, () => this.hooks.process(job.data, this.stopping.signal));
|
|
221
|
+
}
|
|
222
|
+
/** Starts consuming (worker role, after the extensions loaded). */
|
|
223
|
+
start() {
|
|
224
|
+
void this.worker.run().catch((err) => this.error(err));
|
|
225
|
+
}
|
|
226
|
+
/** Aborts the calls in flight and closes the worker. */
|
|
227
|
+
async close() {
|
|
228
|
+
this.stopping.abort();
|
|
229
|
+
await this.worker.close();
|
|
230
|
+
}
|
|
231
|
+
failed(job, err) {
|
|
232
|
+
this.hooks.onFailed(job, err);
|
|
233
|
+
}
|
|
234
|
+
error(err) {
|
|
235
|
+
this.log.error("lifecycle hook worker error", { err });
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
__decorate([
|
|
239
|
+
OnWorkerEvent("failed")
|
|
240
|
+
], LifecycleHooksProcessor.prototype, "failed", null);
|
|
241
|
+
__decorate([
|
|
242
|
+
OnWorkerEvent("error")
|
|
243
|
+
], LifecycleHooksProcessor.prototype, "error", null);
|
|
244
|
+
LifecycleHooksProcessor = __decorate([
|
|
245
|
+
Injectable(),
|
|
246
|
+
Processor(LIFECYCLE_QUEUE, { autorun: false, concurrency: LIFECYCLE_CONCURRENCY }),
|
|
247
|
+
__param(0, Inject(LifecycleHooks)),
|
|
248
|
+
__param(1, Inject(KERNEL_LOGGER))
|
|
249
|
+
], LifecycleHooksProcessor);
|
|
250
|
+
export { LifecycleHooksProcessor };
|
|
251
|
+
const LIFECYCLE_QUEUE_MODULE = QueueModule.registerQueue(LIFECYCLE_QUEUE);
|
|
252
|
+
/** Lifecycle hook delivery: owing the jobs, and the queue the outbox adds them to, on every role. */
|
|
253
|
+
let LifecycleHooksModule = class LifecycleHooksModule {
|
|
254
|
+
};
|
|
255
|
+
LifecycleHooksModule = __decorate([
|
|
256
|
+
Module({
|
|
257
|
+
imports: [ExtensionsModule, LIFECYCLE_QUEUE_MODULE],
|
|
258
|
+
providers: [LifecycleHooks],
|
|
259
|
+
exports: [LifecycleHooks, LIFECYCLE_QUEUE_MODULE],
|
|
260
|
+
})
|
|
261
|
+
], LifecycleHooksModule);
|
|
262
|
+
export { LifecycleHooksModule };
|
|
263
|
+
/** The lifecycle queue's consumer; imported only by the worker role (`WorkerModule`). */
|
|
264
|
+
let LifecycleHookWorkersModule = class LifecycleHookWorkersModule {
|
|
265
|
+
};
|
|
266
|
+
LifecycleHookWorkersModule = __decorate([
|
|
267
|
+
Module({
|
|
268
|
+
imports: [LifecycleHooksModule],
|
|
269
|
+
providers: [LifecycleHooksProcessor],
|
|
270
|
+
exports: [LifecycleHooksProcessor],
|
|
271
|
+
})
|
|
272
|
+
], LifecycleHookWorkersModule);
|
|
273
|
+
export { LifecycleHookWorkersModule };
|
|
@@ -110,6 +110,16 @@ export interface OrgExtensionPutOptions {
|
|
|
110
110
|
*/
|
|
111
111
|
validateOnly?: boolean;
|
|
112
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* An org's stored config of a plugin no longer matches its org schema (e.g. after a plugin upgrade):
|
|
115
|
+
* a known state, not a read error. The message names the plugin, the org and the failing paths,
|
|
116
|
+
* never the values.
|
|
117
|
+
*/
|
|
118
|
+
export declare class OrgConfigInvalidError extends Error {
|
|
119
|
+
readonly plugin: string;
|
|
120
|
+
readonly orgId: string;
|
|
121
|
+
constructor(plugin: string, orgId: string, detail: string);
|
|
122
|
+
}
|
|
113
123
|
/**
|
|
114
124
|
* Org-level extension config: enablement and the `config.org` config stored per org.
|
|
115
125
|
* The non-secret part is stored in `capybara.extension_org_configs.config`; each secret field is
|
|
@@ -210,7 +220,9 @@ export declare class OrgExtensionConfigs {
|
|
|
210
220
|
* A required bootstrap plugin is enabled without a row (its stored config, or `{}`, is validated).
|
|
211
221
|
* 无记录 → undefined;插件无 `config.org` 或已停用(`enabled=false`,不校验、不读密钥)时 `config` 为 null。
|
|
212
222
|
* 配置行与密钥在同一个 `REPEATABLE READ` 只读事务里读取。
|
|
213
|
-
*
|
|
223
|
+
* A stored config that no longer matches the schema (e.g. after a plugin upgrade) throws
|
|
224
|
+
* {@link OrgConfigInvalidError} (plugin, org ID and the failing paths, no values); other errors are
|
|
225
|
+
* read errors.
|
|
214
226
|
*/
|
|
215
227
|
get(orgId: string, plugin: string): Promise<{
|
|
216
228
|
enabled: boolean;
|
|
@@ -88,6 +88,21 @@ function secretPaths(schema) {
|
|
|
88
88
|
.filter((f) => f.meta?.secret)
|
|
89
89
|
.map((f) => f.path);
|
|
90
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* An org's stored config of a plugin no longer matches its org schema (e.g. after a plugin upgrade):
|
|
93
|
+
* a known state, not a read error. The message names the plugin, the org and the failing paths,
|
|
94
|
+
* never the values.
|
|
95
|
+
*/
|
|
96
|
+
export class OrgConfigInvalidError extends Error {
|
|
97
|
+
plugin;
|
|
98
|
+
orgId;
|
|
99
|
+
constructor(plugin, orgId, detail) {
|
|
100
|
+
super(`plugin ${plugin}: stored organization config for org ${orgId} is invalid: ${detail}`);
|
|
101
|
+
this.plugin = plugin;
|
|
102
|
+
this.orgId = orgId;
|
|
103
|
+
this.name = "OrgConfigInvalidError";
|
|
104
|
+
}
|
|
105
|
+
}
|
|
91
106
|
/**
|
|
92
107
|
* Org-level extension config: enablement and the `config.org` config stored per org.
|
|
93
108
|
* The non-secret part is stored in `capybara.extension_org_configs.config`; each secret field is
|
|
@@ -444,7 +459,9 @@ let OrgExtensionConfigs = class OrgExtensionConfigs {
|
|
|
444
459
|
* A required bootstrap plugin is enabled without a row (its stored config, or `{}`, is validated).
|
|
445
460
|
* 无记录 → undefined;插件无 `config.org` 或已停用(`enabled=false`,不校验、不读密钥)时 `config` 为 null。
|
|
446
461
|
* 配置行与密钥在同一个 `REPEATABLE READ` 只读事务里读取。
|
|
447
|
-
*
|
|
462
|
+
* A stored config that no longer matches the schema (e.g. after a plugin upgrade) throws
|
|
463
|
+
* {@link OrgConfigInvalidError} (plugin, org ID and the failing paths, no values); other errors are
|
|
464
|
+
* read errors.
|
|
448
465
|
*/
|
|
449
466
|
async get(orgId, plugin) {
|
|
450
467
|
const manifest = this.manifest(plugin);
|
|
@@ -488,7 +505,7 @@ let OrgExtensionConfigs = class OrgExtensionConfigs {
|
|
|
488
505
|
const detail = toIssues(r.error)
|
|
489
506
|
.map((i) => `${i.path || "<root>"}: ${i.message}`)
|
|
490
507
|
.join("; ");
|
|
491
|
-
throw new
|
|
508
|
+
throw new OrgConfigInvalidError(plugin, orgId, detail);
|
|
492
509
|
}
|
|
493
510
|
return { enabled: true, config: r.data };
|
|
494
511
|
}
|
|
@@ -30,7 +30,7 @@ export interface PermissionGroup {
|
|
|
30
30
|
* ({@link SENSITIVE_EXTENSION_POINTS}; the code is the extension point name without `@<major>`, with
|
|
31
31
|
* `-` replaced by `_`).
|
|
32
32
|
*/
|
|
33
|
-
export type SensitiveReason = "secrets" | "public_routes" | "tools" | "hooks" | "ui_embed" | "client_code" | "ui_component" | "channel" | "directory" | "identity_source" | "sandbox" | "object_store" | "migration_warnings";
|
|
33
|
+
export type SensitiveReason = "secrets" | "public_routes" | "tools" | "hooks" | "ui_embed" | "client_code" | "ui_component" | "channel" | "directory" | "identity_source" | "sandbox" | "object_store" | "context" | "migration_warnings";
|
|
34
34
|
/** 权限清单里的一项(分组里的一项,或 `added` 里的一个权限键)。 */
|
|
35
35
|
export interface PermissionItem {
|
|
36
36
|
value: string;
|
|
@@ -39,10 +39,14 @@ export interface PermissionItem {
|
|
|
39
39
|
reason?: SensitiveReason;
|
|
40
40
|
}
|
|
41
41
|
/**
|
|
42
|
-
*
|
|
43
|
-
* `
|
|
44
|
-
* `
|
|
45
|
-
*
|
|
42
|
+
* Sensitive capabilities (D10 + planned extensions, F-AI): `sandbox@2` runs arbitrary commands in
|
|
43
|
+
* the deployment, `object-store@1` holds every attachment and plugin package (D10); `channel@1`
|
|
44
|
+
* receives the org's IM conversations (Ruling A33), `directory@1` can add people to orgs (creating
|
|
45
|
+
* their accounts and identities), `identity-source@1` decides who can sign in as which account;
|
|
46
|
+
* `context@1` adds text to the model's context in conversations of orgs that enable it (memory
|
|
47
|
+
* plugin spec §6.1). A plugin providing any of them has its "provides" group marked sensitive and
|
|
48
|
+
* `sensitive` true in the admin plugin list, for the super admin to approve. Display only: the keys
|
|
49
|
+
* of the permission snapshot do not change.
|
|
46
50
|
*/
|
|
47
51
|
export declare const SENSITIVE_EXTENSION_POINTS: readonly string[];
|
|
48
52
|
/**
|
|
@@ -18,10 +18,14 @@ export function reviewHash(set, warnings = []) {
|
|
|
18
18
|
return h.digest("hex").slice(0, 16);
|
|
19
19
|
}
|
|
20
20
|
/**
|
|
21
|
-
*
|
|
22
|
-
* `
|
|
23
|
-
* `
|
|
24
|
-
*
|
|
21
|
+
* Sensitive capabilities (D10 + planned extensions, F-AI): `sandbox@2` runs arbitrary commands in
|
|
22
|
+
* the deployment, `object-store@1` holds every attachment and plugin package (D10); `channel@1`
|
|
23
|
+
* receives the org's IM conversations (Ruling A33), `directory@1` can add people to orgs (creating
|
|
24
|
+
* their accounts and identities), `identity-source@1` decides who can sign in as which account;
|
|
25
|
+
* `context@1` adds text to the model's context in conversations of orgs that enable it (memory
|
|
26
|
+
* plugin spec §6.1). A plugin providing any of them has its "provides" group marked sensitive and
|
|
27
|
+
* `sensitive` true in the admin plugin list, for the super admin to approve. Display only: the keys
|
|
28
|
+
* of the permission snapshot do not change.
|
|
25
29
|
*/
|
|
26
30
|
export const SENSITIVE_EXTENSION_POINTS = [
|
|
27
31
|
"channel@1",
|
|
@@ -29,6 +33,7 @@ export const SENSITIVE_EXTENSION_POINTS = [
|
|
|
29
33
|
"identity-source@1",
|
|
30
34
|
"sandbox@2",
|
|
31
35
|
"object-store@1",
|
|
36
|
+
"context@1",
|
|
32
37
|
];
|
|
33
38
|
/** The one item of the "client" group (client extensions spec §5.4). */
|
|
34
39
|
const CLIENT_ITEM = "client code";
|
|
@@ -18,6 +18,16 @@ export type PointResolution<T> = {
|
|
|
18
18
|
ok: false;
|
|
19
19
|
reason: "none" | "ambiguous";
|
|
20
20
|
};
|
|
21
|
+
/**
|
|
22
|
+
* One provider of a multi-provider extension point for an org ({@link ExtensionPointDefaults.providersFor}),
|
|
23
|
+
* with its org config and its effective deployment config for the org, both to pass back to it.
|
|
24
|
+
*/
|
|
25
|
+
export interface PointProvider<T> {
|
|
26
|
+
extension: string;
|
|
27
|
+
impl: T;
|
|
28
|
+
orgConfig: unknown;
|
|
29
|
+
config: unknown;
|
|
30
|
+
}
|
|
21
31
|
/** One `org-default` extension point of an org (the admin view). */
|
|
22
32
|
export interface OrgPointDefaultView {
|
|
23
33
|
point: string;
|
|
@@ -50,6 +60,19 @@ export declare class ExtensionPointDefaults {
|
|
|
50
60
|
* point has no `selection`.
|
|
51
61
|
*/
|
|
52
62
|
resolve<T>(orgId: string, point: string): Promise<PointResolution<T>>;
|
|
63
|
+
/**
|
|
64
|
+
* Every provider of `point` (the name without the major, e.g. `context`) that could serve
|
|
65
|
+
* `orgId` now, in extension-name order: the same candidates {@link resolve} chooses from (loaded
|
|
66
|
+
* here, enabled in the org, valid org and effective config, `configured()` not false), for
|
|
67
|
+
* points whose every enabled provider is used (no `selection`). `failed`: the providers enabled
|
|
68
|
+
* in the org whose status could not be read (reading a config or `configured()` threw, e.g. a
|
|
69
|
+
* transient database error); they are neither serving nor known to be off. An invalid stored
|
|
70
|
+
* org config ({@link OrgConfigInvalidError}) is not a read error: that provider is off.
|
|
71
|
+
*/
|
|
72
|
+
providersFor<T>(orgId: string, point: string): Promise<{
|
|
73
|
+
providers: PointProvider<T>[];
|
|
74
|
+
failed: Set<string>;
|
|
75
|
+
}>;
|
|
53
76
|
/** Every `org-default` point with its candidates, stored default and effective provider. */
|
|
54
77
|
list(orgId: string): Promise<OrgPointDefaultView[]>;
|
|
55
78
|
/** Whether each `org-default` point has a provider for the org now (no extension names). */
|
|
@@ -65,7 +88,11 @@ export declare class ExtensionPointDefaults {
|
|
|
65
88
|
setDefault(orgId: string, point: string, extension: string, actorId: string,
|
|
66
89
|
/** Merged into the audit detail (a write made through the Platform tool). */
|
|
67
90
|
auditDetail?: Record<string, unknown>): Promise<void>;
|
|
68
|
-
/**
|
|
91
|
+
/**
|
|
92
|
+
* The candidates of `point` in `orgId`, by extension name, and the enabled providers whose status
|
|
93
|
+
* could not be read (a read or `configured()` threw: excluded, logged). A provider whose stored
|
|
94
|
+
* org config or effective config is invalid is excluded but not `failed`: it is known to be off.
|
|
95
|
+
*/
|
|
69
96
|
private candidates;
|
|
70
97
|
private choose;
|
|
71
98
|
/** The org's stored defaults, point → extension. */
|
|
@@ -17,7 +17,7 @@ import { Transactional } from "../infra/database/transactional.decorator.js";
|
|
|
17
17
|
import { KERNEL_LOGGER } from "../infra/tokens.js";
|
|
18
18
|
import { EffectiveConfigs } from "./effective-config.js";
|
|
19
19
|
import { enabledExtensionsQuery, isExtensionEnabled } from "./enabled.js";
|
|
20
|
-
import { OrgExtensionConfigs } from "./org-config.js";
|
|
20
|
+
import { OrgConfigInvalidError, OrgExtensionConfigs } from "./org-config.js";
|
|
21
21
|
import { ExtensionRegistry } from "./registry.js";
|
|
22
22
|
/** The extension points whose provider each org chooses (`selection: "org-default"`). */
|
|
23
23
|
function selectionPoints() {
|
|
@@ -58,7 +58,7 @@ let ExtensionPointDefaults = class ExtensionPointDefaults {
|
|
|
58
58
|
if (!hasSelection(point)) {
|
|
59
59
|
throw new Error(`extension point ${point} has no org-default selection`);
|
|
60
60
|
}
|
|
61
|
-
const candidates = await this.candidates(orgId, point);
|
|
61
|
+
const { candidates } = await this.candidates(orgId, point);
|
|
62
62
|
const chosen = await this.choose(orgId, point, candidates);
|
|
63
63
|
if (!chosen.ok)
|
|
64
64
|
return chosen;
|
|
@@ -71,12 +71,33 @@ let ExtensionPointDefaults = class ExtensionPointDefaults {
|
|
|
71
71
|
config: c.config,
|
|
72
72
|
};
|
|
73
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* Every provider of `point` (the name without the major, e.g. `context`) that could serve
|
|
76
|
+
* `orgId` now, in extension-name order: the same candidates {@link resolve} chooses from (loaded
|
|
77
|
+
* here, enabled in the org, valid org and effective config, `configured()` not false), for
|
|
78
|
+
* points whose every enabled provider is used (no `selection`). `failed`: the providers enabled
|
|
79
|
+
* in the org whose status could not be read (reading a config or `configured()` threw, e.g. a
|
|
80
|
+
* transient database error); they are neither serving nor known to be off. An invalid stored
|
|
81
|
+
* org config ({@link OrgConfigInvalidError}) is not a read error: that provider is off.
|
|
82
|
+
*/
|
|
83
|
+
async providersFor(orgId, point) {
|
|
84
|
+
const { candidates, failed } = await this.candidates(orgId, point);
|
|
85
|
+
return {
|
|
86
|
+
providers: candidates.map((c) => ({
|
|
87
|
+
extension: c.extension,
|
|
88
|
+
impl: c.impl,
|
|
89
|
+
orgConfig: c.orgConfig,
|
|
90
|
+
config: c.config,
|
|
91
|
+
})),
|
|
92
|
+
failed,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
74
95
|
/** Every `org-default` point with its candidates, stored default and effective provider. */
|
|
75
96
|
async list(orgId) {
|
|
76
97
|
const stored = await this.storedDefaults(orgId);
|
|
77
98
|
const out = [];
|
|
78
99
|
for (const point of selectionPoints()) {
|
|
79
|
-
const candidates = await this.candidates(orgId, point);
|
|
100
|
+
const { candidates } = await this.candidates(orgId, point);
|
|
80
101
|
const chosen = pick(candidates, stored.get(point));
|
|
81
102
|
out.push({
|
|
82
103
|
point,
|
|
@@ -128,7 +149,11 @@ let ExtensionPointDefaults = class ExtensionPointDefaults {
|
|
|
128
149
|
detail: { point, extension, ...auditDetail },
|
|
129
150
|
});
|
|
130
151
|
}
|
|
131
|
-
/**
|
|
152
|
+
/**
|
|
153
|
+
* The candidates of `point` in `orgId`, by extension name, and the enabled providers whose status
|
|
154
|
+
* could not be read (a read or `configured()` threw: excluded, logged). A provider whose stored
|
|
155
|
+
* org config or effective config is invalid is excluded but not `failed`: it is known to be off.
|
|
156
|
+
*/
|
|
132
157
|
async candidates(orgId, point) {
|
|
133
158
|
const db = this.kdb.db;
|
|
134
159
|
const enabled = new Set((await db
|
|
@@ -141,18 +166,21 @@ let ExtensionPointDefaults = class ExtensionPointDefaults {
|
|
|
141
166
|
providers.set(e.plugin, e.impl);
|
|
142
167
|
}
|
|
143
168
|
const out = [];
|
|
169
|
+
const failed = new Set();
|
|
144
170
|
for (const [extension, impl] of providers) {
|
|
145
171
|
let cfg;
|
|
146
172
|
try {
|
|
147
173
|
cfg = await this.orgConfigs.get(orgId, extension);
|
|
148
174
|
}
|
|
149
175
|
catch (err) {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
176
|
+
// a stored org config that no longer validates is a known state: the provider is off
|
|
177
|
+
// (as for an invalid effective config below); only a read that failed is `failed`
|
|
178
|
+
const invalid = err instanceof OrgConfigInvalidError;
|
|
179
|
+
this.log.warn(invalid
|
|
180
|
+
? "extension point provider excluded, its stored org config is invalid"
|
|
181
|
+
: "extension point provider excluded, cannot read its org config", { orgId, point, extension, err });
|
|
182
|
+
if (!invalid)
|
|
183
|
+
failed.add(extension);
|
|
156
184
|
continue;
|
|
157
185
|
}
|
|
158
186
|
if (!cfg?.enabled)
|
|
@@ -163,12 +191,13 @@ let ExtensionPointDefaults = class ExtensionPointDefaults {
|
|
|
163
191
|
effective = await this.effective.get(orgId, extension);
|
|
164
192
|
}
|
|
165
193
|
catch (err) {
|
|
166
|
-
this.log.warn("
|
|
194
|
+
this.log.warn("extension point provider excluded, cannot read its effective config", {
|
|
167
195
|
orgId,
|
|
168
196
|
point,
|
|
169
197
|
extension,
|
|
170
198
|
err,
|
|
171
199
|
});
|
|
200
|
+
failed.add(extension);
|
|
172
201
|
continue;
|
|
173
202
|
}
|
|
174
203
|
if (!effective.ok)
|
|
@@ -181,12 +210,13 @@ let ExtensionPointDefaults = class ExtensionPointDefaults {
|
|
|
181
210
|
ok = configured.call(impl, cfg.config, effective.config) !== false;
|
|
182
211
|
}
|
|
183
212
|
catch (err) {
|
|
184
|
-
this.log.warn("
|
|
213
|
+
this.log.warn("extension point provider excluded, configured() threw", {
|
|
185
214
|
orgId,
|
|
186
215
|
point,
|
|
187
216
|
extension,
|
|
188
217
|
err,
|
|
189
218
|
});
|
|
219
|
+
failed.add(extension);
|
|
190
220
|
continue;
|
|
191
221
|
}
|
|
192
222
|
if (!ok)
|
|
@@ -194,7 +224,8 @@ let ExtensionPointDefaults = class ExtensionPointDefaults {
|
|
|
194
224
|
}
|
|
195
225
|
out.push({ extension, impl, orgConfig: cfg.config, config: effective.config });
|
|
196
226
|
}
|
|
197
|
-
|
|
227
|
+
out.sort((a, b) => (a.extension < b.extension ? -1 : a.extension > b.extension ? 1 : 0));
|
|
228
|
+
return { candidates: out, failed };
|
|
198
229
|
}
|
|
199
230
|
async choose(orgId, point, candidates) {
|
|
200
231
|
if (candidates.length < 2)
|
|
@@ -6,34 +6,20 @@ import { ApprovalsService, type ApprovalView } from "../tools/approvals.service.
|
|
|
6
6
|
import type { OrgNoticeDetails } from "./org-notices.js";
|
|
7
7
|
/** The most open org notices one inbox read returns. */
|
|
8
8
|
export declare const ORG_NOTICES_LIST_MAX = 100;
|
|
9
|
-
/** The skill the platform components migration renamed (`skill_renamed`). */
|
|
10
|
-
export interface SkillRenamedDetail {
|
|
11
|
-
skillId: string;
|
|
12
|
-
from: string;
|
|
13
|
-
to: string;
|
|
14
|
-
platformSkillId: string;
|
|
15
|
-
plugin: string | null;
|
|
16
|
-
}
|
|
17
9
|
/**
|
|
18
|
-
* A notice for the org's admins (`org_notices`), shown until one of them dismisses it
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* name a platform skill on in the org has; agents keep it, by id;
|
|
22
|
-
* - `item_shared`, `shared_item_updated`, `upload_confirmed`, `upload_rejected` (`detail`): the
|
|
23
|
-
* shapes of {@link OrgNoticeDetails} (plugin scope phase 3, P3-22).
|
|
10
|
+
* A notice for the org's admins (`org_notices`), shown until one of them dismisses it:
|
|
11
|
+
* `item_shared`, `shared_item_updated`, `upload_confirmed`, `upload_rejected`, `detail` in the
|
|
12
|
+
* shape of {@link OrgNoticeDetails} (plugin scope phase 3, P3-22).
|
|
24
13
|
*/
|
|
25
14
|
export type OrgNoticeView = {
|
|
26
15
|
id: string;
|
|
27
16
|
createdAt: string;
|
|
28
|
-
} &
|
|
29
|
-
kind: "skill_renamed";
|
|
30
|
-
skillRenamed: SkillRenamedDetail;
|
|
31
|
-
} | {
|
|
17
|
+
} & {
|
|
32
18
|
[K in keyof OrgNoticeDetails]: {
|
|
33
19
|
kind: K;
|
|
34
20
|
detail: OrgNoticeDetails[K];
|
|
35
21
|
};
|
|
36
|
-
}[keyof OrgNoticeDetails]
|
|
22
|
+
}[keyof OrgNoticeDetails];
|
|
37
23
|
/** 收件箱一次最多返回的事项数。 */
|
|
38
24
|
export declare const INBOX_MAX_ITEMS = 100;
|
|
39
25
|
/**
|
|
@@ -51,7 +37,7 @@ export declare const INBOX_MAX_ITEMS = 100;
|
|
|
51
37
|
* - `schedule_attention`: the current user's own scheduled task that needs them (spec §8): paused
|
|
52
38
|
* for any reason but their own pause, its latest run failed, or its latest run was refused tools
|
|
53
39
|
* or connections; derived live, gone once the task is resumed or runs clean;
|
|
54
|
-
* - the org notices (`
|
|
40
|
+
* - the org notices (`item_shared`, `shared_item_updated`, `upload_confirmed`,
|
|
55
41
|
* `upload_rejected`; `type` = the notice's kind): for org admins, until one dismisses it
|
|
56
42
|
* (`POST inbox/notices/:id/dismiss`).
|
|
57
43
|
*/
|
|
@@ -112,9 +112,6 @@ let InboxService = class InboxService {
|
|
|
112
112
|
.execute();
|
|
113
113
|
return rows.map((r) => {
|
|
114
114
|
const base = { id: r.id, createdAt: r.created_at.toISOString() };
|
|
115
|
-
if (r.kind === "skill_renamed") {
|
|
116
|
-
return { ...base, kind: r.kind, skillRenamed: r.detail };
|
|
117
|
-
}
|
|
118
115
|
return { ...base, kind: r.kind, detail: r.detail };
|
|
119
116
|
});
|
|
120
117
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type OwnedRef, type Owner } from "../catalog/ownership.js";
|
|
2
2
|
import type { KernelSchemaDb, OrgNoticeKind } from "../db/kernel-schema.js";
|
|
3
3
|
export type { OrgNoticeKind };
|
|
4
|
-
/** The `detail` of each notice kind
|
|
4
|
+
/** The `detail` of each notice kind. */
|
|
5
5
|
export interface OrgNoticeDetails {
|
|
6
6
|
/** An org was added to the item's list, or its scope widened (`all_orgs`). */
|
|
7
7
|
/** `displayName`: a plugin's display name, which the inbox names it by (null otherwise). */
|