@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,121 @@
|
|
|
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 { Inject, Injectable } from "@nestjs/common";
|
|
11
|
+
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
12
|
+
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
13
|
+
import { Transactional } from "../infra/database/transactional.decorator.js";
|
|
14
|
+
import { DevicePolicyService, } from "../local-devices/device-policy.service.js";
|
|
15
|
+
import { opVisibleIn, pendingPolicyOpsOf, } from "../local-devices/policy-summary.js";
|
|
16
|
+
import { ApprovalsService } from "./approvals.service.js";
|
|
17
|
+
/**
|
|
18
|
+
* The `device-op-resolve` job (transactional outbox spec §7): records what became of a device
|
|
19
|
+
* policy op whose outcome its caller did not record (a crash between the call and the resolve
|
|
20
|
+
* transaction, an answer that never came). Owed by the op's intent for when the op can no longer
|
|
21
|
+
* reach the machine, and by a hello that reported the op (`device-op-reported-<opId>`).
|
|
22
|
+
*
|
|
23
|
+
* An op without an entry any more is resolved already: a no-op. Otherwise the outcome is the one
|
|
24
|
+
* the hello reported, else the machine's answer to a read-only `policyStatus`: `applied` →
|
|
25
|
+
* recorded applied; `refused`, never received (`unknown` to the machine, or `absent` from its
|
|
26
|
+
* hello, once the op can no longer reach it) → failed; `started` (the machine stopped between
|
|
27
|
+
* its log and its answer), once the op can no longer reach it → applied when the policy it
|
|
28
|
+
* reports holds the change, else failed (`interrupted`); no answer (`offline`) → `unknown`, left
|
|
29
|
+
* for the machine's next hello. It never sends the change
|
|
30
|
+
* again (a widening after its ask closed is not allowed). An op that answers a network ask
|
|
31
|
+
* decides it when applied and the ask is still pending, else leaves it pending with the reason
|
|
32
|
+
* (`ApprovalsService.settleFromResolver`).
|
|
33
|
+
*/
|
|
34
|
+
let DeviceOpResolver = class DeviceOpResolver {
|
|
35
|
+
db;
|
|
36
|
+
txHost;
|
|
37
|
+
policies;
|
|
38
|
+
approvals;
|
|
39
|
+
constructor(db, txHost, policies, approvals) {
|
|
40
|
+
this.db = db;
|
|
41
|
+
this.txHost = txHost;
|
|
42
|
+
this.policies = policies;
|
|
43
|
+
this.approvals = approvals;
|
|
44
|
+
}
|
|
45
|
+
async resolve(data) {
|
|
46
|
+
const row = await this.db
|
|
47
|
+
.selectFrom("device_machines")
|
|
48
|
+
.select(["device_id", "pending_policy_ops"])
|
|
49
|
+
.where("id", "=", data.machineId)
|
|
50
|
+
.executeTakeFirst();
|
|
51
|
+
const op = pendingPolicyOpsOf(row?.pending_policy_ops).find((o) => o.id === data.opId);
|
|
52
|
+
if (row === undefined || op === undefined)
|
|
53
|
+
return;
|
|
54
|
+
const result = op.reported !== undefined
|
|
55
|
+
? await this.reportedResult(row.device_id, op, op.reported.outcome)
|
|
56
|
+
: statusResult(op, await this.policies.outcomeOf(row.device_id, op.id));
|
|
57
|
+
await this.settle(data, op.source.approvalId, result);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* The outcome a hello reported; a refusal's message (a hello carries none) is the machine's
|
|
61
|
+
* answer to `policyStatus` when it is connected.
|
|
62
|
+
*/
|
|
63
|
+
async reportedResult(deviceId, op, outcome) {
|
|
64
|
+
if (outcome === "applied")
|
|
65
|
+
return { outcome: "applied" };
|
|
66
|
+
if (outcome === "absent")
|
|
67
|
+
return { outcome: "not_received" };
|
|
68
|
+
if (outcome === "interrupted")
|
|
69
|
+
return { outcome: "interrupted" };
|
|
70
|
+
const s = await this.policies.outcomeOf(deviceId, op.id);
|
|
71
|
+
const message = s.outcome === "refused" ? s.message : undefined;
|
|
72
|
+
return { outcome: "refused", message: (message ?? REFUSED_MESSAGE).slice(0, MAX_MESSAGE) };
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* The resolve transaction: the op's outcome, and the network ask it answers decided or given
|
|
76
|
+
* the reason in the same transaction (`ApprovalsService.settleFromResolver`).
|
|
77
|
+
*/
|
|
78
|
+
async settle(data, approvalId, result) {
|
|
79
|
+
if (approvalId === null)
|
|
80
|
+
await this.policies.settleIn(data, result);
|
|
81
|
+
else
|
|
82
|
+
await this.approvals.settleFromResolver({ ...data, approvalId }, result);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
__decorate([
|
|
86
|
+
Transactional()
|
|
87
|
+
], DeviceOpResolver.prototype, "settle", null);
|
|
88
|
+
DeviceOpResolver = __decorate([
|
|
89
|
+
Injectable(),
|
|
90
|
+
__param(0, Inject(KernelDb)),
|
|
91
|
+
__param(1, Inject(TransactionHost)),
|
|
92
|
+
__param(2, Inject(DevicePolicyService)),
|
|
93
|
+
__param(3, Inject(ApprovalsService))
|
|
94
|
+
], DeviceOpResolver);
|
|
95
|
+
export { DeviceOpResolver };
|
|
96
|
+
const REFUSED_MESSAGE = "The device's machine refused the policy change.";
|
|
97
|
+
/** The longest refusal message kept from the machine (as its own op log keeps it). */
|
|
98
|
+
const MAX_MESSAGE = 500;
|
|
99
|
+
function statusResult(op, s) {
|
|
100
|
+
switch (s.outcome) {
|
|
101
|
+
case "applied":
|
|
102
|
+
return { outcome: "applied", ...(s.policy !== undefined && { policy: s.policy }) };
|
|
103
|
+
case "refused":
|
|
104
|
+
return { outcome: "refused", message: (s.message ?? REFUSED_MESSAGE).slice(0, MAX_MESSAGE) };
|
|
105
|
+
case "started":
|
|
106
|
+
// logged `started` only: once the op can no longer reach the machine, the policy it reports
|
|
107
|
+
// tells whether the change was written before it stopped
|
|
108
|
+
if (Date.now() < op.resolveAt || s.policy === undefined)
|
|
109
|
+
return { outcome: "unknown" };
|
|
110
|
+
return opVisibleIn(op, s.policy)
|
|
111
|
+
? { outcome: "applied", policy: s.policy }
|
|
112
|
+
: { outcome: "interrupted" };
|
|
113
|
+
case "unknown":
|
|
114
|
+
// unknown to the machine: never received, once the op can no longer reach it (a job run
|
|
115
|
+
// early, on a clock running ahead, cannot tell yet)
|
|
116
|
+
return Date.now() >= op.resolveAt ? { outcome: "not_received" } : { outcome: "unknown" };
|
|
117
|
+
default:
|
|
118
|
+
// offline: its next hello tells
|
|
119
|
+
return { outcome: "unknown" };
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -85,7 +85,7 @@ export declare function blockedEventHosts(blocked: readonly NetworkBlockedHost[]
|
|
|
85
85
|
* the rest:
|
|
86
86
|
* - `address` and `org` layers: a note each (in an unattended run an `org` host is a missing grant);
|
|
87
87
|
* `unknown`: nothing (the device's trailer explains);
|
|
88
|
-
* - `device` layer on a program without `
|
|
88
|
+
* - `device` layer on a program without `policyOpId`: nothing (no ask, the trailer explains);
|
|
89
89
|
* - approved by the scheduled task the run serves (`run.taskGranted`) yet refused: a note that
|
|
90
90
|
* the device ignores the task's grants (never asked, never a missing grant);
|
|
91
91
|
* - refused earlier in the run tree: the denied note; an unattended run in `fail` mode: its note
|
|
@@ -90,7 +90,7 @@ export function blockedEventHosts(blocked) {
|
|
|
90
90
|
* the rest:
|
|
91
91
|
* - `address` and `org` layers: a note each (in an unattended run an `org` host is a missing grant);
|
|
92
92
|
* `unknown`: nothing (the device's trailer explains);
|
|
93
|
-
* - `device` layer on a program without `
|
|
93
|
+
* - `device` layer on a program without `policyOpId`: nothing (no ask, the trailer explains);
|
|
94
94
|
* - approved by the scheduled task the run serves (`run.taskGranted`) yet refused: a note that
|
|
95
95
|
* the device ignores the task's grants (never asked, never a missing grant);
|
|
96
96
|
* - refused earlier in the run tree: the denied note; an unattended run in `fail` mode: its note
|
|
@@ -107,9 +107,10 @@ export function planNetworkAsk(blocked, run, ctx) {
|
|
|
107
107
|
const taskIgnored = [];
|
|
108
108
|
const candidates = [];
|
|
109
109
|
const missingGrants = [];
|
|
110
|
-
// a machine that reports blocked hosts and takes policy changes (Docker mode has
|
|
110
|
+
// a machine that reports blocked hosts and takes policy changes under op ids (Docker mode has
|
|
111
|
+
// no report; a program without op ids cannot take 「一直允许」)
|
|
111
112
|
const canAsk = ctx.device?.features.includes("networkReport") === true &&
|
|
112
|
-
ctx.device.features.includes("
|
|
113
|
+
ctx.device.features.includes("policyOpId");
|
|
113
114
|
// no run grant or policy change reaches a device whose owner turned remote grants off
|
|
114
115
|
const remote = ctx.device?.policy?.remoteGrants !== "off";
|
|
115
116
|
for (const b of hostsOf(blocked)) {
|
package/dist/tools/pipeline.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type FormAnswer, type InputForm, type Logger } from "@capybara-hq/plugin-sdk";
|
|
1
|
+
import { type ConversationSource, type FormAnswer, type InputForm, type Logger } from "@capybara-hq/plugin-sdk";
|
|
2
2
|
import type { AgentDefinition } from "../agents/definition.js";
|
|
3
3
|
import { EventLog } from "../conversations/event-log.js";
|
|
4
4
|
import type { KernelSchemaDb } from "../db/kernel-schema.js";
|
|
@@ -222,8 +222,17 @@ export interface ToolStepContext {
|
|
|
222
222
|
/** 运行所用的 agent 版本(审计链记录)。 */
|
|
223
223
|
agentVersion: number;
|
|
224
224
|
agent: AgentDefinition;
|
|
225
|
-
/**
|
|
225
|
+
/** The run's trigger account (personal credentials, the audit actor). */
|
|
226
226
|
accountId: string;
|
|
227
|
+
/**
|
|
228
|
+
* The conversation's source (memory plugin spec §6.2): extension tools limited by
|
|
229
|
+
* `spec.sources` are resolved with it, and plugin handlers receive it.
|
|
230
|
+
*/
|
|
231
|
+
source: ConversationSource;
|
|
232
|
+
/** The conversation's owner (`conversations.account_id`), given to plugin handlers. */
|
|
233
|
+
ownerAccountId: string;
|
|
234
|
+
/** The trigger account's current org role, given to plugin handlers. */
|
|
235
|
+
role: OrgRole;
|
|
227
236
|
/** run 的中止信号(用户中止 / 截止时间)。 */
|
|
228
237
|
signal: AbortSignal;
|
|
229
238
|
/** run 的截止时间(epoch 毫秒);传给内置工具(沙箱命令据此收紧超时,Ruling M20)。 */
|
package/dist/tools/pipeline.js
CHANGED
|
@@ -314,6 +314,7 @@ let ToolPipeline = class ToolPipeline {
|
|
|
314
314
|
forkChild: ctx.parent?.forSkill === true,
|
|
315
315
|
sandboxConversationId: ctx.parent?.rootConversationId ?? ctx.conversationId,
|
|
316
316
|
replay: ctx.conversationKind === "eval",
|
|
317
|
+
source: ctx.source,
|
|
317
318
|
});
|
|
318
319
|
}
|
|
319
320
|
return resolved;
|
|
@@ -1001,6 +1002,10 @@ let ToolPipeline = class ToolPipeline {
|
|
|
1001
1002
|
conversationId: ctx.conversationId,
|
|
1002
1003
|
callId,
|
|
1003
1004
|
...(resume !== undefined && { resume }),
|
|
1005
|
+
// who and where (memory plugin spec §6.2): `accountId` stays the trigger account
|
|
1006
|
+
source: ctx.source,
|
|
1007
|
+
ownerAccountId: ctx.ownerAccountId,
|
|
1008
|
+
role: ctx.role,
|
|
1004
1009
|
...(tool.source.kind === "builtin" && stepTools !== undefined && { tools: stepTools }),
|
|
1005
1010
|
...(tool.source.kind === "builtin" && { runId: ctx.runId }),
|
|
1006
1011
|
...(tool.source.kind === "builtin" &&
|
|
@@ -105,11 +105,22 @@ export declare class QuestionsService {
|
|
|
105
105
|
*/
|
|
106
106
|
cancelPendingIn(runId: string): Promise<QuestionRecord[]>;
|
|
107
107
|
/**
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
108
|
+
* The conversations of `orgId` holding a pending question of a run `accountId` started (only the
|
|
109
|
+
* run's starter answers), read without locks: offboarding locks them, by tree root first
|
|
110
|
+
* (`lockConversationTrees`), before {@link cancelPendingFor}.
|
|
111
111
|
*/
|
|
112
|
-
|
|
112
|
+
pendingConversationsFor(orgId: string, accountId: string): Promise<string[]>;
|
|
113
|
+
/**
|
|
114
|
+
* In the caller's transaction (`mandatory`; offboarding): cancels every pending question of a run
|
|
115
|
+
* `accountId` started in `orgId` within `conversationIds`, the conversations the caller already
|
|
116
|
+
* locked (by tree root first, `lockConversationTrees`; lock order D7: conversation, then question
|
|
117
|
+
* rows), along the same path as {@link cancelPendingIn}: `cancelled`,
|
|
118
|
+
* `question_answered{cancelled}`. A question asked meanwhile in another conversation waits for
|
|
119
|
+
* its run's stop (offboarding owes it), so no conversation is locked after a row. Returns the
|
|
120
|
+
* cancelled records and the highest seq written per conversation; the caller publishes after the
|
|
121
|
+
* commit.
|
|
122
|
+
*/
|
|
123
|
+
cancelPendingFor(orgId: string, accountId: string, conversationIds: readonly string[]): Promise<{
|
|
113
124
|
records: QuestionRecord[];
|
|
114
125
|
seqs: Map<string, number>;
|
|
115
126
|
}>;
|
|
@@ -324,14 +324,12 @@ let QuestionsService = class QuestionsService {
|
|
|
324
324
|
return out;
|
|
325
325
|
}
|
|
326
326
|
/**
|
|
327
|
-
*
|
|
328
|
-
*
|
|
329
|
-
*
|
|
327
|
+
* The conversations of `orgId` holding a pending question of a run `accountId` started (only the
|
|
328
|
+
* run's starter answers), read without locks: offboarding locks them, by tree root first
|
|
329
|
+
* (`lockConversationTrees`), before {@link cancelPendingFor}.
|
|
330
330
|
*/
|
|
331
|
-
async
|
|
332
|
-
const
|
|
333
|
-
// the conversations first, in id order (lock order D7: conversation, then question rows)
|
|
334
|
-
const convs = await t
|
|
331
|
+
async pendingConversationsFor(orgId, accountId) {
|
|
332
|
+
const rows = await this.db
|
|
335
333
|
.selectFrom("questions")
|
|
336
334
|
.select("conversation_id")
|
|
337
335
|
.distinct()
|
|
@@ -339,24 +337,28 @@ let QuestionsService = class QuestionsService {
|
|
|
339
337
|
.where("status", "=", "pending")
|
|
340
338
|
.where("run_id", "in", (eb) => eb.selectFrom("runs").select("id").where("trigger_account_id", "=", accountId))
|
|
341
339
|
.execute();
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
340
|
+
return rows.map((r) => r.conversation_id);
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* In the caller's transaction (`mandatory`; offboarding): cancels every pending question of a run
|
|
344
|
+
* `accountId` started in `orgId` within `conversationIds`, the conversations the caller already
|
|
345
|
+
* locked (by tree root first, `lockConversationTrees`; lock order D7: conversation, then question
|
|
346
|
+
* rows), along the same path as {@link cancelPendingIn}: `cancelled`,
|
|
347
|
+
* `question_answered{cancelled}`. A question asked meanwhile in another conversation waits for
|
|
348
|
+
* its run's stop (offboarding owes it), so no conversation is locked after a row. Returns the
|
|
349
|
+
* cancelled records and the highest seq written per conversation; the caller publishes after the
|
|
350
|
+
* commit.
|
|
351
|
+
*/
|
|
352
|
+
async cancelPendingFor(orgId, accountId, conversationIds) {
|
|
353
|
+
const t = this.db.db;
|
|
354
|
+
const convs = [...conversationIds];
|
|
353
355
|
if (convs.length === 0)
|
|
354
356
|
return { records: [], seqs: new Map() };
|
|
355
357
|
const rows = await t
|
|
356
358
|
.selectFrom("questions")
|
|
357
359
|
.selectAll()
|
|
358
360
|
.where("org_id", "=", orgId)
|
|
359
|
-
.where("conversation_id", "in", convs
|
|
361
|
+
.where("conversation_id", "in", convs)
|
|
360
362
|
.where("status", "=", "pending")
|
|
361
363
|
.where("run_id", "in", (eb) => eb.selectFrom("runs").select("id").where("trigger_account_id", "=", accountId))
|
|
362
364
|
.orderBy("created_at")
|
package/dist/tools/registry.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Connector, HookParentScope, InputRequired, NetworkBlockedHost, ToolAnnotations, ToolCallContext, ToolResult, ToolSpec } from "@capybara-hq/plugin-sdk";
|
|
1
|
+
import type { Connector, ConversationSource, HookParentScope, InputRequired, NetworkBlockedHost, ToolAnnotations, ToolCallContext, ToolResult, ToolSpec } from "@capybara-hq/plugin-sdk";
|
|
2
2
|
import { type AgentDefinition, type DisabledToolRef, type SubagentTools } from "../agents/definition.js";
|
|
3
3
|
import type { ToolView } from "../conversations/events.js";
|
|
4
4
|
import { EffectiveConfigs } from "../extensions/effective-config.js";
|
|
@@ -213,6 +213,8 @@ export declare function withoutDisplayFields<T>(spec: T): T;
|
|
|
213
213
|
export declare function specHash(spec: unknown): string;
|
|
214
214
|
/** 由注解推导默认策略:只读 → allow;其余(破坏性 / 无注解)→ ask。 */
|
|
215
215
|
export declare function policyFromAnnotations(a: ToolAnnotations | undefined): "allow" | "ask";
|
|
216
|
+
/** Why an extension tool limited by `spec.sources` is not offered in a `source` conversation. */
|
|
217
|
+
export declare const sourceHiddenReason: (source: ConversationSource) => string;
|
|
216
218
|
/** `forAgent` 的结果。 */
|
|
217
219
|
export interface AgentTools {
|
|
218
220
|
tools: ResolvedTool[];
|
|
@@ -362,6 +364,11 @@ export declare class ToolRegistry {
|
|
|
362
364
|
sandboxConversationId?: string;
|
|
363
365
|
/** An eval replay's listing: its calls are never executed (the sandbox tools are listed). */
|
|
364
366
|
replay?: boolean;
|
|
367
|
+
/**
|
|
368
|
+
* The conversation's source (memory plugin spec §6.2): an extension tool whose
|
|
369
|
+
* `spec.sources` leaves it out is hidden. Required, so nothing defaults to the widest.
|
|
370
|
+
*/
|
|
371
|
+
source: ConversationSource;
|
|
365
372
|
}): Promise<AgentTools>;
|
|
366
373
|
/** 是否启用 `ToolSearch`(见 {@link forAgent});先比阈值,超过才读组织设置。 */
|
|
367
374
|
private searchActive;
|
package/dist/tools/registry.js
CHANGED
|
@@ -59,6 +59,8 @@ export function specHash(spec) {
|
|
|
59
59
|
export function policyFromAnnotations(a) {
|
|
60
60
|
return a?.readOnlyHint === true ? "allow" : "ask";
|
|
61
61
|
}
|
|
62
|
+
/** Why an extension tool limited by `spec.sources` is not offered in a `source` conversation. */
|
|
63
|
+
export const sourceHiddenReason = (source) => `Not available in ${source} conversations`;
|
|
62
64
|
// `ToolSearch` is added by the registry by org setting and threshold, `Skill` when the log has a
|
|
63
65
|
// skills catalog snapshot; neither comes from agent definitions (`./builtin-names.ts`).
|
|
64
66
|
/** 工具定义(模型可见三项)的估算 token 数(与请求估算同一口径)。 */
|
|
@@ -348,6 +350,7 @@ let ToolRegistry = class ToolRegistry {
|
|
|
348
350
|
}
|
|
349
351
|
async resolve(orgId, def) {
|
|
350
352
|
const disabled = def.disabledTools ?? [];
|
|
353
|
+
const { source } = def;
|
|
351
354
|
const scope = {
|
|
352
355
|
orgId,
|
|
353
356
|
...(def.sandboxConversationId !== undefined && {
|
|
@@ -435,6 +438,12 @@ let ToolRegistry = class ToolRegistry {
|
|
|
435
438
|
hidden.push({ ref: key, reason: toolTurnedOffReason(key) });
|
|
436
439
|
continue;
|
|
437
440
|
}
|
|
441
|
+
// offered only in some conversation sources; the source never changes, so this also
|
|
442
|
+
// refuses a resumed call (the pipeline resolves the step's tools with the same source)
|
|
443
|
+
if (t.spec.sources !== undefined && !t.spec.sources.includes(source)) {
|
|
444
|
+
hidden.push({ ref: key, reason: sourceHiddenReason(source) });
|
|
445
|
+
continue;
|
|
446
|
+
}
|
|
438
447
|
if (!(await configuredIn(t.plugin))) {
|
|
439
448
|
hidden.push({ ref: key, reason: `Plugin ${t.plugin} ${NEEDS_CONFIG_REASON}` });
|
|
440
449
|
continue;
|
|
@@ -34,6 +34,7 @@ import { ConnectorAccountPolicies } from "./connector-account-policies.js";
|
|
|
34
34
|
import { ConnectorContent } from "./connector-content.js";
|
|
35
35
|
import { ConnectorsService } from "./connectors.service.js";
|
|
36
36
|
import { Credentials } from "./credentials.js";
|
|
37
|
+
import { DeviceOpResolver } from "./device-op-resolver.js";
|
|
37
38
|
import { ToolDrift } from "./drift.js";
|
|
38
39
|
import { ConversationToolInfo, OrgExtensionTools } from "./info.js";
|
|
39
40
|
import { OAuthFlow } from "./oauth/flow.js";
|
|
@@ -106,6 +107,8 @@ ToolsModule = __decorate([
|
|
|
106
107
|
ConversationToolInfo,
|
|
107
108
|
OrgExtensionTools,
|
|
108
109
|
OrgToolSwitches,
|
|
110
|
+
// the `device-op-resolve` job's body (it may decide the network ask the op answers)
|
|
111
|
+
DeviceOpResolver,
|
|
109
112
|
],
|
|
110
113
|
exports: [
|
|
111
114
|
ScheduleToolsPort,
|
|
@@ -126,6 +129,7 @@ ToolsModule = __decorate([
|
|
|
126
129
|
ConversationToolInfo,
|
|
127
130
|
OrgExtensionTools,
|
|
128
131
|
OrgToolSwitches,
|
|
132
|
+
DeviceOpResolver,
|
|
129
133
|
],
|
|
130
134
|
})
|
|
131
135
|
], ToolsModule);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@capybara-hq/kernel",
|
|
3
|
-
"version": "0.1.0-rc.
|
|
3
|
+
"version": "0.1.0-rc.26",
|
|
4
4
|
"description": "Capybara kernel: config, database, plugin host and HTTP API of the Capybara agent platform",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"yaml": "^2.9.1",
|
|
66
66
|
"yauzl": "3.4.0",
|
|
67
67
|
"zod": "^4.6.7",
|
|
68
|
-
"@capybara-hq/plugin-sdk": "^0.1.0-rc.
|
|
68
|
+
"@capybara-hq/plugin-sdk": "^0.1.0-rc.20"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"@langchain/langgraph-checkpoint-validation": "^1.2.0",
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
"@types/ws": "^8.18.2",
|
|
75
75
|
"@types/yauzl": "3.4.0",
|
|
76
76
|
"ws": "^8.22.0",
|
|
77
|
-
"@capybara-hq/testing": "0.1.0-rc.
|
|
77
|
+
"@capybara-hq/testing": "0.1.0-rc.17"
|
|
78
78
|
},
|
|
79
79
|
"scripts": {
|
|
80
80
|
"build": "tsc -b"
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
-- 0121, transactional outbox spec §8.2: the platform components migration job is gone (an import
|
|
2
|
+
-- writes its platform components in its transaction), so nothing writes its runs table any more and
|
|
3
|
+
-- the platform inbox no longer shows a run; the org notice kind `skill_renamed`, which only that job
|
|
4
|
+
-- wrote, goes with it.
|
|
5
|
+
-- capybara:allow-destructive the job that wrote this table is removed in this release and nothing reads it (project not released, no compatibility layer)
|
|
6
|
+
DROP TABLE IF EXISTS platform_migration_runs;
|
|
7
|
+
-- capybara:allow-destructive notices of a kind only the removed job wrote; the inbox no longer renders the kind (project not released)
|
|
8
|
+
DELETE FROM org_notices WHERE kind = 'skill_renamed';
|
|
9
|
+
-- capybara:allow-destructive the check is added back without `skill_renamed` in the next statement; no row has that kind any more
|
|
10
|
+
ALTER TABLE org_notices DROP CONSTRAINT IF EXISTS org_notices_kind_check;
|
|
11
|
+
ALTER TABLE org_notices ADD CONSTRAINT org_notices_kind_check
|
|
12
|
+
CHECK (kind IN ('item_shared', 'shared_item_updated', 'upload_confirmed', 'upload_rejected'));
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
-- 0122, transactional outbox spec §8.1 / §8.2: the transaction that writes a pending task run owes
|
|
2
|
+
-- its fire job in the outbox, so the scheduled task scan's repair is gone, and with it the column
|
|
3
|
+
-- that rotated the repair's rechecks of started runs.
|
|
4
|
+
-- capybara:allow-destructive the repair that read and wrote this column is removed in this release and nothing else reads it (project not released, no compatibility layer)
|
|
5
|
+
ALTER TABLE scheduled_task_runs DROP COLUMN IF EXISTS rechecked_at;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
-- pgvector, when the server offers it (memory plugin spec §6.4). Plugins store embeddings in a
|
|
2
|
+
-- `vector` column when the type is visible from their search_path (`plugin_<name>, public`) and fall
|
|
3
|
+
-- back to `real[]` otherwise, so pgvector is recommended, not required:
|
|
4
|
+
-- - not offered by the server (no row in pg_available_extensions): nothing to do;
|
|
5
|
+
-- - already installed, in whatever schema a DBA put it: left where it is;
|
|
6
|
+
-- - refused (insufficient_privilege: the migrating role may not create it; feature_not_supported:
|
|
7
|
+
-- a managed server lists it but its vendor allow-list refuses it, e.g. Azure Flexible Server):
|
|
8
|
+
-- a notice naming the SQLSTATE, the migration succeeds. Any other error still fails it.
|
|
9
|
+
-- `SCHEMA public` matters: the migrator's search_path is `capybara, public`, and a bare
|
|
10
|
+
-- CREATE EXTENSION would put the type and `<=>` in `capybara`, invisible to plugin schemas.
|
|
11
|
+
-- capybara:allow-destructive not destructive: a DO block is the only way to make CREATE EXTENSION conditional on availability and to turn a refused CREATE EXTENSION into a notice
|
|
12
|
+
DO $$
|
|
13
|
+
BEGIN
|
|
14
|
+
IF NOT EXISTS (SELECT 1 FROM pg_available_extensions WHERE name = 'vector') THEN
|
|
15
|
+
RAISE NOTICE 'pgvector is not available on this server; plugins fall back to real[] embeddings';
|
|
16
|
+
RETURN;
|
|
17
|
+
END IF;
|
|
18
|
+
IF EXISTS (SELECT 1 FROM pg_extension WHERE extname = 'vector') THEN
|
|
19
|
+
RETURN;
|
|
20
|
+
END IF;
|
|
21
|
+
BEGIN
|
|
22
|
+
EXECUTE 'CREATE EXTENSION IF NOT EXISTS vector SCHEMA public';
|
|
23
|
+
EXCEPTION WHEN insufficient_privilege OR feature_not_supported THEN
|
|
24
|
+
RAISE NOTICE 'pgvector not installed: CREATE EXTENSION was refused (SQLSTATE %: %); plugins fall back to real[] embeddings', SQLSTATE, SQLERRM;
|
|
25
|
+
END;
|
|
26
|
+
END
|
|
27
|
+
$$;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
-- Memory plugin spec §6.2: where a conversation comes from (web, channel_dm, channel_group,
|
|
2
|
+
-- subagent), set once at creation and never changed; extension tools and context@1 providers are
|
|
3
|
+
-- offered by it.
|
|
4
|
+
-- The column is added with a constant default, so existing rows read 'web' without a rewrite.
|
|
5
|
+
-- The backfill then sets the other sources, each UPDATE touching only rows still at 'web', so a
|
|
6
|
+
-- re-run updates nothing; later UPDATEs skip rows an earlier one set:
|
|
7
|
+
-- 1. kind 'subagent' -> subagent;
|
|
8
|
+
-- 2. a channel thread row of any archive state (DM /new archives the row, the conversation stays
|
|
9
|
+
-- a DM's): only 'dm:' keys -> channel_dm, any other key -> channel_group (a conversation with
|
|
10
|
+
-- both is a group's: the safer of the two);
|
|
11
|
+
-- 3. a scheduled group run whose thread is still to bind at its first post (no thread row yet):
|
|
12
|
+
-- its delivery's target chat type.
|
|
13
|
+
-- The rest stays web. The CHECK is added last (dropped first, so a re-run re-adds it).
|
|
14
|
+
ALTER TABLE conversations ADD COLUMN IF NOT EXISTS source text NOT NULL DEFAULT 'web';
|
|
15
|
+
|
|
16
|
+
UPDATE conversations SET source = 'subagent'
|
|
17
|
+
WHERE kind = 'subagent' AND source = 'web';
|
|
18
|
+
|
|
19
|
+
UPDATE conversations c
|
|
20
|
+
SET source = CASE WHEN t.dm THEN 'channel_dm' ELSE 'channel_group' END
|
|
21
|
+
FROM (
|
|
22
|
+
SELECT conversation_id, bool_and(thread_key LIKE 'dm:%') AS dm
|
|
23
|
+
FROM channel_threads
|
|
24
|
+
GROUP BY conversation_id
|
|
25
|
+
) t
|
|
26
|
+
WHERE t.conversation_id = c.id AND c.source = 'web';
|
|
27
|
+
|
|
28
|
+
UPDATE conversations c
|
|
29
|
+
SET source = CASE WHEN d.target->>'chatType' = 'dm' THEN 'channel_dm' ELSE 'channel_group' END
|
|
30
|
+
FROM channel_deliveries d
|
|
31
|
+
WHERE d.conversation_id = c.id AND d.target->>'chatType' IN ('dm', 'group') AND c.source = 'web';
|
|
32
|
+
|
|
33
|
+
-- capybara:allow-destructive the CHECK is re-added unchanged right below (a re-run of this file)
|
|
34
|
+
ALTER TABLE conversations DROP CONSTRAINT IF EXISTS conversations_source_check;
|
|
35
|
+
ALTER TABLE conversations ADD CONSTRAINT conversations_source_check
|
|
36
|
+
CHECK (source IN ('web', 'channel_dm', 'channel_group', 'subagent'));
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
-- Agent map spec §6 (changed in acceptance 2026-11-13): a person's stop of a root conversation
|
|
2
|
+
-- stops its whole sub-agent tree and holds the tree's wakes. Set on the root by that stop, cleared
|
|
3
|
+
-- when the root next starts a run; while set, no sub-agent delivery wakes the root and no
|
|
4
|
+
-- sub-agent of its tree starts a run. Null on every existing row (no stop is held).
|
|
5
|
+
ALTER TABLE conversations ADD COLUMN IF NOT EXISTS tree_stopped_at timestamptz;
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
import { KernelConfigService } from "../infra/config/kernel-config.service.js";
|
|
2
|
-
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
3
|
-
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
4
|
-
import { LockService } from "../infra/lock/lock.service.js";
|
|
5
|
-
import type { KernelLogger } from "../log.js";
|
|
6
|
-
import { OrgLock } from "../orgs/org-lock.js";
|
|
7
|
-
import { PlatformComponents } from "./platform-components.js";
|
|
8
|
-
/** The job's name (`@LockedInterval`, run lock `cron:platform-components-migrate`). */
|
|
9
|
-
export declare const PLATFORM_MIGRATION_JOB = "platform-components-migrate";
|
|
10
|
-
/** The audit action and inbox notice of a renamed skill. */
|
|
11
|
-
export declare const SKILL_RENAMED = "skill_renamed";
|
|
12
|
-
/** What one run changed (also stored in `platform_migration_runs.stats`). */
|
|
13
|
-
export interface PlatformMigrationStats {
|
|
14
|
-
/** Catalog items whose platform rows were written (`<name>@<version>`). */
|
|
15
|
-
itemsSynced: string[];
|
|
16
|
-
/** Catalog items whose sync failed (logged; tried again by the next run). */
|
|
17
|
-
itemsFailed: string[];
|
|
18
|
-
/** Org skill copies linked to their platform skill, and how many of them were pinned. */
|
|
19
|
-
skillsLinked: number;
|
|
20
|
-
skillsPinned: number;
|
|
21
|
-
/** Org skills renamed because a platform skill of the same name is on in their org. */
|
|
22
|
-
skillsRenamed: number;
|
|
23
|
-
/** Org template copies linked to their platform template, and how many of them were pinned. */
|
|
24
|
-
templatesLinked: number;
|
|
25
|
-
templatesPinned: number;
|
|
26
|
-
/** Connectors linked to the platform connector template they were made from. */
|
|
27
|
-
connectorsLinked: number;
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* The name a renamed skill gets: `<name>-org`, then `-org-2`, `-org-3`, … (the base shortened
|
|
31
|
-
* when the result would be longer than a skill name may be), the first one not in `taken`.
|
|
32
|
-
*/
|
|
33
|
-
export declare function renamedSkillName(name: string, taken: ReadonlySet<string>): string;
|
|
34
|
-
/**
|
|
35
|
-
* The platform components migration (spec §6, §13.5), a worker job (`platform-components-migrate`,
|
|
36
|
-
* `@LockedInterval` with `runOnStart`: once across replicas at every worker start, then daily).
|
|
37
|
-
* Steps, each on the rows that still need it:
|
|
38
|
-
* 1. every live catalog item never synced (`platform_synced_version` null) gets its platform rows
|
|
39
|
-
* (`syncFromVersion`, which records the version) at the highest
|
|
40
|
-
* version installed in an org (spec §6: "the plugin's version becomes the highest installed
|
|
41
|
-
* one"), else its latest version;
|
|
42
|
-
* 2. an org skill copy with plugin provenance (on the skill and its current version) links to the
|
|
43
|
-
* platform skill of its plugin and name; it is pinned to its own current version when its files
|
|
44
|
-
* differ from the platform version's. A copy whose platform skill is not on in its org is left
|
|
45
|
-
* unlinked (it keeps serving what it served) until a later run finds it on: a linked copy,
|
|
46
|
-
* pinned or not, follows its platform skill's availability (ruling P2-18);
|
|
47
|
-
* 3. one-shot (spec §5 rule 10, ruling P2-5: nothing is renamed outside the migration): only while
|
|
48
|
-
* no run has finished (the first run's own execution), an org skill of the org's own (no
|
|
49
|
-
* provenance) whose name a platform skill on in its org has stays the org's: it is renamed (`<name>-org`, `-org-2`, …), the org admins get an inbox notice
|
|
50
|
-
* (`skill_renamed`) and that platform skill is switched off in the org (a switch the org already
|
|
51
|
-
* has is kept). Agents select skills by id, so they keep it; the name changes in new runs. Once a
|
|
52
|
-
* run has finished, a clash is only logged as a warning and left alone (the org's skill wins);
|
|
53
|
-
* 4. an org agent template links to the platform template of its plugin and name, pinned like
|
|
54
|
-
* skills (definitions compared as canonical JSON). Templates are never renamed: an org template
|
|
55
|
-
* always has its plugin's type, which is the platform template's;
|
|
56
|
-
* 5. a connector made from a connector template (the install ledger's `template_id`) links to that
|
|
57
|
-
* platform connector template (ruling E2).
|
|
58
|
-
* Plugin switches exist since phase 1 and new platform rows follow the plugin's scope, so nothing
|
|
59
|
-
* else is written. Writes run per org under its row lock and re-check each row; nothing is deleted
|
|
60
|
-
* or overwritten except the renamed skill's name. Each run is recorded in `platform_migration_runs`
|
|
61
|
-
* (rows older than 30 days are pruned, except the first finished run: it marks the renames done).
|
|
62
|
-
*/
|
|
63
|
-
export declare class PlatformMigration {
|
|
64
|
-
private readonly kdb;
|
|
65
|
-
private readonly orgLock;
|
|
66
|
-
private readonly platform;
|
|
67
|
-
readonly txHost: TransactionHost;
|
|
68
|
-
readonly config: KernelConfigService;
|
|
69
|
-
readonly lockService: LockService;
|
|
70
|
-
readonly log: KernelLogger;
|
|
71
|
-
constructor(kdb: KernelDb, orgLock: OrgLock, platform: PlatformComponents, txHost: TransactionHost, config: KernelConfigService, lockService: LockService, log: KernelLogger);
|
|
72
|
-
private get db();
|
|
73
|
-
/** The scheduled run (see the class). */
|
|
74
|
-
migrate(signal?: AbortSignal): Promise<void>;
|
|
75
|
-
/**
|
|
76
|
-
* One run of every step; returns what it changed. `signal` (the stop, or a lost run lock) is
|
|
77
|
-
* read between items and orgs: an aborted run stops there and its row is deleted (every step is
|
|
78
|
-
* idempotent, so the next run carries on; an unfinished run never counts as the first one).
|
|
79
|
-
*/
|
|
80
|
-
runOnce(signal?: AbortSignal): Promise<PlatformMigrationStats>;
|
|
81
|
-
/** Deletes finished runs older than {@link RUNS_KEPT_MS}, never the first one (`first`). */
|
|
82
|
-
private pruneRuns;
|
|
83
|
-
/**
|
|
84
|
-
* Step 1: the live catalog items whose platform components were never written
|
|
85
|
-
* (`platform_synced_version` null; `syncFromVersion` sets it). An item whose components were
|
|
86
|
-
* removed since is not synced again.
|
|
87
|
-
*/
|
|
88
|
-
private syncItems;
|
|
89
|
-
/** The highest version of the item installed in an org (and still stored), else `latest`. */
|
|
90
|
-
private platformVersion;
|
|
91
|
-
/** Step 2: org skill copies with plugin provenance, not linked yet. */
|
|
92
|
-
private linkSkills;
|
|
93
|
-
/** Links `links` of one org under its row lock (each only while it is still as read). */
|
|
94
|
-
private linkSkillsIn;
|
|
95
|
-
/**
|
|
96
|
-
* Step 3 (one-shot): org skills of the org's own whose name a platform skill on in their org has,
|
|
97
|
-
* renamed only in the first run (`rename`: no run has finished yet); later ones are only logged.
|
|
98
|
-
*/
|
|
99
|
-
private renameClashes;
|
|
100
|
-
/**
|
|
101
|
-
* Renames the clashing skills of one org under its row lock (each only while it still has the
|
|
102
|
-
* name and no provenance), switches the clashing platform skills off in the org (unless the org
|
|
103
|
-
* has a switch for them already), and writes one inbox notice and one audit event per skill.
|
|
104
|
-
*/
|
|
105
|
-
private renameIn;
|
|
106
|
-
/** Step 4: org agent templates not linked yet, by plugin and name. */
|
|
107
|
-
private linkTemplates;
|
|
108
|
-
/** Links `links` of one org under its row lock (each only while it is still as read). */
|
|
109
|
-
private linkTemplatesIn;
|
|
110
|
-
/**
|
|
111
|
-
* Step 5: connectors not linked yet whose install ledger row names the connector template they
|
|
112
|
-
* were made from; one statement (a connector only gains a link, nothing else changes).
|
|
113
|
-
*/
|
|
114
|
-
private linkConnectors;
|
|
115
|
-
}
|