@capybara-hq/kernel 0.1.0-rc.23 → 0.1.0-rc.25
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 +9 -3
- package/dist/admin/health.service.d.ts +2 -2
- package/dist/admin/health.service.js +6 -2
- 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/agents.module.js +3 -0
- package/dist/agents/agents.service.d.ts +6 -1
- package/dist/agents/agents.service.js +11 -2
- 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-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.d.ts +2 -1
- package/dist/catalog/catalog.module.js +9 -4
- 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/platform-import-update.d.ts +50 -0
- package/dist/catalog/platform-import-update.js +116 -0
- package/dist/catalog/plugin-import.d.ts +64 -26
- package/dist/catalog/plugin-import.js +113 -77
- package/dist/catalog/plugin-upgrade.d.ts +22 -4
- package/dist/catalog/plugin-upgrade.js +26 -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 +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 +4 -17
- package/dist/channels/jobs.d.ts +28 -18
- package/dist/channels/jobs.js +63 -26
- package/dist/channels/threads.d.ts +4 -3
- package/dist/channels/threads.js +4 -3
- 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.controller.js +4 -2
- package/dist/conversations/conversations.service.d.ts +61 -19
- package/dist/conversations/conversations.service.js +69 -43
- package/dist/conversations/event-log.d.ts +7 -10
- package/dist/conversations/event-log.js +24 -15
- package/dist/conversations/events.d.ts +21 -0
- package/dist/conversations/events.js +26 -2
- package/dist/conversations/queue.service.d.ts +8 -3
- package/dist/conversations/queue.service.js +37 -13
- package/dist/conversations/source.d.ts +10 -0
- package/dist/conversations/source.js +17 -0
- package/dist/conversations/titles.d.ts +21 -8
- package/dist/conversations/titles.js +42 -15
- package/dist/conversations/tree.d.ts +10 -0
- package/dist/conversations/tree.js +35 -0
- package/dist/db/kernel-schema.d.ts +35 -22
- package/dist/devices/devices.service.d.ts +23 -11
- package/dist/devices/devices.service.js +34 -26
- package/dist/errors.d.ts +11 -0
- package/dist/errors.js +11 -0
- package/dist/evals/run-replay.d.ts +13 -2
- package/dist/evals/run-replay.js +42 -9
- package/dist/evals/snapshot.d.ts +7 -1
- package/dist/evals/snapshot.js +2 -1
- package/dist/extensions/extensions.module.js +3 -0
- 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/lifecycle.d.ts +6 -1
- package/dist/extensions/lifecycle.js +9 -2
- 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/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/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/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 +244 -0
- package/dist/infra/outbox/registry.js +234 -0
- package/dist/infra/queue/queue-names.d.ts +8 -0
- package/dist/infra/queue/queue-names.js +17 -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/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 +22 -6
- package/dist/overrides.d.ts +23 -3
- package/dist/overrides.js +12 -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/inbox.ops.js +4 -20
- package/dist/platform/ops/outbox.ops.d.ts +21 -0
- package/dist/platform/ops/outbox.ops.js +125 -0
- package/dist/platform/ops/platform.ops.d.ts +1 -0
- package/dist/platform/ops/platform.ops.js +6 -3
- 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-role.d.ts +10 -5
- package/dist/roles/worker-role.js +19 -9
- package/dist/roles/worker.module.js +11 -8
- 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 +17 -5
- package/dist/runtime/limits.d.ts +15 -0
- package/dist/runtime/limits.js +25 -0
- package/dist/runtime/loop.d.ts +16 -3
- package/dist/runtime/loop.js +2 -1
- package/dist/runtime/maintenance.d.ts +7 -3
- package/dist/runtime/maintenance.js +52 -12
- package/dist/runtime/model-call.d.ts +20 -17
- package/dist/runtime/model-call.js +15 -24
- package/dist/runtime/plugin-context.service.d.ts +69 -0
- package/dist/runtime/plugin-context.service.js +208 -0
- 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 +47 -5
- package/dist/runtime/run-control.js +79 -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 +75 -53
- package/dist/runtime/run-queue.js +189 -263
- package/dist/runtime/run-steps.d.ts +1 -2
- package/dist/runtime/run-steps.js +8 -11
- package/dist/runtime/run-worker.d.ts +26 -16
- package/dist/runtime/run-worker.js +132 -37
- package/dist/runtime/runtime.module.d.ts +2 -3
- package/dist/runtime/runtime.module.js +7 -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 +14 -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 +8 -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/jobs.d.ts +20 -10
- package/dist/sandbox/jobs.js +23 -17
- 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 +26 -7
- package/dist/schedules/fire.js +91 -22
- package/dist/schedules/job-ids.d.ts +66 -17
- package/dist/schedules/job-ids.js +81 -13
- package/dist/schedules/run-tracker.d.ts +0 -5
- package/dist/schedules/run-tracker.js +2 -14
- package/dist/schedules/scan.job.d.ts +13 -32
- package/dist/schedules/scan.job.js +50 -164
- 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 +22 -6
- package/dist/schedules/scheduled-tasks.service.js +70 -14
- package/dist/schedules/schedules.controller.d.ts +5 -1
- package/dist/schedules/schedules.controller.js +15 -1
- package/dist/schedules/schedules.module.js +0 -4
- 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/skills/changes.controller.d.ts +1 -1
- package/dist/skills/changes.controller.js +1 -1
- package/dist/tools/approvals.service.d.ts +41 -11
- package/dist/tools/approvals.service.js +108 -14
- 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 +12 -3
- package/dist/tools/pipeline.js +6 -1
- package/dist/tools/questions.service.d.ts +30 -10
- package/dist/tools/questions.service.js +41 -27
- package/dist/tools/registry.d.ts +12 -3
- package/dist/tools/registry.js +13 -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
- 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/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
|
@@ -4,6 +4,7 @@ import { OwnedPluginCleanup } from "../catalog/owned-plugin-cleanup.js";
|
|
|
4
4
|
import { ScheduledRuns } from "../infra/schedule/scheduled-runs.js";
|
|
5
5
|
import type { KernelLogger } from "../log.js";
|
|
6
6
|
import { DirectorySync } from "../orgs/directory-sync.js";
|
|
7
|
+
import { Offboarding } from "../orgs/offboarding.js";
|
|
7
8
|
import { SandboxService } from "../sandbox/sandbox.service.js";
|
|
8
9
|
import { ScheduledTaskFirer } from "../schedules/fire.js";
|
|
9
10
|
import { ToolDrift } from "../tools/drift.js";
|
|
@@ -16,8 +17,10 @@ export declare const MAINTENANCE_CONCURRENCY = 5;
|
|
|
16
17
|
* The one consumer of `cap-kernel-maintenance` (decision R11 of the Nest kernel spec): dispatches
|
|
17
18
|
* each one-off job by name to its domain (`wait-timeout` → {@link Waits}, `sandbox-destroy-for` and
|
|
18
19
|
* `sandbox-reset-files` → {@link SandboxService}, `drift-connector` → {@link ToolDrift}, `directory-sync` →
|
|
19
|
-
* {@link DirectorySync}, `bg-wake` → {@link RunQueue.ensureRun}, `
|
|
20
|
-
* {@link
|
|
20
|
+
* {@link DirectorySync}, `run-stop` → {@link Offboarding.stopRun}, `bg-wake` and `ensure-run` → {@link RunQueue.ensureRun}, `stop-tree` →
|
|
21
|
+
* {@link processStopTree} and `deliver-to-parent` → {@link processDeliverToParent} (both in
|
|
22
|
+
* runtime/subagents/tree-jobs.ts, over {@link Waits}), `owned-plugin-cleanup` →
|
|
23
|
+
* {@link OwnedPluginCleanup}, `sched-fire` and `sched-settle` → {@link ScheduledTaskFirer}). The name of a periodic job (a `@LockedCron` / `@LockedInterval` job,
|
|
21
24
|
* such as `sandbox-suspend-idle`) runs that job's body once ({@link ScheduledRuns.runNow}), as the
|
|
22
25
|
* maintenance handlers did when those jobs were schedulers on this queue: an operator's or a
|
|
23
26
|
* test's one-off run, not a second schedule (decision R23 of the Nest kernel spec). Any other
|
|
@@ -28,13 +31,14 @@ export declare class MaintenanceProcessor extends WorkerHost<Worker> {
|
|
|
28
31
|
private readonly sandbox;
|
|
29
32
|
private readonly drift;
|
|
30
33
|
private readonly directory;
|
|
34
|
+
private readonly offboarding;
|
|
31
35
|
private readonly runs;
|
|
32
36
|
private readonly ownedCleanup;
|
|
33
37
|
private readonly scheduledTasks;
|
|
34
38
|
private readonly scheduled;
|
|
35
39
|
private readonly log;
|
|
36
40
|
private readonly queue;
|
|
37
|
-
constructor(waits: Pick<Waits, "processTimeout">, sandbox: Pick<SandboxService, "destroyDevice" | "resetFileSystem">, drift: Pick<ToolDrift, "check">, directory: Pick<DirectorySync, "runSyncJob">, runs: Pick<RunQueue, "ensureRun">, ownedCleanup: Pick<OwnedPluginCleanup, "run">, scheduledTasks: Pick<ScheduledTaskFirer, "fire">, scheduled: Pick<ScheduledRuns, "runNow">, log: Pick<KernelLogger, "error" | "warn">, queue: Pick<Queue, "removeJobScheduler">);
|
|
41
|
+
constructor(waits: Pick<Waits, "processTimeout" | "childFinished" | "stopChildren" | "stopDescendantsOfStopped" | "stopTree">, sandbox: Pick<SandboxService, "destroyDevice" | "resetFileSystem">, drift: Pick<ToolDrift, "check">, directory: Pick<DirectorySync, "runSyncJob">, offboarding: Pick<Offboarding, "stopRun">, runs: Pick<RunQueue, "ensureRun">, ownedCleanup: Pick<OwnedPluginCleanup, "run">, scheduledTasks: Pick<ScheduledTaskFirer, "fire" | "settle">, scheduled: Pick<ScheduledRuns, "runNow">, log: Pick<KernelLogger, "error" | "warn">, queue: Pick<Queue, "removeJobScheduler">);
|
|
38
42
|
/**
|
|
39
43
|
* Removes the retired job schedulers (`RETIRED_SCHEDULER_IDS`, runtime/retired-schedulers.ts): every fixed-period
|
|
40
44
|
* maintenance job is a `@LockedCron` / `@LockedInterval` now. Idempotent; called once when a
|
|
@@ -20,19 +20,21 @@ import { KernelScheduleModule } from "../infra/schedule/schedule.module.js";
|
|
|
20
20
|
import { ScheduledRuns } from "../infra/schedule/scheduled-runs.js";
|
|
21
21
|
import { KERNEL_LOGGER } from "../infra/tokens.js";
|
|
22
22
|
import { DIRECTORY_SYNC_JOB, DirectorySync } from "../orgs/directory-sync.js";
|
|
23
|
+
import { Offboarding, RUN_STOP_JOB } from "../orgs/offboarding.js";
|
|
23
24
|
import { OrgsModule } from "../orgs/orgs.module.js";
|
|
24
25
|
import { runSandboxDestroyFor, runSandboxResetFiles, SANDBOX_DESTROY_FOR_JOB, SANDBOX_RESET_FILES_JOB, } from "../sandbox/jobs.js";
|
|
25
26
|
import { SandboxModule } from "../sandbox/sandbox.module.js";
|
|
26
27
|
import { SandboxService } from "../sandbox/sandbox.service.js";
|
|
27
28
|
import { ScheduledTaskFireModule, ScheduledTaskFirer } from "../schedules/fire.js";
|
|
28
|
-
import { SCHED_FIRE_JOB } from "../schedules/job-ids.js";
|
|
29
|
+
import { SCHED_FIRE_JOB, SCHED_SETTLE_JOB, } from "../schedules/job-ids.js";
|
|
29
30
|
import { ToolDrift } from "../tools/drift.js";
|
|
30
31
|
import { DRIFT_CONNECTOR_JOB } from "../tools/job-ids.js";
|
|
31
32
|
import { ToolsModule } from "../tools/tools.module.js";
|
|
32
33
|
import { MAINTENANCE_QUEUE_NAME } from "./maintenance-queue.js";
|
|
33
34
|
import { RETIRED_SCHEDULER_IDS } from "./retired-schedulers.js";
|
|
34
|
-
import { RunQueue } from "./run-queue.js";
|
|
35
|
+
import { ENSURE_RUN_JOB, RunQueue } from "./run-queue.js";
|
|
35
36
|
import { RuntimeModule } from "./runtime.module.js";
|
|
37
|
+
import { DELIVER_TO_PARENT_JOB, processDeliverToParent, processStopTree, STOP_TREE_JOB, } from "./subagents/tree-jobs.js";
|
|
36
38
|
import { WAIT_TIMEOUT_JOB, Waits } from "./waits.js";
|
|
37
39
|
export { MAINTENANCE_QUEUE_NAME } from "./maintenance-queue.js";
|
|
38
40
|
/** The maintenance worker's concurrency (as the hand-made worker had). */
|
|
@@ -41,8 +43,10 @@ export const MAINTENANCE_CONCURRENCY = 5;
|
|
|
41
43
|
* The one consumer of `cap-kernel-maintenance` (decision R11 of the Nest kernel spec): dispatches
|
|
42
44
|
* each one-off job by name to its domain (`wait-timeout` → {@link Waits}, `sandbox-destroy-for` and
|
|
43
45
|
* `sandbox-reset-files` → {@link SandboxService}, `drift-connector` → {@link ToolDrift}, `directory-sync` →
|
|
44
|
-
* {@link DirectorySync}, `bg-wake` → {@link RunQueue.ensureRun}, `
|
|
45
|
-
* {@link
|
|
46
|
+
* {@link DirectorySync}, `run-stop` → {@link Offboarding.stopRun}, `bg-wake` and `ensure-run` → {@link RunQueue.ensureRun}, `stop-tree` →
|
|
47
|
+
* {@link processStopTree} and `deliver-to-parent` → {@link processDeliverToParent} (both in
|
|
48
|
+
* runtime/subagents/tree-jobs.ts, over {@link Waits}), `owned-plugin-cleanup` →
|
|
49
|
+
* {@link OwnedPluginCleanup}, `sched-fire` and `sched-settle` → {@link ScheduledTaskFirer}). The name of a periodic job (a `@LockedCron` / `@LockedInterval` job,
|
|
46
50
|
* such as `sandbox-suspend-idle`) runs that job's body once ({@link ScheduledRuns.runNow}), as the
|
|
47
51
|
* maintenance handlers did when those jobs were schedulers on this queue: an operator's or a
|
|
48
52
|
* test's one-off run, not a second schedule (decision R23 of the Nest kernel spec). Any other
|
|
@@ -53,18 +57,20 @@ let MaintenanceProcessor = class MaintenanceProcessor extends WorkerHost {
|
|
|
53
57
|
sandbox;
|
|
54
58
|
drift;
|
|
55
59
|
directory;
|
|
60
|
+
offboarding;
|
|
56
61
|
runs;
|
|
57
62
|
ownedCleanup;
|
|
58
63
|
scheduledTasks;
|
|
59
64
|
scheduled;
|
|
60
65
|
log;
|
|
61
66
|
queue;
|
|
62
|
-
constructor(waits, sandbox, drift, directory, runs, ownedCleanup, scheduledTasks, scheduled, log, queue) {
|
|
67
|
+
constructor(waits, sandbox, drift, directory, offboarding, runs, ownedCleanup, scheduledTasks, scheduled, log, queue) {
|
|
63
68
|
super();
|
|
64
69
|
this.waits = waits;
|
|
65
70
|
this.sandbox = sandbox;
|
|
66
71
|
this.drift = drift;
|
|
67
72
|
this.directory = directory;
|
|
73
|
+
this.offboarding = offboarding;
|
|
68
74
|
this.runs = runs;
|
|
69
75
|
this.ownedCleanup = ownedCleanup;
|
|
70
76
|
this.scheduledTasks = scheduledTasks;
|
|
@@ -106,6 +112,23 @@ let MaintenanceProcessor = class MaintenanceProcessor extends WorkerHost {
|
|
|
106
112
|
case DIRECTORY_SYNC_JOB:
|
|
107
113
|
await this.directory.runSyncJob(job.data);
|
|
108
114
|
return;
|
|
115
|
+
// a departed member's run stop, owed by the offboarding (transactional outbox spec §8.1)
|
|
116
|
+
case RUN_STOP_JOB:
|
|
117
|
+
try {
|
|
118
|
+
await this.offboarding.stopRun(job.data);
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
// nothing stops the run after its last attempt: say so (the failed job is kept)
|
|
122
|
+
if (job.attemptsMade + 1 >= (job.opts.attempts ?? 1)) {
|
|
123
|
+
this.log.error("a departed member's run could not be stopped", {
|
|
124
|
+
jobId: job.id,
|
|
125
|
+
runId: job.data.runId,
|
|
126
|
+
err,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
throw err;
|
|
130
|
+
}
|
|
131
|
+
return;
|
|
109
132
|
// the end of a conversation's wake window (background Bash spec §3)
|
|
110
133
|
case BG_WAKE_JOB: {
|
|
111
134
|
// fired before the window ended (clock skew): it reschedules under its next retry id
|
|
@@ -113,6 +136,18 @@ let MaintenanceProcessor = class MaintenanceProcessor extends WorkerHost {
|
|
|
113
136
|
await this.runs.ensureRun(conversationId, { wakeRetry: (retry ?? 0) + 1 });
|
|
114
137
|
return;
|
|
115
138
|
}
|
|
139
|
+
// the ensure-run backstop of a message, wake or run end (transactional outbox spec §6)
|
|
140
|
+
case ENSURE_RUN_JOB:
|
|
141
|
+
await this.runs.ensureRun(job.data.conversationId);
|
|
142
|
+
return;
|
|
143
|
+
// the stops a run's end or a root's delete owes its sub-agent tree (outbox spec §6)
|
|
144
|
+
case STOP_TREE_JOB:
|
|
145
|
+
await processStopTree(this.waits, job.data);
|
|
146
|
+
return;
|
|
147
|
+
// a finished sub-agent run's delivery to its parent (transactional outbox spec §6)
|
|
148
|
+
case DELIVER_TO_PARENT_JOB:
|
|
149
|
+
await processDeliverToParent(this.waits, job.data);
|
|
150
|
+
return;
|
|
116
151
|
// a removed org-owned plugin's extension (plugin scope phase 3, P3-20)
|
|
117
152
|
case OWNED_PLUGIN_CLEANUP_JOB:
|
|
118
153
|
await this.ownedCleanup.run(job.data.itemId);
|
|
@@ -121,6 +156,10 @@ let MaintenanceProcessor = class MaintenanceProcessor extends WorkerHost {
|
|
|
121
156
|
case SCHED_FIRE_JOB:
|
|
122
157
|
await this.scheduledTasks.fire(job.data.taskRunId);
|
|
123
158
|
return;
|
|
159
|
+
// a task run's settle check, claimed or unfired (ruling R26)
|
|
160
|
+
case SCHED_SETTLE_JOB:
|
|
161
|
+
await this.scheduledTasks.settle(job.data);
|
|
162
|
+
return;
|
|
124
163
|
default:
|
|
125
164
|
if (await this.scheduled.runNow(job.name))
|
|
126
165
|
return;
|
|
@@ -146,12 +185,13 @@ MaintenanceProcessor = __decorate([
|
|
|
146
185
|
__param(1, Inject(SandboxService)),
|
|
147
186
|
__param(2, Inject(ToolDrift)),
|
|
148
187
|
__param(3, Inject(DirectorySync)),
|
|
149
|
-
__param(4, Inject(
|
|
150
|
-
__param(5, Inject(
|
|
151
|
-
__param(6, Inject(
|
|
152
|
-
__param(7, Inject(
|
|
153
|
-
__param(8, Inject(
|
|
154
|
-
__param(9, Inject(
|
|
188
|
+
__param(4, Inject(Offboarding)),
|
|
189
|
+
__param(5, Inject(RunQueue)),
|
|
190
|
+
__param(6, Inject(OwnedPluginCleanup)),
|
|
191
|
+
__param(7, Inject(ScheduledTaskFirer)),
|
|
192
|
+
__param(8, Inject(ScheduledRuns)),
|
|
193
|
+
__param(9, Inject(KERNEL_LOGGER)),
|
|
194
|
+
__param(10, Inject(getQueueToken(MAINTENANCE_QUEUE_NAME)))
|
|
155
195
|
], MaintenanceProcessor);
|
|
156
196
|
export { MaintenanceProcessor };
|
|
157
197
|
/**
|
|
@@ -171,7 +211,7 @@ MaintenanceModule = __decorate([
|
|
|
171
211
|
SandboxModule,
|
|
172
212
|
ToolsModule,
|
|
173
213
|
OrgsModule,
|
|
174
|
-
// the removed org plugins' cleanup
|
|
214
|
+
// the removed org plugins' cleanup job (worker-only through this module)
|
|
175
215
|
OwnedPluginCleanupModule,
|
|
176
216
|
// the scheduled task fire job and the scanner (worker-only through this module)
|
|
177
217
|
ScheduledTaskFireModule,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ContentBlock, type Logger, type ModelRequest, type ModelStreamEvent, type ModelUsage, type StopReason } from "@capybara-hq/plugin-sdk";
|
|
1
|
+
import { type ContentBlock, type ConversationSource, type Logger, type ModelRequest, type ModelStreamEvent, type ModelUsage, type StopReason } from "@capybara-hq/plugin-sdk";
|
|
2
2
|
import type { AgentDefinition } from "../agents/definition.js";
|
|
3
3
|
import type { AppendEvent, EventLog } from "../conversations/event-log.js";
|
|
4
4
|
import { type ConversationEvent } from "../conversations/events.js";
|
|
@@ -11,7 +11,6 @@ import type { SubagentParent } from "./loop.js";
|
|
|
11
11
|
import { MediaCache } from "./media-cache.js";
|
|
12
12
|
import { type BoundProvider, type ProviderRegistry } from "./providers.js";
|
|
13
13
|
import type { StepLog } from "./step-log.js";
|
|
14
|
-
import type { UsageReporter } from "./usage-report.js";
|
|
15
14
|
/** 单轮收尾的尾部提醒(模型可见,英文;spec 6 轮次额度收尾)。 */
|
|
16
15
|
export declare const WRAP_UP_REMINDER = "Step limit reached. Do not call tools. Summarize what you found and what remains for the user.";
|
|
17
16
|
/**
|
|
@@ -48,8 +47,11 @@ export type StreamSink = (delta: {
|
|
|
48
47
|
export interface ModelStepDeps {
|
|
49
48
|
db: KernelSchemaDb;
|
|
50
49
|
eventLog: EventLog;
|
|
51
|
-
/**
|
|
52
|
-
|
|
50
|
+
/**
|
|
51
|
+
* The step's transactional writes (a model call with its tool specs, a denial with its audit row,
|
|
52
|
+
* usage events with their owed `usage.report` deliveries, a sub-agent's counted usage).
|
|
53
|
+
*/
|
|
54
|
+
stepLog: Pick<StepLog, "appendCounted" | "appendReported" | "appendAudited" | "appendWithSpecs">;
|
|
53
55
|
registry: Pick<ProviderRegistry, "resolve">;
|
|
54
56
|
/** 工具注册表:每步开始时解析 agent 引用的工具(状态可能在步骤之间变化)。 */
|
|
55
57
|
tools: Pick<ToolRegistry, "forAgent">;
|
|
@@ -67,11 +69,6 @@ export interface ModelStepDeps {
|
|
|
67
69
|
* `conversationKind: "chat"` on the step input.
|
|
68
70
|
*/
|
|
69
71
|
hooks?: HookRunner;
|
|
70
|
-
/**
|
|
71
|
-
* `usage.report` delivery (P-17): each written `usage` event is enqueued (chat runs and full-run eval
|
|
72
|
-
* replays, which both log usage). `enqueue` never throws and returns at once without handlers.
|
|
73
|
-
*/
|
|
74
|
-
usageReport?: Pick<UsageReporter, "enqueue">;
|
|
75
72
|
}
|
|
76
73
|
/** 构建请求用到的附件来源。 */
|
|
77
74
|
export interface MediaSourceDeps {
|
|
@@ -107,6 +104,11 @@ export interface ModelStepInput {
|
|
|
107
104
|
/** Hook payload scope: the run's agent, its trigger account and the conversation kind. */
|
|
108
105
|
agentId?: string;
|
|
109
106
|
accountId?: string;
|
|
107
|
+
/**
|
|
108
|
+
* The conversation's source (memory plugin spec §6.2): extension tools limited by
|
|
109
|
+
* `spec.sources` are listed by it.
|
|
110
|
+
*/
|
|
111
|
+
source: ConversationSource;
|
|
110
112
|
/** Hooks fire for `chat` and `subagent` (never for eval replays). */
|
|
111
113
|
conversationKind?: ConversationKindForHooks;
|
|
112
114
|
/** A sub-agent's run: where it was dispatched from (hook payloads carry it as `parent`). */
|
|
@@ -121,7 +123,8 @@ export interface ModelStepInput {
|
|
|
121
123
|
parentRunId?: string;
|
|
122
124
|
/**
|
|
123
125
|
* The run's step limit (`LoopContext.limits.maxSteps`) for the turn-limit reminder; a child's
|
|
124
|
-
* may be below `runtime.maxSteps` (final review m4).
|
|
126
|
+
* may be below `runtime.maxSteps` (final review m4). Absent: the run has no step limit, and no
|
|
127
|
+
* reminder is sent.
|
|
125
128
|
*/
|
|
126
129
|
maxSteps?: number;
|
|
127
130
|
}
|
|
@@ -176,15 +179,15 @@ export declare function runModelStep(deps: ModelStepDeps, input: ModelStepInput)
|
|
|
176
179
|
export declare function runFinalWrapUp(deps: ModelStepDeps, input: ModelStepInput, reason?: WrapUpReason): Promise<ModelStepResult>;
|
|
177
180
|
/**
|
|
178
181
|
* Appends a model call's events (its reply or compaction outcome with its `usage`; also the usage
|
|
179
|
-
* of a `WebFetch` extraction call, `tools/builtins/web/extract.ts`)
|
|
180
|
-
*
|
|
181
|
-
*
|
|
182
|
-
*
|
|
183
|
-
* exactly once.
|
|
182
|
+
* of a `WebFetch` extraction call, `tools/builtins/web/extract.ts`) in one transaction that also
|
|
183
|
+
* owes the `usage.report` delivery of the usage events it newly wrote (transactional outbox spec
|
|
184
|
+
* §8.1). For a sub-agent's run (`input.parentRunId`) that transaction also adds their tokens to the
|
|
185
|
+
* budget run's `subagent_tokens_used` (sub-agents spec §8.3): a usage event already in the log (an
|
|
186
|
+
* idempotent hit after a crash) adds nothing, so each call counts and is reported exactly once.
|
|
184
187
|
*/
|
|
185
188
|
export declare function appendCounted(deps: {
|
|
186
|
-
eventLog: Pick<EventLog, "
|
|
187
|
-
stepLog: Pick<StepLog, "appendCounted">;
|
|
189
|
+
eventLog: Pick<EventLog, "notify">;
|
|
190
|
+
stepLog: Pick<StepLog, "appendCounted" | "appendReported">;
|
|
188
191
|
}, input: {
|
|
189
192
|
conversationId: string;
|
|
190
193
|
parentRunId?: string | undefined;
|
|
@@ -123,12 +123,6 @@ wrapUpReason) {
|
|
|
123
123
|
const { conversationId, runId, step, signal, sink } = input;
|
|
124
124
|
const existing = await findExisting(deps.eventLog, conversationId, runId, step);
|
|
125
125
|
if (existing) {
|
|
126
|
-
// resumed after a crash between writing usage and enqueueing it: enqueue again (job id dedupes)
|
|
127
|
-
if (deps.usageReport && existing.usage !== null) {
|
|
128
|
-
const u = await deps.eventLog.findByIdem(conversationId, idem.usage(runId, step));
|
|
129
|
-
if (u)
|
|
130
|
-
void deps.usageReport.enqueue(conversationId, u.seq);
|
|
131
|
-
}
|
|
132
126
|
// a crash between the assistant_message and clearing its draft leaves one behind
|
|
133
127
|
sink({ kind: "step_end", runId, step });
|
|
134
128
|
return existing;
|
|
@@ -165,6 +159,7 @@ wrapUpReason) {
|
|
|
165
159
|
forkChild: input.parent?.forSkill === true,
|
|
166
160
|
sandboxConversationId: input.parent?.rootConversationId ?? input.conversationId,
|
|
167
161
|
replay: input.conversationKind === "eval",
|
|
162
|
+
source: input.source,
|
|
168
163
|
skillLoad: skillLoadOffered(events.findLast((e) => e.type === "skills_catalog"), hasInvocation(events)),
|
|
169
164
|
});
|
|
170
165
|
const tools = {
|
|
@@ -230,8 +225,9 @@ wrapUpReason) {
|
|
|
230
225
|
}
|
|
231
226
|
}
|
|
232
227
|
const { outcome, ref } = await ladder(deps, input, events, runtime, tools, wrapUpReason, streamed);
|
|
233
|
-
// assistant_message
|
|
234
|
-
|
|
228
|
+
// assistant_message and usage in one append = one transaction, which also owes the usage's
|
|
229
|
+
// usage.report delivery (transactional outbox spec §8.1)
|
|
230
|
+
await appendCounted(deps, input, [
|
|
235
231
|
{
|
|
236
232
|
type: "assistant_message",
|
|
237
233
|
runId,
|
|
@@ -245,10 +241,6 @@ wrapUpReason) {
|
|
|
245
241
|
payload: { step, provider: ref.provider, model: ref.model, ...outcome.usage },
|
|
246
242
|
},
|
|
247
243
|
]);
|
|
248
|
-
// usage.report metering (never awaited on the run's path; enqueue logs and swallows failures)
|
|
249
|
-
const usageEvent = appended.find((e) => e.type === "usage");
|
|
250
|
-
if (deps.usageReport && usageEvent)
|
|
251
|
-
void deps.usageReport.enqueue(conversationId, usageEvent.seq);
|
|
252
244
|
sink({ kind: "step_end", runId, step });
|
|
253
245
|
return {
|
|
254
246
|
content: outcome.content,
|
|
@@ -277,18 +269,18 @@ wrapUpReason) {
|
|
|
277
269
|
}
|
|
278
270
|
/**
|
|
279
271
|
* Appends a model call's events (its reply or compaction outcome with its `usage`; also the usage
|
|
280
|
-
* of a `WebFetch` extraction call, `tools/builtins/web/extract.ts`)
|
|
281
|
-
*
|
|
282
|
-
*
|
|
283
|
-
*
|
|
284
|
-
* exactly once.
|
|
272
|
+
* of a `WebFetch` extraction call, `tools/builtins/web/extract.ts`) in one transaction that also
|
|
273
|
+
* owes the `usage.report` delivery of the usage events it newly wrote (transactional outbox spec
|
|
274
|
+
* §8.1). For a sub-agent's run (`input.parentRunId`) that transaction also adds their tokens to the
|
|
275
|
+
* budget run's `subagent_tokens_used` (sub-agents spec §8.3): a usage event already in the log (an
|
|
276
|
+
* idempotent hit after a crash) adds nothing, so each call counts and is reported exactly once.
|
|
285
277
|
*/
|
|
286
278
|
export async function appendCounted(deps, input, events) {
|
|
287
279
|
const { conversationId, parentRunId } = input;
|
|
288
|
-
if (parentRunId === undefined)
|
|
289
|
-
return deps.eventLog.append(conversationId, events);
|
|
290
280
|
const budgetRunId = input.budgetRunId ?? input.parent?.budgetRunId;
|
|
291
|
-
const out =
|
|
281
|
+
const out = parentRunId === undefined
|
|
282
|
+
? await deps.stepLog.appendReported(conversationId, events)
|
|
283
|
+
: await deps.stepLog.appendCounted(conversationId, { parentRunId, ...(budgetRunId !== undefined && { budgetRunId }) }, events);
|
|
292
284
|
if (out.toSeq !== null)
|
|
293
285
|
await deps.eventLog.notify(conversationId, out.toSeq);
|
|
294
286
|
return out.written;
|
|
@@ -691,9 +683,6 @@ async function maybeCompact(deps, input, events, runtime, tools) {
|
|
|
691
683
|
});
|
|
692
684
|
}
|
|
693
685
|
const written = await appendCounted(deps, input, batch);
|
|
694
|
-
const usageEvent = written.find((e) => e.type === "usage");
|
|
695
|
-
if (deps.usageReport && usageEvent)
|
|
696
|
-
void deps.usageReport.enqueue(conversationId, usageEvent.seq);
|
|
697
686
|
return mergeLocal(events, written);
|
|
698
687
|
}
|
|
699
688
|
/** Merges written events into the local list (idempotent hits already in it are not added twice). */
|
|
@@ -719,7 +708,9 @@ async function ladder(deps, input, events, runtime, tools, wrapUpReason, streame
|
|
|
719
708
|
? BUDGET_WRAP_UP_REMINDER
|
|
720
709
|
: wrapUp
|
|
721
710
|
? WRAP_UP_REMINDER
|
|
722
|
-
:
|
|
711
|
+
: input.maxSteps === undefined
|
|
712
|
+
? null
|
|
713
|
+
: turnLimitReminder(step, input.maxSteps);
|
|
723
714
|
let evs = events;
|
|
724
715
|
const rungs = [
|
|
725
716
|
{ ref: agent.model, fallback: false },
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { type ConversationSource } from "@capybara-hq/plugin-sdk";
|
|
2
|
+
import { EventLog } from "../conversations/event-log.js";
|
|
3
|
+
import { ExtensionPointDefaults } from "../extensions/point-defaults.js";
|
|
4
|
+
import { ExtensionRegistry } from "../extensions/registry.js";
|
|
5
|
+
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
6
|
+
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
7
|
+
import type { KernelLogger } from "../log.js";
|
|
8
|
+
import type { OrgRole } from "../orgs/roles.js";
|
|
9
|
+
/** What a run start tells {@link PluginContextService.record}. */
|
|
10
|
+
export interface PluginContextInput {
|
|
11
|
+
orgId: string;
|
|
12
|
+
conversationId: string;
|
|
13
|
+
runId: string;
|
|
14
|
+
agentId: string;
|
|
15
|
+
/** The run's agent definition name. */
|
|
16
|
+
agentName: string;
|
|
17
|
+
/** The conversation's owner (`conversations.account_id`) and display name. */
|
|
18
|
+
ownerAccountId: string;
|
|
19
|
+
ownerName: string;
|
|
20
|
+
/** The account whose message started the run. */
|
|
21
|
+
triggerAccountId: string;
|
|
22
|
+
source: ConversationSource;
|
|
23
|
+
/** The trigger account's role in the org. */
|
|
24
|
+
role: OrgRole;
|
|
25
|
+
/** The conversation's kind: only a chat run asks the providers. */
|
|
26
|
+
kind: "chat" | "eval" | "subagent";
|
|
27
|
+
/**
|
|
28
|
+
* The keys of the run's resolved tool set, by plugin (resolved with `forAgent` once, and only
|
|
29
|
+
* when some provider is asked).
|
|
30
|
+
*/
|
|
31
|
+
toolKeysByPlugin: () => Promise<Map<string, string[]>>;
|
|
32
|
+
/** Aborts every provider call (with its own time limit). */
|
|
33
|
+
signal: AbortSignal;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Run start's plugin context (memory plugin spec §6.1): asks every `context@1` provider enabled in
|
|
37
|
+
* the org (valid config) for its text, in parallel and outside any transaction, each under the
|
|
38
|
+
* run's signal and {@link CONTEXT_LIMITS}.timeoutMs; then appends a `plugin_context` event for each
|
|
39
|
+
* plugin whose text differs from the latest one it recorded in the conversation. Fail open: a
|
|
40
|
+
* provider that throws, times out or returns too much is skipped for this run (its earlier text
|
|
41
|
+
* stays in effect) and logged. A provider loaded here that recorded text but no longer serves the
|
|
42
|
+
* org (switched off, its stored org config or its effective config invalid) gets an empty event,
|
|
43
|
+
* so its text stops following summaries; a plugin not loaded on this replica is left alone
|
|
44
|
+
* (another replica may still load it during a rollout, and blanking it here would make its text
|
|
45
|
+
* flap), and so is one whose status could not be read (a transient read error must not flip the
|
|
46
|
+
* model's context). Chat runs only: an
|
|
47
|
+
* eval replay reads the recorded events, and a sub-agent gets no plugin context (spec §5.4).
|
|
48
|
+
*/
|
|
49
|
+
export declare class PluginContextService {
|
|
50
|
+
private readonly db;
|
|
51
|
+
private readonly eventLog;
|
|
52
|
+
readonly txHost: TransactionHost;
|
|
53
|
+
private readonly points;
|
|
54
|
+
private readonly registry;
|
|
55
|
+
private readonly log;
|
|
56
|
+
constructor(db: KernelDb, eventLog: Pick<EventLog, "appendIn" | "notify">, txHost: TransactionHost, points: Pick<ExtensionPointDefaults, "providersFor">, registry: Pick<ExtensionRegistry, "list">, log: KernelLogger);
|
|
57
|
+
/** Asks the providers and records what changed (see the class); notifies after the commit. */
|
|
58
|
+
record(input: PluginContextInput): Promise<void>;
|
|
59
|
+
/** One provider's call, bounded by the run's signal and the time limit. */
|
|
60
|
+
private ask;
|
|
61
|
+
/**
|
|
62
|
+
* One transaction under the conversation row lock (as `RunContextService`): compares each
|
|
63
|
+
* answer's hash with the plugin's latest `plugin_context` and appends the ones that differ (a
|
|
64
|
+
* plugin with no earlier event and no text writes nothing), plus an empty event for each plugin
|
|
65
|
+
* in `off` whose latest text is not empty. Idem key per run and plugin, so a retried run
|
|
66
|
+
* start finds its events or nothing changed. Returns the last seq written.
|
|
67
|
+
*/
|
|
68
|
+
private append;
|
|
69
|
+
}
|
|
@@ -0,0 +1,208 @@
|
|
|
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 { createHash } from "node:crypto";
|
|
11
|
+
import { CONTEXT_LIMITS, } from "@capybara-hq/plugin-sdk";
|
|
12
|
+
import { Inject, Injectable } from "@nestjs/common";
|
|
13
|
+
import { EventLog } from "../conversations/event-log.js";
|
|
14
|
+
import { ExtensionPointDefaults } from "../extensions/point-defaults.js";
|
|
15
|
+
import { ExtensionRegistry } from "../extensions/registry.js";
|
|
16
|
+
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
17
|
+
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
18
|
+
import { Transactional } from "../infra/database/transactional.decorator.js";
|
|
19
|
+
import { KERNEL_LOGGER } from "../infra/tokens.js";
|
|
20
|
+
import { latestPluginContexts, pluginContextIdemKey } from "./context/plugin-context.js";
|
|
21
|
+
const sha256 = (text) => createHash("sha256").update(text).digest("hex");
|
|
22
|
+
/**
|
|
23
|
+
* Run start's plugin context (memory plugin spec §6.1): asks every `context@1` provider enabled in
|
|
24
|
+
* the org (valid config) for its text, in parallel and outside any transaction, each under the
|
|
25
|
+
* run's signal and {@link CONTEXT_LIMITS}.timeoutMs; then appends a `plugin_context` event for each
|
|
26
|
+
* plugin whose text differs from the latest one it recorded in the conversation. Fail open: a
|
|
27
|
+
* provider that throws, times out or returns too much is skipped for this run (its earlier text
|
|
28
|
+
* stays in effect) and logged. A provider loaded here that recorded text but no longer serves the
|
|
29
|
+
* org (switched off, its stored org config or its effective config invalid) gets an empty event,
|
|
30
|
+
* so its text stops following summaries; a plugin not loaded on this replica is left alone
|
|
31
|
+
* (another replica may still load it during a rollout, and blanking it here would make its text
|
|
32
|
+
* flap), and so is one whose status could not be read (a transient read error must not flip the
|
|
33
|
+
* model's context). Chat runs only: an
|
|
34
|
+
* eval replay reads the recorded events, and a sub-agent gets no plugin context (spec §5.4).
|
|
35
|
+
*/
|
|
36
|
+
let PluginContextService = class PluginContextService {
|
|
37
|
+
db;
|
|
38
|
+
eventLog;
|
|
39
|
+
txHost;
|
|
40
|
+
points;
|
|
41
|
+
registry;
|
|
42
|
+
log;
|
|
43
|
+
constructor(db, eventLog, txHost, points, registry, log) {
|
|
44
|
+
this.db = db;
|
|
45
|
+
this.eventLog = eventLog;
|
|
46
|
+
this.txHost = txHost;
|
|
47
|
+
this.points = points;
|
|
48
|
+
this.registry = registry;
|
|
49
|
+
this.log = log;
|
|
50
|
+
}
|
|
51
|
+
/** Asks the providers and records what changed (see the class); notifies after the commit. */
|
|
52
|
+
async record(input) {
|
|
53
|
+
if (input.kind !== "chat")
|
|
54
|
+
return;
|
|
55
|
+
let found;
|
|
56
|
+
try {
|
|
57
|
+
found = await this.points.providersFor(input.orgId, "context");
|
|
58
|
+
}
|
|
59
|
+
catch (err) {
|
|
60
|
+
// fail open: without the org's providers nothing is asked and nothing is switched off
|
|
61
|
+
this.log.warn("context providers could not be listed; plugin context left as recorded", {
|
|
62
|
+
orgId: input.orgId,
|
|
63
|
+
conversationId: input.conversationId,
|
|
64
|
+
runId: input.runId,
|
|
65
|
+
err,
|
|
66
|
+
});
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const { providers } = found;
|
|
70
|
+
const tools = providers.length > 0 ? await input.toolKeysByPlugin() : new Map();
|
|
71
|
+
const answers = await Promise.all(providers.map(async (p) => {
|
|
72
|
+
const answer = await this.ask(p, input, tools.get(p.extension) ?? []);
|
|
73
|
+
if (!answer.ok) {
|
|
74
|
+
this.log.warn("context provider skipped for this run", {
|
|
75
|
+
orgId: input.orgId,
|
|
76
|
+
conversationId: input.conversationId,
|
|
77
|
+
runId: input.runId,
|
|
78
|
+
plugin: p.extension,
|
|
79
|
+
reason: answer.reason,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
return [p.extension, answer];
|
|
83
|
+
}));
|
|
84
|
+
const texts = new Map();
|
|
85
|
+
for (const [plugin, answer] of answers)
|
|
86
|
+
if (answer.ok)
|
|
87
|
+
texts.set(plugin, answer.text);
|
|
88
|
+
const serving = new Set(providers.map((p) => p.extension));
|
|
89
|
+
const off = new Set(this.registry
|
|
90
|
+
.list("context")
|
|
91
|
+
.map((e) => e.plugin)
|
|
92
|
+
// a provider whose status could not be read (a transient error) keeps its recorded text
|
|
93
|
+
.filter((plugin) => !serving.has(plugin) && !found.failed.has(plugin)));
|
|
94
|
+
// nothing answered and nothing to switch off: no transaction, no row lock
|
|
95
|
+
if (texts.size === 0 && off.size === 0)
|
|
96
|
+
return;
|
|
97
|
+
const seq = await this.append(input, texts, off);
|
|
98
|
+
if (seq !== null)
|
|
99
|
+
await this.eventLog.notify(input.conversationId, seq);
|
|
100
|
+
}
|
|
101
|
+
/** One provider's call, bounded by the run's signal and the time limit. */
|
|
102
|
+
async ask(p, input, tools) {
|
|
103
|
+
const signal = AbortSignal.any([input.signal, AbortSignal.timeout(CONTEXT_LIMITS.timeoutMs)]);
|
|
104
|
+
let result;
|
|
105
|
+
try {
|
|
106
|
+
result = await untilAborted(Promise.resolve().then(() => p.impl.provide({
|
|
107
|
+
orgId: input.orgId,
|
|
108
|
+
accountId: input.triggerAccountId,
|
|
109
|
+
agentId: input.agentId,
|
|
110
|
+
agentName: input.agentName,
|
|
111
|
+
conversationId: input.conversationId,
|
|
112
|
+
runId: input.runId,
|
|
113
|
+
source: input.source,
|
|
114
|
+
role: input.role,
|
|
115
|
+
ownerAccountId: input.ownerAccountId,
|
|
116
|
+
ownerName: input.ownerName,
|
|
117
|
+
tools,
|
|
118
|
+
config: p.config,
|
|
119
|
+
signal,
|
|
120
|
+
})), signal);
|
|
121
|
+
}
|
|
122
|
+
catch (err) {
|
|
123
|
+
if (signal.aborted) {
|
|
124
|
+
return { ok: false, reason: input.signal.aborted ? "run aborted" : "timed out" };
|
|
125
|
+
}
|
|
126
|
+
return { ok: false, reason: `threw: ${err instanceof Error ? err.message : String(err)}` };
|
|
127
|
+
}
|
|
128
|
+
if (result === null)
|
|
129
|
+
return { ok: true, text: "" };
|
|
130
|
+
const text = result?.text;
|
|
131
|
+
if (typeof text !== "string")
|
|
132
|
+
return { ok: false, reason: "returned no text" };
|
|
133
|
+
const bytes = Buffer.byteLength(text, "utf8");
|
|
134
|
+
if (bytes > CONTEXT_LIMITS.maxBytes) {
|
|
135
|
+
return { ok: false, reason: `text of ${bytes} bytes is over ${CONTEXT_LIMITS.maxBytes}` };
|
|
136
|
+
}
|
|
137
|
+
return { ok: true, text };
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* One transaction under the conversation row lock (as `RunContextService`): compares each
|
|
141
|
+
* answer's hash with the plugin's latest `plugin_context` and appends the ones that differ (a
|
|
142
|
+
* plugin with no earlier event and no text writes nothing), plus an empty event for each plugin
|
|
143
|
+
* in `off` whose latest text is not empty. Idem key per run and plugin, so a retried run
|
|
144
|
+
* start finds its events or nothing changed. Returns the last seq written.
|
|
145
|
+
*/
|
|
146
|
+
async append(input, texts, off) {
|
|
147
|
+
const t = this.db.db;
|
|
148
|
+
await t
|
|
149
|
+
.selectFrom("conversations")
|
|
150
|
+
.select("id")
|
|
151
|
+
.where("id", "=", input.conversationId)
|
|
152
|
+
.forUpdate()
|
|
153
|
+
.executeTakeFirst();
|
|
154
|
+
const rows = await t
|
|
155
|
+
.selectFrom("conversation_events")
|
|
156
|
+
.select(["seq", "payload"])
|
|
157
|
+
.where("conversation_id", "=", input.conversationId)
|
|
158
|
+
.where("type", "=", "plugin_context")
|
|
159
|
+
.orderBy("seq", "asc")
|
|
160
|
+
.execute();
|
|
161
|
+
const latest = latestPluginContexts(rows.map((r) => ({ type: "plugin_context", seq: Number(r.seq), payload: r.payload })), Number.POSITIVE_INFINITY);
|
|
162
|
+
const next = new Map(texts);
|
|
163
|
+
for (const [plugin, prev] of latest) {
|
|
164
|
+
if (off.has(plugin) && prev.text !== "")
|
|
165
|
+
next.set(plugin, "");
|
|
166
|
+
}
|
|
167
|
+
const changed = [...next]
|
|
168
|
+
.filter(([plugin, text]) => {
|
|
169
|
+
const prev = latest.get(plugin);
|
|
170
|
+
return prev === undefined ? text !== "" : prev.hash !== sha256(text);
|
|
171
|
+
})
|
|
172
|
+
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
|
|
173
|
+
if (changed.length === 0)
|
|
174
|
+
return null;
|
|
175
|
+
const written = await this.eventLog.appendIn(input.conversationId, changed.map(([plugin, text]) => ({
|
|
176
|
+
type: "plugin_context",
|
|
177
|
+
runId: input.runId,
|
|
178
|
+
idemKey: pluginContextIdemKey(input.runId, plugin),
|
|
179
|
+
payload: { plugin, text, hash: sha256(text) },
|
|
180
|
+
})));
|
|
181
|
+
return Math.max(...written.map((e) => e.seq));
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
__decorate([
|
|
185
|
+
Transactional()
|
|
186
|
+
], PluginContextService.prototype, "append", null);
|
|
187
|
+
PluginContextService = __decorate([
|
|
188
|
+
Injectable(),
|
|
189
|
+
__param(0, Inject(KernelDb)),
|
|
190
|
+
__param(1, Inject(EventLog)),
|
|
191
|
+
__param(2, Inject(TransactionHost)),
|
|
192
|
+
__param(3, Inject(ExtensionPointDefaults)),
|
|
193
|
+
__param(4, Inject(ExtensionRegistry)),
|
|
194
|
+
__param(5, Inject(KERNEL_LOGGER))
|
|
195
|
+
], PluginContextService);
|
|
196
|
+
export { PluginContextService };
|
|
197
|
+
/** `p`, or a rejection once `signal` aborts (a provider that ignores its signal never holds a run). */
|
|
198
|
+
function untilAborted(p, signal) {
|
|
199
|
+
return new Promise((resolve, reject) => {
|
|
200
|
+
const onAbort = () => reject(signal.reason);
|
|
201
|
+
if (signal.aborted) {
|
|
202
|
+
onAbort();
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
206
|
+
p.then(resolve, reject).finally(() => signal.removeEventListener("abort", onAbort));
|
|
207
|
+
});
|
|
208
|
+
}
|