@capybara-hq/kernel 0.1.0-rc.23 → 0.1.0-rc.24
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/bootstrap.json +25 -25
- package/dist/admin/admin.module.d.ts +2 -1
- package/dist/admin/admin.module.js +7 -3
- package/dist/admin/outbox-dead.controller.d.ts +23 -0
- package/dist/admin/outbox-dead.controller.js +62 -0
- package/dist/admin/outbox-dead.schemas.d.ts +8 -0
- package/dist/admin/outbox-dead.schemas.js +18 -0
- package/dist/admin/outbox-dead.service.d.ts +66 -0
- package/dist/admin/outbox-dead.service.js +179 -0
- package/dist/admin/overview.service.d.ts +40 -5
- package/dist/admin/overview.service.js +41 -7
- package/dist/agents/from-template.js +1 -1
- package/dist/background/finish.d.ts +5 -2
- package/dist/background/finish.js +14 -3
- package/dist/background/launch.d.ts +2 -1
- package/dist/background/launch.js +5 -2
- package/dist/background/store.d.ts +8 -0
- package/dist/background/store.js +24 -0
- package/dist/catalog/admin-catalog.controller.d.ts +21 -2
- package/dist/catalog/admin-catalog.controller.js +45 -6
- package/dist/catalog/admin-catalog.schemas.d.ts +5 -0
- package/dist/catalog/admin-catalog.schemas.js +8 -0
- package/dist/catalog/catalog.module.d.ts +2 -1
- package/dist/catalog/catalog.module.js +5 -1
- package/dist/catalog/marketplaces.service.d.ts +1 -1
- package/dist/catalog/platform-import-update.d.ts +50 -0
- package/dist/catalog/platform-import-update.js +116 -0
- package/dist/catalog/plugin-import.d.ts +36 -1
- package/dist/catalog/plugin-import.js +23 -1
- package/dist/catalog/plugin-upgrade.d.ts +18 -0
- package/dist/catalog/plugin-upgrade.js +19 -0
- package/dist/channels/channels.module.d.ts +5 -2
- package/dist/channels/channels.module.js +9 -2
- package/dist/channels/deliver-job.d.ts +25 -0
- package/dist/channels/deliver-job.js +25 -0
- package/dist/channels/inbound.d.ts +1 -1
- package/dist/channels/inbound.js +2 -15
- package/dist/channels/jobs.d.ts +28 -18
- package/dist/channels/jobs.js +63 -26
- package/dist/config/schema.d.ts +0 -3
- package/dist/config/schema.js +11 -20
- package/dist/conversations/agent-tree.d.ts +16 -0
- package/dist/conversations/agent-tree.js +36 -0
- package/dist/conversations/conversations.service.d.ts +44 -14
- package/dist/conversations/conversations.service.js +52 -38
- package/dist/conversations/event-log.d.ts +7 -10
- package/dist/conversations/event-log.js +24 -15
- package/dist/conversations/events.js +2 -1
- package/dist/conversations/queue.service.d.ts +8 -3
- package/dist/conversations/queue.service.js +37 -13
- package/dist/conversations/titles.d.ts +21 -8
- package/dist/conversations/titles.js +42 -15
- package/dist/db/kernel-schema.d.ts +26 -8
- package/dist/devices/devices.service.d.ts +12 -3
- package/dist/devices/devices.service.js +18 -3
- package/dist/errors.d.ts +11 -0
- package/dist/errors.js +11 -0
- package/dist/evals/run-replay.js +14 -8
- package/dist/extensions/extensions.module.js +3 -0
- package/dist/extensions/lifecycle.d.ts +6 -1
- package/dist/extensions/lifecycle.js +9 -2
- package/dist/extensions/usage-subscribers.d.ts +43 -0
- package/dist/extensions/usage-subscribers.js +108 -0
- package/dist/http/api.module.js +2 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/infra/cli/kernel-cli.module.js +5 -0
- package/dist/infra/events/kernel-events.d.ts +9 -35
- package/dist/infra/outbox/outbox-relay.module.d.ts +6 -0
- package/dist/infra/outbox/outbox-relay.module.js +18 -0
- package/dist/infra/outbox/outbox.dispatcher.d.ts +103 -0
- package/dist/infra/outbox/outbox.dispatcher.js +310 -0
- package/dist/infra/outbox/outbox.module.d.ts +24 -0
- package/dist/infra/outbox/outbox.module.js +50 -0
- package/dist/infra/outbox/outbox.relay.d.ts +31 -0
- package/dist/infra/outbox/outbox.relay.js +76 -0
- package/dist/infra/outbox/outbox.service.d.ts +51 -0
- package/dist/infra/outbox/outbox.service.js +131 -0
- package/dist/infra/outbox/registry.d.ts +120 -0
- package/dist/infra/outbox/registry.js +108 -0
- package/dist/infra/queue/queue-names.d.ts +8 -0
- package/dist/infra/queue/queue-names.js +16 -0
- package/dist/kernel.module.js +3 -0
- package/dist/local-devices/device-policy.service.d.ts +4 -0
- package/dist/local-devices/device-policy.service.js +61 -3
- package/dist/local-devices/machines.d.ts +13 -5
- package/dist/local-devices/machines.js +45 -7
- package/dist/local-devices/policy-summary.d.ts +44 -1
- package/dist/local-devices/policy-summary.js +69 -0
- package/dist/models/embeddings.d.ts +9 -9
- package/dist/models/embeddings.js +11 -18
- package/dist/models/models.module.js +3 -0
- package/dist/orgs/orgs.service.js +7 -0
- package/dist/overrides.d.ts +20 -3
- package/dist/overrides.js +10 -2
- package/dist/platform/exclusions.js +5 -0
- package/dist/platform/ops/admin.ops.js +18 -2
- package/dist/platform/ops/catalog-admin.ops.d.ts +3 -1
- package/dist/platform/ops/catalog-admin.ops.js +32 -4
- package/dist/platform/ops/outbox.ops.d.ts +21 -0
- package/dist/platform/ops/outbox.ops.js +125 -0
- package/dist/platform/ops/schedules.ops.js +40 -1
- package/dist/platform/platform-ops.module.d.ts +2 -1
- package/dist/platform/platform-ops.module.js +3 -0
- package/dist/platform/route-schemas.js +3 -0
- package/dist/roles/worker.module.js +9 -4
- package/dist/runtime/langgraph-loop.js +13 -5
- package/dist/runtime/limits.d.ts +15 -0
- package/dist/runtime/limits.js +25 -0
- package/dist/runtime/loop.d.ts +8 -3
- package/dist/runtime/loop.js +2 -1
- package/dist/runtime/maintenance.d.ts +4 -2
- package/dist/runtime/maintenance.js +17 -2
- package/dist/runtime/model-call.d.ts +14 -16
- package/dist/runtime/model-call.js +14 -24
- package/dist/runtime/queue-lost-jobs.d.ts +24 -0
- package/dist/runtime/queue-lost-jobs.js +51 -0
- package/dist/runtime/run-control.d.ts +42 -5
- package/dist/runtime/run-control.js +66 -12
- package/dist/runtime/run-finish.d.ts +39 -12
- package/dist/runtime/run-finish.js +97 -14
- package/dist/runtime/run-jobs.d.ts +95 -13
- package/dist/runtime/run-jobs.js +199 -28
- package/dist/runtime/run-orphan-check.job.d.ts +142 -0
- package/dist/runtime/run-orphan-check.job.js +339 -0
- package/dist/runtime/run-processors.d.ts +16 -9
- package/dist/runtime/run-processors.js +1 -1
- package/dist/runtime/run-queue.d.ts +64 -55
- package/dist/runtime/run-queue.js +147 -284
- package/dist/runtime/run-steps.d.ts +1 -2
- package/dist/runtime/run-steps.js +8 -11
- package/dist/runtime/run-worker.d.ts +6 -17
- package/dist/runtime/run-worker.js +32 -34
- package/dist/runtime/runtime.module.d.ts +2 -3
- package/dist/runtime/runtime.module.js +5 -6
- package/dist/runtime/step-log.d.ts +16 -4
- package/dist/runtime/step-log.js +26 -6
- package/dist/runtime/stream.controller.d.ts +2 -4
- package/dist/runtime/stream.controller.js +5 -11
- package/dist/runtime/subagents/child.js +1 -1
- package/dist/runtime/subagents/definition.d.ts +2 -1
- package/dist/runtime/subagents/definition.js +4 -2
- package/dist/runtime/subagents/deliveries.d.ts +6 -3
- package/dist/runtime/subagents/deliveries.js +16 -7
- package/dist/runtime/subagents/dispatch.d.ts +12 -12
- package/dist/runtime/subagents/dispatch.js +13 -38
- package/dist/runtime/subagents/messages.d.ts +5 -1
- package/dist/runtime/subagents/messages.js +11 -3
- package/dist/runtime/subagents/skill-fork.d.ts +3 -3
- package/dist/runtime/subagents/skill-fork.js +5 -5
- package/dist/runtime/subagents/subagents.service.js +1 -1
- package/dist/runtime/subagents/tree-jobs.d.ts +61 -0
- package/dist/runtime/subagents/tree-jobs.js +86 -0
- package/dist/runtime/usage-report-jobs.d.ts +23 -48
- package/dist/runtime/usage-report-jobs.js +67 -119
- package/dist/runtime/usage-report.d.ts +28 -77
- package/dist/runtime/usage-report.js +35 -213
- package/dist/runtime/waits.d.ts +48 -198
- package/dist/runtime/waits.js +103 -805
- package/dist/sandbox/sandbox.service.d.ts +17 -4
- package/dist/sandbox/sandbox.service.js +55 -14
- package/dist/schedules/calendar.d.ts +134 -0
- package/dist/schedules/calendar.js +410 -0
- package/dist/schedules/fire.d.ts +1 -3
- package/dist/schedules/fire.js +5 -10
- package/dist/schedules/run-tracker.d.ts +0 -5
- package/dist/schedules/run-tracker.js +0 -13
- package/dist/schedules/scan.job.js +15 -20
- package/dist/schedules/schedule-math.d.ts +42 -0
- package/dist/schedules/schedule-math.js +52 -0
- package/dist/schedules/scheduled-tasks.service.d.ts +16 -0
- package/dist/schedules/scheduled-tasks.service.js +53 -0
- package/dist/schedules/schedules.controller.d.ts +5 -1
- package/dist/schedules/schedules.controller.js +15 -1
- package/dist/schedules/schedules.schemas.d.ts +35 -0
- package/dist/schedules/schedules.schemas.js +27 -0
- package/dist/schedules/store.d.ts +5 -0
- package/dist/schedules/store.js +20 -0
- package/dist/tools/approvals.service.d.ts +26 -7
- package/dist/tools/approvals.service.js +88 -10
- package/dist/tools/builtin-names.d.ts +5 -3
- package/dist/tools/builtin-names.js +12 -4
- package/dist/tools/builtins/builtin-tools.d.ts +1 -2
- package/dist/tools/builtins/builtin-tools.js +21 -17
- package/dist/tools/builtins/sandbox/bash.js +9 -3
- package/dist/tools/builtins/sandbox/common.d.ts +18 -1
- package/dist/tools/builtins/sandbox/common.js +14 -1
- package/dist/tools/builtins/sandbox/index.d.ts +2 -1
- package/dist/tools/builtins/sandbox/index.js +4 -1
- package/dist/tools/builtins/sandbox/task-list.d.ts +12 -0
- package/dist/tools/builtins/sandbox/task-list.js +130 -0
- package/dist/tools/builtins/sandbox/task-output.d.ts +8 -1
- package/dist/tools/builtins/sandbox/task-output.js +21 -3
- package/dist/tools/builtins/web/extract.d.ts +2 -4
- package/dist/tools/builtins/web/extract.js +2 -5
- package/dist/tools/pipeline.d.ts +1 -1
- package/dist/tools/pipeline.js +1 -1
- package/dist/tools/questions.service.d.ts +15 -6
- package/dist/tools/questions.service.js +20 -8
- package/dist/tools/registry.d.ts +4 -2
- package/dist/tools/registry.js +4 -3
- package/dist/tools/tools.module.js +4 -3
- package/package.json +3 -3
- package/src/migrations/0118_device_policy_ops.sql +6 -0
- package/src/migrations/0119_outbox.sql +22 -0
- package/src/migrations/0120_drop_usage_report_sweep.sql +7 -0
|
@@ -104,6 +104,13 @@ export function validateTrigger(t, now) {
|
|
|
104
104
|
export function nextTick(t, after) {
|
|
105
105
|
return ticker(t)(after);
|
|
106
106
|
}
|
|
107
|
+
/**
|
|
108
|
+
* The scanner's ticker for one trigger ({@link nextTick} without rebuilding its `CronTime`s per
|
|
109
|
+
* call), for callers that walk the same trigger many times ({@link ticksIn}).
|
|
110
|
+
*/
|
|
111
|
+
export function tickerOf(t) {
|
|
112
|
+
return ticker(t);
|
|
113
|
+
}
|
|
107
114
|
/** How far back a DST transition can move a tick: longer than any gap (spec §5). */
|
|
108
115
|
const TRANSITION_LOOKBACK_MS = 6 * 3600_000;
|
|
109
116
|
/**
|
|
@@ -138,6 +145,51 @@ function ticker(t) {
|
|
|
138
145
|
return null;
|
|
139
146
|
};
|
|
140
147
|
}
|
|
148
|
+
/**
|
|
149
|
+
* The ticks in `(after, until]` (before jitter), at most `max`, from the same ticker
|
|
150
|
+
* {@link nextTick} and {@link dueTicks} use, so a list of planned runs equals what the scanner
|
|
151
|
+
* fires (calendar spec §5.2). `exhausted`: `max` ticks were taken before a tick past `until` was
|
|
152
|
+
* seen, so more may follow. A `once` trigger yields its one tick when it lies in the window.
|
|
153
|
+
* Costs one tick evaluation per tick returned, plus one when the walk reaches `until`. A caller
|
|
154
|
+
* that walks one trigger in many windows passes a {@link tickerOf} it built once.
|
|
155
|
+
*/
|
|
156
|
+
export function ticksIn(t, after, until, max) {
|
|
157
|
+
const ticks = [];
|
|
158
|
+
if (after.getTime() >= until.getTime())
|
|
159
|
+
return { ticks, exhausted: false };
|
|
160
|
+
const next = typeof t === "function" ? t : ticker(t);
|
|
161
|
+
let cursor = after;
|
|
162
|
+
while (ticks.length < max) {
|
|
163
|
+
const tick = next(cursor);
|
|
164
|
+
if (!tick || tick.getTime() > until.getTime())
|
|
165
|
+
return { ticks, exhausted: false };
|
|
166
|
+
ticks.push(tick);
|
|
167
|
+
cursor = tick;
|
|
168
|
+
}
|
|
169
|
+
return { ticks, exhausted: true };
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* One scan of a due task (`next_run_at <= now`) as of `now`, the schedule math alone: the ticks
|
|
173
|
+
* are before jitter, so the due tick is `nextRunAt - jitter` and a tick is due once
|
|
174
|
+
* `tick + jitter <= now`. The scanner applies the result; the calendar's parity test drives it.
|
|
175
|
+
* Throws when the trigger cannot be evaluated.
|
|
176
|
+
*/
|
|
177
|
+
export function scanStep(task, now, catchUpWindowMinutes) {
|
|
178
|
+
const jitterMs = task.jitterSeconds * 1000;
|
|
179
|
+
const dueTick = new Date(task.nextRunAt.getTime() - jitterMs);
|
|
180
|
+
const until = new Date(now.getTime() - jitterMs);
|
|
181
|
+
const ticks = dueTicks(task.trigger, new Date(dueTick.getTime() - 1), until, catchUpWindowMinutes);
|
|
182
|
+
const next = nextTick(task.trigger, until);
|
|
183
|
+
const expires = task.expiresAt?.getTime() ?? Number.POSITIVE_INFINITY;
|
|
184
|
+
const fire = ticks.fire !== null && ticks.fire.getTime() <= expires ? ticks.fire : null;
|
|
185
|
+
const first = ticks.firstMissed;
|
|
186
|
+
const missed = first !== null && first.getTime() <= expires ? first : null;
|
|
187
|
+
if (next === null)
|
|
188
|
+
return { missed, fire, nextRunAt: null, end: "done" };
|
|
189
|
+
if (next.getTime() > expires)
|
|
190
|
+
return { missed, fire, nextRunAt: null, end: "expired" };
|
|
191
|
+
return { missed, fire, nextRunAt: new Date(next.getTime() + jitterMs), end: null };
|
|
192
|
+
}
|
|
141
193
|
/** The wall clock of `at` in `timeZone`, read as if it were UTC (epoch ms, seconds dropped). */
|
|
142
194
|
function localWallClock(at, timeZone) {
|
|
143
195
|
return Math.floor(wallOf(at.getTime(), timeZone) / 60000) * 60000;
|
|
@@ -13,6 +13,7 @@ import { OrgLock } from "../orgs/org-lock.js";
|
|
|
13
13
|
import { ProviderRegistry } from "../runtime/providers.js";
|
|
14
14
|
import { SandboxService } from "../sandbox/sandbox.service.js";
|
|
15
15
|
import { ToolRegistry } from "../tools/registry.js";
|
|
16
|
+
import { type CalendarAnswer, type CalendarLimits } from "./calendar.js";
|
|
16
17
|
import { type GrantContext, type GrantRefusalCode, grantRefusal, grantRefusalOf, networkGrantRefusal } from "./grant-rules.js";
|
|
17
18
|
import { ScheduledTaskStore } from "./store.js";
|
|
18
19
|
import type { ScheduleChat, ScheduledTask, ScheduledTaskGrant, ScheduleMode, SchedulePauseReason, TaskRun } from "./types.js";
|
|
@@ -231,6 +232,21 @@ export declare class ScheduledTasksService {
|
|
|
231
232
|
all?: boolean;
|
|
232
233
|
conversationId?: string;
|
|
233
234
|
}): Promise<ScheduledTaskListItem[]>;
|
|
235
|
+
/**
|
|
236
|
+
* The calendar between two instants (calendar spec §5): the runs and planned occurrences of the
|
|
237
|
+
* actor's own tasks, or with `all` (org admins only, else `forbidden`) every task of the org; with
|
|
238
|
+
* `taskId` only that task (one the actor may see, else `schedule.not_found`), never collapsed.
|
|
239
|
+
* Another creator's runs come without `conversationId`, `error` and `missingGrants` (§15 Q3).
|
|
240
|
+
* A range longer than 43 days (2 with `taskId`), or not forward, is `validation_failed`. `now`
|
|
241
|
+
* is for tests.
|
|
242
|
+
*/
|
|
243
|
+
calendar(orgId: string, actor: ScheduleActor, q: {
|
|
244
|
+
from: Date;
|
|
245
|
+
to: Date;
|
|
246
|
+
timeZone: string;
|
|
247
|
+
all?: boolean;
|
|
248
|
+
taskId?: string;
|
|
249
|
+
}, now?: Date, limits?: CalendarLimits): Promise<CalendarAnswer>;
|
|
234
250
|
/**
|
|
235
251
|
* The tasks of `tasks` that post into the conversation: a `conversation` task naming it, or a
|
|
236
252
|
* `chat` task whose chat the conversation is a thread of (spec §10 side panel).
|
|
@@ -32,6 +32,7 @@ import { ProviderRegistry } from "../runtime/providers.js";
|
|
|
32
32
|
import { SandboxService } from "../sandbox/sandbox.service.js";
|
|
33
33
|
import { isTimeZone } from "../time-zone.js";
|
|
34
34
|
import { ToolRegistry } from "../tools/registry.js";
|
|
35
|
+
import { assembleCalendar, CALENDAR_LIMITS, CALENDAR_RANGE_DAYS, CALENDAR_TASK_RANGE_DAYS, calendarRunReadLimit, } from "./calendar.js";
|
|
35
36
|
import { readScheduleSettings } from "./effective-settings.js";
|
|
36
37
|
import { grantNotAllowed, scheduleDisabled, scheduleForbidden, scheduleNotFound, targetInvalid, } from "./errors.js";
|
|
37
38
|
import { grantRefusal, grantRefusalOf, isNetworkGrantKey, isPolicyGrantKey, isTierGrantKey, NETWORK_GRANT_PREFIX, NETWORK_GRANT_SPEC_HASH, networkGrantHost, networkGrantRefusal, TIER_GRANT_SPEC_HASH, tierGrantRefusal, } from "./grant-rules.js";
|
|
@@ -363,6 +364,58 @@ let ScheduledTasksService = class ScheduledTasksService {
|
|
|
363
364
|
}
|
|
364
365
|
return this.withLastRuns(tasks);
|
|
365
366
|
}
|
|
367
|
+
/**
|
|
368
|
+
* The calendar between two instants (calendar spec §5): the runs and planned occurrences of the
|
|
369
|
+
* actor's own tasks, or with `all` (org admins only, else `forbidden`) every task of the org; with
|
|
370
|
+
* `taskId` only that task (one the actor may see, else `schedule.not_found`), never collapsed.
|
|
371
|
+
* Another creator's runs come without `conversationId`, `error` and `missingGrants` (§15 Q3).
|
|
372
|
+
* A range longer than 43 days (2 with `taskId`), or not forward, is `validation_failed`. `now`
|
|
373
|
+
* is for tests.
|
|
374
|
+
*/
|
|
375
|
+
async calendar(orgId, actor, q, now = new Date(), limits = CALENDAR_LIMITS) {
|
|
376
|
+
const role = await this.liveRole(orgId, actor.accountId);
|
|
377
|
+
const manage = can(role, "schedules.manage");
|
|
378
|
+
if (q.all === true && !manage) {
|
|
379
|
+
throw new ApiError("forbidden", "Only org admins can read every scheduled task");
|
|
380
|
+
}
|
|
381
|
+
const span = q.to.getTime() - q.from.getTime();
|
|
382
|
+
const maxDays = q.taskId === undefined ? CALENDAR_RANGE_DAYS : CALENDAR_TASK_RANGE_DAYS;
|
|
383
|
+
if (!(span > 0))
|
|
384
|
+
throw new ApiError("validation_failed", "from must be before to");
|
|
385
|
+
if (span > maxDays * DAY_MS) {
|
|
386
|
+
throw new ApiError("validation_failed", `The range is at most ${maxDays} days${q.taskId === undefined ? "" : " with taskId"}`);
|
|
387
|
+
}
|
|
388
|
+
const t = this.db.db;
|
|
389
|
+
let tasks;
|
|
390
|
+
if (q.taskId !== undefined) {
|
|
391
|
+
const task = await this.store.getTask(t, orgId, q.taskId);
|
|
392
|
+
if (!task || (task.accountId !== actor.accountId && !manage))
|
|
393
|
+
throw scheduleNotFound();
|
|
394
|
+
tasks = [task];
|
|
395
|
+
}
|
|
396
|
+
else {
|
|
397
|
+
tasks =
|
|
398
|
+
q.all === true
|
|
399
|
+
? await this.store.listForOrg(t, orgId)
|
|
400
|
+
: await this.store.listForAccount(t, orgId, actor.accountId);
|
|
401
|
+
}
|
|
402
|
+
const runs = await this.store.listRunsBetween(t, orgId, tasks.map((x) => x.id), q.from, q.to,
|
|
403
|
+
// one row past the caps tells the assembly where to cut
|
|
404
|
+
calendarRunReadLimit(limits));
|
|
405
|
+
const settings = await readScheduleSettings(t, this.config, orgId);
|
|
406
|
+
return assembleCalendar({
|
|
407
|
+
tasks,
|
|
408
|
+
runs,
|
|
409
|
+
viewerAccountId: actor.accountId,
|
|
410
|
+
from: q.from,
|
|
411
|
+
to: q.to,
|
|
412
|
+
timeZone: q.timeZone,
|
|
413
|
+
now,
|
|
414
|
+
plan: settings.enabled,
|
|
415
|
+
catchUpWindowMinutes: settings.catchUpWindowMinutes,
|
|
416
|
+
collapse: q.taskId === undefined,
|
|
417
|
+
}, limits);
|
|
418
|
+
}
|
|
366
419
|
/**
|
|
367
420
|
* The tasks of `tasks` that post into the conversation: a `conversation` task naming it, or a
|
|
368
421
|
* `chat` task whose chat the conversation is a thread of (spec §10 side panel).
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { z } from "zod";
|
|
2
2
|
import { type Principal } from "../identity/principal.js";
|
|
3
3
|
import { type OrgContext } from "../orgs/org.guard.js";
|
|
4
|
+
import type { CalendarAnswer } from "./calendar.js";
|
|
4
5
|
import { type ChatTarget, type GrantableTool, type ScheduledTaskGrantView, type ScheduledTaskListItem, ScheduledTasksService } from "./scheduled-tasks.service.js";
|
|
5
|
-
import { type ScheduleChatTargetsQuery, type ScheduleCreateBody, type ScheduleGrantableQuery, type ScheduleGrantParams, type ScheduleGrantsBody, type ScheduleListQuery, type ScheduleParams, type ScheduleRunBody, type ScheduleRunsQuery, type ScheduleUpdateBody } from "./schedules.schemas.js";
|
|
6
|
+
import { type ScheduleCalendarQuery, type ScheduleChatTargetsQuery, type ScheduleCreateBody, type ScheduleGrantableQuery, type ScheduleGrantParams, type ScheduleGrantsBody, type ScheduleListQuery, type ScheduleParams, type ScheduleRunBody, type ScheduleRunsQuery, type ScheduleUpdateBody } from "./schedules.schemas.js";
|
|
6
7
|
import type { ScheduledTask, ScheduledTaskGrant, TaskRun } from "./types.js";
|
|
7
8
|
/**
|
|
8
9
|
* Scheduled tasks (`/api/v1/orgs/:orgId/schedules`, spec 2026-11-11-scheduled-tasks §10); human
|
|
@@ -10,6 +11,8 @@ import type { ScheduledTask, ScheduledTaskGrant, TaskRun } from "./types.js";
|
|
|
10
11
|
* - `GET /` → `{ items }`: the caller's own tasks, each with its `lastRun`; `?all=true` every task
|
|
11
12
|
* of the org (org admins, `schedules.manage`, enforced by the service: 403 `forbidden`
|
|
12
13
|
* otherwise); `?conversationId=` only those posting into that conversation;
|
|
14
|
+
* - `GET /calendar?from&to&timeZone[&all][&taskId]` → the calendar (runs, planned occurrences,
|
|
15
|
+
* collapsed task-days; calendar spec §5.1), `org.read` like the list, `all` as the list's;
|
|
13
16
|
* - `GET /chat-targets` → `{ items }`: the chats the caller can point a `chat` task at;
|
|
14
17
|
* - `GET /grantable-tools?agentId=` → `{ items }`: the agent's tools, each grantable or why not;
|
|
15
18
|
* - `POST /` → 201 `{ task }` (`timeZone` is the browser zone, a fallback only);
|
|
@@ -30,6 +33,7 @@ export declare class SchedulesController {
|
|
|
30
33
|
list(principal: Principal, org: OrgContext, q: z.infer<typeof ScheduleListQuery>): Promise<{
|
|
31
34
|
items: ScheduledTaskListItem[];
|
|
32
35
|
}>;
|
|
36
|
+
calendar(principal: Principal, org: OrgContext, q: z.infer<typeof ScheduleCalendarQuery>): Promise<CalendarAnswer>;
|
|
33
37
|
chatTargets(principal: Principal, org: OrgContext, _q: z.infer<typeof ScheduleChatTargetsQuery>): Promise<{
|
|
34
38
|
items: ChatTarget[];
|
|
35
39
|
}>;
|
|
@@ -12,13 +12,15 @@ import { CurrentPrincipal, requireUser } from "../identity/principal.js";
|
|
|
12
12
|
import { ZBody, ZParam, ZQuery } from "../infra/validation/zod-params.js";
|
|
13
13
|
import { CurrentOrg, OrgAction } from "../orgs/org.guard.js";
|
|
14
14
|
import { ScheduledTasksService, } from "./scheduled-tasks.service.js";
|
|
15
|
-
import { createInputOf, grantsOf, ScheduleChatTargetsQueryDto, ScheduleCreateBodyDto, ScheduleGrantableQueryDto, ScheduleGrantParamsDto, ScheduleGrantsBodyDto, ScheduleListQueryDto, ScheduleParamsDto, ScheduleRunBodyDto, ScheduleRunsQueryDto, ScheduleUpdateBodyDto, updateInputOf, } from "./schedules.schemas.js";
|
|
15
|
+
import { calendarInputOf, createInputOf, grantsOf, ScheduleCalendarQueryDto, ScheduleChatTargetsQueryDto, ScheduleCreateBodyDto, ScheduleGrantableQueryDto, ScheduleGrantParamsDto, ScheduleGrantsBodyDto, ScheduleListQueryDto, ScheduleParamsDto, ScheduleRunBodyDto, ScheduleRunsQueryDto, ScheduleUpdateBodyDto, updateInputOf, } from "./schedules.schemas.js";
|
|
16
16
|
/**
|
|
17
17
|
* Scheduled tasks (`/api/v1/orgs/:orgId/schedules`, spec 2026-11-11-scheduled-tasks §10); human
|
|
18
18
|
* users only (a service token gets 403).
|
|
19
19
|
* - `GET /` → `{ items }`: the caller's own tasks, each with its `lastRun`; `?all=true` every task
|
|
20
20
|
* of the org (org admins, `schedules.manage`, enforced by the service: 403 `forbidden`
|
|
21
21
|
* otherwise); `?conversationId=` only those posting into that conversation;
|
|
22
|
+
* - `GET /calendar?from&to&timeZone[&all][&taskId]` → the calendar (runs, planned occurrences,
|
|
23
|
+
* collapsed task-days; calendar spec §5.1), `org.read` like the list, `all` as the list's;
|
|
22
24
|
* - `GET /chat-targets` → `{ items }`: the chats the caller can point a `chat` task at;
|
|
23
25
|
* - `GET /grantable-tools?agentId=` → `{ items }`: the agent's tools, each grantable or why not;
|
|
24
26
|
* - `POST /` → 201 `{ task }` (`timeZone` is the browser zone, a fallback only);
|
|
@@ -48,6 +50,11 @@ let SchedulesController = class SchedulesController {
|
|
|
48
50
|
});
|
|
49
51
|
return { items };
|
|
50
52
|
}
|
|
53
|
+
// declared before `:id`; org.read like the list (a creator demoted to guest still reads their own)
|
|
54
|
+
async calendar(principal, org, q) {
|
|
55
|
+
const p = requireUser(principal);
|
|
56
|
+
return this.tasks.calendar(org.orgId, { accountId: p.account.id }, calendarInputOf(q));
|
|
57
|
+
}
|
|
51
58
|
async chatTargets(principal, org, _q) {
|
|
52
59
|
const p = requireUser(principal);
|
|
53
60
|
return { items: await this.tasks.chatTargets(org.orgId, p.account.id) };
|
|
@@ -134,6 +141,13 @@ __decorate([
|
|
|
134
141
|
__param(1, CurrentOrg()),
|
|
135
142
|
__param(2, ZQuery(ScheduleListQueryDto))
|
|
136
143
|
], SchedulesController.prototype, "list", null);
|
|
144
|
+
__decorate([
|
|
145
|
+
Get("calendar"),
|
|
146
|
+
OrgAction("org.read"),
|
|
147
|
+
__param(0, CurrentPrincipal()),
|
|
148
|
+
__param(1, CurrentOrg()),
|
|
149
|
+
__param(2, ZQuery(ScheduleCalendarQueryDto))
|
|
150
|
+
], SchedulesController.prototype, "calendar", null);
|
|
137
151
|
__decorate([
|
|
138
152
|
Get("chat-targets"),
|
|
139
153
|
OrgAction("schedules.use"),
|
|
@@ -138,6 +138,21 @@ export declare const ScheduleListQuery: z.ZodObject<{
|
|
|
138
138
|
}>>;
|
|
139
139
|
conversationId: z.ZodOptional<z.ZodString>;
|
|
140
140
|
}, z.core.$strict>;
|
|
141
|
+
/**
|
|
142
|
+
* The calendar's range (`from < to`, at most 43 days, 2 with `taskId`: checked by the service),
|
|
143
|
+
* the viewer's zone (local days for the collapse and the cut), `all` (org admins) and `taskId`
|
|
144
|
+
* (one task, never collapsed).
|
|
145
|
+
*/
|
|
146
|
+
export declare const ScheduleCalendarQuery: z.ZodObject<{
|
|
147
|
+
from: z.ZodString;
|
|
148
|
+
to: z.ZodString;
|
|
149
|
+
timeZone: z.ZodString;
|
|
150
|
+
all: z.ZodOptional<z.ZodEnum<{
|
|
151
|
+
false: "false";
|
|
152
|
+
true: "true";
|
|
153
|
+
}>>;
|
|
154
|
+
taskId: z.ZodOptional<z.ZodString>;
|
|
155
|
+
}, z.core.$strict>;
|
|
141
156
|
export declare const ScheduleChatTargetsQuery: z.ZodObject<{}, z.core.$strict>;
|
|
142
157
|
export declare const ScheduleGrantableQuery: z.ZodObject<{
|
|
143
158
|
agentId: z.ZodString;
|
|
@@ -258,6 +273,18 @@ declare const ScheduleListQueryDto_base: import("../infra/validation/create-zod-
|
|
|
258
273
|
}, z.core.$strict>>;
|
|
259
274
|
export declare class ScheduleListQueryDto extends ScheduleListQueryDto_base {
|
|
260
275
|
}
|
|
276
|
+
declare const ScheduleCalendarQueryDto_base: import("../infra/validation/create-zod-dto.js").ZodDtoClass<z.ZodObject<{
|
|
277
|
+
from: z.ZodString;
|
|
278
|
+
to: z.ZodString;
|
|
279
|
+
timeZone: z.ZodString;
|
|
280
|
+
all: z.ZodOptional<z.ZodEnum<{
|
|
281
|
+
false: "false";
|
|
282
|
+
true: "true";
|
|
283
|
+
}>>;
|
|
284
|
+
taskId: z.ZodOptional<z.ZodString>;
|
|
285
|
+
}, z.core.$strict>>;
|
|
286
|
+
export declare class ScheduleCalendarQueryDto extends ScheduleCalendarQueryDto_base {
|
|
287
|
+
}
|
|
261
288
|
declare const ScheduleChatTargetsQueryDto_base: import("../infra/validation/create-zod-dto.js").ZodDtoClass<z.ZodObject<{}, z.core.$strict>>;
|
|
262
289
|
export declare class ScheduleChatTargetsQueryDto extends ScheduleChatTargetsQueryDto_base {
|
|
263
290
|
}
|
|
@@ -313,4 +340,12 @@ export declare function createInputOf(body: z.infer<typeof ScheduleCreateBody>):
|
|
|
313
340
|
};
|
|
314
341
|
/** A patch body as the service's input (an explicit `timeZone` changes the task's zone). */
|
|
315
342
|
export declare function updateInputOf(body: z.infer<typeof ScheduleUpdateBody>): UpdateTaskInput;
|
|
343
|
+
/** A calendar query as the service's input. */
|
|
344
|
+
export declare const calendarInputOf: (q: z.infer<typeof ScheduleCalendarQuery>) => {
|
|
345
|
+
from: Date;
|
|
346
|
+
to: Date;
|
|
347
|
+
timeZone: string;
|
|
348
|
+
all: boolean;
|
|
349
|
+
taskId?: string;
|
|
350
|
+
};
|
|
316
351
|
export {};
|
|
@@ -86,6 +86,22 @@ export const ScheduleListQuery = z
|
|
|
86
86
|
conversationId: Id.optional(),
|
|
87
87
|
})
|
|
88
88
|
.strict();
|
|
89
|
+
/** An instant with an offset (`2026-11-09T00:00:00+08:00`). */
|
|
90
|
+
const Instant = z.string().datetime({ offset: true });
|
|
91
|
+
/**
|
|
92
|
+
* The calendar's range (`from < to`, at most 43 days, 2 with `taskId`: checked by the service),
|
|
93
|
+
* the viewer's zone (local days for the collapse and the cut), `all` (org admins) and `taskId`
|
|
94
|
+
* (one task, never collapsed).
|
|
95
|
+
*/
|
|
96
|
+
export const ScheduleCalendarQuery = z
|
|
97
|
+
.object({
|
|
98
|
+
from: Instant,
|
|
99
|
+
to: Instant,
|
|
100
|
+
timeZone: IanaTimeZone,
|
|
101
|
+
all: z.enum(["true", "false"]).optional(),
|
|
102
|
+
taskId: Id.optional(),
|
|
103
|
+
})
|
|
104
|
+
.strict();
|
|
89
105
|
export const ScheduleChatTargetsQuery = z.object({}).strict();
|
|
90
106
|
export const ScheduleGrantableQuery = z.object({ agentId: Id }).strict();
|
|
91
107
|
export const ScheduleParams = z.object({ id: Id });
|
|
@@ -103,6 +119,8 @@ export class ScheduleUpdateBodyDto extends createZodDto(ScheduleUpdateBody, "Sch
|
|
|
103
119
|
}
|
|
104
120
|
export class ScheduleListQueryDto extends createZodDto(ScheduleListQuery, "ScheduleListQuery") {
|
|
105
121
|
}
|
|
122
|
+
export class ScheduleCalendarQueryDto extends createZodDto(ScheduleCalendarQuery, "ScheduleCalendarQuery") {
|
|
123
|
+
}
|
|
106
124
|
export class ScheduleChatTargetsQueryDto extends createZodDto(ScheduleChatTargetsQuery, "ScheduleChatTargetsQuery") {
|
|
107
125
|
}
|
|
108
126
|
export class ScheduleGrantableQueryDto extends createZodDto(ScheduleGrantableQuery, "ScheduleGrantableQuery") {
|
|
@@ -120,6 +138,7 @@ export class ScheduleGrantParamsDto extends createZodDto(ScheduleGrantParams, "S
|
|
|
120
138
|
/** The schedule routes the Platform operations mirror (`check:platform-ops` keys). */
|
|
121
139
|
export const SCHEDULE_ROUTES = [
|
|
122
140
|
{ route: "GET /api/v1/orgs/:orgId/schedules", query: ScheduleListQuery },
|
|
141
|
+
{ route: "GET /api/v1/orgs/:orgId/schedules/calendar", query: ScheduleCalendarQuery },
|
|
123
142
|
{ route: "GET /api/v1/orgs/:orgId/schedules/chat-targets", query: ScheduleChatTargetsQuery },
|
|
124
143
|
{ route: "GET /api/v1/orgs/:orgId/schedules/grantable-tools", query: ScheduleGrantableQuery },
|
|
125
144
|
{ route: "POST /api/v1/orgs/:orgId/schedules", body: ScheduleCreateBody },
|
|
@@ -184,3 +203,11 @@ export function updateInputOf(body) {
|
|
|
184
203
|
...(expiresAt !== undefined && { expiresAt: expiresAt === null ? null : new Date(expiresAt) }),
|
|
185
204
|
};
|
|
186
205
|
}
|
|
206
|
+
/** A calendar query as the service's input. */
|
|
207
|
+
export const calendarInputOf = (q) => ({
|
|
208
|
+
from: new Date(q.from),
|
|
209
|
+
to: new Date(q.to),
|
|
210
|
+
timeZone: q.timeZone,
|
|
211
|
+
all: q.all === "true",
|
|
212
|
+
...(q.taskId !== undefined && { taskId: q.taskId }),
|
|
213
|
+
});
|
|
@@ -68,6 +68,11 @@ export declare class ScheduledTaskStore {
|
|
|
68
68
|
countFiredSince(t: KernelSchemaDb, orgId: string, since: Date): Promise<number>;
|
|
69
69
|
/** A task's runs, latest tick first. */
|
|
70
70
|
listRuns(t: KernelSchemaDb, taskId: string, limit: number): Promise<TaskRun[]>;
|
|
71
|
+
/**
|
|
72
|
+
* The runs of these tasks of the org with `scheduled_for` in `[from, to)`, trial included, by tick, at most
|
|
73
|
+
* `limit` (the calendar; `scheduled_task_runs_task_idx` per task).
|
|
74
|
+
*/
|
|
75
|
+
listRunsBetween(t: KernelSchemaDb, orgId: string, taskIds: readonly string[], from: Date, to: Date, limit: number): Promise<TaskRun[]>;
|
|
71
76
|
listGrants(t: KernelSchemaDb, taskId: string): Promise<ScheduledTaskGrant[]>;
|
|
72
77
|
/** Adds a grant, replacing the task's grant of the same tool key. */
|
|
73
78
|
addGrant(t: KernelSchemaDb, taskId: string, grant: ScheduledTaskGrant): Promise<void>;
|
package/dist/schedules/store.js
CHANGED
|
@@ -329,6 +329,26 @@ let ScheduledTaskStore = class ScheduledTaskStore {
|
|
|
329
329
|
.execute();
|
|
330
330
|
return rows.map(toRun);
|
|
331
331
|
}
|
|
332
|
+
/**
|
|
333
|
+
* The runs of these tasks of the org with `scheduled_for` in `[from, to)`, trial included, by tick, at most
|
|
334
|
+
* `limit` (the calendar; `scheduled_task_runs_task_idx` per task).
|
|
335
|
+
*/
|
|
336
|
+
async listRunsBetween(t, orgId, taskIds, from, to, limit) {
|
|
337
|
+
if (taskIds.length === 0)
|
|
338
|
+
return [];
|
|
339
|
+
const rows = await t
|
|
340
|
+
.selectFrom("scheduled_task_runs")
|
|
341
|
+
.selectAll()
|
|
342
|
+
.where("org_id", "=", orgId)
|
|
343
|
+
.where("task_id", "in", [...taskIds])
|
|
344
|
+
.where("scheduled_for", ">=", from)
|
|
345
|
+
.where("scheduled_for", "<", to)
|
|
346
|
+
.orderBy("scheduled_for")
|
|
347
|
+
.orderBy("id")
|
|
348
|
+
.limit(limit)
|
|
349
|
+
.execute();
|
|
350
|
+
return rows.map(toRun);
|
|
351
|
+
}
|
|
332
352
|
async listGrants(t, taskId) {
|
|
333
353
|
const rows = await t
|
|
334
354
|
.selectFrom("scheduled_task_grants")
|
|
@@ -8,6 +8,7 @@ import { KernelEvents } from "../infra/events/kernel-events.js";
|
|
|
8
8
|
import { LockService } from "../infra/lock/lock.service.js";
|
|
9
9
|
import { DevicePolicyService } from "../local-devices/device-policy.service.js";
|
|
10
10
|
import { type OrgRole } from "../orgs/roles.js";
|
|
11
|
+
import { RunResume } from "../runtime/run-jobs.js";
|
|
11
12
|
import { SandboxService } from "../sandbox/sandbox.service.js";
|
|
12
13
|
import { ScheduledTaskStore } from "../schedules/store.js";
|
|
13
14
|
import type { ApprovalPolicySource } from "./pipeline.js";
|
|
@@ -206,10 +207,12 @@ export interface ToolGrantView {
|
|
|
206
207
|
* 工具调用审批:新建(loop 调用)、列出 / 查看 / 处理(HTTP),以及「以后都允许」授权的查看与撤销。
|
|
207
208
|
* 状态全部在 PG:处理决定在事务里 `SELECT … FOR UPDATE` 审批行,多副本并发处理同一审批时恰好一个生效。
|
|
208
209
|
*
|
|
209
|
-
* A decision
|
|
210
|
-
*
|
|
211
|
-
*
|
|
212
|
-
*
|
|
210
|
+
* A decision resumes its waiting run in the decision's transaction (`RunResume.resumeIn`, after the
|
|
211
|
+
* decided row: lock order approval, then run; transactional outbox spec §6). It is announced after
|
|
212
|
+
* the commit as the domain event `approval.decided` (exactly once: only the decision that changed
|
|
213
|
+
* the row from pending; no kernel subscriber: the IM card update is owed by the `approval_decided`
|
|
214
|
+
* append, transactional outbox spec §8.1). Events are appended in the context's transaction
|
|
215
|
+
* (`EventLog.appendIn`) and published after the commit.
|
|
213
216
|
*/
|
|
214
217
|
export declare class ApprovalsService {
|
|
215
218
|
private readonly db;
|
|
@@ -226,6 +229,8 @@ export declare class ApprovalsService {
|
|
|
226
229
|
readonly lockService: LockService;
|
|
227
230
|
/** A trial run's 「这次允许」 becomes a task grant only within the org's egress. */
|
|
228
231
|
private readonly sandbox;
|
|
232
|
+
/** A decision resumes its waiting run in the decision's transaction. */
|
|
233
|
+
private readonly resumes;
|
|
229
234
|
constructor(db: KernelDb, config: Pick<KernelConfigService, "get">, eventLog: Pick<EventLog, "appendIn" | "notify">, events: Pick<KernelEvents, "emit">, txHost: TransactionHost, schedules: Pick<ScheduledTaskStore, "addGrant">,
|
|
230
235
|
/** A trial approval is kept only for a tool the task may hold as a grant (its agent's tools). */
|
|
231
236
|
registry: Pick<ToolRegistry, "forAgent">,
|
|
@@ -234,7 +239,9 @@ export declare class ApprovalsService {
|
|
|
234
239
|
/** One decision of a network ask at a time, across replicas (the op is sent before the transaction). */
|
|
235
240
|
lockService: LockService,
|
|
236
241
|
/** A trial run's 「这次允许」 becomes a task grant only within the org's egress. */
|
|
237
|
-
sandbox: Pick<SandboxService, "ceilingLimits"
|
|
242
|
+
sandbox: Pick<SandboxService, "ceilingLimits">,
|
|
243
|
+
/** A decision resumes its waiting run in the decision's transaction. */
|
|
244
|
+
resumes: Pick<RunResume, "resumeIn">);
|
|
238
245
|
/**
|
|
239
246
|
* 新建 pending 审批(`expires_at` = 现在 + `tools.approvalTimeoutHours`)并写 `approval_requested`,同一事务。
|
|
240
247
|
* 按 (run_id, call_id, attempt) 幂等:已存在则返回已有记录(不覆盖),事件按 `approval_requested:<approvalId>` 幂等。
|
|
@@ -346,13 +353,25 @@ export declare class ApprovalsService {
|
|
|
346
353
|
* active runs `authorAccountId` started, in the conversation and in its sub-agents'
|
|
347
354
|
* conversations at any depth (their asks are shown in the root). Each becomes `skipped` with
|
|
348
355
|
* `approval_decided{skipped}` by the author; access stays blocked. Returns the skipped records and
|
|
349
|
-
* the highest seq written per conversation; the caller
|
|
350
|
-
*
|
|
356
|
+
* the highest seq written per conversation; the caller resumes their waiting runs in this
|
|
357
|
+
* transaction once every skipped row is locked (`ConversationsService.skipWaitsIn`), and notifies
|
|
358
|
+
* and emits the domain event `approval.decided{skipped}` after the commit (its IM card update is
|
|
359
|
+
* owed by the append, transactional outbox spec §8.1).
|
|
351
360
|
*/
|
|
352
361
|
skipNetworkIn(conversationId: string, authorAccountId: string): Promise<{
|
|
353
362
|
records: ApprovalRecord[];
|
|
354
363
|
seqs: Map<string, number>;
|
|
355
364
|
}>;
|
|
365
|
+
/**
|
|
366
|
+
* Locks the conversation row of approval `id` (`SELECT … FOR UPDATE`; re-entrant with the lock
|
|
367
|
+
* `EventLog.appendIn` takes later). Every write to an approval row takes its conversation's lock
|
|
368
|
+
* first, then the approval row, then the run row, as the stop of a waiting run
|
|
369
|
+
* (`Waits.abortWaitingIn`) and the questions do, so no two of them can deadlock. False: no such
|
|
370
|
+
* approval (in `orgId`).
|
|
371
|
+
*/
|
|
372
|
+
private lockConversationOf;
|
|
373
|
+
/** Locks, in id order, the conversations `ids` selects; returns their ids. */
|
|
374
|
+
private lockConversations;
|
|
356
375
|
/** `conversationId` and its sub-agents' conversations at any depth (at most 16 levels). */
|
|
357
376
|
private subtreeOf;
|
|
358
377
|
/**
|