@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.
Files changed (85) hide show
  1. package/dist/admin/outbox-dead.service.d.ts +7 -3
  2. package/dist/admin/outbox-dead.service.js +17 -6
  3. package/dist/admin/overview.service.d.ts +6 -6
  4. package/dist/admin/overview.service.js +7 -7
  5. package/dist/background/wake-rules.d.ts +5 -0
  6. package/dist/background/wake-rules.js +16 -5
  7. package/dist/catalog/marketplaces.service.d.ts +1 -1
  8. package/dist/channels/channels.module.d.ts +9 -8
  9. package/dist/channels/channels.module.js +11 -10
  10. package/dist/channels/connections.service.d.ts +3 -3
  11. package/dist/channels/connections.service.js +6 -6
  12. package/dist/channels/deliver-job.d.ts +10 -3
  13. package/dist/channels/deliver-job.js +7 -3
  14. package/dist/channels/jobs.d.ts +11 -4
  15. package/dist/channels/jobs.js +18 -3
  16. package/dist/channels/outbound.d.ts +5 -2
  17. package/dist/channels/outbound.js +9 -2
  18. package/dist/conversations/conversations.controller.d.ts +4 -2
  19. package/dist/conversations/conversations.controller.js +4 -2
  20. package/dist/conversations/conversations.service.d.ts +37 -4
  21. package/dist/conversations/conversations.service.js +77 -20
  22. package/dist/conversations/events.d.ts +6 -0
  23. package/dist/conversations/events.js +6 -0
  24. package/dist/db/kernel-schema.d.ts +10 -1
  25. package/dist/devices/devices.service.d.ts +14 -6
  26. package/dist/devices/devices.service.js +20 -7
  27. package/dist/evals/evals.module.js +5 -1
  28. package/dist/evals/jobs.d.ts +14 -6
  29. package/dist/evals/jobs.js +21 -19
  30. package/dist/evals/runs.service.d.ts +5 -6
  31. package/dist/evals/runs.service.js +11 -33
  32. package/dist/infra/outbox/outbox.service.d.ts +6 -0
  33. package/dist/infra/outbox/outbox.service.js +51 -17
  34. package/dist/infra/outbox/registry.d.ts +52 -0
  35. package/dist/infra/outbox/registry.js +57 -0
  36. package/dist/local-devices/device-policy.service.d.ts +131 -26
  37. package/dist/local-devices/device-policy.service.js +341 -145
  38. package/dist/local-devices/machines.d.ts +17 -2
  39. package/dist/local-devices/machines.js +66 -5
  40. package/dist/local-devices/policy-op-jobs.d.ts +15 -0
  41. package/dist/local-devices/policy-op-jobs.js +12 -0
  42. package/dist/local-devices/policy-summary.d.ts +36 -3
  43. package/dist/local-devices/policy-summary.js +47 -1
  44. package/dist/orgs/orgs.service.js +4 -1
  45. package/dist/overrides.d.ts +4 -0
  46. package/dist/overrides.js +2 -0
  47. package/dist/platform/ops/conversations.ops.js +1 -1
  48. package/dist/runtime/maintenance.d.ts +6 -3
  49. package/dist/runtime/maintenance.js +21 -7
  50. package/dist/runtime/plugin-context.service.d.ts +9 -3
  51. package/dist/runtime/plugin-context.service.js +17 -5
  52. package/dist/runtime/run-context.service.d.ts +7 -3
  53. package/dist/runtime/run-context.service.js +12 -2
  54. package/dist/runtime/run-finish.d.ts +2 -2
  55. package/dist/runtime/run-finish.js +11 -6
  56. package/dist/runtime/run-jobs.d.ts +21 -4
  57. package/dist/runtime/run-jobs.js +29 -6
  58. package/dist/runtime/run-orphan-check.job.d.ts +14 -11
  59. package/dist/runtime/run-orphan-check.job.js +16 -11
  60. package/dist/runtime/run-queue.d.ts +23 -31
  61. package/dist/runtime/run-queue.js +62 -64
  62. package/dist/runtime/run-status.d.ts +12 -0
  63. package/dist/runtime/run-status.js +21 -0
  64. package/dist/runtime/run-worker.d.ts +10 -2
  65. package/dist/runtime/run-worker.js +44 -19
  66. package/dist/runtime/step-log.d.ts +6 -5
  67. package/dist/runtime/step-log.js +6 -5
  68. package/dist/runtime/usage-report.d.ts +3 -1
  69. package/dist/runtime/usage-report.js +4 -2
  70. package/dist/runtime/waits.d.ts +16 -6
  71. package/dist/runtime/waits.js +37 -15
  72. package/dist/schedules/scan.job.d.ts +6 -6
  73. package/dist/schedules/scan.job.js +6 -6
  74. package/dist/skills/catalog.d.ts +7 -3
  75. package/dist/skills/catalog.js +12 -2
  76. package/dist/tools/approvals.service.d.ts +96 -19
  77. package/dist/tools/approvals.service.js +300 -67
  78. package/dist/tools/builtins/sandbox/task-output.js +21 -6
  79. package/dist/tools/device-op-resolver.d.ts +40 -0
  80. package/dist/tools/device-op-resolver.js +121 -0
  81. package/dist/tools/network-asks.d.ts +1 -1
  82. package/dist/tools/network-asks.js +4 -3
  83. package/dist/tools/tools.module.js +4 -0
  84. package/package.json +2 -2
  85. 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 `policyOp`: nothing (no ask, the trailer explains);
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 `policyOp`: nothing (no ask, the trailer explains);
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 no report)
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("policyOp");
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.25",
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.19"
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;