@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
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { formatElapsed, lostReasonText } from "../../../background/render.js";
|
|
2
|
+
import { clip, display, sandboxBaseTimeoutMs, unavailableWhenNoSandbox, } from "./common.js";
|
|
3
|
+
import { outputFile, taskEnv } from "./task-output.js";
|
|
4
|
+
/** How many finished background tasks (and finished sub-agents) `TaskList` lists at most. */
|
|
5
|
+
export const TASK_LIST_FINISHED_LIMIT = 20;
|
|
6
|
+
const SPEC = {
|
|
7
|
+
name: "TaskList",
|
|
8
|
+
// Display only (the tool row; not in the fingerprint, never sent to the model).
|
|
9
|
+
keyArgs: ["status"],
|
|
10
|
+
description: "List this conversation's background tasks (started with Bash and run_in_background) and the " +
|
|
11
|
+
"sub-agents it dispatched. Running tasks come first, then the most recently finished ones " +
|
|
12
|
+
"(at most 20); each has its id, description, status (running, " +
|
|
13
|
+
"completed, failed, timed_out, stopped or lost), elapsed time, exit code once finished and " +
|
|
14
|
+
"output file. Sub-agents are listed with their agentId, description and status. Use it to " +
|
|
15
|
+
"find a task id you lost track of, for example after a long run; then use TaskOutput to read " +
|
|
16
|
+
"a task's output or TaskStop to stop it. Statuses are as last checked: TaskOutput checks a " +
|
|
17
|
+
"task now. It only reads, and lists nothing from other conversations.",
|
|
18
|
+
inputSchema: {
|
|
19
|
+
type: "object",
|
|
20
|
+
properties: {
|
|
21
|
+
status: {
|
|
22
|
+
type: "string",
|
|
23
|
+
enum: ["running", "all"],
|
|
24
|
+
description: `"running" lists only what is still running; "all" (the default) also lists the ${TASK_LIST_FINISHED_LIMIT} most recently finished tasks and sub-agents.`,
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
additionalProperties: false,
|
|
28
|
+
},
|
|
29
|
+
annotations: { readOnlyHint: true },
|
|
30
|
+
};
|
|
31
|
+
/** `running for 2m05s`, `failed, exit code 3, after 4m12s`, `lost (the sandbox was suspended), after 3s`. */
|
|
32
|
+
function taskState(task, now) {
|
|
33
|
+
const end = task.status === "running" ? now : (task.finishedAt ?? now);
|
|
34
|
+
const elapsed = formatElapsed(end.getTime() - task.startedAt.getTime());
|
|
35
|
+
if (task.status === "running") {
|
|
36
|
+
return task.sandboxCreatedAt === null ? "starting" : `running for ${elapsed}`;
|
|
37
|
+
}
|
|
38
|
+
let s = task.status;
|
|
39
|
+
if (task.lostReason !== null)
|
|
40
|
+
s += ` (${lostReasonText(task.lostReason)})`;
|
|
41
|
+
if (task.exitCode !== null)
|
|
42
|
+
s += `, exit code ${task.exitCode}`;
|
|
43
|
+
return `${s}, after ${elapsed}`;
|
|
44
|
+
}
|
|
45
|
+
function taskEntry(task, now) {
|
|
46
|
+
const output = task.sandboxCreatedAt === null && task.status !== "running"
|
|
47
|
+
? "none (the command never started)"
|
|
48
|
+
: outputFile(task);
|
|
49
|
+
return `- ${task.id} ${taskState(task, now)}: ${JSON.stringify(task.description)}\n Output: ${output}`;
|
|
50
|
+
}
|
|
51
|
+
const ACTIVE_AGENT = new Set(["queued", "running", "waiting"]);
|
|
52
|
+
const agentStatus = (s) => (s === "aborted" ? "stopped" : s);
|
|
53
|
+
const agentEntry = (a) => `- ${a.id} ${agentStatus(a.status)}: ${JSON.stringify(clip(a.description, 200))}`;
|
|
54
|
+
/** `1 running, 2 finished` (a zero count left out; `none` when both are). */
|
|
55
|
+
function counts(running, finished, onlyRunning) {
|
|
56
|
+
const parts = [];
|
|
57
|
+
if (running > 0)
|
|
58
|
+
parts.push(`${running} running`);
|
|
59
|
+
if (finished > 0)
|
|
60
|
+
parts.push(`${finished} finished`);
|
|
61
|
+
if (parts.length === 0)
|
|
62
|
+
return onlyRunning ? "none running" : "none";
|
|
63
|
+
return parts.join(", ");
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Built-in `TaskList`: the calling conversation's own background tasks (never its sub-agents' or
|
|
67
|
+
* another conversation's: owner `conversation_id`, as `TaskOutput` checks) and the sub-agents it
|
|
68
|
+
* dispatched itself. Running entries first (oldest first), then with `status: "all"` the
|
|
69
|
+
* {@link TASK_LIST_FINISHED_LIMIT} most recently finished. Reads PG only: it never checks a task in
|
|
70
|
+
* its sandbox, so a status is the poller's or a tool's last one.
|
|
71
|
+
*/
|
|
72
|
+
export function taskListTool(deps) {
|
|
73
|
+
return {
|
|
74
|
+
spec: SPEC,
|
|
75
|
+
defaultPolicy: "allow",
|
|
76
|
+
unavailable: unavailableWhenNoSandbox(deps),
|
|
77
|
+
timeoutMs: () => sandboxBaseTimeoutMs(deps.config.get()),
|
|
78
|
+
async run(input, ctx) {
|
|
79
|
+
const onlyRunning = input.status === "running";
|
|
80
|
+
const env = await taskEnv(deps, ctx);
|
|
81
|
+
const store = deps.background;
|
|
82
|
+
const [running, finished, agents] = await Promise.all([
|
|
83
|
+
store.runningOwnedBy(ctx.conversationId),
|
|
84
|
+
onlyRunning
|
|
85
|
+
? { rows: [], total: 0 }
|
|
86
|
+
: store.finishedOwnedBy(ctx.conversationId, TASK_LIST_FINISHED_LIMIT),
|
|
87
|
+
env.subagents?.childrenOf(ctx.orgId, ctx.conversationId) ?? [],
|
|
88
|
+
]);
|
|
89
|
+
const now = new Date();
|
|
90
|
+
const activeAgents = agents.filter((a) => ACTIVE_AGENT.has(a.status));
|
|
91
|
+
const doneAgents = onlyRunning
|
|
92
|
+
? []
|
|
93
|
+
: agents
|
|
94
|
+
.filter((a) => !ACTIVE_AGENT.has(a.status))
|
|
95
|
+
.sort((a, b) => (b.finishedAt?.getTime() ?? 0) - (a.finishedAt?.getTime() ?? 0));
|
|
96
|
+
const listedDone = doneAgents.slice(0, TASK_LIST_FINISHED_LIMIT);
|
|
97
|
+
if (running.length + finished.total + activeAgents.length + doneAgents.length === 0) {
|
|
98
|
+
return result(onlyRunning
|
|
99
|
+
? "No background tasks or sub-agents are running in this conversation."
|
|
100
|
+
: "No background tasks or sub-agents in this conversation.");
|
|
101
|
+
}
|
|
102
|
+
const lines = [
|
|
103
|
+
`Background tasks: ${counts(running.length, finished.total, onlyRunning)}.`,
|
|
104
|
+
...running.map((t) => taskEntry(t, now)),
|
|
105
|
+
...finished.rows.map((t) => taskEntry(t, now)),
|
|
106
|
+
];
|
|
107
|
+
const olderTasks = finished.total - finished.rows.length;
|
|
108
|
+
if (olderTasks > 0)
|
|
109
|
+
lines.push(`(${olderTasks} older finished tasks are not listed.)`);
|
|
110
|
+
if (agents.length > 0) {
|
|
111
|
+
lines.push("", `Sub-agents: ${counts(activeAgents.length, doneAgents.length, onlyRunning)}.`, ...activeAgents.map(agentEntry), ...listedDone.map(agentEntry));
|
|
112
|
+
const olderAgents = doneAgents.length - listedDone.length;
|
|
113
|
+
if (olderAgents > 0)
|
|
114
|
+
lines.push(`(${olderAgents} older finished sub-agents are not listed.)`);
|
|
115
|
+
}
|
|
116
|
+
lines.push("", "Statuses are as last checked. Use TaskOutput to check a task now and read its output, TaskStop to stop it." +
|
|
117
|
+
(agents.length > 0
|
|
118
|
+
? " A sub-agent's report arrives on its own; use SendMessage to give it more instructions."
|
|
119
|
+
: ""));
|
|
120
|
+
return result(lines.join("\n"));
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
function result(text) {
|
|
125
|
+
return {
|
|
126
|
+
content: [{ type: "text", text }],
|
|
127
|
+
isError: false,
|
|
128
|
+
display: display("列出后台任务", "List background tasks"),
|
|
129
|
+
};
|
|
130
|
+
}
|
|
@@ -16,6 +16,7 @@ export interface TaskEnv {
|
|
|
16
16
|
ifRunning: SandboxService["ifRunning"];
|
|
17
17
|
devices: SandboxToolDeps["devices"];
|
|
18
18
|
log: SandboxToolDeps["log"];
|
|
19
|
+
subagents?: SandboxToolDeps["subagents"];
|
|
19
20
|
}
|
|
20
21
|
/**
|
|
21
22
|
* The task tools' dependencies; throws the tool's error when they are missing, or when the
|
|
@@ -23,9 +24,15 @@ export interface TaskEnv {
|
|
|
23
24
|
* ruling R8).
|
|
24
25
|
*/
|
|
25
26
|
export declare function taskEnv(deps: SandboxToolDeps, ctx: BuiltinCallContext): Promise<TaskEnv>;
|
|
27
|
+
/**
|
|
28
|
+
* A sub-agent's agentId passed as a task id (as Claude Code's TaskOutput allows for background
|
|
29
|
+
* agents): its report arrives on its own, so polling or sleeping for it only wastes the run.
|
|
30
|
+
*/
|
|
31
|
+
export declare const subagentNotTaskText: (id: string) => string;
|
|
26
32
|
/**
|
|
27
33
|
* The task `id` when it belongs to the calling conversation; a task of another conversation (a
|
|
28
|
-
* sub-agent's, when called from its parent) is not found, the same as an unknown id.
|
|
34
|
+
* sub-agent's, when called from its parent) is not found, the same as an unknown id. A sub-agent's
|
|
35
|
+
* agentId gets its own text ({@link subagentNotTaskText}).
|
|
29
36
|
*/
|
|
30
37
|
export declare function ownedTask(env: TaskEnv, ctx: BuiltinCallContext, id: string): Promise<BackgroundTaskRow>;
|
|
31
38
|
/**
|
|
@@ -67,16 +67,34 @@ export async function taskEnv(deps, ctx) {
|
|
|
67
67
|
ifRunning: deps.sandbox.ifRunning.bind(deps.sandbox),
|
|
68
68
|
devices: deps.devices,
|
|
69
69
|
log: deps.log,
|
|
70
|
+
...(deps.subagents !== undefined && { subagents: deps.subagents }),
|
|
70
71
|
};
|
|
71
72
|
}
|
|
72
|
-
const notFound = (id) => new ToolError(`No background task ${clip(id, 40)} in this conversation. Pass a task id that Bash with run_in_background returned in this conversation.`);
|
|
73
|
+
const notFound = (id) => new ToolError(`No background task ${clip(id, 40)} in this conversation. Pass a task id that Bash with run_in_background returned in this conversation; call TaskList to see this conversation's tasks.`);
|
|
74
|
+
/**
|
|
75
|
+
* A sub-agent's agentId passed as a task id (as Claude Code's TaskOutput allows for background
|
|
76
|
+
* agents): its report arrives on its own, so polling or sleeping for it only wastes the run.
|
|
77
|
+
*/
|
|
78
|
+
export const subagentNotTaskText = (id) => `${clip(id, 40)} is a sub-agent, not a background task. Its report arrives in this conversation as a notification when it hands back or finishes. Don't poll or sleep for it: keep working or end your turn. Use SendMessage to give it more instructions.`;
|
|
79
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
80
|
+
/** {@link notFound}, or {@link subagentNotTaskText} when `id` is a sub-agent of the caller's tree. */
|
|
81
|
+
async function missingTask(env, ctx, id) {
|
|
82
|
+
const trimmed = id.trim();
|
|
83
|
+
if (env.subagents !== undefined &&
|
|
84
|
+
UUID_RE.test(trimmed) &&
|
|
85
|
+
(await env.subagents.inTree(sandboxConversationOf(ctx), trimmed.toLowerCase()))) {
|
|
86
|
+
return new ToolError(subagentNotTaskText(trimmed));
|
|
87
|
+
}
|
|
88
|
+
return notFound(id);
|
|
89
|
+
}
|
|
73
90
|
/**
|
|
74
91
|
* The task `id` when it belongs to the calling conversation; a task of another conversation (a
|
|
75
|
-
* sub-agent's, when called from its parent) is not found, the same as an unknown id.
|
|
92
|
+
* sub-agent's, when called from its parent) is not found, the same as an unknown id. A sub-agent's
|
|
93
|
+
* agentId gets its own text ({@link subagentNotTaskText}).
|
|
76
94
|
*/
|
|
77
95
|
export async function ownedTask(env, ctx, id) {
|
|
78
96
|
if (!TASK_ID_RE.test(id))
|
|
79
|
-
throw
|
|
97
|
+
throw await missingTask(env, ctx, id);
|
|
80
98
|
const task = await env.store.get(id);
|
|
81
99
|
if (!task || task.conversationId !== ctx.conversationId)
|
|
82
100
|
throw notFound(id);
|
|
@@ -4,7 +4,6 @@ import type { KernelConfigService } from "../../../infra/config/kernel-config.se
|
|
|
4
4
|
import { type ModelPurposesService } from "../../../models/purposes.js";
|
|
5
5
|
import { type HookRunner } from "../../../runtime/hook-calls.js";
|
|
6
6
|
import type { StepLog } from "../../../runtime/step-log.js";
|
|
7
|
-
import type { UsageReporter } from "../../../runtime/usage-report.js";
|
|
8
7
|
import type { BuiltinCallContext } from "../../registry.js";
|
|
9
8
|
/**
|
|
10
9
|
* The `WebFetch` extraction call (web tools spec §5): the fetched page and the model's prompt go to
|
|
@@ -33,13 +32,12 @@ export declare const WEB_FETCH_DENIED_TEXT = "An org policy blocked reading this
|
|
|
33
32
|
/** What {@link extractAnswer} needs. */
|
|
34
33
|
export interface WebFetchExtractDeps {
|
|
35
34
|
purposes: Pick<ModelPurposesService, "chat" | "chatLimits">;
|
|
36
|
-
eventLog: Pick<EventLog, "
|
|
37
|
-
stepLog: Pick<StepLog, "appendCounted" | "appendAudited">;
|
|
35
|
+
eventLog: Pick<EventLog, "notify" | "latestOfType">;
|
|
36
|
+
stepLog: Pick<StepLog, "appendCounted" | "appendReported" | "appendAudited">;
|
|
38
37
|
config: Pick<KernelConfigService, "get">;
|
|
39
38
|
log: Pick<Logger, "warn">;
|
|
40
39
|
/** Extension hooks (absent: none fire). */
|
|
41
40
|
hooks?: HookRunner | undefined;
|
|
42
|
-
usageReport?: Pick<UsageReporter, "enqueue"> | undefined;
|
|
43
41
|
}
|
|
44
42
|
/**
|
|
45
43
|
* The user message of the extraction call: the URL, then the page (title, markdown) between
|
|
@@ -192,7 +192,7 @@ export async function extractAnswer(deps, ctx, page, prompt) {
|
|
|
192
192
|
}
|
|
193
193
|
/**
|
|
194
194
|
* Appends the call's `usage` event (the tool call's run and step, `callId`; idempotent per call)
|
|
195
|
-
* and
|
|
195
|
+
* and owes its `usage.report` delivery in the same transaction. A sub-agent's call also counts on its parent run's
|
|
196
196
|
* `subagent_tokens_used`, in the same transaction ({@link appendCounted}).
|
|
197
197
|
*/
|
|
198
198
|
async function logUsage(deps, ctx, runId, step, u) {
|
|
@@ -209,14 +209,11 @@ async function logUsage(deps, ctx, runId, step, u) {
|
|
|
209
209
|
callId: ctx.callId,
|
|
210
210
|
},
|
|
211
211
|
};
|
|
212
|
-
|
|
212
|
+
await appendCounted(deps, {
|
|
213
213
|
conversationId: ctx.conversationId,
|
|
214
214
|
parentRunId: ctx.parentRunId,
|
|
215
215
|
budgetRunId: ctx.budgetRunId,
|
|
216
216
|
}, [event]);
|
|
217
|
-
const usage = written.find((e) => e.type === "usage");
|
|
218
|
-
if (usage)
|
|
219
|
-
void deps.usageReport?.enqueue(ctx.conversationId, usage.seq);
|
|
220
217
|
}
|
|
221
218
|
/**
|
|
222
219
|
* Records a `model.request` denial of the extraction call: the `hook_denied` event (the tool call's
|
package/dist/tools/pipeline.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type FormAnswer, type InputForm, type Logger } from "@capybara-hq/plugin-sdk";
|
|
1
|
+
import { type ConversationSource, type FormAnswer, type InputForm, type Logger } from "@capybara-hq/plugin-sdk";
|
|
2
2
|
import type { AgentDefinition } from "../agents/definition.js";
|
|
3
3
|
import { EventLog } from "../conversations/event-log.js";
|
|
4
4
|
import type { KernelSchemaDb } from "../db/kernel-schema.js";
|
|
@@ -222,8 +222,17 @@ export interface ToolStepContext {
|
|
|
222
222
|
/** 运行所用的 agent 版本(审计链记录)。 */
|
|
223
223
|
agentVersion: number;
|
|
224
224
|
agent: AgentDefinition;
|
|
225
|
-
/**
|
|
225
|
+
/** The run's trigger account (personal credentials, the audit actor). */
|
|
226
226
|
accountId: string;
|
|
227
|
+
/**
|
|
228
|
+
* The conversation's source (memory plugin spec §6.2): extension tools limited by
|
|
229
|
+
* `spec.sources` are resolved with it, and plugin handlers receive it.
|
|
230
|
+
*/
|
|
231
|
+
source: ConversationSource;
|
|
232
|
+
/** The conversation's owner (`conversations.account_id`), given to plugin handlers. */
|
|
233
|
+
ownerAccountId: string;
|
|
234
|
+
/** The trigger account's current org role, given to plugin handlers. */
|
|
235
|
+
role: OrgRole;
|
|
227
236
|
/** run 的中止信号(用户中止 / 截止时间)。 */
|
|
228
237
|
signal: AbortSignal;
|
|
229
238
|
/** run 的截止时间(epoch 毫秒);传给内置工具(沙箱命令据此收紧超时,Ruling M20)。 */
|
|
@@ -310,7 +319,7 @@ export declare class ToolPipeline {
|
|
|
310
319
|
runStep(given: ToolStepContext): Promise<ToolStepOutcome>;
|
|
311
320
|
/**
|
|
312
321
|
* Writes, in the caller's transaction (`mandatory`), what a run that is being stopped (a person,
|
|
313
|
-
* its deadline, a failure,
|
|
322
|
+
* its deadline, a failure, an orphan repair) still owes its log: the report of its fork invocation
|
|
314
323
|
* (phase D, `pairStoppedForks`; a still active fork child is stopped by the caller after its
|
|
315
324
|
* commit). An `Agent` call has its result from its dispatch (agent map spec §4.1), so there is
|
|
316
325
|
* nothing to write for it. Returns the fork's call id (the caller must not write a "not executed"
|
package/dist/tools/pipeline.js
CHANGED
|
@@ -314,6 +314,7 @@ let ToolPipeline = class ToolPipeline {
|
|
|
314
314
|
forkChild: ctx.parent?.forSkill === true,
|
|
315
315
|
sandboxConversationId: ctx.parent?.rootConversationId ?? ctx.conversationId,
|
|
316
316
|
replay: ctx.conversationKind === "eval",
|
|
317
|
+
source: ctx.source,
|
|
317
318
|
});
|
|
318
319
|
}
|
|
319
320
|
return resolved;
|
|
@@ -331,7 +332,7 @@ let ToolPipeline = class ToolPipeline {
|
|
|
331
332
|
}
|
|
332
333
|
/**
|
|
333
334
|
* Writes, in the caller's transaction (`mandatory`), what a run that is being stopped (a person,
|
|
334
|
-
* its deadline, a failure,
|
|
335
|
+
* its deadline, a failure, an orphan repair) still owes its log: the report of its fork invocation
|
|
335
336
|
* (phase D, `pairStoppedForks`; a still active fork child is stopped by the caller after its
|
|
336
337
|
* commit). An `Agent` call has its result from its dispatch (agent map spec §4.1), so there is
|
|
337
338
|
* nothing to write for it. Returns the fork's call id (the caller must not write a "not executed"
|
|
@@ -1001,6 +1002,10 @@ let ToolPipeline = class ToolPipeline {
|
|
|
1001
1002
|
conversationId: ctx.conversationId,
|
|
1002
1003
|
callId,
|
|
1003
1004
|
...(resume !== undefined && { resume }),
|
|
1005
|
+
// who and where (memory plugin spec §6.2): `accountId` stays the trigger account
|
|
1006
|
+
source: ctx.source,
|
|
1007
|
+
ownerAccountId: ctx.ownerAccountId,
|
|
1008
|
+
role: ctx.role,
|
|
1004
1009
|
...(tool.source.kind === "builtin" && stepTools !== undefined && { tools: stepTools }),
|
|
1005
1010
|
...(tool.source.kind === "builtin" && { runId: ctx.runId }),
|
|
1006
1011
|
...(tool.source.kind === "builtin" &&
|
|
@@ -4,6 +4,7 @@ import { KernelConfigService } from "../infra/config/kernel-config.service.js";
|
|
|
4
4
|
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
5
5
|
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
6
6
|
import { KernelEvents } from "../infra/events/kernel-events.js";
|
|
7
|
+
import { RunResume } from "../runtime/run-jobs.js";
|
|
7
8
|
export type QuestionStatus = "pending" | "answered" | "skipped" | "expired" | "cancelled";
|
|
8
9
|
/** 一条输入请求(`AskUserQuestion`、插件工具的 `input_required`、MCP elicitation 共用)。 */
|
|
9
10
|
export interface QuestionRecord {
|
|
@@ -44,9 +45,11 @@ export type QuestionView = Omit<QuestionRecord, "state">;
|
|
|
44
45
|
* 状态全部在 PG:回答 / 超时 / 取消都在事务里 `SELECT … FOR UPDATE` 该行,多副本并发时恰好一个生效,
|
|
45
46
|
* 只有生效的那次写 `question_answered`。HTTP 接口见 questions.controller.ts。
|
|
46
47
|
*
|
|
47
|
-
* An answer
|
|
48
|
-
*
|
|
49
|
-
*
|
|
48
|
+
* An answer resumes its waiting run in the answer's transaction (`RunResume.resumeIn`, after the
|
|
49
|
+
* question row: lock order conversation, question, then run; transactional outbox spec §6). It is
|
|
50
|
+
* announced after the commit as the domain event `question.answered` (exactly once; no kernel
|
|
51
|
+
* subscriber: the IM card update is owed by the `question_answered` append, transactional outbox
|
|
52
|
+
* spec §8.1). Events are appended in the context's transaction (`EventLog.appendIn`).
|
|
50
53
|
*/
|
|
51
54
|
export declare class QuestionsService {
|
|
52
55
|
private readonly db;
|
|
@@ -54,7 +57,11 @@ export declare class QuestionsService {
|
|
|
54
57
|
private readonly eventLog;
|
|
55
58
|
private readonly events;
|
|
56
59
|
readonly txHost: TransactionHost;
|
|
57
|
-
|
|
60
|
+
/** An answer resumes its waiting run in the answer's transaction. */
|
|
61
|
+
private readonly resumes;
|
|
62
|
+
constructor(db: KernelDb, config: Pick<KernelConfigService, "get">, eventLog: Pick<EventLog, "appendIn" | "notify">, events: Pick<KernelEvents, "emit">, txHost: TransactionHost,
|
|
63
|
+
/** An answer resumes its waiting run in the answer's transaction. */
|
|
64
|
+
resumes: Pick<RunResume, "resumeIn">);
|
|
58
65
|
/**
|
|
59
66
|
* 新建 pending 输入请求(`expires_at` = 现在 + `tools.approvalTimeoutHours`)并写 `question_asked`,同一事务。
|
|
60
67
|
* 按 (run_id, call_id, attempt) 幂等:已存在则返回已有记录,事件按 `question_asked:<id>` 幂等。
|
|
@@ -98,11 +105,22 @@ export declare class QuestionsService {
|
|
|
98
105
|
*/
|
|
99
106
|
cancelPendingIn(runId: string): Promise<QuestionRecord[]>;
|
|
100
107
|
/**
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
108
|
+
* The conversations of `orgId` holding a pending question of a run `accountId` started (only the
|
|
109
|
+
* run's starter answers), read without locks: offboarding locks them, by tree root first
|
|
110
|
+
* (`lockConversationTrees`), before {@link cancelPendingFor}.
|
|
104
111
|
*/
|
|
105
|
-
|
|
112
|
+
pendingConversationsFor(orgId: string, accountId: string): Promise<string[]>;
|
|
113
|
+
/**
|
|
114
|
+
* In the caller's transaction (`mandatory`; offboarding): cancels every pending question of a run
|
|
115
|
+
* `accountId` started in `orgId` within `conversationIds`, the conversations the caller already
|
|
116
|
+
* locked (by tree root first, `lockConversationTrees`; lock order D7: conversation, then question
|
|
117
|
+
* rows), along the same path as {@link cancelPendingIn}: `cancelled`,
|
|
118
|
+
* `question_answered{cancelled}`. A question asked meanwhile in another conversation waits for
|
|
119
|
+
* its run's stop (offboarding owes it), so no conversation is locked after a row. Returns the
|
|
120
|
+
* cancelled records and the highest seq written per conversation; the caller publishes after the
|
|
121
|
+
* commit.
|
|
122
|
+
*/
|
|
123
|
+
cancelPendingFor(orgId: string, accountId: string, conversationIds: readonly string[]): Promise<{
|
|
106
124
|
records: QuestionRecord[];
|
|
107
125
|
seqs: Map<string, number>;
|
|
108
126
|
}>;
|
|
@@ -113,8 +131,10 @@ export declare class QuestionsService {
|
|
|
113
131
|
* by a run of it that is `running` or `waiting` and was started by `accountId`. The caller holds
|
|
114
132
|
* the conversation row lock (its `appendIn`), so only questions logged before the message are
|
|
115
133
|
* seen. Each becomes `skipped` with `question_answered { outcome: "skipped", skippedBySeq }`.
|
|
116
|
-
*
|
|
117
|
-
* `
|
|
134
|
+
* The caller resumes their waiting runs in this transaction after it locked every row it skips
|
|
135
|
+
* (`ConversationsService.skipWaitsIn`: conversations, decided rows, runs). Returns the records and
|
|
136
|
+
* the highest seq written (null: none); the caller notifies and emits `question.answered` after
|
|
137
|
+
* the commit.
|
|
118
138
|
*/
|
|
119
139
|
skipPendingIn(conversationId: string, accountId: string, messageSeq: number): Promise<{
|
|
120
140
|
records: QuestionRecord[];
|
|
@@ -18,6 +18,7 @@ import { KernelDb } from "../infra/database/kernel-db.js";
|
|
|
18
18
|
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
19
19
|
import { Transactional } from "../infra/database/transactional.decorator.js";
|
|
20
20
|
import { KernelEvents } from "../infra/events/kernel-events.js";
|
|
21
|
+
import { RunResume } from "../runtime/run-jobs.js";
|
|
21
22
|
const notFound = () => new ApiError("question.not_found", "Question not found");
|
|
22
23
|
const notInitiator = () => new ApiError("forbidden", "Only the user who started the run can answer this question");
|
|
23
24
|
const toView = ({ state: _state, ...rest }) => rest;
|
|
@@ -49,9 +50,11 @@ const isPlainObject = (v) => typeof v === "object" && v !== null && !Array.isArr
|
|
|
49
50
|
* 状态全部在 PG:回答 / 超时 / 取消都在事务里 `SELECT … FOR UPDATE` 该行,多副本并发时恰好一个生效,
|
|
50
51
|
* 只有生效的那次写 `question_answered`。HTTP 接口见 questions.controller.ts。
|
|
51
52
|
*
|
|
52
|
-
* An answer
|
|
53
|
-
*
|
|
54
|
-
*
|
|
53
|
+
* An answer resumes its waiting run in the answer's transaction (`RunResume.resumeIn`, after the
|
|
54
|
+
* question row: lock order conversation, question, then run; transactional outbox spec §6). It is
|
|
55
|
+
* announced after the commit as the domain event `question.answered` (exactly once; no kernel
|
|
56
|
+
* subscriber: the IM card update is owed by the `question_answered` append, transactional outbox
|
|
57
|
+
* spec §8.1). Events are appended in the context's transaction (`EventLog.appendIn`).
|
|
55
58
|
*/
|
|
56
59
|
let QuestionsService = class QuestionsService {
|
|
57
60
|
db;
|
|
@@ -59,12 +62,16 @@ let QuestionsService = class QuestionsService {
|
|
|
59
62
|
eventLog;
|
|
60
63
|
events;
|
|
61
64
|
txHost;
|
|
62
|
-
|
|
65
|
+
resumes;
|
|
66
|
+
constructor(db, config, eventLog, events, txHost,
|
|
67
|
+
/** An answer resumes its waiting run in the answer's transaction. */
|
|
68
|
+
resumes) {
|
|
63
69
|
this.db = db;
|
|
64
70
|
this.config = config;
|
|
65
71
|
this.eventLog = eventLog;
|
|
66
72
|
this.events = events;
|
|
67
73
|
this.txHost = txHost;
|
|
74
|
+
this.resumes = resumes;
|
|
68
75
|
}
|
|
69
76
|
/**
|
|
70
77
|
* 新建 pending 输入请求(`expires_at` = 现在 + `tools.approvalTimeoutHours`)并写 `question_asked`,同一事务。
|
|
@@ -163,7 +170,7 @@ let QuestionsService = class QuestionsService {
|
|
|
163
170
|
async answer(questionId, accountId, answer, orgId) {
|
|
164
171
|
const { record, toSeq } = await this.answerIn(questionId, accountId, answer, orgId);
|
|
165
172
|
await this.eventLog.notify(record.conversationId, toSeq);
|
|
166
|
-
//
|
|
173
|
+
// its IM delivery is enqueued (handler failures are logged); the run resumed in the answer
|
|
167
174
|
await this.events.emit("question.answered", {
|
|
168
175
|
id: record.id,
|
|
169
176
|
orgId: record.orgId,
|
|
@@ -246,6 +253,8 @@ let QuestionsService = class QuestionsService {
|
|
|
246
253
|
.returningAll()
|
|
247
254
|
.executeTakeFirstOrThrow();
|
|
248
255
|
const toSeq = await this.answeredEvent(before, answer, accountId, "answered");
|
|
256
|
+
// the run last; a still-running run is resumed by its suspend's re-check
|
|
257
|
+
await this.resumes.resumeIn(before.runId, questionId);
|
|
249
258
|
return { record: toRecord(updated), toSeq };
|
|
250
259
|
}
|
|
251
260
|
/**
|
|
@@ -315,14 +324,12 @@ let QuestionsService = class QuestionsService {
|
|
|
315
324
|
return out;
|
|
316
325
|
}
|
|
317
326
|
/**
|
|
318
|
-
*
|
|
319
|
-
*
|
|
320
|
-
*
|
|
327
|
+
* The conversations of `orgId` holding a pending question of a run `accountId` started (only the
|
|
328
|
+
* run's starter answers), read without locks: offboarding locks them, by tree root first
|
|
329
|
+
* (`lockConversationTrees`), before {@link cancelPendingFor}.
|
|
321
330
|
*/
|
|
322
|
-
async
|
|
323
|
-
const
|
|
324
|
-
// the conversations first, in id order (lock order D7: conversation, then question rows)
|
|
325
|
-
const convs = await t
|
|
331
|
+
async pendingConversationsFor(orgId, accountId) {
|
|
332
|
+
const rows = await this.db
|
|
326
333
|
.selectFrom("questions")
|
|
327
334
|
.select("conversation_id")
|
|
328
335
|
.distinct()
|
|
@@ -330,24 +337,28 @@ let QuestionsService = class QuestionsService {
|
|
|
330
337
|
.where("status", "=", "pending")
|
|
331
338
|
.where("run_id", "in", (eb) => eb.selectFrom("runs").select("id").where("trigger_account_id", "=", accountId))
|
|
332
339
|
.execute();
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
340
|
+
return rows.map((r) => r.conversation_id);
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* In the caller's transaction (`mandatory`; offboarding): cancels every pending question of a run
|
|
344
|
+
* `accountId` started in `orgId` within `conversationIds`, the conversations the caller already
|
|
345
|
+
* locked (by tree root first, `lockConversationTrees`; lock order D7: conversation, then question
|
|
346
|
+
* rows), along the same path as {@link cancelPendingIn}: `cancelled`,
|
|
347
|
+
* `question_answered{cancelled}`. A question asked meanwhile in another conversation waits for
|
|
348
|
+
* its run's stop (offboarding owes it), so no conversation is locked after a row. Returns the
|
|
349
|
+
* cancelled records and the highest seq written per conversation; the caller publishes after the
|
|
350
|
+
* commit.
|
|
351
|
+
*/
|
|
352
|
+
async cancelPendingFor(orgId, accountId, conversationIds) {
|
|
353
|
+
const t = this.db.db;
|
|
354
|
+
const convs = [...conversationIds];
|
|
344
355
|
if (convs.length === 0)
|
|
345
356
|
return { records: [], seqs: new Map() };
|
|
346
357
|
const rows = await t
|
|
347
358
|
.selectFrom("questions")
|
|
348
359
|
.selectAll()
|
|
349
360
|
.where("org_id", "=", orgId)
|
|
350
|
-
.where("conversation_id", "in", convs
|
|
361
|
+
.where("conversation_id", "in", convs)
|
|
351
362
|
.where("status", "=", "pending")
|
|
352
363
|
.where("run_id", "in", (eb) => eb.selectFrom("runs").select("id").where("trigger_account_id", "=", accountId))
|
|
353
364
|
.orderBy("created_at")
|
|
@@ -375,8 +386,10 @@ let QuestionsService = class QuestionsService {
|
|
|
375
386
|
* by a run of it that is `running` or `waiting` and was started by `accountId`. The caller holds
|
|
376
387
|
* the conversation row lock (its `appendIn`), so only questions logged before the message are
|
|
377
388
|
* seen. Each becomes `skipped` with `question_answered { outcome: "skipped", skippedBySeq }`.
|
|
378
|
-
*
|
|
379
|
-
* `
|
|
389
|
+
* The caller resumes their waiting runs in this transaction after it locked every row it skips
|
|
390
|
+
* (`ConversationsService.skipWaitsIn`: conversations, decided rows, runs). Returns the records and
|
|
391
|
+
* the highest seq written (null: none); the caller notifies and emits `question.answered` after
|
|
392
|
+
* the commit.
|
|
380
393
|
*/
|
|
381
394
|
async skipPendingIn(conversationId, accountId, messageSeq) {
|
|
382
395
|
const t = this.db.db;
|
|
@@ -451,6 +464,7 @@ QuestionsService = __decorate([
|
|
|
451
464
|
__param(1, Inject(KernelConfigService)),
|
|
452
465
|
__param(2, Inject(EventLog)),
|
|
453
466
|
__param(3, Inject(KernelEvents)),
|
|
454
|
-
__param(4, Inject(TransactionHost))
|
|
467
|
+
__param(4, Inject(TransactionHost)),
|
|
468
|
+
__param(5, Inject(RunResume))
|
|
455
469
|
], QuestionsService);
|
|
456
470
|
export { QuestionsService };
|
package/dist/tools/registry.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Connector, HookParentScope, InputRequired, NetworkBlockedHost, ToolAnnotations, ToolCallContext, ToolResult, ToolSpec } from "@capybara-hq/plugin-sdk";
|
|
1
|
+
import type { Connector, ConversationSource, HookParentScope, InputRequired, NetworkBlockedHost, ToolAnnotations, ToolCallContext, ToolResult, ToolSpec } from "@capybara-hq/plugin-sdk";
|
|
2
2
|
import { type AgentDefinition, type DisabledToolRef, type SubagentTools } from "../agents/definition.js";
|
|
3
3
|
import type { ToolView } from "../conversations/events.js";
|
|
4
4
|
import { EffectiveConfigs } from "../extensions/effective-config.js";
|
|
@@ -40,6 +40,8 @@ export interface BuiltinCallContext extends ToolCallContext {
|
|
|
40
40
|
/**
|
|
41
41
|
* 本次运行的截止时间(epoch 毫秒,`runtime.maxRunSeconds` 起算,Ruling M20)。到点运行被中止;
|
|
42
42
|
* 沙箱命令按它收紧超时,赶在截止前自己超时、给模型可执行的说明。
|
|
43
|
+
* Absent when the run has no duration limit (`runtime.maxRunSeconds` 0): commands are then limited
|
|
44
|
+
* only by `sandbox.limits.maxCommandSeconds` and the tool timeouts.
|
|
43
45
|
*/
|
|
44
46
|
runDeadline?: number;
|
|
45
47
|
/**
|
|
@@ -211,6 +213,8 @@ export declare function withoutDisplayFields<T>(spec: T): T;
|
|
|
211
213
|
export declare function specHash(spec: unknown): string;
|
|
212
214
|
/** 由注解推导默认策略:只读 → allow;其余(破坏性 / 无注解)→ ask。 */
|
|
213
215
|
export declare function policyFromAnnotations(a: ToolAnnotations | undefined): "allow" | "ask";
|
|
216
|
+
/** Why an extension tool limited by `spec.sources` is not offered in a `source` conversation. */
|
|
217
|
+
export declare const sourceHiddenReason: (source: ConversationSource) => string;
|
|
214
218
|
/** `forAgent` 的结果。 */
|
|
215
219
|
export interface AgentTools {
|
|
216
220
|
tools: ResolvedTool[];
|
|
@@ -236,7 +240,7 @@ export interface AgentTools {
|
|
|
236
240
|
*
|
|
237
241
|
* What is offered:
|
|
238
242
|
* - builtins: every {@link AGENT_BUILTIN_NAMES} entry registered here that can run (`unavailable`
|
|
239
|
-
* says why not), `TaskOutput` / `TaskStop` with `Bash`, `Agent` while its roster is not empty;
|
|
243
|
+
* says why not), `TaskOutput` / `TaskStop` / `TaskList` with `Bash`, `Agent` while its roster is not empty;
|
|
240
244
|
* - extension tools: every tool registered by an extension loaded on this replica and enabled in
|
|
241
245
|
* the org (`extension_org_configs.enabled`; required bootstrap plugins are enabled in every org,
|
|
242
246
|
* `enabled.ts`), whose effective config for the org is valid (layering spec A §6) and that the
|
|
@@ -360,6 +364,11 @@ export declare class ToolRegistry {
|
|
|
360
364
|
sandboxConversationId?: string;
|
|
361
365
|
/** An eval replay's listing: its calls are never executed (the sandbox tools are listed). */
|
|
362
366
|
replay?: boolean;
|
|
367
|
+
/**
|
|
368
|
+
* The conversation's source (memory plugin spec §6.2): an extension tool whose
|
|
369
|
+
* `spec.sources` leaves it out is hidden. Required, so nothing defaults to the widest.
|
|
370
|
+
*/
|
|
371
|
+
source: ConversationSource;
|
|
363
372
|
}): Promise<AgentTools>;
|
|
364
373
|
/** 是否启用 `ToolSearch`(见 {@link forAgent});先比阈值,超过才读组织设置。 */
|
|
365
374
|
private searchActive;
|
|
@@ -380,7 +389,7 @@ export declare class ToolRegistry {
|
|
|
380
389
|
}
|
|
381
390
|
/**
|
|
382
391
|
* `tools` narrowed by a sub-agent's key filter (tools default-on spec §4): `deny` keys removed,
|
|
383
|
-
* and with `allow` only its keys kept. `TaskOutput` / `TaskStop` follow `Bash`, as they come with
|
|
392
|
+
* and with `allow` only its keys kept. `TaskOutput` / `TaskStop` / `TaskList` follow `Bash`, as they come with
|
|
384
393
|
* it. Without a filter the list is returned as it is.
|
|
385
394
|
*/
|
|
386
395
|
export declare function filterSubagentTools(tools: ResolvedTool[], filter: SubagentTools | undefined): ResolvedTool[];
|
package/dist/tools/registry.js
CHANGED
|
@@ -59,6 +59,8 @@ export function specHash(spec) {
|
|
|
59
59
|
export function policyFromAnnotations(a) {
|
|
60
60
|
return a?.readOnlyHint === true ? "allow" : "ask";
|
|
61
61
|
}
|
|
62
|
+
/** Why an extension tool limited by `spec.sources` is not offered in a `source` conversation. */
|
|
63
|
+
export const sourceHiddenReason = (source) => `Not available in ${source} conversations`;
|
|
62
64
|
// `ToolSearch` is added by the registry by org setting and threshold, `Skill` when the log has a
|
|
63
65
|
// skills catalog snapshot; neither comes from agent definitions (`./builtin-names.ts`).
|
|
64
66
|
/** 工具定义(模型可见三项)的估算 token 数(与请求估算同一口径)。 */
|
|
@@ -92,7 +94,7 @@ const builtinTool = (b, spec) => ({
|
|
|
92
94
|
*
|
|
93
95
|
* What is offered:
|
|
94
96
|
* - builtins: every {@link AGENT_BUILTIN_NAMES} entry registered here that can run (`unavailable`
|
|
95
|
-
* says why not), `TaskOutput` / `TaskStop` with `Bash`, `Agent` while its roster is not empty;
|
|
97
|
+
* says why not), `TaskOutput` / `TaskStop` / `TaskList` with `Bash`, `Agent` while its roster is not empty;
|
|
96
98
|
* - extension tools: every tool registered by an extension loaded on this replica and enabled in
|
|
97
99
|
* the org (`extension_org_configs.enabled`; required bootstrap plugins are enabled in every org,
|
|
98
100
|
* `enabled.ts`), whose effective config for the org is valid (layering spec A §6) and that the
|
|
@@ -348,6 +350,7 @@ let ToolRegistry = class ToolRegistry {
|
|
|
348
350
|
}
|
|
349
351
|
async resolve(orgId, def) {
|
|
350
352
|
const disabled = def.disabledTools ?? [];
|
|
353
|
+
const { source } = def;
|
|
351
354
|
const scope = {
|
|
352
355
|
orgId,
|
|
353
356
|
...(def.sandboxConversationId !== undefined && {
|
|
@@ -396,7 +399,8 @@ let ToolRegistry = class ToolRegistry {
|
|
|
396
399
|
spec = agentToolSpec(roster, await this.maxConcurrentSubagents(orgId));
|
|
397
400
|
}
|
|
398
401
|
add(builtinTool(b, spec));
|
|
399
|
-
// `TaskOutput` and `
|
|
402
|
+
// `TaskOutput`, `TaskStop` and `TaskList` come with an available `Bash` (background Bash
|
|
403
|
+
// spec §2)
|
|
400
404
|
if (name === BUILTIN.Bash) {
|
|
401
405
|
for (const companion of BASH_COMPANION_TOOLS) {
|
|
402
406
|
const c = this.builtins.find((x) => x.spec.name === companion);
|
|
@@ -434,6 +438,12 @@ let ToolRegistry = class ToolRegistry {
|
|
|
434
438
|
hidden.push({ ref: key, reason: toolTurnedOffReason(key) });
|
|
435
439
|
continue;
|
|
436
440
|
}
|
|
441
|
+
// offered only in some conversation sources; the source never changes, so this also
|
|
442
|
+
// refuses a resumed call (the pipeline resolves the step's tools with the same source)
|
|
443
|
+
if (t.spec.sources !== undefined && !t.spec.sources.includes(source)) {
|
|
444
|
+
hidden.push({ ref: key, reason: sourceHiddenReason(source) });
|
|
445
|
+
continue;
|
|
446
|
+
}
|
|
437
447
|
if (!(await configuredIn(t.plugin))) {
|
|
438
448
|
hidden.push({ ref: key, reason: `Plugin ${t.plugin} ${NEEDS_CONFIG_REASON}` });
|
|
439
449
|
continue;
|
|
@@ -585,7 +595,7 @@ ToolRegistry = __decorate([
|
|
|
585
595
|
export { ToolRegistry };
|
|
586
596
|
/**
|
|
587
597
|
* `tools` narrowed by a sub-agent's key filter (tools default-on spec §4): `deny` keys removed,
|
|
588
|
-
* and with `allow` only its keys kept. `TaskOutput` / `TaskStop` follow `Bash`, as they come with
|
|
598
|
+
* and with `allow` only its keys kept. `TaskOutput` / `TaskStop` / `TaskList` follow `Bash`, as they come with
|
|
589
599
|
* it. Without a filter the list is returned as it is.
|
|
590
600
|
*/
|
|
591
601
|
export function filterSubagentTools(tools, filter) {
|