@capybara-hq/kernel 0.1.0-rc.25 → 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/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/background/wake-rules.d.ts +5 -0
- package/dist/background/wake-rules.js +16 -5
- package/dist/catalog/marketplaces.service.d.ts +1 -1
- 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/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/conversations/conversations.controller.d.ts +4 -2
- package/dist/conversations/conversations.controller.js +4 -2
- package/dist/conversations/conversations.service.d.ts +37 -4
- package/dist/conversations/conversations.service.js +77 -20
- package/dist/conversations/events.d.ts +6 -0
- package/dist/conversations/events.js +6 -0
- package/dist/db/kernel-schema.d.ts +10 -1
- package/dist/devices/devices.service.d.ts +14 -6
- package/dist/devices/devices.service.js +20 -7
- 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/runs.service.d.ts +5 -6
- package/dist/evals/runs.service.js +11 -33
- 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 +52 -0
- package/dist/infra/outbox/registry.js +57 -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/orgs.service.js +4 -1
- package/dist/overrides.d.ts +4 -0
- package/dist/overrides.js +2 -0
- package/dist/platform/ops/conversations.ops.js +1 -1
- package/dist/runtime/maintenance.d.ts +6 -3
- package/dist/runtime/maintenance.js +21 -7
- package/dist/runtime/plugin-context.service.d.ts +9 -3
- package/dist/runtime/plugin-context.service.js +17 -5
- package/dist/runtime/run-context.service.d.ts +7 -3
- package/dist/runtime/run-context.service.js +12 -2
- 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 +23 -31
- package/dist/runtime/run-queue.js +62 -64
- package/dist/runtime/run-status.d.ts +12 -0
- package/dist/runtime/run-status.js +21 -0
- package/dist/runtime/run-worker.d.ts +10 -2
- package/dist/runtime/run-worker.js +44 -19
- package/dist/runtime/step-log.d.ts +6 -5
- package/dist/runtime/step-log.js +6 -5
- 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/schedules/scan.job.d.ts +6 -6
- package/dist/schedules/scan.job.js +6 -6
- package/dist/skills/catalog.d.ts +7 -3
- package/dist/skills/catalog.js +12 -2
- package/dist/tools/approvals.service.d.ts +96 -19
- package/dist/tools/approvals.service.js +300 -67
- 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/tools.module.js +4 -0
- package/package.json +2 -2
- package/src/migrations/0125_conversation_tree_stopped_at.sql +5 -0
|
@@ -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)) {
|
|
@@ -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",
|
|
@@ -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;
|