@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
|
@@ -8,31 +8,22 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
|
8
8
|
return function (target, key) { decorator(target, key, paramIndex); }
|
|
9
9
|
};
|
|
10
10
|
import { randomUUID } from "node:crypto";
|
|
11
|
-
import { getQueueToken } from "@nestjs/bullmq";
|
|
12
11
|
import { Inject, Injectable } from "@nestjs/common";
|
|
13
|
-
import { sql } from "kysely";
|
|
14
12
|
import { KernelConfigService } from "../infra/config/kernel-config.service.js";
|
|
15
|
-
import { afterCommit } from "../infra/database/after-commit.js";
|
|
16
13
|
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
17
14
|
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
18
15
|
import { Transactional } from "../infra/database/transactional.decorator.js";
|
|
19
16
|
import { LockService } from "../infra/lock/lock.service.js";
|
|
17
|
+
import { OutboxService } from "../infra/outbox/outbox.service.js";
|
|
20
18
|
import { LockedCron } from "../infra/schedule/locked-cron.decorator.js";
|
|
21
19
|
import { KERNEL_LOGGER } from "../infra/tokens.js";
|
|
22
|
-
import { MAINTENANCE_QUEUE_NAME } from "../runtime/maintenance-queue.js";
|
|
23
20
|
import { readScheduleSettings } from "./effective-settings.js";
|
|
24
|
-
import {
|
|
21
|
+
import { oweSchedFire, oweSchedSettleUnfired, SCHED_SCAN_JOB } from "./job-ids.js";
|
|
25
22
|
import { pauseTaskIn } from "./pause.js";
|
|
26
|
-
import { lockTaskRow, ScheduledTaskRunTracker } from "./run-tracker.js";
|
|
27
23
|
import { scanStep } from "./schedule-math.js";
|
|
28
24
|
import { ScheduledTaskStore } from "./store.js";
|
|
29
25
|
/** Due tasks per scan; what is left waits for the next minute. */
|
|
30
26
|
const MAX_TASKS = 2000;
|
|
31
|
-
/** A pending task run never fired this long after its tick: its fire job is enqueued again. */
|
|
32
|
-
const REPAIR_AFTER_MS = 10 * 60_000;
|
|
33
|
-
/** ... and this long after its tick it is given up (`skipped`, `missed`). */
|
|
34
|
-
const GIVE_UP_AFTER_MS = 60 * 60_000;
|
|
35
|
-
const REPAIR_BATCH = 100;
|
|
36
27
|
/**
|
|
37
28
|
* The schedule math could not evaluate a due task's stored trigger (its transaction rolled back);
|
|
38
29
|
* the scan pauses it. Only the math raises it: a database error fails the scan pass instead (the
|
|
@@ -51,32 +42,28 @@ class TaskScanFailed extends Error {
|
|
|
51
42
|
* same task): the ticks missed outside the org's `catchUpWindowMinutes` become one `skipped` run
|
|
52
43
|
* (`missed`, at the first missed tick), the latest tick in the window gets its run (the unique
|
|
53
44
|
* `(task_id, scheduled_for)` key makes a second insert a no-op), `next_run_at` moves to the next
|
|
54
|
-
* tick plus the task's jitter (or a `once` task ends, or one past its `expires_at`), and
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
* still `pending` (its start failed for good) goes through the run tracker's check, which links,
|
|
62
|
-
* settles or releases it, so a `once` task's last run never stays pending.
|
|
45
|
+
* tick plus the task's jitter (or a `once` task ends, or one past its `expires_at`), and the same
|
|
46
|
+
* transaction owes `sched-fire-<taskRunId>` (transactional outbox spec §8.1), so a written tick is
|
|
47
|
+
* never left unfired and the scan needs no repair of lost fire jobs (a fire delivered too late
|
|
48
|
+
* marks its tick missed itself, `ScheduledTaskFirer`), and the run's unfired settle check (a fire
|
|
49
|
+
* job that failed for good before its claim: the tick is missed, `job-ids.ts`). A task whose stored
|
|
50
|
+
* trigger the schedule math cannot evaluate is paused (`invalid_schedule`) instead of failing the
|
|
51
|
+
* scan; a database error fails the pass (the next minute retries) and pauses nothing.
|
|
63
52
|
*/
|
|
64
53
|
let ScheduledTaskScanJob = class ScheduledTaskScanJob {
|
|
65
54
|
kdb;
|
|
66
55
|
txHost;
|
|
67
56
|
config;
|
|
68
57
|
store;
|
|
69
|
-
|
|
70
|
-
maintenance;
|
|
58
|
+
outbox;
|
|
71
59
|
lockService;
|
|
72
60
|
log;
|
|
73
|
-
constructor(kdb, txHost, config, store,
|
|
61
|
+
constructor(kdb, txHost, config, store, outbox, lockService, log) {
|
|
74
62
|
this.kdb = kdb;
|
|
75
63
|
this.txHost = txHost;
|
|
76
64
|
this.config = config;
|
|
77
65
|
this.store = store;
|
|
78
|
-
this.
|
|
79
|
-
this.maintenance = maintenance;
|
|
66
|
+
this.outbox = outbox;
|
|
80
67
|
this.lockService = lockService;
|
|
81
68
|
this.log = log;
|
|
82
69
|
}
|
|
@@ -86,41 +73,32 @@ let ScheduledTaskScanJob = class ScheduledTaskScanJob {
|
|
|
86
73
|
async scan() {
|
|
87
74
|
await this.scanAt(new Date());
|
|
88
75
|
}
|
|
89
|
-
/**
|
|
90
|
-
|
|
91
|
-
* runs even when the scan loop failed (its error is rethrown afterwards). `repairBatch`: how many
|
|
92
|
-
* rows each repair step takes (tests pass a small one).
|
|
93
|
-
*/
|
|
94
|
-
async scanAt(now, repairBatch = REPAIR_BATCH) {
|
|
76
|
+
/** One scan as of `now` (tests pass their own); returns how many due tasks it handled. */
|
|
77
|
+
async scanAt(now) {
|
|
95
78
|
const settings = new Map();
|
|
96
79
|
let total = 0;
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
await this.quarantine(err.taskId);
|
|
112
|
-
}
|
|
80
|
+
for (; total < MAX_TASKS; total++) {
|
|
81
|
+
try {
|
|
82
|
+
if (!(await this.scanOne(now, settings)))
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
if (!(err instanceof TaskScanFailed))
|
|
87
|
+
throw err;
|
|
88
|
+
// one task that cannot be advanced must not hold the others back every minute
|
|
89
|
+
this.log.error("scheduled task cannot be advanced; paused", {
|
|
90
|
+
taskId: err.taskId,
|
|
91
|
+
err: err.cause,
|
|
92
|
+
});
|
|
93
|
+
await this.quarantine(err.taskId);
|
|
113
94
|
}
|
|
114
|
-
}
|
|
115
|
-
finally {
|
|
116
|
-
await this.repair(now, repairBatch);
|
|
117
95
|
}
|
|
118
96
|
return total;
|
|
119
97
|
}
|
|
120
98
|
/**
|
|
121
|
-
* One due task in its own transaction (a task whose trigger cannot be evaluated rolls back
|
|
122
|
-
*
|
|
123
|
-
* when no task is due.
|
|
99
|
+
* One due task in its own transaction (a task whose trigger cannot be evaluated rolls back alone
|
|
100
|
+
* and is paused, so it cannot block the others): locked with `SKIP LOCKED`, so replicas take
|
|
101
|
+
* different tasks. False when no task is due.
|
|
124
102
|
*/
|
|
125
103
|
async scanOne(now, settings) {
|
|
126
104
|
const t = this.db;
|
|
@@ -134,8 +112,8 @@ let ScheduledTaskScanJob = class ScheduledTaskScanJob {
|
|
|
134
112
|
}
|
|
135
113
|
const fired = await this.advance(t, task, now, s.catchUpWindowMinutes);
|
|
136
114
|
if (fired !== null) {
|
|
137
|
-
|
|
138
|
-
|
|
115
|
+
await oweSchedFire(this.outbox, fired.id, task.orgId);
|
|
116
|
+
await oweSchedSettleUnfired(this.outbox, fired.id, task.orgId, fired.scheduledFor, s.catchUpWindowMinutes);
|
|
139
117
|
}
|
|
140
118
|
return true;
|
|
141
119
|
}
|
|
@@ -143,7 +121,7 @@ let ScheduledTaskScanJob = class ScheduledTaskScanJob {
|
|
|
143
121
|
async quarantine(taskId) {
|
|
144
122
|
await pauseTaskIn(this.db, taskId, "invalid_schedule", null);
|
|
145
123
|
}
|
|
146
|
-
/** One due task (see the class); returns the
|
|
124
|
+
/** One due task (see the class); returns the run to fire, if any. */
|
|
147
125
|
async advance(t, task, now, catchUpWindowMinutes) {
|
|
148
126
|
// the schedule math alone may mark the task broken; database errors fail this pass only
|
|
149
127
|
let step;
|
|
@@ -178,7 +156,7 @@ let ScheduledTaskScanJob = class ScheduledTaskScanJob {
|
|
|
178
156
|
scheduledFor: step.fire,
|
|
179
157
|
trial: false,
|
|
180
158
|
});
|
|
181
|
-
fired = run
|
|
159
|
+
fired = run ?? null;
|
|
182
160
|
}
|
|
183
161
|
const patch = step.end === "done"
|
|
184
162
|
? { status: "ended", nextRunAt: null }
|
|
@@ -188,86 +166,6 @@ let ScheduledTaskScanJob = class ScheduledTaskScanJob {
|
|
|
188
166
|
await this.store.updateTask(t, task.id, patch);
|
|
189
167
|
return fired;
|
|
190
168
|
}
|
|
191
|
-
/** Enqueues the fire jobs (after the commit; a failure is repaired by a later scan). */
|
|
192
|
-
async enqueue(ids) {
|
|
193
|
-
for (const id of ids) {
|
|
194
|
-
const job = schedFireJob(id);
|
|
195
|
-
try {
|
|
196
|
-
await this.maintenance.add(job.name, job.data, job.opts);
|
|
197
|
-
}
|
|
198
|
-
catch (err) {
|
|
199
|
-
this.log.warn("enqueueing a scheduled task run failed", { taskRunId: id, err });
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
/** Lost fire jobs (see the class). */
|
|
204
|
-
async repair(now, batch) {
|
|
205
|
-
const stale = await this.db
|
|
206
|
-
.selectFrom("scheduled_task_runs")
|
|
207
|
-
.select(["id", "scheduled_for"])
|
|
208
|
-
.where("status", "=", "pending")
|
|
209
|
-
.where("fired_at", "is", null)
|
|
210
|
-
.where("scheduled_for", "<", new Date(now.getTime() - REPAIR_AFTER_MS))
|
|
211
|
-
.orderBy("scheduled_for")
|
|
212
|
-
.limit(batch)
|
|
213
|
-
.execute();
|
|
214
|
-
const retry = [];
|
|
215
|
-
const lost = [];
|
|
216
|
-
for (const r of stale) {
|
|
217
|
-
const at = r.scheduled_for instanceof Date ? r.scheduled_for : new Date(r.scheduled_for);
|
|
218
|
-
(now.getTime() - at.getTime() > GIVE_UP_AFTER_MS ? lost : retry).push(r.id);
|
|
219
|
-
}
|
|
220
|
-
for (const id of lost)
|
|
221
|
-
await this.giveUp(id);
|
|
222
|
-
await this.enqueue(retry);
|
|
223
|
-
// the least recently rechecked first, so runs still in flight do not starve the others
|
|
224
|
-
const started = await this.db
|
|
225
|
-
.selectFrom("scheduled_task_runs")
|
|
226
|
-
.select("id")
|
|
227
|
-
.where("status", "=", "pending")
|
|
228
|
-
.where("fired_at", "<", new Date(now.getTime() - GIVE_UP_AFTER_MS))
|
|
229
|
-
.orderBy(sql `rechecked_at NULLS FIRST`)
|
|
230
|
-
.orderBy("scheduled_for")
|
|
231
|
-
.limit(batch)
|
|
232
|
-
.execute();
|
|
233
|
-
for (const r of started)
|
|
234
|
-
await this.recheck(r.id, now);
|
|
235
|
-
}
|
|
236
|
-
/** A run never fired an hour after its tick: given up, task row first (the one lock order). */
|
|
237
|
-
async giveUp(taskRunId) {
|
|
238
|
-
const owner = await this.db
|
|
239
|
-
.selectFrom("scheduled_task_runs")
|
|
240
|
-
.select("task_id")
|
|
241
|
-
.where("id", "=", taskRunId)
|
|
242
|
-
.executeTakeFirst();
|
|
243
|
-
if (!owner)
|
|
244
|
-
return;
|
|
245
|
-
await lockTaskRow(this.db, owner.task_id);
|
|
246
|
-
await this.db
|
|
247
|
-
.updateTable("scheduled_task_runs")
|
|
248
|
-
.set({ status: "skipped", skip_reason: "missed", error: "The run could not be started" })
|
|
249
|
-
.where("id", "=", taskRunId)
|
|
250
|
-
.where("status", "=", "pending")
|
|
251
|
-
.where("fired_at", "is", null)
|
|
252
|
-
.execute();
|
|
253
|
-
}
|
|
254
|
-
/**
|
|
255
|
-
* A started run still pending long after: the tracker links, settles or releases it; one still
|
|
256
|
-
* in flight is stamped (`rechecked_at`) and goes to the back of the next rechecks.
|
|
257
|
-
*/
|
|
258
|
-
async recheck(taskRunId, now) {
|
|
259
|
-
const run = await this.store.updateRun(this.db, taskRunId, {});
|
|
260
|
-
if (run?.status !== "pending")
|
|
261
|
-
return;
|
|
262
|
-
// inFlightIn takes the task row before the run (the one lock order)
|
|
263
|
-
if (await this.tracker.inFlightIn(run, now)) {
|
|
264
|
-
await this.db
|
|
265
|
-
.updateTable("scheduled_task_runs")
|
|
266
|
-
.set({ rechecked_at: now })
|
|
267
|
-
.where("id", "=", taskRunId)
|
|
268
|
-
.execute();
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
169
|
};
|
|
272
170
|
__decorate([
|
|
273
171
|
LockedCron("* * * * *", { name: SCHED_SCAN_JOB })
|
|
@@ -278,21 +176,14 @@ __decorate([
|
|
|
278
176
|
__decorate([
|
|
279
177
|
Transactional()
|
|
280
178
|
], ScheduledTaskScanJob.prototype, "quarantine", null);
|
|
281
|
-
__decorate([
|
|
282
|
-
Transactional()
|
|
283
|
-
], ScheduledTaskScanJob.prototype, "giveUp", null);
|
|
284
|
-
__decorate([
|
|
285
|
-
Transactional()
|
|
286
|
-
], ScheduledTaskScanJob.prototype, "recheck", null);
|
|
287
179
|
ScheduledTaskScanJob = __decorate([
|
|
288
180
|
Injectable(),
|
|
289
181
|
__param(0, Inject(KernelDb)),
|
|
290
182
|
__param(1, Inject(TransactionHost)),
|
|
291
183
|
__param(2, Inject(KernelConfigService)),
|
|
292
184
|
__param(3, Inject(ScheduledTaskStore)),
|
|
293
|
-
__param(4, Inject(
|
|
294
|
-
__param(5, Inject(
|
|
295
|
-
__param(6, Inject(
|
|
296
|
-
__param(7, Inject(KERNEL_LOGGER))
|
|
185
|
+
__param(4, Inject(OutboxService)),
|
|
186
|
+
__param(5, Inject(LockService)),
|
|
187
|
+
__param(6, Inject(KERNEL_LOGGER))
|
|
297
188
|
], ScheduledTaskScanJob);
|
|
298
189
|
export { ScheduledTaskScanJob };
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { Queue } from "bullmq";
|
|
2
1
|
import { AgentLookup } from "../agents/agent-lookup.js";
|
|
3
2
|
import type { ModelRef } from "../agents/definition.js";
|
|
4
3
|
import { ConversationAccess } from "../conversations/access.js";
|
|
@@ -7,6 +6,7 @@ import { ExtensionRegistry } from "../extensions/registry.js";
|
|
|
7
6
|
import { KernelConfigService } from "../infra/config/kernel-config.service.js";
|
|
8
7
|
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
9
8
|
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
9
|
+
import { OutboxService } from "../infra/outbox/outbox.service.js";
|
|
10
10
|
import { DevicePolicyService } from "../local-devices/device-policy.service.js";
|
|
11
11
|
import type { KernelLogger } from "../log.js";
|
|
12
12
|
import { OrgLock } from "../orgs/org-lock.js";
|
|
@@ -198,8 +198,8 @@ export declare class ScheduledTasksService {
|
|
|
198
198
|
/** A task's model must resolve for the org and declare its effort (as a message's switch). */
|
|
199
199
|
private readonly providers;
|
|
200
200
|
private readonly log;
|
|
201
|
-
/** "Run now"
|
|
202
|
-
private readonly
|
|
201
|
+
/** "Run now" owes the fire job (consumed by the worker role). */
|
|
202
|
+
private readonly outbox;
|
|
203
203
|
/** Network grants: the org's effective egress (network ask-to-allow spec §8.1). */
|
|
204
204
|
private readonly sandbox;
|
|
205
205
|
/** Network grants: the task's device's local policy summary (`remoteGrants`). */
|
|
@@ -207,8 +207,8 @@ export declare class ScheduledTasksService {
|
|
|
207
207
|
constructor(db: KernelDb, txHost: TransactionHost, config: Pick<KernelConfigService, "get">, store: ScheduledTaskStore, orgLock: Pick<OrgLock, "lockAndAuthorize">, agents: Pick<AgentLookup, "get">, access: Pick<ConversationAccess, "isAccessible">, tools: Pick<ToolRegistry, "forAgent">, extensions: Pick<ExtensionRegistry, "list">,
|
|
208
208
|
/** A task's model must resolve for the org and declare its effort (as a message's switch). */
|
|
209
209
|
providers: Pick<ProviderRegistry, "resolve">, log: Pick<KernelLogger, "warn">,
|
|
210
|
-
/** "Run now"
|
|
211
|
-
|
|
210
|
+
/** "Run now" owes the fire job (consumed by the worker role). */
|
|
211
|
+
outbox: Pick<OutboxService, "add">,
|
|
212
212
|
/** Network grants: the org's effective egress (network ask-to-allow spec §8.1). */
|
|
213
213
|
sandbox: Pick<SandboxService, "ceilingLimits" | "devicePolicy">,
|
|
214
214
|
/** Network grants: the task's device's local policy summary (`remoteGrants`). */
|
|
@@ -301,7 +301,7 @@ export declare class ScheduledTasksService {
|
|
|
301
301
|
/**
|
|
302
302
|
* "Run now" (spec §6), creator only: a task run for the request time, outside the schedule (no
|
|
303
303
|
* unique-key conflict with a tick, and `next_run_at` does not move), fired at once through the
|
|
304
|
-
* fire job
|
|
304
|
+
* fire job its transaction owes. `trial`: an attended trial run whose approvals become the task's
|
|
305
305
|
* grants (§7). Counted in the rate limit like any run. An ended task cannot run; a paused one
|
|
306
306
|
* can (only its creator gets here: the run is `manual`, which the fire's paused check lets by).
|
|
307
307
|
*/
|
|
@@ -9,7 +9,6 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
|
9
9
|
};
|
|
10
10
|
import { randomUUID } from "node:crypto";
|
|
11
11
|
import { CHANNEL_EXTENSION_POINT } from "@capybara-hq/plugin-sdk";
|
|
12
|
-
import { getQueueToken } from "@nestjs/bullmq";
|
|
13
12
|
import { Inject, Injectable } from "@nestjs/common";
|
|
14
13
|
import { AgentLookup } from "../agents/agent-lookup.js";
|
|
15
14
|
import { ConversationAccess } from "../conversations/access.js";
|
|
@@ -18,16 +17,15 @@ import { ApiError } from "../errors.js";
|
|
|
18
17
|
import { ExtensionRegistry } from "../extensions/registry.js";
|
|
19
18
|
import { audit } from "../identity/audit.js";
|
|
20
19
|
import { KernelConfigService } from "../infra/config/kernel-config.service.js";
|
|
21
|
-
import { afterCommit } from "../infra/database/after-commit.js";
|
|
22
20
|
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
23
21
|
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
24
22
|
import { Transactional } from "../infra/database/transactional.decorator.js";
|
|
23
|
+
import { OutboxService } from "../infra/outbox/outbox.service.js";
|
|
25
24
|
import { KERNEL_LOGGER } from "../infra/tokens.js";
|
|
26
25
|
import { DevicePolicyService } from "../local-devices/device-policy.service.js";
|
|
27
26
|
import { orgAllows } from "../local-devices/network-grants.js";
|
|
28
27
|
import { OrgLock } from "../orgs/org-lock.js";
|
|
29
28
|
import { can, roleAtLeast } from "../orgs/roles.js";
|
|
30
|
-
import { MAINTENANCE_QUEUE_NAME } from "../runtime/maintenance-queue.js";
|
|
31
29
|
import { ProviderRegistry } from "../runtime/providers.js";
|
|
32
30
|
import { SandboxService } from "../sandbox/sandbox.service.js";
|
|
33
31
|
import { isTimeZone } from "../time-zone.js";
|
|
@@ -36,7 +34,7 @@ import { assembleCalendar, CALENDAR_LIMITS, CALENDAR_RANGE_DAYS, CALENDAR_TASK_R
|
|
|
36
34
|
import { readScheduleSettings } from "./effective-settings.js";
|
|
37
35
|
import { grantNotAllowed, scheduleDisabled, scheduleForbidden, scheduleNotFound, targetInvalid, } from "./errors.js";
|
|
38
36
|
import { grantRefusal, grantRefusalOf, isNetworkGrantKey, isPolicyGrantKey, isTierGrantKey, NETWORK_GRANT_PREFIX, NETWORK_GRANT_SPEC_HASH, networkGrantHost, networkGrantRefusal, TIER_GRANT_SPEC_HASH, tierGrantRefusal, } from "./grant-rules.js";
|
|
39
|
-
import {
|
|
37
|
+
import { oweSchedFire, oweSchedSettleUnfired } from "./job-ids.js";
|
|
40
38
|
import { pauseTaskIn } from "./pause.js";
|
|
41
39
|
import { jitterForGap, minGapMinutes, nextTick, validateTrigger } from "./schedule-math.js";
|
|
42
40
|
import { ScheduledTaskStore } from "./store.js";
|
|
@@ -75,14 +73,14 @@ let ScheduledTasksService = class ScheduledTasksService {
|
|
|
75
73
|
extensions;
|
|
76
74
|
providers;
|
|
77
75
|
log;
|
|
78
|
-
|
|
76
|
+
outbox;
|
|
79
77
|
sandbox;
|
|
80
78
|
devicePolicy;
|
|
81
79
|
constructor(db, txHost, config, store, orgLock, agents, access, tools, extensions,
|
|
82
80
|
/** A task's model must resolve for the org and declare its effort (as a message's switch). */
|
|
83
81
|
providers, log,
|
|
84
|
-
/** "Run now"
|
|
85
|
-
|
|
82
|
+
/** "Run now" owes the fire job (consumed by the worker role). */
|
|
83
|
+
outbox,
|
|
86
84
|
/** Network grants: the org's effective egress (network ask-to-allow spec §8.1). */
|
|
87
85
|
sandbox,
|
|
88
86
|
/** Network grants: the task's device's local policy summary (`remoteGrants`). */
|
|
@@ -98,7 +96,7 @@ let ScheduledTasksService = class ScheduledTasksService {
|
|
|
98
96
|
this.extensions = extensions;
|
|
99
97
|
this.providers = providers;
|
|
100
98
|
this.log = log;
|
|
101
|
-
this.
|
|
99
|
+
this.outbox = outbox;
|
|
102
100
|
this.sandbox = sandbox;
|
|
103
101
|
this.devicePolicy = devicePolicy;
|
|
104
102
|
}
|
|
@@ -525,6 +523,8 @@ let ScheduledTasksService = class ScheduledTasksService {
|
|
|
525
523
|
disabledTools: d.disabledTools ?? [],
|
|
526
524
|
subagentTools: d.subagentTools,
|
|
527
525
|
subagents: d.subagents,
|
|
526
|
+
// no conversation at hand: a web one's tools; a run elsewhere still refuses by spec.sources
|
|
527
|
+
source: "web",
|
|
528
528
|
});
|
|
529
529
|
const ctx = {
|
|
530
530
|
role,
|
|
@@ -789,6 +789,8 @@ let ScheduledTasksService = class ScheduledTasksService {
|
|
|
789
789
|
disabledTools: d.disabledTools ?? [],
|
|
790
790
|
subagentTools: d.subagentTools,
|
|
791
791
|
subagents: d.subagents,
|
|
792
|
+
// no conversation at hand: a web one's tools; a run elsewhere still refuses by spec.sources
|
|
793
|
+
source: "web",
|
|
792
794
|
});
|
|
793
795
|
return new Map(tools.map((x) => [
|
|
794
796
|
x.key,
|
|
@@ -820,7 +822,7 @@ let ScheduledTasksService = class ScheduledTasksService {
|
|
|
820
822
|
/**
|
|
821
823
|
* "Run now" (spec §6), creator only: a task run for the request time, outside the schedule (no
|
|
822
824
|
* unique-key conflict with a tick, and `next_run_at` does not move), fired at once through the
|
|
823
|
-
* fire job
|
|
825
|
+
* fire job its transaction owes. `trial`: an attended trial run whose approvals become the task's
|
|
824
826
|
* grants (§7). Counted in the rate limit like any run. An ended task cannot run; a paused one
|
|
825
827
|
* can (only its creator gets here: the run is `manual`, which the fire's paused check lets by).
|
|
826
828
|
*/
|
|
@@ -850,10 +852,9 @@ let ScheduledTasksService = class ScheduledTasksService {
|
|
|
850
852
|
target: task.id,
|
|
851
853
|
detail: { taskRunId: run.id, trial: opts.trial },
|
|
852
854
|
});
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
});
|
|
855
|
+
await oweSchedFire(this.outbox, run.id, orgId);
|
|
856
|
+
// a fire job that fails for good before its claim leaves the run to this check (`removed`)
|
|
857
|
+
await oweSchedSettleUnfired(this.outbox, run.id, orgId, run.scheduledFor, null);
|
|
857
858
|
return run;
|
|
858
859
|
}
|
|
859
860
|
/** The live role of a member of the org; not a member → `schedule.not_found`. */
|
|
@@ -1083,6 +1084,8 @@ let ScheduledTasksService = class ScheduledTasksService {
|
|
|
1083
1084
|
disabledTools: d.disabledTools ?? [],
|
|
1084
1085
|
subagentTools: d.subagentTools,
|
|
1085
1086
|
subagents: d.subagents,
|
|
1087
|
+
// no conversation at hand: a web one's tools; a run elsewhere still refuses by spec.sources
|
|
1088
|
+
source: "web",
|
|
1086
1089
|
});
|
|
1087
1090
|
for (const g of toolGrants) {
|
|
1088
1091
|
const tool = tools.find((x) => x.key === g.toolKey);
|
|
@@ -1168,7 +1171,7 @@ ScheduledTasksService = __decorate([
|
|
|
1168
1171
|
__param(8, Inject(ExtensionRegistry)),
|
|
1169
1172
|
__param(9, Inject(ProviderRegistry)),
|
|
1170
1173
|
__param(10, Inject(KERNEL_LOGGER)),
|
|
1171
|
-
__param(11, Inject(
|
|
1174
|
+
__param(11, Inject(OutboxService)),
|
|
1172
1175
|
__param(12, Inject(SandboxService)),
|
|
1173
1176
|
__param(13, Inject(DevicePolicyService))
|
|
1174
1177
|
], ScheduledTasksService);
|
|
@@ -11,10 +11,8 @@ import { Inject, Module } from "@nestjs/common";
|
|
|
11
11
|
import { AgentLookupModule } from "../agents/agent-lookup.js";
|
|
12
12
|
import { ConversationAccessModule } from "../conversations/access.js";
|
|
13
13
|
import { ExtensionRegistryModule } from "../extensions/registry.js";
|
|
14
|
-
import { QueueModule } from "../infra/queue/queue.module.js";
|
|
15
14
|
import { LocalDevicesModule } from "../local-devices/local-devices.module.js";
|
|
16
15
|
import { OrgLockModule } from "../orgs/org-lock.js";
|
|
17
|
-
import { MAINTENANCE_QUEUE_NAME } from "../runtime/maintenance-queue.js";
|
|
18
16
|
import { ProvidersModule } from "../runtime/providers.js";
|
|
19
17
|
import { SandboxModule } from "../sandbox/sandbox.module.js";
|
|
20
18
|
import { ScheduleToolsPort } from "../tools/builtins/schedules.js";
|
|
@@ -53,8 +51,6 @@ SchedulesModule = __decorate([
|
|
|
53
51
|
// network grants: the org's effective egress and the task device's policy summary
|
|
54
52
|
SandboxModule,
|
|
55
53
|
LocalDevicesModule,
|
|
56
|
-
// "run now" enqueues the fire job
|
|
57
|
-
QueueModule.registerQueue(MAINTENANCE_QUEUE_NAME),
|
|
58
54
|
],
|
|
59
55
|
providers: [ScheduledTasksService],
|
|
60
56
|
exports: [ScheduledTasksService, ScheduledTaskStoreModule],
|
package/dist/skills/catalog.d.ts
CHANGED
|
@@ -156,13 +156,17 @@ export declare class SkillsCatalogService {
|
|
|
156
156
|
constructor(db: KernelDb, eventLog: EventLog, log: KernelLogger, txHost: TransactionHost, config: KernelConfigService);
|
|
157
157
|
/**
|
|
158
158
|
* Records the run start's snapshot (see the class comment). `opts.log`: the run's logger
|
|
159
|
-
* (default the kernel logger).
|
|
159
|
+
* (default the kernel logger). False: the run had ended (a stop got there first), so nothing was
|
|
160
|
+
* appended and the run must not go on.
|
|
160
161
|
*/
|
|
161
162
|
record(input: EffectiveSkillsInput & {
|
|
162
163
|
runId: string;
|
|
163
164
|
}, opts?: {
|
|
164
165
|
log?: Pick<Logger, "warn" | "error">;
|
|
165
|
-
}): Promise<
|
|
166
|
-
/**
|
|
166
|
+
}): Promise<boolean>;
|
|
167
|
+
/**
|
|
168
|
+
* Under the conversation row lock, resolves the skills and appends a changed snapshot; `ended`
|
|
169
|
+
* (nothing appended) when the run is terminal under the locks.
|
|
170
|
+
*/
|
|
167
171
|
private append;
|
|
168
172
|
}
|
package/dist/skills/catalog.js
CHANGED
|
@@ -18,6 +18,7 @@ import { Transactional } from "../infra/database/transactional.decorator.js";
|
|
|
18
18
|
import { KERNEL_LOGGER } from "../infra/tokens.js";
|
|
19
19
|
import { CLAUDE_CODE_TOOL_MAP } from "../runtime/context/claude-tools.js";
|
|
20
20
|
import { catalogLine, sameSnapshot, } from "../runtime/context/skills.js";
|
|
21
|
+
import { runEndedIn } from "../runtime/run-status.js";
|
|
21
22
|
import { agentOwnSkillIds, agentSkills, builtinSkillIds, builtinSkillPlugins, } from "./usable.js";
|
|
22
23
|
/**
|
|
23
24
|
* The skills in effect for this conversation now, sorted by name (code point order), as
|
|
@@ -264,16 +265,25 @@ let SkillsCatalogService = class SkillsCatalogService {
|
|
|
264
265
|
}
|
|
265
266
|
/**
|
|
266
267
|
* Records the run start's snapshot (see the class comment). `opts.log`: the run's logger
|
|
267
|
-
* (default the kernel logger).
|
|
268
|
+
* (default the kernel logger). False: the run had ended (a stop got there first), so nothing was
|
|
269
|
+
* appended and the run must not go on.
|
|
268
270
|
*/
|
|
269
271
|
async record(input, opts = {}) {
|
|
270
272
|
const toSeq = await this.append(input, opts.log ?? this.log);
|
|
273
|
+
if (toSeq === "ended")
|
|
274
|
+
return false;
|
|
271
275
|
if (toSeq !== null)
|
|
272
276
|
await this.eventLog.notify(input.conversationId, toSeq);
|
|
277
|
+
return true;
|
|
273
278
|
}
|
|
274
|
-
/**
|
|
279
|
+
/**
|
|
280
|
+
* Under the conversation row lock, resolves the skills and appends a changed snapshot; `ended`
|
|
281
|
+
* (nothing appended) when the run is terminal under the locks.
|
|
282
|
+
*/
|
|
275
283
|
async append(input, log) {
|
|
276
284
|
const last = await lockLatestCatalog(this.db.db, input.conversationId);
|
|
285
|
+
if (await runEndedIn(this.db.db, input.runId))
|
|
286
|
+
return "ended";
|
|
277
287
|
const all = await resolveSkills(this.db.db, input, log);
|
|
278
288
|
const resolved = forModel(all, input, log);
|
|
279
289
|
const budget = this.config.get().skills.catalogBudgetChars;
|
|
@@ -40,7 +40,7 @@ export declare class InboxController {
|
|
|
40
40
|
private readonly inbox;
|
|
41
41
|
constructor(inbox: InboxService);
|
|
42
42
|
list(principal: Principal, org: OrgContext): Promise<InboxView>;
|
|
43
|
-
/** An org admin dismisses an org notice
|
|
43
|
+
/** An org admin dismisses an org notice -> 204. */
|
|
44
44
|
dismissNotice(principal: Principal, org: OrgContext, params: z.infer<typeof NoticeParamsSchema>): Promise<void>;
|
|
45
45
|
}
|
|
46
46
|
export {};
|
|
@@ -86,7 +86,7 @@ let InboxController = class InboxController {
|
|
|
86
86
|
const p = requireUser(principal);
|
|
87
87
|
return this.inbox.list(org.orgId, p.account.id);
|
|
88
88
|
}
|
|
89
|
-
/** An org admin dismisses an org notice
|
|
89
|
+
/** An org admin dismisses an org notice -> 204. */
|
|
90
90
|
async dismissNotice(principal, org, params) {
|
|
91
91
|
const p = requireUser(principal);
|
|
92
92
|
await this.inbox.dismissNotice(org.orgId, p.account.id, params.id);
|