@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
|
@@ -7,8 +7,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
8
8
|
return function (target, key) { decorator(target, key, paramIndex); }
|
|
9
9
|
};
|
|
10
|
-
import {
|
|
11
|
-
import { Inject, Injectable } from "@nestjs/common";
|
|
10
|
+
import { Inject, Injectable, Optional } from "@nestjs/common";
|
|
12
11
|
import { ApiError } from "../errors.js";
|
|
13
12
|
import { ExtensionLifecycle } from "../extensions/lifecycle.js";
|
|
14
13
|
import { ExtensionRegistry } from "../extensions/registry.js";
|
|
@@ -17,12 +16,13 @@ import { KernelDb } from "../infra/database/kernel-db.js";
|
|
|
17
16
|
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
18
17
|
import { Transactional } from "../infra/database/transactional.decorator.js";
|
|
19
18
|
import { LockService } from "../infra/lock/lock.service.js";
|
|
20
|
-
import {
|
|
19
|
+
import { OutboxService } from "../infra/outbox/outbox.service.js";
|
|
21
20
|
import { KERNEL_LOGGER } from "../infra/tokens.js";
|
|
22
21
|
import { connectorTemplatesOn, templateIdOfPath } from "./component-state.js";
|
|
23
22
|
import { isUsable } from "./ownership.js";
|
|
24
23
|
import { PluginInstaller } from "./plugin-install.js";
|
|
25
24
|
import { wantedOn } from "./scope.js";
|
|
25
|
+
import { oweScopeItem, PLUGIN_SCOPE_QUEUE, SCOPE_BACKSTOP_DELAY, SCOPE_BACKSTOP_DELAY_MS, SCOPE_ITEM_JOB, SCOPE_ORG_JOB, scopeOrgJobId, scopeRetryJobId, } from "./scope-jobs.js";
|
|
26
26
|
import { ScopeNoLongerWanted, ScopeStillWanted } from "./scope-state.js";
|
|
27
27
|
/**
|
|
28
28
|
* The plugin scope sync (plugin and component management spec §5, phase 1): converges every org
|
|
@@ -46,30 +46,18 @@ import { ScopeNoLongerWanted, ScopeStillWanted } from "./scope-state.js";
|
|
|
46
46
|
* super admin's inbox, until a later sync of the pair does not fail) and not retried until the next
|
|
47
47
|
* enqueue. An error outside those steps (a read, the failure write itself) ends the attempt without
|
|
48
48
|
* touching the pair's row, so an older failure stays open.
|
|
49
|
-
* Every worker start enqueues every live item once ({@link PluginScopeSync.enqueueAll}), which
|
|
50
|
-
* also catches a lost enqueue.
|
|
51
49
|
*
|
|
52
50
|
* Jobs (queue {@link PLUGIN_SCOPE_QUEUE}): one per item and scope version
|
|
53
51
|
* (`scope-item-<itemId>-<scope_version>`, every org), one per org (`scope-org-<orgId>`, every item,
|
|
54
|
-
*
|
|
52
|
+
* owed by the org's creation). Both are owed in the outbox by the transaction that changes what
|
|
53
|
+
* they converge to (transactional outbox spec §8.1), so none is lost; a migration that changes a
|
|
54
|
+
* scope inserts its outbox rows itself. A confirmation (import, permissions, org upload) owes the
|
|
55
|
+
* immediate item job after its deploy and, in its commit, a backstop `scope-item-backstop` due
|
|
56
|
+
* five minutes later (ruling R24) that waits, retrying, for the extension to be enabled. Both are removed when done, so the next add with the same
|
|
55
57
|
* id is not swallowed. Pairs of one org are serialized across replicas by a Redis lock per org; the
|
|
56
58
|
* installer's org row lock and its one-live-install rule are the backstop.
|
|
57
59
|
*/
|
|
58
|
-
|
|
59
|
-
export const PLUGIN_SCOPE_QUEUE = "cap-kernel-plugin-scope";
|
|
60
|
-
/** The job converging one item in every org. */
|
|
61
|
-
export const SCOPE_ITEM_JOB = "scope-item";
|
|
62
|
-
/** The job converging every item in one org. */
|
|
63
|
-
export const SCOPE_ORG_JOB = "scope-org";
|
|
64
|
-
/** One item job per scope version (no `:`: item ids are UUIDs). */
|
|
65
|
-
export const scopeItemJobId = (itemId, version) => `scope-item-${itemId}-${version}`;
|
|
66
|
-
/** One org job at a time per org. */
|
|
67
|
-
export const scopeOrgJobId = (orgId) => `scope-org-${orgId}`;
|
|
68
|
-
/**
|
|
69
|
-
* The delayed retry `n` of an item job whose extension this replica had not loaded yet (ruling
|
|
70
|
-
* P3-38); deterministic, so replicas deferring the same item add it once.
|
|
71
|
-
*/
|
|
72
|
-
export const scopeRetryJobId = (itemId, version, n) => `scope-item-${itemId}-${version}-r${n}`;
|
|
60
|
+
export { PLUGIN_SCOPE_QUEUE, SCOPE_BACKSTOP_DELAY, SCOPE_BACKSTOP_DELAY_MS, SCOPE_BACKSTOP_JOB, SCOPE_ITEM_JOB, SCOPE_ORG_JOB, scopeBackstopJobId, scopeItemJobId, scopeOrgJobId, scopeRetryJobId, } from "./scope-jobs.js";
|
|
73
61
|
/** How many delayed retries an item job gets while its extension is not loaded here (P3-38). */
|
|
74
62
|
export const SCOPE_LOAD_RETRIES = 6;
|
|
75
63
|
/** The delay of each such retry. */
|
|
@@ -98,19 +86,6 @@ export async function reconcileWithin(lifecycle, reason, ms, log) {
|
|
|
98
86
|
clearTimeout(timer);
|
|
99
87
|
}
|
|
100
88
|
}
|
|
101
|
-
/** The item job of a scope version (what {@link PluginScopeSync.enqueueItem} adds). */
|
|
102
|
-
export function scopeItemJob(itemId, version) {
|
|
103
|
-
return {
|
|
104
|
-
name: SCOPE_ITEM_JOB,
|
|
105
|
-
data: { kind: "item", itemId },
|
|
106
|
-
opts: {
|
|
107
|
-
jobId: scopeItemJobId(itemId, version),
|
|
108
|
-
attempts: 1,
|
|
109
|
-
removeOnComplete: true,
|
|
110
|
-
removeOnFail: true,
|
|
111
|
-
},
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
89
|
/** How long one org's pairs wait for each other (an install can take a while). */
|
|
115
90
|
const ORG_LOCK_TTL_MS = 60_000;
|
|
116
91
|
const ORG_LOCK_WAIT_MS = 10 * 60_000;
|
|
@@ -119,62 +94,67 @@ export const PLUGIN_SCOPE_CONCURRENCY = 2;
|
|
|
119
94
|
const LIVE = ["installing", "installed", "upgrading"];
|
|
120
95
|
let PluginScopeSync = class PluginScopeSync {
|
|
121
96
|
kdb;
|
|
122
|
-
|
|
97
|
+
outbox;
|
|
123
98
|
installer;
|
|
124
99
|
registry;
|
|
125
100
|
lifecycle;
|
|
126
101
|
locks;
|
|
127
102
|
log;
|
|
128
103
|
txHost;
|
|
129
|
-
|
|
104
|
+
backstopDelayMs;
|
|
105
|
+
constructor(kdb, outbox, installer, registry,
|
|
130
106
|
/** This replica's reconcile, awaited (bounded) before deferring an install (P3-38). */
|
|
131
|
-
lifecycle, locks, log, txHost
|
|
107
|
+
lifecycle, locks, log, txHost,
|
|
108
|
+
/** The backstop's delay (a test seam; default {@link SCOPE_BACKSTOP_DELAY_MS}). */
|
|
109
|
+
backstopDelayMs = SCOPE_BACKSTOP_DELAY_MS) {
|
|
132
110
|
this.kdb = kdb;
|
|
133
|
-
this.
|
|
111
|
+
this.outbox = outbox;
|
|
134
112
|
this.installer = installer;
|
|
135
113
|
this.registry = registry;
|
|
136
114
|
this.lifecycle = lifecycle;
|
|
137
115
|
this.locks = locks;
|
|
138
116
|
this.log = log;
|
|
139
117
|
this.txHost = txHost;
|
|
118
|
+
this.backstopDelayMs = backstopDelayMs;
|
|
140
119
|
}
|
|
141
120
|
get db() {
|
|
142
121
|
return this.kdb.db;
|
|
143
122
|
}
|
|
144
|
-
/**
|
|
123
|
+
/**
|
|
124
|
+
* Owes the sync of an item in every org, once per scope version (the version read in the
|
|
125
|
+
* caller's transaction, so after its own bump), in the transaction that changes the scope.
|
|
126
|
+
*/
|
|
145
127
|
async enqueueItem(itemId) {
|
|
146
|
-
|
|
147
|
-
.selectFrom("catalog_items")
|
|
148
|
-
.select("scope_version")
|
|
149
|
-
.where("id", "=", itemId)
|
|
150
|
-
.executeTakeFirst();
|
|
151
|
-
if (!row)
|
|
152
|
-
return;
|
|
153
|
-
const job = scopeItemJob(itemId, row.scope_version);
|
|
154
|
-
await this.queue.add(job.name, job.data, job.opts);
|
|
128
|
+
await oweScopeItem(this.outbox, this.db, itemId);
|
|
155
129
|
}
|
|
156
130
|
/**
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
*
|
|
131
|
+
* Owes the delayed backstop of the item's sync (ruling R24) in the transaction in context: a
|
|
132
|
+
* confirmation owes it before (or with) its deploy, so a crash before its post-deploy add still
|
|
133
|
+
* converges the orgs.
|
|
160
134
|
*/
|
|
161
|
-
async
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
.
|
|
165
|
-
|
|
166
|
-
.where("builtin", "=", false)
|
|
167
|
-
.orderBy("id")
|
|
168
|
-
.execute();
|
|
169
|
-
for (const i of items)
|
|
170
|
-
await this.enqueueItem(i.id);
|
|
135
|
+
async enqueueBackstop(itemId) {
|
|
136
|
+
await oweScopeItem(this.outbox, this.db, itemId, {
|
|
137
|
+
backstop: true,
|
|
138
|
+
backstopDelayMs: this.backstopDelayMs,
|
|
139
|
+
});
|
|
171
140
|
}
|
|
172
|
-
/**
|
|
141
|
+
/** Owes the sync of every item in an org, in the transaction that creates the org. */
|
|
173
142
|
async enqueueOrg(orgId) {
|
|
174
|
-
await this.
|
|
143
|
+
await this.outbox.add({
|
|
144
|
+
queue: PLUGIN_SCOPE_QUEUE,
|
|
145
|
+
name: SCOPE_ORG_JOB,
|
|
146
|
+
jobId: scopeOrgJobId(orgId),
|
|
147
|
+
data: { kind: "org", orgId },
|
|
148
|
+
orgId,
|
|
149
|
+
});
|
|
175
150
|
}
|
|
176
|
-
/**
|
|
151
|
+
/**
|
|
152
|
+
* One job: every org of an item, or every item of an org; `signal` stops between pairs. A
|
|
153
|
+
* backstop job first checks the item's extension is deployed ({@link assertDeployed}).
|
|
154
|
+
*/
|
|
177
155
|
async process(data, signal) {
|
|
156
|
+
if (data.kind === "item" && data.backstop === true)
|
|
157
|
+
await this.assertDeployed(data.itemId);
|
|
178
158
|
const pairs = data.kind === "item"
|
|
179
159
|
? (await this.db
|
|
180
160
|
.selectFrom("orgs")
|
|
@@ -214,7 +194,55 @@ let PluginScopeSync = class PluginScopeSync {
|
|
|
214
194
|
}
|
|
215
195
|
}
|
|
216
196
|
}
|
|
217
|
-
/**
|
|
197
|
+
/**
|
|
198
|
+
* A backstop job (ruling R24) runs only once the item's extension is loaded here or enabled on
|
|
199
|
+
* the deployment; otherwise it throws, so BullMQ retries it with its backoff (bounded by its
|
|
200
|
+
* attempts) and no pair records a `catalog.not_deployed` failure that nothing would retry. An item without an
|
|
201
|
+
* extension, or one no longer live (deleted, disabled, still waiting, builtin), passes: its pairs
|
|
202
|
+
* converge (or change nothing) as for an immediate job.
|
|
203
|
+
*/
|
|
204
|
+
async assertDeployed(itemId) {
|
|
205
|
+
const item = await this.db
|
|
206
|
+
.selectFrom("catalog_items")
|
|
207
|
+
.select([
|
|
208
|
+
"builtin",
|
|
209
|
+
"disabled",
|
|
210
|
+
"deleted_at",
|
|
211
|
+
"awaiting_confirmation",
|
|
212
|
+
"latest_version",
|
|
213
|
+
"platform_synced_version",
|
|
214
|
+
])
|
|
215
|
+
.where("id", "=", itemId)
|
|
216
|
+
.executeTakeFirst();
|
|
217
|
+
if (!item || item.builtin || !isUsable(item))
|
|
218
|
+
return;
|
|
219
|
+
const version = item.platform_synced_version ?? item.latest_version;
|
|
220
|
+
const v = await this.db
|
|
221
|
+
.selectFrom("catalog_item_versions")
|
|
222
|
+
.select("manifest")
|
|
223
|
+
.where("item_id", "=", itemId)
|
|
224
|
+
.where("version", "=", version)
|
|
225
|
+
.executeTakeFirst();
|
|
226
|
+
const m = v?.manifest;
|
|
227
|
+
if (!m?.extension)
|
|
228
|
+
return;
|
|
229
|
+
// loaded here (a config-declared extension has no install record), or enabled on the
|
|
230
|
+
// deployment (a replica that has not loaded it yet defers as P3-38 says)
|
|
231
|
+
if (this.registry.manifests().some((x) => x.name === m.name))
|
|
232
|
+
return;
|
|
233
|
+
const install = await this.db
|
|
234
|
+
.selectFrom("extension_installs")
|
|
235
|
+
.select("desired_state")
|
|
236
|
+
.where("name", "=", m.name)
|
|
237
|
+
.executeTakeFirst();
|
|
238
|
+
if (install?.desired_state === "enabled")
|
|
239
|
+
return;
|
|
240
|
+
throw new Error(`the extension ${m.name} of catalog item ${itemId} is not enabled on the deployment yet; the scope sync backstop is retried`);
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Owes the delayed retry `n` of an item job (deterministic id, P3-38) in a transaction of its
|
|
244
|
+
* own: the job's own follow-up, delivered by the relay once due.
|
|
245
|
+
*/
|
|
218
246
|
async enqueueRetry(itemId, n) {
|
|
219
247
|
const row = await this.db
|
|
220
248
|
.selectFrom("catalog_items")
|
|
@@ -223,12 +251,12 @@ let PluginScopeSync = class PluginScopeSync {
|
|
|
223
251
|
.executeTakeFirst();
|
|
224
252
|
if (!row)
|
|
225
253
|
return;
|
|
226
|
-
await this.
|
|
254
|
+
await this.outbox.add({
|
|
255
|
+
queue: PLUGIN_SCOPE_QUEUE,
|
|
256
|
+
name: SCOPE_ITEM_JOB,
|
|
227
257
|
jobId: scopeRetryJobId(itemId, row.scope_version, n),
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
removeOnComplete: true,
|
|
231
|
-
removeOnFail: true,
|
|
258
|
+
data: { kind: "item", itemId, retry: n },
|
|
259
|
+
runAt: new Date(Date.now() + SCOPE_LOAD_RETRY_DELAY_MS),
|
|
232
260
|
});
|
|
233
261
|
}
|
|
234
262
|
/**
|
|
@@ -509,68 +537,32 @@ let PluginScopeSync = class PluginScopeSync {
|
|
|
509
537
|
.execute();
|
|
510
538
|
}
|
|
511
539
|
};
|
|
540
|
+
__decorate([
|
|
541
|
+
Transactional({ propagation: "mandatory" })
|
|
542
|
+
], PluginScopeSync.prototype, "enqueueItem", null);
|
|
543
|
+
__decorate([
|
|
544
|
+
Transactional({ propagation: "mandatory" })
|
|
545
|
+
], PluginScopeSync.prototype, "enqueueBackstop", null);
|
|
546
|
+
__decorate([
|
|
547
|
+
Transactional({ propagation: "mandatory" })
|
|
548
|
+
], PluginScopeSync.prototype, "enqueueOrg", null);
|
|
549
|
+
__decorate([
|
|
550
|
+
Transactional()
|
|
551
|
+
], PluginScopeSync.prototype, "enqueueRetry", null);
|
|
512
552
|
__decorate([
|
|
513
553
|
Transactional({ propagation: "requires_new" })
|
|
514
554
|
], PluginScopeSync.prototype, "recordFailure", null);
|
|
515
555
|
PluginScopeSync = __decorate([
|
|
516
556
|
Injectable(),
|
|
517
557
|
__param(0, Inject(KernelDb)),
|
|
518
|
-
__param(1, Inject(
|
|
558
|
+
__param(1, Inject(OutboxService)),
|
|
519
559
|
__param(2, Inject(PluginInstaller)),
|
|
520
560
|
__param(3, Inject(ExtensionRegistry)),
|
|
521
561
|
__param(4, Inject(ExtensionLifecycle)),
|
|
522
562
|
__param(5, Inject(LockService)),
|
|
523
563
|
__param(6, Inject(KERNEL_LOGGER)),
|
|
524
|
-
__param(7, Inject(TransactionHost))
|
|
564
|
+
__param(7, Inject(TransactionHost)),
|
|
565
|
+
__param(8, Optional()),
|
|
566
|
+
__param(8, Inject(SCOPE_BACKSTOP_DELAY))
|
|
525
567
|
], PluginScopeSync);
|
|
526
568
|
export { PluginScopeSync };
|
|
527
|
-
/** The consumer of {@link PLUGIN_SCOPE_QUEUE}: {@link PluginScopeSync}. */
|
|
528
|
-
let PluginScopeSyncProcessor = class PluginScopeSyncProcessor extends WorkerHost {
|
|
529
|
-
sync;
|
|
530
|
-
log;
|
|
531
|
-
stopping = new AbortController();
|
|
532
|
-
constructor(sync, log) {
|
|
533
|
-
super();
|
|
534
|
-
this.sync = sync;
|
|
535
|
-
this.log = log;
|
|
536
|
-
}
|
|
537
|
-
process(job) {
|
|
538
|
-
return runJob(PLUGIN_SCOPE_QUEUE, job, () => this.sync.process(job.data, this.stopping.signal));
|
|
539
|
-
}
|
|
540
|
-
/** Starts consuming (worker role, after the extensions loaded; built with `autorun: false`). */
|
|
541
|
-
start() {
|
|
542
|
-
void this.worker.run().catch((err) => this.error(err));
|
|
543
|
-
}
|
|
544
|
-
/**
|
|
545
|
-
* Enqueues every live item's sync once (the worker role, after the start gate opened), in the
|
|
546
|
-
* background; a failure is only logged (the next worker start, or the next change, enqueues).
|
|
547
|
-
*/
|
|
548
|
-
resync() {
|
|
549
|
-
void this.sync
|
|
550
|
-
.enqueueAll()
|
|
551
|
-
.catch((err) => this.log.error("plugin scope resync failed", { err }));
|
|
552
|
-
}
|
|
553
|
-
/** Stops between pairs and closes the worker. */
|
|
554
|
-
async close() {
|
|
555
|
-
this.stopping.abort();
|
|
556
|
-
await this.worker.close();
|
|
557
|
-
}
|
|
558
|
-
failed(job, err) {
|
|
559
|
-
this.log.error("plugin scope job failed", { name: job?.name, jobId: job?.id, err });
|
|
560
|
-
}
|
|
561
|
-
error(err) {
|
|
562
|
-
this.log.error("plugin scope worker error", { err });
|
|
563
|
-
}
|
|
564
|
-
};
|
|
565
|
-
__decorate([
|
|
566
|
-
OnWorkerEvent("failed")
|
|
567
|
-
], PluginScopeSyncProcessor.prototype, "failed", null);
|
|
568
|
-
__decorate([
|
|
569
|
-
OnWorkerEvent("error")
|
|
570
|
-
], PluginScopeSyncProcessor.prototype, "error", null);
|
|
571
|
-
PluginScopeSyncProcessor = __decorate([
|
|
572
|
-
Injectable(),
|
|
573
|
-
__param(0, Inject(PluginScopeSync)),
|
|
574
|
-
__param(1, Inject(KERNEL_LOGGER))
|
|
575
|
-
], PluginScopeSyncProcessor);
|
|
576
|
-
export { PluginScopeSyncProcessor };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { WorkerHost } from "@nestjs/bullmq";
|
|
2
2
|
import { type DynamicModule } from "@nestjs/common";
|
|
3
|
-
import type
|
|
3
|
+
import { type Job, type Worker } from "bullmq";
|
|
4
4
|
import type { KernelConfig } from "../config/schema.js";
|
|
5
5
|
import { type ProcessorWorkerOptions } from "../infra/queue/configured-processor.js";
|
|
6
6
|
import { type ChannelJobData } from "./jobs.js";
|
|
@@ -14,7 +14,10 @@ import { ChannelOutbound } from "./outbound.js";
|
|
|
14
14
|
*/
|
|
15
15
|
export declare class ChannelsModule {
|
|
16
16
|
}
|
|
17
|
-
/**
|
|
17
|
+
/**
|
|
18
|
+
* The consumer of `cap-kernel-channels`: deliveries (`DELIVER_JOB`, owed through the outbox or
|
|
19
|
+
* hinted) and inbound jobs (`INBOUND_JOB`), both handled by {@link ChannelOutbound}.
|
|
20
|
+
*/
|
|
18
21
|
export declare class ChannelsProcessor extends WorkerHost<Worker<ChannelJobData>> {
|
|
19
22
|
private readonly outbound;
|
|
20
23
|
constructor(outbound: ChannelOutbound);
|
|
@@ -10,6 +10,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
|
10
10
|
var ChannelWorkersModule_1;
|
|
11
11
|
import { OnWorkerEvent, WorkerHost } from "@nestjs/bullmq";
|
|
12
12
|
import { Inject, Injectable, Module } from "@nestjs/common";
|
|
13
|
+
import { UnrecoverableError } from "bullmq";
|
|
13
14
|
import { ConversationsModule } from "../conversations/conversations.module.js";
|
|
14
15
|
import { EventLogModule } from "../conversations/event-log.js";
|
|
15
16
|
import { DevicesModule } from "../devices/devices.module.js";
|
|
@@ -25,7 +26,7 @@ import { SchedulesModule } from "../schedules/schedules.module.js";
|
|
|
25
26
|
import { ToolsModule } from "../tools/tools.module.js";
|
|
26
27
|
import { ChannelConnections } from "./connections.service.js";
|
|
27
28
|
import { ChannelInbound } from "./inbound.js";
|
|
28
|
-
import { CHANNEL_QUEUE_NAME, ChannelJobs } from "./jobs.js";
|
|
29
|
+
import { CHANNEL_QUEUE_NAME, ChannelJobs, DELIVER_JOB, INBOUND_JOB, } from "./jobs.js";
|
|
29
30
|
import { ChannelOutbound, deliverBackoff } from "./outbound.js";
|
|
30
31
|
import { AnonymousGuestLock, AnonymousGuests } from "./threads.js";
|
|
31
32
|
/** The channel queue's registration (its producer here; the admin health reads its counts). */
|
|
@@ -74,7 +75,10 @@ ChannelsModule = __decorate([
|
|
|
74
75
|
})
|
|
75
76
|
], ChannelsModule);
|
|
76
77
|
export { ChannelsModule };
|
|
77
|
-
/**
|
|
78
|
+
/**
|
|
79
|
+
* The consumer of `cap-kernel-channels`: deliveries (`DELIVER_JOB`, owed through the outbox or
|
|
80
|
+
* hinted) and inbound jobs (`INBOUND_JOB`), both handled by {@link ChannelOutbound}.
|
|
81
|
+
*/
|
|
78
82
|
let ChannelsProcessor = class ChannelsProcessor extends WorkerHost {
|
|
79
83
|
outbound;
|
|
80
84
|
constructor(outbound) {
|
|
@@ -82,6 +86,9 @@ let ChannelsProcessor = class ChannelsProcessor extends WorkerHost {
|
|
|
82
86
|
this.outbound = outbound;
|
|
83
87
|
}
|
|
84
88
|
process(job, token) {
|
|
89
|
+
if (job.name !== DELIVER_JOB && job.name !== INBOUND_JOB) {
|
|
90
|
+
return Promise.reject(new UnrecoverableError(`unknown channel job "${job.name}"`));
|
|
91
|
+
}
|
|
85
92
|
return runJob(CHANNEL_QUEUE_NAME, job, () => this.outbound.process(job, token));
|
|
86
93
|
}
|
|
87
94
|
failed(job, err) {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { OutboxService } from "../infra/outbox/outbox.service.js";
|
|
2
|
+
/**
|
|
3
|
+
* The channel delivery job as the outbox owes it (transactional outbox spec §8.1): a leaf module
|
|
4
|
+
* with no BullMQ and no Nest class, so the event log can owe it without importing the channels
|
|
5
|
+
* domain. `jobs.ts` (the producer) re-exports these names.
|
|
6
|
+
*/
|
|
7
|
+
/** 渠道队列(出站投递与入站任务,不含 `:`),只在 worker 角色上消费。 */
|
|
8
|
+
export declare const CHANNEL_QUEUE_NAME = "cap-kernel-channels";
|
|
9
|
+
/** 投递任务名。 */
|
|
10
|
+
export declare const DELIVER_JOB = "deliver";
|
|
11
|
+
/** 投递任务 id(不含 `:`):同一会话、同一日志位置只排一个任务。 */
|
|
12
|
+
export declare const deliverJobId: (conversationId: string, seq: number) => string;
|
|
13
|
+
export interface DeliverJobData {
|
|
14
|
+
conversationId: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Owes the conversation's delivery job for the log position `seq` in the transaction in context:
|
|
18
|
+
* the caller knows the conversation has a channel target. Shares the job id of
|
|
19
|
+
* `ChannelJobs.enqueue`, so a hint for the same position is deduped by BullMQ.
|
|
20
|
+
*/
|
|
21
|
+
export declare function oweDelivery(outbox: Pick<OutboxService, "add">, d: {
|
|
22
|
+
conversationId: string;
|
|
23
|
+
orgId: string;
|
|
24
|
+
seq: number;
|
|
25
|
+
}): Promise<boolean>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The channel delivery job as the outbox owes it (transactional outbox spec §8.1): a leaf module
|
|
3
|
+
* with no BullMQ and no Nest class, so the event log can owe it without importing the channels
|
|
4
|
+
* domain. `jobs.ts` (the producer) re-exports these names.
|
|
5
|
+
*/
|
|
6
|
+
/** 渠道队列(出站投递与入站任务,不含 `:`),只在 worker 角色上消费。 */
|
|
7
|
+
export const CHANNEL_QUEUE_NAME = "cap-kernel-channels";
|
|
8
|
+
/** 投递任务名。 */
|
|
9
|
+
export const DELIVER_JOB = "deliver";
|
|
10
|
+
/** 投递任务 id(不含 `:`):同一会话、同一日志位置只排一个任务。 */
|
|
11
|
+
export const deliverJobId = (conversationId, seq) => `deliver-${conversationId}-${seq}`;
|
|
12
|
+
/**
|
|
13
|
+
* Owes the conversation's delivery job for the log position `seq` in the transaction in context:
|
|
14
|
+
* the caller knows the conversation has a channel target. Shares the job id of
|
|
15
|
+
* `ChannelJobs.enqueue`, so a hint for the same position is deduped by BullMQ.
|
|
16
|
+
*/
|
|
17
|
+
export function oweDelivery(outbox, d) {
|
|
18
|
+
return outbox.add({
|
|
19
|
+
queue: CHANNEL_QUEUE_NAME,
|
|
20
|
+
name: DELIVER_JOB,
|
|
21
|
+
jobId: deliverJobId(d.conversationId, d.seq),
|
|
22
|
+
data: { conversationId: d.conversationId },
|
|
23
|
+
orgId: d.orgId,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
@@ -72,7 +72,7 @@ export declare class ChannelInbound {
|
|
|
72
72
|
/** 通讯录同步的投递(按 (组织, 插件) 每分钟去重的维护队列任务)。 */
|
|
73
73
|
directorySync: Pick<DirectorySync, "enqueue">,
|
|
74
74
|
/** The channel queue: inbound jobs (`onEvent` only dedupes and enqueues) and deliveries. */
|
|
75
|
-
jobs: Pick<ChannelJobs, "enqueue" | "enqueueInbound">,
|
|
75
|
+
jobs: Pick<ChannelJobs, "enqueue" | "enqueueInbound" | "bind">,
|
|
76
76
|
/** 卡片操作:审批决定与提问回答(授权由这两个服务按点击者映射到的账号判定)。 */
|
|
77
77
|
approvals: Pick<ApprovalsService, "decide">, questions: Pick<QuestionsService, "answer">, guests: Pick<AnonymousGuests, "ensure">, devices: Pick<DevicesService, "list" | "imDefault" | "setImDefault">, log: Pick<Logger, "warn" | "info">);
|
|
78
78
|
/**
|
package/dist/channels/inbound.js
CHANGED
|
@@ -11,6 +11,7 @@ import { randomUUID } from "node:crypto";
|
|
|
11
11
|
import { Inject, Injectable } from "@nestjs/common";
|
|
12
12
|
import { ConversationsService } from "../conversations/conversations.service.js";
|
|
13
13
|
import { imSenderContext, recipientLocale, } from "../conversations/message-context.js";
|
|
14
|
+
import { channelSource } from "../conversations/source.js";
|
|
14
15
|
import { DevicesService } from "../devices/devices.service.js";
|
|
15
16
|
import { DEFAULT_DEVICE_ALIASES, deviceLabel } from "../devices/label.js";
|
|
16
17
|
import { ApiError } from "../errors.js";
|
|
@@ -227,8 +228,7 @@ let ChannelInbound = class ChannelInbound {
|
|
|
227
228
|
try {
|
|
228
229
|
// bound to the person's IM default device, else their default (environments spec
|
|
229
230
|
// §4.2 item 8); an anonymous sender's stays unbound (no sandbox)
|
|
230
|
-
created = (await this.conversations.create(row.orgId, accountId, row.agentId, { im: true }))
|
|
231
|
-
.id;
|
|
231
|
+
created = (await this.conversations.create(row.orgId, accountId, row.agentId, { im: true }, { source: channelSource(e.target.chatType) })).id;
|
|
232
232
|
}
|
|
233
233
|
catch (err) {
|
|
234
234
|
if (err instanceof ApiError && err.code === "agent.not_found") {
|
|
@@ -277,21 +277,8 @@ let ChannelInbound = class ChannelInbound {
|
|
|
277
277
|
}
|
|
278
278
|
throw err;
|
|
279
279
|
}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
.values({
|
|
283
|
-
conversation_id: conversationId,
|
|
284
|
-
connection_id: connectionId,
|
|
285
|
-
target: JSON.stringify(replyTarget),
|
|
286
|
-
last_seq: seq,
|
|
287
|
-
})
|
|
288
|
-
.onConflict((oc) => oc.column("conversation_id").doUpdateSet({
|
|
289
|
-
connection_id: connectionId,
|
|
290
|
-
target: JSON.stringify(replyTarget),
|
|
291
|
-
updated_at: new Date(),
|
|
292
|
-
}))
|
|
293
|
-
.execute();
|
|
294
|
-
await this.jobs.enqueue(conversationId);
|
|
280
|
+
// the reply target and its delivery, owed in one transaction (transactional outbox spec §8.1)
|
|
281
|
+
await this.jobs.bind(row.orgId, conversationId, connectionId, replyTarget, seq);
|
|
295
282
|
}
|
|
296
283
|
/**
|
|
297
284
|
* Direct-message `/device` (environments spec §4.2 item 8): lists the person's usable devices,
|
package/dist/channels/jobs.d.ts
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
import type { InboundEvent, Logger } from "@capybara-hq/plugin-sdk";
|
|
1
|
+
import type { ChannelTarget, InboundEvent, Logger } from "@capybara-hq/plugin-sdk";
|
|
2
2
|
import { type OnApplicationShutdown } from "@nestjs/common";
|
|
3
3
|
import type { Queue } from "bullmq";
|
|
4
4
|
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
5
|
+
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
5
6
|
import { KernelEvents } from "../infra/events/kernel-events.js";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export declare const DELIVER_JOB = "deliver";
|
|
10
|
-
/** 投递任务 id(不含 `:`):同一会话、同一日志位置只排一个任务。 */
|
|
11
|
-
export declare const deliverJobId: (conversationId: string, seq: number) => string;
|
|
7
|
+
import { OutboxService } from "../infra/outbox/outbox.service.js";
|
|
8
|
+
import { type DeliverJobData } from "./deliver-job.js";
|
|
9
|
+
export { CHANNEL_QUEUE_NAME, DELIVER_JOB, type DeliverJobData, deliverJobId, oweDelivery, } from "./deliver-job.js";
|
|
12
10
|
/**
|
|
13
11
|
* 结束后复核排的任务 id:每次唯一(带随机后缀,不含 `:`)。不能复用 `deliver-<会话>-<seq>`——同 id 的任务
|
|
14
12
|
* 可能正因锁被占而即将直接结束,BullMQ 会把新任务去重进它,于是没人投递。
|
|
@@ -20,10 +18,7 @@ export declare const followUpJobId: (conversationId: string, seq: number) => str
|
|
|
20
18
|
*/
|
|
21
19
|
export declare function idempotencyKey(raw: string): string;
|
|
22
20
|
/** 可重试失败(限流、5xx、网络、超时)的总尝试次数。 */
|
|
23
|
-
export declare const DELIVER_ATTEMPTS
|
|
24
|
-
export interface DeliverJobData {
|
|
25
|
-
conversationId: string;
|
|
26
|
-
}
|
|
21
|
+
export declare const DELIVER_ATTEMPTS: 8;
|
|
27
22
|
/** 入站任务(Ruling A21):插件 `onEvent` 收到的事件,原样排进队列由 worker 处理。 */
|
|
28
23
|
export interface InboundJobData {
|
|
29
24
|
connectionId: string;
|
|
@@ -37,7 +32,7 @@ export declare const INBOUND_JOB = "inbound";
|
|
|
37
32
|
* 用户发来的消息不能排在它们后面。
|
|
38
33
|
*/
|
|
39
34
|
export declare const INBOUND_PRIORITY = 1;
|
|
40
|
-
export declare const DELIVER_PRIORITY
|
|
35
|
+
export declare const DELIVER_PRIORITY: 10;
|
|
41
36
|
/** 入站任务的总尝试次数(处理失败由 BullMQ 指数退避重试:1s、2s、4s、8s)。 */
|
|
42
37
|
export declare const INBOUND_ATTEMPTS = 5;
|
|
43
38
|
/**
|
|
@@ -49,24 +44,39 @@ export declare const inboundJobId: (connectionId: string, messageId: string) =>
|
|
|
49
44
|
* The producer side of the channel queue `cap-kernel-channels` (any role): delivery jobs for a
|
|
50
45
|
* conversation with an IM target, inbound jobs (Ruling A21), and the catch-up of a connection that
|
|
51
46
|
* can deliver again. Its consumer is {@link ChannelOutbound} through the `@Processor` of the worker
|
|
52
|
-
* role.
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
47
|
+
* role. A delivery is owed (transactional outbox spec §8.1): every event append of a conversation
|
|
48
|
+
* with a channel target adds its deliver job to the outbox in the append's transaction (a reply, an
|
|
49
|
+
* approval decision, a question answer, a wait stop all append), and {@link bind} owes the first
|
|
50
|
+
* one with the target. The direct {@link enqueue} is left for hints and catch-ups: a run starting
|
|
51
|
+
* here (`run.started`, so its streaming drafts start at once), a finished delivery job's re-check,
|
|
52
|
+
* a card click's thread and a connection that can deliver again.
|
|
56
53
|
*/
|
|
57
54
|
export declare class ChannelJobs implements OnApplicationShutdown {
|
|
58
55
|
private readonly db;
|
|
59
56
|
private readonly queue;
|
|
60
57
|
private readonly log;
|
|
58
|
+
private readonly outbox;
|
|
59
|
+
readonly txHost: TransactionHost;
|
|
61
60
|
/** Stopped (the stop's channel-outbound phase): enqueues do nothing, inbound enqueues throw. */
|
|
62
61
|
private closed;
|
|
63
62
|
private readonly unsubscribe;
|
|
64
|
-
constructor(db: KernelDb, queue: Queue<ChannelJobData>, events: Pick<KernelEvents, "on">, log: Pick<Logger, "warn" | "error"
|
|
63
|
+
constructor(db: KernelDb, queue: Queue<ChannelJobData>, events: Pick<KernelEvents, "on">, log: Pick<Logger, "warn" | "error">, outbox: Pick<OutboxService, "add">, txHost: TransactionHost);
|
|
65
64
|
/** Unsubscribes after the stop phases: events emitted while they drain are still handled. */
|
|
66
65
|
onApplicationShutdown(): void;
|
|
66
|
+
/**
|
|
67
|
+
* Makes `target` (on `connectionId`) the conversation's delivery target and owes its delivery
|
|
68
|
+
* from the current log position, in one transaction. A new target starts delivering after
|
|
69
|
+
* `seq` (the message that bound it); a known one keeps its progress. The conversation row is
|
|
70
|
+
* locked first, as an append locks it: an append either committed before (its events are below
|
|
71
|
+
* the owed position) or reads the target (and owes its own job).
|
|
72
|
+
*/
|
|
73
|
+
bind(orgId: string, conversationId: string, connectionId: string, target: ChannelTarget, seq: number): Promise<void>;
|
|
67
74
|
/** 会话有 IM 投递目标时排一个出站任务,不等入队完成;失败只记日志。 */
|
|
68
75
|
private enqueueSoon;
|
|
69
|
-
/**
|
|
76
|
+
/**
|
|
77
|
+
* A hint (not owed work, see the class): a delivery job for a conversation with an IM target (one
|
|
78
|
+
* per log position; `followUp` gets a fresh id); does nothing once closed.
|
|
79
|
+
*/
|
|
70
80
|
enqueue(conversationId: string, opts?: {
|
|
71
81
|
followUp?: boolean;
|
|
72
82
|
}): Promise<void>;
|