@capybara-hq/kernel 0.1.0-rc.3 → 0.1.0-rc.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/admin.controller.d.ts +9 -4
- package/dist/admin/admin.controller.js +13 -4
- package/dist/admin/extensions.controller.d.ts +8 -1
- package/dist/admin/extensions.controller.js +22 -2
- package/dist/agents/agent-lookup.d.ts +3 -0
- package/dist/agents/agent-lookup.js +10 -0
- package/dist/agents/agent-profile.service.d.ts +29 -0
- package/dist/agents/agent-profile.service.js +117 -0
- package/dist/agents/agents.controller.d.ts +93 -3
- package/dist/agents/agents.controller.js +114 -4
- package/dist/agents/agents.module.js +6 -2
- package/dist/agents/agents.service.d.ts +13 -5
- package/dist/agents/agents.service.js +30 -7
- package/dist/agents/definition.d.ts +4 -0
- package/dist/agents/definition.js +10 -0
- package/dist/agents/instruction-files.d.ts +60 -0
- package/dist/agents/instruction-files.js +111 -0
- package/dist/agents/models.controller.d.ts +9 -3
- package/dist/agents/models.controller.js +19 -4
- package/dist/agents/profile.d.ts +96 -0
- package/dist/agents/profile.js +51 -0
- package/dist/agents/readiness.controller.d.ts +26 -0
- package/dist/agents/readiness.controller.js +56 -0
- package/dist/agents/templates.js +1 -1
- package/dist/background/background.controller.d.ts +38 -0
- package/dist/background/background.controller.js +87 -0
- package/dist/background/background.module.d.ts +10 -0
- package/dist/background/background.module.js +32 -0
- package/dist/background/finish.d.ts +43 -0
- package/dist/background/finish.js +94 -0
- package/dist/background/launch-lock.d.ts +14 -0
- package/dist/background/launch-lock.js +39 -0
- package/dist/background/launch.d.ts +36 -0
- package/dist/background/launch.js +70 -0
- package/dist/background/poll.job.d.ts +53 -0
- package/dist/background/poll.job.js +182 -0
- package/dist/background/render.d.ts +23 -0
- package/dist/background/render.js +48 -0
- package/dist/background/session.d.ts +37 -0
- package/dist/background/session.js +66 -0
- package/dist/background/status.d.ts +35 -0
- package/dist/background/status.js +57 -0
- package/dist/background/stop.d.ts +62 -0
- package/dist/background/stop.js +225 -0
- package/dist/background/store.d.ts +150 -0
- package/dist/background/store.js +351 -0
- package/dist/background/store.module.d.ts +6 -0
- package/dist/background/store.module.js +22 -0
- package/dist/background/tasks.service.d.ts +67 -0
- package/dist/background/tasks.service.js +186 -0
- package/dist/background/wake-rules.d.ts +57 -0
- package/dist/background/wake-rules.js +83 -0
- package/dist/background/wake.d.ts +28 -0
- package/dist/background/wake.js +57 -0
- package/dist/catalog/claude/agents.js +2 -0
- package/dist/catalog/marketplaces.service.d.ts +1 -1
- package/dist/catalog/plugin-import.js +1 -1
- package/dist/catalog/plugin-upgrade.js +1 -1
- package/dist/channels/cards.js +1 -1
- package/dist/channels/connections.service.d.ts +16 -1
- package/dist/channels/connections.service.js +38 -4
- package/dist/channels/threads.d.ts +5 -0
- package/dist/channels/threads.js +8 -0
- package/dist/config/schema.d.ts +18 -0
- package/dist/config/schema.js +102 -0
- package/dist/conversations/access.js +2 -0
- package/dist/conversations/attachments.service.d.ts +30 -0
- package/dist/conversations/attachments.service.js +130 -5
- package/dist/conversations/conversations.controller.d.ts +36 -5
- package/dist/conversations/conversations.controller.js +62 -12
- package/dist/conversations/conversations.module.js +3 -0
- package/dist/conversations/conversations.service.d.ts +44 -2
- package/dist/conversations/conversations.service.js +140 -12
- package/dist/conversations/event-log.d.ts +1 -1
- package/dist/conversations/event-log.js +10 -1
- package/dist/conversations/events.d.ts +58 -0
- package/dist/conversations/events.js +57 -2
- package/dist/conversations/queue.d.ts +3 -1
- package/dist/conversations/queue.js +13 -2
- package/dist/conversations/queue.service.js +3 -1
- package/dist/conversations/titles.d.ts +7 -0
- package/dist/conversations/titles.js +15 -0
- package/dist/db/kernel-schema.d.ts +58 -2
- package/dist/db/pool.d.ts +5 -0
- package/dist/db/pool.js +3 -0
- package/dist/errors.d.ts +8 -0
- package/dist/errors.js +8 -0
- package/dist/evals/run-replay.d.ts +5 -2
- package/dist/evals/run-replay.js +30 -7
- package/dist/evals/step-replay.js +12 -6
- package/dist/evals/trajectory.js +1 -1
- package/dist/extensions/admin-ops.js +2 -0
- package/dist/extensions/admin-view.d.ts +3 -1
- package/dist/extensions/admin-view.js +10 -2
- package/dist/extensions/blobs.js +1 -0
- package/dist/extensions/config-issues.d.ts +13 -1
- package/dist/extensions/config-issues.js +22 -5
- package/dist/extensions/config-json-schema.d.ts +43 -7
- package/dist/extensions/config-json-schema.js +124 -58
- package/dist/extensions/deployment-config-store.d.ts +6 -0
- package/dist/extensions/deployment-config.d.ts +50 -3
- package/dist/extensions/deployment-config.js +125 -28
- package/dist/extensions/display-name.d.ts +22 -0
- package/dist/extensions/display-name.js +77 -0
- package/dist/extensions/extensions.module.js +6 -0
- package/dist/extensions/host.d.ts +26 -1
- package/dist/extensions/host.js +82 -9
- package/dist/extensions/org-config.controller.d.ts +11 -3
- package/dist/extensions/org-config.controller.js +14 -6
- package/dist/extensions/org-config.d.ts +6 -0
- package/dist/extensions/org-config.js +12 -13
- package/dist/extensions/package.d.ts +14 -1
- package/dist/extensions/package.js +51 -2
- package/dist/extensions/permission-view.d.ts +3 -0
- package/dist/extensions/point-defaults.controller.d.ts +34 -0
- package/dist/extensions/point-defaults.controller.js +71 -0
- package/dist/extensions/point-defaults.d.ts +64 -0
- package/dist/extensions/point-defaults.js +206 -0
- package/dist/extensions/registry.d.ts +1 -1
- package/dist/extensions/registry.js +2 -0
- package/dist/http/api.module.js +8 -0
- package/dist/http/nest-logger.d.ts +6 -5
- package/dist/http/nest-logger.js +24 -10
- package/dist/identity/auth.controller.js +9 -5
- package/dist/identity/cookies.d.ts +20 -5
- package/dist/identity/cookies.js +44 -6
- package/dist/identity/me.controller.d.ts +5 -1
- package/dist/identity/me.controller.js +12 -3
- package/dist/identity/sessions.d.ts +12 -0
- package/dist/identity/sessions.js +45 -0
- package/dist/index.d.ts +1 -1
- package/dist/infra/config/kernel-config.service.js +6 -0
- package/dist/infra/database/database.module.js +1 -0
- package/dist/infra/events/kernel-events.d.ts +8 -0
- package/dist/infra/lifecycle/process-guards.js +4 -0
- package/dist/infra/logging/logging.module.d.ts +4 -2
- package/dist/infra/logging/logging.module.js +24 -6
- package/dist/infra/queue/redis-connection.d.ts +6 -0
- package/dist/infra/queue/redis-connection.js +7 -0
- package/dist/infra/redis/redis.module.js +6 -2
- package/dist/infra/tokens.d.ts +6 -1
- package/dist/infra/tokens.js +6 -1
- package/dist/log.d.ts +13 -1
- package/dist/log.js +93 -17
- package/dist/models/purposes.d.ts +23 -0
- package/dist/models/purposes.js +31 -10
- package/dist/orgs/offboarding.d.ts +3 -1
- package/dist/orgs/offboarding.js +26 -16
- package/dist/orgs/settings.service.d.ts +5 -0
- package/dist/orgs/settings.service.js +13 -2
- package/dist/roles/worker.module.js +6 -0
- package/dist/runtime/context/build.d.ts +9 -7
- package/dist/runtime/context/build.js +27 -19
- package/dist/runtime/context/claude-tools.d.ts +2 -1
- package/dist/runtime/context/claude-tools.js +5 -4
- package/dist/runtime/context/compaction.js +4 -0
- package/dist/runtime/langgraph-loop.js +3 -2
- package/dist/runtime/loop.d.ts +4 -3
- package/dist/runtime/maintenance.d.ts +4 -2
- package/dist/runtime/maintenance.js +17 -5
- package/dist/runtime/model-call.d.ts +17 -1
- package/dist/runtime/model-call.js +3 -2
- package/dist/runtime/providers.d.ts +6 -0
- package/dist/runtime/providers.js +8 -0
- package/dist/runtime/run-queue.d.ts +25 -2
- package/dist/runtime/run-queue.js +129 -33
- package/dist/runtime/run-worker.js +16 -3
- package/dist/runtime/runtime.module.js +8 -3
- package/dist/runtime/step-log.d.ts +6 -0
- package/dist/runtime/step-log.js +16 -2
- package/dist/runtime/subagents/definition.js +3 -1
- package/dist/runtime/usage-report.d.ts +2 -1
- package/dist/runtime/usage-report.js +2 -1
- package/dist/runtime/waits.d.ts +8 -2
- package/dist/runtime/waits.js +37 -11
- package/dist/sandbox/sandbox.module.js +6 -1
- package/dist/sandbox/sandbox.service.d.ts +61 -3
- package/dist/sandbox/sandbox.service.js +141 -7
- package/dist/tools/approvals.service.js +2 -0
- package/dist/tools/builtin-names.d.ts +20 -2
- package/dist/tools/builtin-names.js +29 -2
- package/dist/tools/builtins/builtin-tools.d.ts +15 -1
- package/dist/tools/builtins/builtin-tools.js +34 -2
- package/dist/tools/builtins/sandbox/bash.d.ts +1 -1
- package/dist/tools/builtins/sandbox/bash.js +230 -3
- package/dist/tools/builtins/sandbox/common.d.ts +27 -4
- package/dist/tools/builtins/sandbox/common.js +7 -3
- package/dist/tools/builtins/sandbox/deliver.js +1 -1
- package/dist/tools/builtins/sandbox/index.d.ts +2 -0
- package/dist/tools/builtins/sandbox/index.js +6 -0
- package/dist/tools/builtins/sandbox/task-output.d.ts +63 -0
- package/dist/tools/builtins/sandbox/task-output.js +304 -0
- package/dist/tools/builtins/sandbox/task-stop.d.ts +10 -0
- package/dist/tools/builtins/sandbox/task-stop.js +112 -0
- package/dist/tools/builtins/web/errors.d.ts +8 -0
- package/dist/tools/builtins/web/errors.js +53 -0
- package/dist/tools/builtins/web/extract.d.ts +62 -0
- package/dist/tools/builtins/web/extract.js +267 -0
- package/dist/tools/builtins/web/fetch.d.ts +55 -0
- package/dist/tools/builtins/web/fetch.js +174 -0
- package/dist/tools/builtins/web/index.d.ts +13 -0
- package/dist/tools/builtins/web/index.js +12 -0
- package/dist/tools/builtins/web/page-cache.d.ts +25 -0
- package/dist/tools/builtins/web/page-cache.js +77 -0
- package/dist/tools/builtins/web/search.d.ts +25 -0
- package/dist/tools/builtins/web/search.js +209 -0
- package/dist/tools/builtins/web/url.d.ts +17 -0
- package/dist/tools/builtins/web/url.js +43 -0
- package/dist/tools/oauth/flow-paths.d.ts +1 -1
- package/dist/tools/oauth/flow-paths.js +1 -1
- package/dist/tools/pipeline.js +15 -0
- package/dist/tools/registry.d.ts +22 -3
- package/dist/tools/registry.js +26 -11
- package/dist/tools/tools.module.js +12 -0
- package/dist/tools/validate.js +1 -1
- package/package.json +3 -3
- package/src/migrations/0064_account_last_org.sql +7 -0
- package/src/migrations/0065_conversation_archive.sql +8 -0
- package/src/migrations/0066_extension_display_name.sql +4 -0
- package/src/migrations/0067_agent_profiles.sql +20 -0
- package/src/migrations/0068_agent_avatar_uploads.sql +7 -0
- package/src/migrations/0069_attachments_agent_idx.sql +3 -0
- package/src/migrations/0070_background_tasks.sql +37 -0
- package/src/migrations/0071_background_tasks_reserve.sql +6 -0
- package/src/migrations/0072_background_tasks_stopping.sql +5 -0
- package/src/migrations/0073_org_extension_point_defaults.sql +11 -0
|
@@ -226,13 +226,14 @@ async function runStep(deps, input, wrapUp) {
|
|
|
226
226
|
}
|
|
227
227
|
}
|
|
228
228
|
/**
|
|
229
|
-
* Appends a model call's events (its reply or compaction outcome with its `usage
|
|
229
|
+
* Appends a model call's events (its reply or compaction outcome with its `usage`; also the usage
|
|
230
|
+
* of a `WebFetch` extraction call, `tools/builtins/web/extract.ts`). For a
|
|
230
231
|
* sub-agent's run (`input.parentRunId`) in one transaction that also adds the tokens of the usage
|
|
231
232
|
* events it newly wrote to the parent run's `subagent_tokens_used` (sub-agents spec §8.3): a usage
|
|
232
233
|
* event already in the log (an idempotent hit after a crash) adds nothing, so each call counts
|
|
233
234
|
* exactly once.
|
|
234
235
|
*/
|
|
235
|
-
async function appendCounted(deps, input, events) {
|
|
236
|
+
export async function appendCounted(deps, input, events) {
|
|
236
237
|
const { conversationId, parentRunId } = input;
|
|
237
238
|
if (parentRunId === undefined)
|
|
238
239
|
return deps.eventLog.append(conversationId, events);
|
|
@@ -67,6 +67,12 @@ export declare class ProviderRegistry {
|
|
|
67
67
|
listTimeoutMs?: number);
|
|
68
68
|
/** `listModels()` 加时限(到时按失败处理,供应商的调用留在后台、结果丢弃)。 */
|
|
69
69
|
private listModelsWithTimeout;
|
|
70
|
+
/**
|
|
71
|
+
* Whether at least one model-provider extension is loaded on this replica (enabled, configured,
|
|
72
|
+
* set up). Read from the extension host only: never calls a vendor, so a provider whose vendor
|
|
73
|
+
* is down still counts (org readiness, web-order spec §3.8).
|
|
74
|
+
*/
|
|
75
|
+
hasProvider(): boolean;
|
|
70
76
|
/** 每个插件取一个实现(`@2` 优先),按插件首次出现的顺序;不支持的主版本记 warn 后忽略。 */
|
|
71
77
|
private providers;
|
|
72
78
|
private models;
|
|
@@ -132,6 +132,14 @@ let ProviderRegistry = class ProviderRegistry {
|
|
|
132
132
|
clearTimeout(timer);
|
|
133
133
|
}
|
|
134
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* Whether at least one model-provider extension is loaded on this replica (enabled, configured,
|
|
137
|
+
* set up). Read from the extension host only: never calls a vendor, so a provider whose vendor
|
|
138
|
+
* is down still counts (org readiness, web-order spec §3.8).
|
|
139
|
+
*/
|
|
140
|
+
hasProvider() {
|
|
141
|
+
return this.providers().length > 0;
|
|
142
|
+
}
|
|
135
143
|
/** 每个插件取一个实现(`@2` 优先),按插件首次出现的顺序;不支持的主版本记 warn 后忽略。 */
|
|
136
144
|
providers() {
|
|
137
145
|
const chosen = new Map();
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { Logger } from "@capybara-hq/plugin-sdk";
|
|
2
|
+
import type { Queue } from "bullmq";
|
|
2
3
|
import { EventLog } from "../conversations/event-log.js";
|
|
3
4
|
import type { ConversationKind } from "../db/kernel-schema.js";
|
|
5
|
+
import { KernelConfigService } from "../infra/config/kernel-config.service.js";
|
|
4
6
|
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
5
7
|
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
6
8
|
import { KernelEvents } from "../infra/events/kernel-events.js";
|
|
@@ -29,11 +31,16 @@ export declare class RunQueue {
|
|
|
29
31
|
private readonly events;
|
|
30
32
|
private readonly log;
|
|
31
33
|
readonly txHost: TransactionHost;
|
|
34
|
+
private readonly config;
|
|
35
|
+
/** The producer side of the delayed `bg-wake` job (`MaintenanceProcessor` consumes it). */
|
|
36
|
+
private readonly maintenance;
|
|
32
37
|
constructor(kdb: KernelDb, eventLog: EventLog, lock: Pick<ConversationLock, "holder">, jobs: RunJobs, finisher: Pick<RunFinish, "finishIn">,
|
|
33
38
|
/** 运行以孤儿结束(标记失败 / 直接中止)后删除它的 checkpoint thread(thread_id = runId)。 */
|
|
34
39
|
checkpoints: Pick<KyselyCheckpointSaver, "deleteThread">,
|
|
35
40
|
/** The run's open `Agent` call results, in the orphan repair's transaction (sub-agents spec §12). */
|
|
36
|
-
pipeline: Pick<ToolPipeline, "stopOpenAgentCalls">, events: KernelEvents, log: Pick<Logger, "error" | "warn">, txHost: TransactionHost
|
|
41
|
+
pipeline: Pick<ToolPipeline, "stopOpenAgentCalls">, events: KernelEvents, log: Pick<Logger, "error" | "warn">, txHost: TransactionHost, config: KernelConfigService,
|
|
42
|
+
/** The producer side of the delayed `bg-wake` job (`MaintenanceProcessor` consumes it). */
|
|
43
|
+
maintenance: Pick<Queue, "add">);
|
|
37
44
|
private get db();
|
|
38
45
|
/**
|
|
39
46
|
* The `beforeTerminal` of an orphan repair: writes the run's open `Agent` call results in the
|
|
@@ -44,6 +51,11 @@ export declare class RunQueue {
|
|
|
44
51
|
* 有未消费的用户消息(最新 user_message 的 seq > `consumed_seq`)、会话锁无人持有、
|
|
45
52
|
* 且没有 queued / running / waiting 的运行时:插入 `runs`(queued)并投递任务,返回 runId;否则返回 null。
|
|
46
53
|
*
|
|
54
|
+
* The boundary (`trigger_seq`) is that newest message; with none unconsumed, the queue's head
|
|
55
|
+
* once it is sent; with no head either, a wake-eligible finished background task (background
|
|
56
|
+
* Bash spec §3, {@link wakeDecisionIn}): the run is then attributed to the task's trigger
|
|
57
|
+
* account, and a finish inside the wake window schedules the delayed `bg-wake` job instead.
|
|
58
|
+
*
|
|
47
59
|
* 孤儿清理(R29,按需进行,没有定时器):已有的 queued / running 运行若 BullMQ 任务已不存在、
|
|
48
60
|
* 会话锁也无人持有(queued 的还要超过宽限期),说明没有任何 worker 会再执行它——写
|
|
49
61
|
* `run_failed { code: "run.orphaned" }` 把它标记为失败(consumed_seq 推进到它的触发消息),
|
|
@@ -52,9 +64,20 @@ export declare class RunQueue {
|
|
|
52
64
|
*
|
|
53
65
|
* agent 已软删除的会话不起新运行(孤儿清理照常进行)。
|
|
54
66
|
*/
|
|
55
|
-
ensureRun(conversationId: string
|
|
67
|
+
ensureRun(conversationId: string,
|
|
68
|
+
/** From the `bg-wake` job: it fired `wakeRetry - 1` times before its window ended. */
|
|
69
|
+
opts?: {
|
|
70
|
+
wakeRetry?: number;
|
|
71
|
+
}): Promise<string | null>;
|
|
56
72
|
/** {@link ensureRun}'s transaction: what it decided goes back through `st`. */
|
|
57
73
|
private ensureRunIn;
|
|
74
|
+
/**
|
|
75
|
+
* Schedules the delayed `bg-wake` job at the end of the conversation's wake window (after the
|
|
76
|
+
* commit): one job per conversation and window end, so finishes inside one window share it. It
|
|
77
|
+
* calls {@link ensureRun}, which wakes the conversation if a finish is still unconsumed. A failed
|
|
78
|
+
* enqueue is logged: the next message, run end or page opening re-derives the wake.
|
|
79
|
+
*/
|
|
80
|
+
private scheduleWake;
|
|
58
81
|
/**
|
|
59
82
|
* Enqueues a run job under the caller's job id (resumes, dispatches, repairs), with the same
|
|
60
83
|
* options as {@link ensureRun} (see {@link RunJobs.enqueue}).
|
|
@@ -8,11 +8,14 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
|
8
8
|
return function (target, key) { decorator(target, key, paramIndex); }
|
|
9
9
|
};
|
|
10
10
|
import { randomUUID } from "node:crypto";
|
|
11
|
+
import { getQueueToken } from "@nestjs/bullmq";
|
|
11
12
|
import { Inject, Injectable } from "@nestjs/common";
|
|
12
13
|
import { sql } from "kysely";
|
|
13
14
|
import { visibleTo } from "../agents/agent-lookup.js";
|
|
15
|
+
import { BG_WAKE_JOB, bgWakeJobId, wakeDecisionIn, } from "../background/wake-rules.js";
|
|
14
16
|
import { EventLog } from "../conversations/event-log.js";
|
|
15
17
|
import { dispatchQueuedIn, pauseQueueWith } from "../conversations/queue.js";
|
|
18
|
+
import { KernelConfigService } from "../infra/config/kernel-config.service.js";
|
|
16
19
|
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
17
20
|
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
18
21
|
import { Transactional } from "../infra/database/transactional.decorator.js";
|
|
@@ -21,6 +24,7 @@ import { KERNEL_LOGGER } from "../infra/tokens.js";
|
|
|
21
24
|
import { ToolPipeline } from "../tools/pipeline.js";
|
|
22
25
|
import { KyselyCheckpointSaver } from "./checkpointer.js";
|
|
23
26
|
import { ConversationLock } from "./conversation-lock.js";
|
|
27
|
+
import { MAINTENANCE_QUEUE_NAME } from "./maintenance-queue.js";
|
|
24
28
|
import { notifyFinished, RunFinish } from "./run-finish.js";
|
|
25
29
|
import { ACTIVE_RUN_STATUSES, RunJobs, runJobId, runQueueNameFor, } from "./run-jobs.js";
|
|
26
30
|
export { ACTIVE_RUN_STATUSES, RUN_QUEUE_NAME, runJobId, runQueueNameFor, SUBAGENT_RUN_QUEUE_NAME, } from "./run-jobs.js";
|
|
@@ -29,6 +33,12 @@ export { ACTIVE_RUN_STATUSES, RUN_QUEUE_NAME, runJobId, runQueueNameFor, SUBAGEN
|
|
|
29
33
|
* 只有进入 queued 超过这么久(`COALESCE(queued_at, created_at)`)仍没有任务的 queued 行才视为孤儿。
|
|
30
34
|
*/
|
|
31
35
|
const ORPHAN_QUEUED_GRACE_SECONDS = 30;
|
|
36
|
+
/**
|
|
37
|
+
* Margin added to the delayed wake job: its window is read on the database's clock, the delay runs
|
|
38
|
+
* on this process's and Redis's, so the job does not usually fire just before the window ends (one
|
|
39
|
+
* that does reschedules itself under its next retry id, `bgWakeJobId`).
|
|
40
|
+
*/
|
|
41
|
+
const WAKE_JOB_MARGIN_MS = 1000;
|
|
32
42
|
/**
|
|
33
43
|
* 运行队列(生产方):`ensureRun` 在会话有未消费的用户消息、且没有进行中的运行时插入 `runs` 行并投递任务。
|
|
34
44
|
* 「是否已有运行」以 PG 为准(会话行锁内检查 `runs` 中的 queued / running),并参考会话锁的持有者;
|
|
@@ -45,11 +55,15 @@ let RunQueue = class RunQueue {
|
|
|
45
55
|
events;
|
|
46
56
|
log;
|
|
47
57
|
txHost;
|
|
58
|
+
config;
|
|
59
|
+
maintenance;
|
|
48
60
|
constructor(kdb, eventLog, lock, jobs, finisher,
|
|
49
61
|
/** 运行以孤儿结束(标记失败 / 直接中止)后删除它的 checkpoint thread(thread_id = runId)。 */
|
|
50
62
|
checkpoints,
|
|
51
63
|
/** The run's open `Agent` call results, in the orphan repair's transaction (sub-agents spec §12). */
|
|
52
|
-
pipeline, events, log, txHost
|
|
64
|
+
pipeline, events, log, txHost, config,
|
|
65
|
+
/** The producer side of the delayed `bg-wake` job (`MaintenanceProcessor` consumes it). */
|
|
66
|
+
maintenance) {
|
|
53
67
|
this.kdb = kdb;
|
|
54
68
|
this.eventLog = eventLog;
|
|
55
69
|
this.lock = lock;
|
|
@@ -60,6 +74,8 @@ let RunQueue = class RunQueue {
|
|
|
60
74
|
this.events = events;
|
|
61
75
|
this.log = log;
|
|
62
76
|
this.txHost = txHost;
|
|
77
|
+
this.config = config;
|
|
78
|
+
this.maintenance = maintenance;
|
|
63
79
|
}
|
|
64
80
|
get db() {
|
|
65
81
|
return this.kdb.db;
|
|
@@ -81,6 +97,11 @@ let RunQueue = class RunQueue {
|
|
|
81
97
|
* 有未消费的用户消息(最新 user_message 的 seq > `consumed_seq`)、会话锁无人持有、
|
|
82
98
|
* 且没有 queued / running / waiting 的运行时:插入 `runs`(queued)并投递任务,返回 runId;否则返回 null。
|
|
83
99
|
*
|
|
100
|
+
* The boundary (`trigger_seq`) is that newest message; with none unconsumed, the queue's head
|
|
101
|
+
* once it is sent; with no head either, a wake-eligible finished background task (background
|
|
102
|
+
* Bash spec §3, {@link wakeDecisionIn}): the run is then attributed to the task's trigger
|
|
103
|
+
* account, and a finish inside the wake window schedules the delayed `bg-wake` job instead.
|
|
104
|
+
*
|
|
84
105
|
* 孤儿清理(R29,按需进行,没有定时器):已有的 queued / running 运行若 BullMQ 任务已不存在、
|
|
85
106
|
* 会话锁也无人持有(queued 的还要超过宽限期),说明没有任何 worker 会再执行它——写
|
|
86
107
|
* `run_failed { code: "run.orphaned" }` 把它标记为失败(consumed_seq 推进到它的触发消息),
|
|
@@ -89,7 +110,9 @@ let RunQueue = class RunQueue {
|
|
|
89
110
|
*
|
|
90
111
|
* agent 已软删除的会话不起新运行(孤儿清理照常进行)。
|
|
91
112
|
*/
|
|
92
|
-
async ensureRun(conversationId
|
|
113
|
+
async ensureRun(conversationId,
|
|
114
|
+
/** From the `bg-wake` job: it fired `wakeRetry - 1` times before its window ended. */
|
|
115
|
+
opts = {}) {
|
|
93
116
|
if ((await this.lock.holder(conversationId)) !== null)
|
|
94
117
|
return null;
|
|
95
118
|
const st = {
|
|
@@ -97,11 +120,17 @@ let RunQueue = class RunQueue {
|
|
|
97
120
|
orphanRunId: null,
|
|
98
121
|
dispatchedSeq: null,
|
|
99
122
|
dispatchedSwitch: false,
|
|
123
|
+
dispatchedUnarchived: false,
|
|
100
124
|
pausedNow: false,
|
|
101
125
|
orphanStop: undefined,
|
|
126
|
+
wakeLater: null,
|
|
102
127
|
};
|
|
103
128
|
const created = await this.ensureRunIn(conversationId, st);
|
|
129
|
+
if (st.wakeLater !== null) {
|
|
130
|
+
await this.scheduleWake(conversationId, st.wakeLater, opts.wakeRetry ?? 0);
|
|
131
|
+
}
|
|
104
132
|
const { orphanSeq, orphanRunId, dispatchedSeq, dispatchedSwitch, pausedNow, orphanStop } = st;
|
|
133
|
+
const { dispatchedUnarchived } = st;
|
|
105
134
|
if (orphanSeq !== null) {
|
|
106
135
|
await notifyFinished(this.eventLog, conversationId, orphanSeq, "failed");
|
|
107
136
|
orphanStop?.fire();
|
|
@@ -114,6 +143,8 @@ let RunQueue = class RunQueue {
|
|
|
114
143
|
await this.eventLog.publishChanged(conversationId, "queue");
|
|
115
144
|
if (dispatchedSwitch)
|
|
116
145
|
await this.eventLog.publishChanged(conversationId, "selection");
|
|
146
|
+
if (dispatchedUnarchived)
|
|
147
|
+
await this.eventLog.publishChanged(conversationId, "archived");
|
|
117
148
|
}
|
|
118
149
|
if (orphanRunId !== null) {
|
|
119
150
|
await this.dropCheckpoints(orphanRunId);
|
|
@@ -153,14 +184,25 @@ let RunQueue = class RunQueue {
|
|
|
153
184
|
const t = this.db;
|
|
154
185
|
const conv = await t
|
|
155
186
|
.selectFrom("conversations")
|
|
156
|
-
.select([
|
|
187
|
+
.select([
|
|
188
|
+
"consumed_seq",
|
|
189
|
+
"org_id",
|
|
190
|
+
"agent_id",
|
|
191
|
+
"kind",
|
|
192
|
+
"queue_paused",
|
|
193
|
+
"account_id",
|
|
194
|
+
"deleted_at",
|
|
195
|
+
])
|
|
157
196
|
.where("id", "=", conversationId)
|
|
158
197
|
.forUpdate()
|
|
159
198
|
.executeTakeFirst();
|
|
160
199
|
// 评测回放的隐藏对话:运行由评测 worker 在里面直接驱动(工具只回放记录),绝不为它起真实运行
|
|
161
200
|
if (conv?.kind !== "chat")
|
|
162
201
|
return null;
|
|
163
|
-
|
|
202
|
+
// A deleted conversation starts no run, whatever is still unconsumed or queued
|
|
203
|
+
if (conv.deleted_at !== null)
|
|
204
|
+
return null;
|
|
205
|
+
const newest = await t
|
|
164
206
|
.selectFrom("conversation_events")
|
|
165
207
|
.select(["seq", "payload"])
|
|
166
208
|
.where("conversation_id", "=", conversationId)
|
|
@@ -168,11 +210,25 @@ let RunQueue = class RunQueue {
|
|
|
168
210
|
.orderBy("seq", "desc")
|
|
169
211
|
.limit(1)
|
|
170
212
|
.executeTakeFirst();
|
|
171
|
-
|
|
213
|
+
/** The run's boundary: its `trigger_seq` and the account it runs as. */
|
|
214
|
+
let last = newest && {
|
|
215
|
+
seq: newest.seq,
|
|
216
|
+
accountId: newest.payload.authorAccountId,
|
|
217
|
+
};
|
|
218
|
+
const idleActive = !last || Number(last.seq) <= Number(conv.consumed_seq)
|
|
219
|
+
? await this.activeRun(t, conversationId)
|
|
220
|
+
: undefined;
|
|
221
|
+
if (idleActive && Number(idleActive.trigger_seq) > Number(conv.consumed_seq)) {
|
|
222
|
+
// No unconsumed message, but a run whose boundary is not consumed yet (a wake run, whose
|
|
223
|
+
// boundary is a finished background task): it goes through the orphan check below with
|
|
224
|
+
// that boundary, as a message's run does
|
|
225
|
+
last = { seq: idleActive.trigger_seq, accountId: idleActive.trigger_account_id };
|
|
226
|
+
}
|
|
227
|
+
else if (!last || Number(last.seq) <= Number(conv.consumed_seq)) {
|
|
172
228
|
// Nothing unconsumed: the queue's head goes out when the conversation is idle (no active
|
|
173
229
|
// run, which includes runs waiting for approval or an answer) and the queue is not paused.
|
|
174
230
|
// Interjections are always unconsumed messages, so the queue never jumps ahead of them.
|
|
175
|
-
if (conv.queue_paused ||
|
|
231
|
+
if (conv.queue_paused || idleActive)
|
|
176
232
|
return null;
|
|
177
233
|
const owner = await t
|
|
178
234
|
.selectFrom("agents")
|
|
@@ -187,32 +243,46 @@ let RunQueue = class RunQueue {
|
|
|
187
243
|
.where("conversation_id", "=", conversationId)
|
|
188
244
|
.limit(1)
|
|
189
245
|
.executeTakeFirst();
|
|
190
|
-
if (
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
246
|
+
if (head) {
|
|
247
|
+
// A queued message may wait for hours: it goes out only while its author can still use
|
|
248
|
+
// the conversation (active, a member, the agent visible to them; the isAccessible rule).
|
|
249
|
+
// Otherwise the queue pauses instead of posting as someone who lost access.
|
|
250
|
+
if (!(await authorCanSend(t, conversationId, conv.account_id))) {
|
|
251
|
+
await pauseQueueWith(t, conversationId, "author_cannot_send");
|
|
252
|
+
st.pausedNow = true;
|
|
253
|
+
this.log.warn("queued message not sent: its author can no longer use the conversation", {
|
|
254
|
+
conversationId,
|
|
255
|
+
});
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
const sent = await dispatchQueuedIn(t, this.eventLog, conversationId);
|
|
259
|
+
if (!sent)
|
|
260
|
+
return null;
|
|
261
|
+
if ("paused" in sent) {
|
|
262
|
+
st.pausedNow = true;
|
|
263
|
+
this.log.warn("queued skill invocation not sent: the skill can no longer be invoked", {
|
|
264
|
+
conversationId,
|
|
265
|
+
});
|
|
266
|
+
return null;
|
|
267
|
+
}
|
|
268
|
+
st.dispatchedSeq = sent.event.seq;
|
|
269
|
+
st.dispatchedSwitch = sent.switched;
|
|
270
|
+
st.dispatchedUnarchived = sent.unarchived;
|
|
271
|
+
last = {
|
|
272
|
+
seq: String(sent.event.seq),
|
|
273
|
+
accountId: sent.event.payload.authorAccountId,
|
|
274
|
+
};
|
|
202
275
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
276
|
+
else {
|
|
277
|
+
// Nothing queued either: a finished background task may wake the idle conversation
|
|
278
|
+
// (background Bash spec §3; a person's message or the queue always goes first)
|
|
279
|
+
const wake = await wakeDecisionIn(t, { id: conversationId, orgId: conv.org_id }, Number(conv.consumed_seq), this.config.get().runtime.wakeOnBackgroundTask, (accountId) => authorCanSend(t, conversationId, accountId));
|
|
280
|
+
if (wake?.kind === "later")
|
|
281
|
+
st.wakeLater = wake;
|
|
282
|
+
if (wake?.kind !== "wake")
|
|
283
|
+
return null;
|
|
284
|
+
last = { seq: String(wake.seq), accountId: wake.triggerAccountId };
|
|
212
285
|
}
|
|
213
|
-
st.dispatchedSeq = sent.event.seq;
|
|
214
|
-
st.dispatchedSwitch = sent.switched;
|
|
215
|
-
last = { seq: String(sent.event.seq), payload: sent.event.payload };
|
|
216
286
|
}
|
|
217
287
|
const active = await this.activeRun(t, conversationId);
|
|
218
288
|
if (active) {
|
|
@@ -254,7 +324,9 @@ let RunQueue = class RunQueue {
|
|
|
254
324
|
.orderBy("seq")
|
|
255
325
|
.limit(1)
|
|
256
326
|
.executeTakeFirst();
|
|
257
|
-
const trigger = fork
|
|
327
|
+
const trigger = fork
|
|
328
|
+
? { seq: fork.seq, accountId: fork.payload.authorAccountId }
|
|
329
|
+
: last;
|
|
258
330
|
// BullMQ 对重复的 jobId 静默忽略(add 返回的 Job 由调用方数据构造,看不出是否真的入队):
|
|
259
331
|
// 先显式查同一触发点的任务;仍存在(例如属于刚结束、尚未从队列移除的运行)就不插入 runs 行。
|
|
260
332
|
const jobId = runJobId(conversationId, Number(trigger.seq));
|
|
@@ -283,12 +355,33 @@ let RunQueue = class RunQueue {
|
|
|
283
355
|
conversation_id: conversationId,
|
|
284
356
|
status: "queued",
|
|
285
357
|
agent_version: agent.current_version,
|
|
286
|
-
trigger_account_id: trigger.
|
|
358
|
+
trigger_account_id: trigger.accountId,
|
|
287
359
|
trigger_seq: Number(trigger.seq),
|
|
288
360
|
})
|
|
289
361
|
.execute();
|
|
290
362
|
return { runId, jobId };
|
|
291
363
|
}
|
|
364
|
+
/**
|
|
365
|
+
* Schedules the delayed `bg-wake` job at the end of the conversation's wake window (after the
|
|
366
|
+
* commit): one job per conversation and window end, so finishes inside one window share it. It
|
|
367
|
+
* calls {@link ensureRun}, which wakes the conversation if a finish is still unconsumed. A failed
|
|
368
|
+
* enqueue is logged: the next message, run end or page opening re-derives the wake.
|
|
369
|
+
*/
|
|
370
|
+
async scheduleWake(conversationId, w, retry) {
|
|
371
|
+
const data = { conversationId, ...(retry > 0 && { retry }) };
|
|
372
|
+
try {
|
|
373
|
+
await this.maintenance.add(BG_WAKE_JOB, data, {
|
|
374
|
+
// a job that fired early reschedules under a new id (its own is still taken)
|
|
375
|
+
jobId: bgWakeJobId(conversationId, w.windowEnd, retry),
|
|
376
|
+
delay: w.delayMs + WAKE_JOB_MARGIN_MS,
|
|
377
|
+
removeOnComplete: true,
|
|
378
|
+
removeOnFail: true,
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
catch (err) {
|
|
382
|
+
this.log.error("cannot schedule the background task wake", { conversationId, err });
|
|
383
|
+
}
|
|
384
|
+
}
|
|
292
385
|
/**
|
|
293
386
|
* Enqueues a run job under the caller's job id (resumes, dispatches, repairs), with the same
|
|
294
387
|
* options as {@link ensureRun} (see {@link RunJobs.enqueue}).
|
|
@@ -407,6 +500,7 @@ let RunQueue = class RunQueue {
|
|
|
407
500
|
"id",
|
|
408
501
|
"status",
|
|
409
502
|
"trigger_seq",
|
|
503
|
+
"trigger_account_id",
|
|
410
504
|
"job_id",
|
|
411
505
|
sql `COALESCE(queued_at, created_at) < now() - make_interval(secs => ${ORPHAN_QUEUED_GRACE_SECONDS})`.as("past_grace"),
|
|
412
506
|
])
|
|
@@ -462,7 +556,9 @@ RunQueue = __decorate([
|
|
|
462
556
|
__param(6, Inject(ToolPipeline)),
|
|
463
557
|
__param(7, Inject(KernelEvents)),
|
|
464
558
|
__param(8, Inject(KERNEL_LOGGER)),
|
|
465
|
-
__param(9, Inject(TransactionHost))
|
|
559
|
+
__param(9, Inject(TransactionHost)),
|
|
560
|
+
__param(10, Inject(KernelConfigService)),
|
|
561
|
+
__param(11, Inject(getQueueToken(MAINTENANCE_QUEUE_NAME)))
|
|
466
562
|
], RunQueue);
|
|
467
563
|
export { RunQueue };
|
|
468
564
|
/** The conversation's owner is active and can still see its agent (`isAccessible`'s rule). */
|
|
@@ -11,6 +11,7 @@ import { Inject, Injectable, Optional } from "@nestjs/common";
|
|
|
11
11
|
import { DelayedError } from "bullmq";
|
|
12
12
|
import { sql } from "kysely";
|
|
13
13
|
import { conversationTopic, EventLog, } from "../conversations/event-log.js";
|
|
14
|
+
import { isAbsorbable, } from "../conversations/events.js";
|
|
14
15
|
import { effectiveDefinition, NO_SELECTION, readSelection, sameModel, sameSelection, } from "../conversations/selection.js";
|
|
15
16
|
import { TitleQueue } from "../conversations/titles.js";
|
|
16
17
|
import { ApiError } from "../errors.js";
|
|
@@ -642,15 +643,16 @@ let RunWorker = class RunWorker {
|
|
|
642
643
|
.executeTakeFirstOrThrow();
|
|
643
644
|
const startConsumed = Number(consumed_seq);
|
|
644
645
|
let absorbed = startConsumed;
|
|
645
|
-
const
|
|
646
|
+
const pendingAbsorbable = async (afterSeq) => {
|
|
646
647
|
// loop 声明已吸收到 afterSeq(续跑时来自 checkpoint,可能包含之前进程吸收的消息)。
|
|
647
648
|
absorbed = Math.max(absorbed, afterSeq);
|
|
648
649
|
const out = [];
|
|
649
650
|
let after = Math.max(afterSeq, startConsumed);
|
|
650
651
|
for (;;) {
|
|
651
652
|
const page = await this.eventLog.readAfter(conversationId, after, READ_PAGE);
|
|
653
|
+
// messages and finished background tasks (spec 2026-11-05-background-bash-design §2.4)
|
|
652
654
|
for (const e of page)
|
|
653
|
-
if (e
|
|
655
|
+
if (isAbsorbable(e))
|
|
654
656
|
out.push(e);
|
|
655
657
|
const last = page.at(-1);
|
|
656
658
|
if (page.length < READ_PAGE || !last)
|
|
@@ -677,7 +679,7 @@ let RunWorker = class RunWorker {
|
|
|
677
679
|
signal: ac.signal,
|
|
678
680
|
shutdown: state.shutdown.signal,
|
|
679
681
|
sink: this.sinkFor(conversationId),
|
|
680
|
-
|
|
682
|
+
pendingAbsorbable,
|
|
681
683
|
limits: { maxSteps, deadline },
|
|
682
684
|
// a sub-agent's conversation fires hooks like a chat, with `parent` set (spec §11)
|
|
683
685
|
conversationKind: conv.kind === "eval" ? "eval" : conv.kind === "subagent" ? "subagent" : "chat",
|
|
@@ -814,6 +816,13 @@ let RunWorker = class RunWorker {
|
|
|
814
816
|
const agent = saved !== undefined
|
|
815
817
|
? effectiveDefinition(saved, await readSelection(t, conversationId))
|
|
816
818
|
: current;
|
|
819
|
+
// a wake run's boundary is a finished background task, not a message (background Bash §3)
|
|
820
|
+
const boundary = await t
|
|
821
|
+
.selectFrom("conversation_events")
|
|
822
|
+
.select("type")
|
|
823
|
+
.where("conversation_id", "=", conversationId)
|
|
824
|
+
.where("seq", "=", run.trigger_seq)
|
|
825
|
+
.executeTakeFirst();
|
|
817
826
|
const [ev] = await this.eventLog.appendIn(conversationId, [
|
|
818
827
|
{
|
|
819
828
|
type: "run_started",
|
|
@@ -823,6 +832,10 @@ let RunWorker = class RunWorker {
|
|
|
823
832
|
agentVersion: run.agent_version,
|
|
824
833
|
model: agent.model,
|
|
825
834
|
triggerAccountId: run.trigger_account_id,
|
|
835
|
+
...(boundary?.type === "background_task_finished" && {
|
|
836
|
+
trigger: "background_task",
|
|
837
|
+
triggerSeq: Number(run.trigger_seq),
|
|
838
|
+
}),
|
|
826
839
|
},
|
|
827
840
|
},
|
|
828
841
|
]);
|
|
@@ -5,6 +5,8 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
7
|
import { Module } from "@nestjs/common";
|
|
8
|
+
import { BackgroundModule } from "../background/background.module.js";
|
|
9
|
+
import { BackgroundWake } from "../background/wake.js";
|
|
8
10
|
import { ConversationAccessModule } from "../conversations/access.js";
|
|
9
11
|
import { AttachmentsModule } from "../conversations/attachments.module.js";
|
|
10
12
|
import { EventLogModule } from "../conversations/event-log.js";
|
|
@@ -28,7 +30,7 @@ import { RunJobsModule } from "./run-jobs.js";
|
|
|
28
30
|
import { RunQueue } from "./run-queue.js";
|
|
29
31
|
import { AgentLoops, RunSteps } from "./run-steps.js";
|
|
30
32
|
import { RunWorker } from "./run-worker.js";
|
|
31
|
-
import {
|
|
33
|
+
import { StepLogModule } from "./step-log.js";
|
|
32
34
|
import { ConversationStreams } from "./stream.js";
|
|
33
35
|
import { SubagentsModule } from "./subagents/subagents.service.js";
|
|
34
36
|
import { UsageReportModule } from "./usage-report.js";
|
|
@@ -58,6 +60,8 @@ RuntimeModule = __decorate([
|
|
|
58
60
|
SubagentsModule,
|
|
59
61
|
UsageReportModule,
|
|
60
62
|
TitlesModule,
|
|
63
|
+
StepLogModule,
|
|
64
|
+
BackgroundModule,
|
|
61
65
|
// the producer side of `wait-timeout` jobs (`MaintenanceProcessor` consumes them)
|
|
62
66
|
QueueModule.registerQueue(MAINTENANCE_QUEUE_NAME),
|
|
63
67
|
],
|
|
@@ -67,7 +71,6 @@ RuntimeModule = __decorate([
|
|
|
67
71
|
RunAbort,
|
|
68
72
|
KyselyCheckpointSaver,
|
|
69
73
|
RunFinish,
|
|
70
|
-
StepLog,
|
|
71
74
|
RunQueue,
|
|
72
75
|
Waits,
|
|
73
76
|
DecisionResumer,
|
|
@@ -75,6 +78,8 @@ RuntimeModule = __decorate([
|
|
|
75
78
|
AgentLoops,
|
|
76
79
|
RunSteps,
|
|
77
80
|
RunWorker,
|
|
81
|
+
// a finished background task wakes an idle conversation (background Bash spec §3)
|
|
82
|
+
BackgroundWake,
|
|
78
83
|
// one quota per key prefix (the org run quota, the org eval quota)
|
|
79
84
|
{
|
|
80
85
|
provide: RUN_QUOTA,
|
|
@@ -93,7 +98,7 @@ RuntimeModule = __decorate([
|
|
|
93
98
|
RunAbort,
|
|
94
99
|
KyselyCheckpointSaver,
|
|
95
100
|
RunFinish,
|
|
96
|
-
|
|
101
|
+
StepLogModule,
|
|
97
102
|
RunQueue,
|
|
98
103
|
Waits,
|
|
99
104
|
RunControl,
|
|
@@ -38,3 +38,9 @@ export declare class StepLog {
|
|
|
38
38
|
spec: ToolSpec;
|
|
39
39
|
}[], events: AppendEvent[]): Promise<ConversationEvent[]>;
|
|
40
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* The step writes ({@link StepLog}): a module of its own, so the tools domain (the `WebFetch`
|
|
43
|
+
* extraction's usage and denials) uses them without importing the runtime.
|
|
44
|
+
*/
|
|
45
|
+
export declare class StepLogModule {
|
|
46
|
+
}
|
package/dist/runtime/step-log.js
CHANGED
|
@@ -7,9 +7,9 @@ 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 { Inject, Injectable } from "@nestjs/common";
|
|
10
|
+
import { Inject, Injectable, Module } from "@nestjs/common";
|
|
11
11
|
import { sql } from "kysely";
|
|
12
|
-
import { EventLog } from "../conversations/event-log.js";
|
|
12
|
+
import { EventLog, EventLogModule } from "../conversations/event-log.js";
|
|
13
13
|
import { usageTokens } from "../evals/budget.js";
|
|
14
14
|
import { audit } from "../identity/audit.js";
|
|
15
15
|
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
@@ -92,3 +92,17 @@ StepLog = __decorate([
|
|
|
92
92
|
__param(2, Inject(TransactionHost))
|
|
93
93
|
], StepLog);
|
|
94
94
|
export { StepLog };
|
|
95
|
+
/**
|
|
96
|
+
* The step writes ({@link StepLog}): a module of its own, so the tools domain (the `WebFetch`
|
|
97
|
+
* extraction's usage and denials) uses them without importing the runtime.
|
|
98
|
+
*/
|
|
99
|
+
let StepLogModule = class StepLogModule {
|
|
100
|
+
};
|
|
101
|
+
StepLogModule = __decorate([
|
|
102
|
+
Module({
|
|
103
|
+
imports: [EventLogModule],
|
|
104
|
+
providers: [StepLog],
|
|
105
|
+
exports: [StepLog],
|
|
106
|
+
})
|
|
107
|
+
], StepLogModule);
|
|
108
|
+
export { StepLogModule };
|
|
@@ -228,7 +228,9 @@ function orgAgentDefinition(d, a) {
|
|
|
228
228
|
}
|
|
229
229
|
const agentId = a.entry.agentId ?? "";
|
|
230
230
|
const own = a.orgAgent.definition;
|
|
231
|
-
|
|
231
|
+
// prompt files are authoring data: the child's model sees only `instructions` (their join), and
|
|
232
|
+
// `subagent_started.definition` keeps its shape (spec 2026-11-04 §3.3)
|
|
233
|
+
const { subagents: _subagents, skills, instructionFiles: _files, ...rest } = own;
|
|
232
234
|
const definition = {
|
|
233
235
|
...rest,
|
|
234
236
|
// its own tools, not intersected with the parent's; only the child-removed ones go
|
|
@@ -93,7 +93,8 @@ export declare function narrowPending(stored: readonly string[] | undefined, fai
|
|
|
93
93
|
* using up an attempt.
|
|
94
94
|
* Handlers must be idempotent by `eventId` (`<conversationId>/<seq>`). Nothing is written to the event
|
|
95
95
|
* log (never model-visible). Compaction calls log usage too (`purpose: "compaction"`) and are reported,
|
|
96
|
-
* as are conversation title calls (`purpose: "title"`, no run: `runId` is "")
|
|
96
|
+
* as are conversation title calls (`purpose: "title"`, no run: `runId` is "") and `WebFetch`
|
|
97
|
+
* extraction calls (`purpose: "web_fetch"`).
|
|
97
98
|
* Single-step eval replays and judge requests log no usage: not reported (M7).
|
|
98
99
|
*/
|
|
99
100
|
export declare class UsageReporter {
|
|
@@ -160,7 +160,8 @@ export function narrowPending(stored, failed) {
|
|
|
160
160
|
* using up an attempt.
|
|
161
161
|
* Handlers must be idempotent by `eventId` (`<conversationId>/<seq>`). Nothing is written to the event
|
|
162
162
|
* log (never model-visible). Compaction calls log usage too (`purpose: "compaction"`) and are reported,
|
|
163
|
-
* as are conversation title calls (`purpose: "title"`, no run: `runId` is "")
|
|
163
|
+
* as are conversation title calls (`purpose: "title"`, no run: `runId` is "") and `WebFetch`
|
|
164
|
+
* extraction calls (`purpose: "web_fetch"`).
|
|
164
165
|
* Single-step eval replays and judge requests log no usage: not reported (M7).
|
|
165
166
|
*/
|
|
166
167
|
let UsageReporter = class UsageReporter {
|
package/dist/runtime/waits.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Logger } from "@capybara-hq/plugin-sdk";
|
|
2
2
|
import { type OnApplicationShutdown } from "@nestjs/common";
|
|
3
3
|
import { type Job, type Queue } from "bullmq";
|
|
4
|
+
import { BackgroundTaskStopper } from "../background/stop.js";
|
|
4
5
|
import { EventLog } from "../conversations/event-log.js";
|
|
5
6
|
import type { KernelSchemaDb } from "../db/kernel-schema.js";
|
|
6
7
|
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
@@ -91,6 +92,8 @@ export declare class Waits implements OnApplicationShutdown {
|
|
|
91
92
|
/** The maintenance queue: wait timeout jobs (`MaintenanceProcessor` consumes them). */
|
|
92
93
|
private readonly timeouts;
|
|
93
94
|
private readonly log;
|
|
95
|
+
/** Stops a finished sub-agent run's background tasks ({@link childFinished}). */
|
|
96
|
+
private readonly background;
|
|
94
97
|
readonly txHost: TransactionHost;
|
|
95
98
|
/** Drops the `run.orphaned` subscription (the events may outlive this instance). */
|
|
96
99
|
private readonly unsubscribe;
|
|
@@ -109,7 +112,9 @@ export declare class Waits implements OnApplicationShutdown {
|
|
|
109
112
|
*/
|
|
110
113
|
abort: Pick<RunAbort, "requested" | "request">,
|
|
111
114
|
/** The maintenance queue: wait timeout jobs (`MaintenanceProcessor` consumes them). */
|
|
112
|
-
timeouts: Pick<Queue, "add">, events: KernelEvents, log: Pick<Logger, "error" | "warn">,
|
|
115
|
+
timeouts: Pick<Queue, "add">, events: KernelEvents, log: Pick<Logger, "error" | "warn">,
|
|
116
|
+
/** Stops a finished sub-agent run's background tasks ({@link childFinished}). */
|
|
117
|
+
background: Pick<BackgroundTaskStopper, "stopSubagentTasks">, txHost: TransactionHost);
|
|
113
118
|
/** Unsubscribes after the stop phases: events emitted while they drain are still handled. */
|
|
114
119
|
onApplicationShutdown(): void;
|
|
115
120
|
/** The context's handle (the transaction of a `@Transactional` method, else the pool). */
|
|
@@ -247,7 +252,8 @@ export declare class Waits implements OnApplicationShutdown {
|
|
|
247
252
|
* the conditional resume lets exactly one succeed, the last to see all its siblings terminal. A
|
|
248
253
|
* run that is not a dispatched child, or not terminal yet, is left alone. A crash in between is
|
|
249
254
|
* repaired by the wait sweep and by the parent's tools node, which writes a missing
|
|
250
|
-
* `subagent_finished` itself.
|
|
255
|
+
* `subagent_finished` itself. The running background tasks the terminal child run started are
|
|
256
|
+
* then stopped (not those of a later follow-up run of the same child).
|
|
251
257
|
*/
|
|
252
258
|
childFinished(childRunId: string): Promise<void>;
|
|
253
259
|
/**
|