@capybara-hq/kernel 0.1.0-rc.2 → 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 +20 -2
- 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 +49 -13
- package/dist/agents/definition.d.ts +35 -8
- package/dist/agents/definition.js +203 -17
- 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 +20 -5
- 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/catalog-content.js +4 -1
- package/dist/catalog/claude/agents.js +15 -10
- 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.d.ts +8 -2
- package/dist/channels/cards.js +17 -9
- 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 +104 -2
- package/dist/conversations/access.js +2 -0
- package/dist/conversations/artifacts.d.ts +1 -1
- package/dist/conversations/artifacts.js +1 -1
- 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 +12 -1
- package/dist/conversations/event-log.js +29 -1
- package/dist/conversations/events.d.ts +81 -3
- package/dist/conversations/events.js +83 -16
- 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 +60 -4
- 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/recorded-tools.d.ts +27 -1
- package/dist/evals/recorded-tools.js +85 -10
- package/dist/evals/run-replay.d.ts +10 -2
- package/dist/evals/run-replay.js +49 -12
- package/dist/evals/step-replay.js +12 -6
- package/dist/evals/trajectory.d.ts +2 -2
- 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/identity/settings.js +1 -1
- 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/lifecycle/kernel-lifecycle.js +5 -0
- 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 +7 -2
- package/dist/orgs/settings.service.js +15 -4
- package/dist/roles/worker.module.js +6 -0
- package/dist/runtime/context/build.d.ts +15 -13
- package/dist/runtime/context/build.js +45 -27
- package/dist/runtime/context/claude-tools.d.ts +3 -1
- package/dist/runtime/context/claude-tools.js +12 -12
- package/dist/runtime/context/compaction.js +4 -0
- package/dist/runtime/context/legacy-text.d.ts +22 -0
- package/dist/runtime/context/legacy-text.js +37 -0
- package/dist/runtime/context/skills.d.ts +29 -11
- package/dist/runtime/context/skills.js +37 -30
- package/dist/runtime/langgraph-loop.js +3 -2
- package/dist/runtime/loop.d.ts +5 -4
- 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 +4 -3
- package/dist/runtime/providers.d.ts +6 -0
- package/dist/runtime/providers.js +8 -0
- package/dist/runtime/run-finish.d.ts +1 -1
- package/dist/runtime/run-queue.d.ts +28 -5
- package/dist/runtime/run-queue.js +131 -35
- package/dist/runtime/run-worker.d.ts +2 -2
- package/dist/runtime/run-worker.js +20 -7
- 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.d.ts +1 -6
- package/dist/runtime/subagents/definition.js +14 -13
- package/dist/runtime/subagents/dispatch.d.ts +2 -2
- package/dist/runtime/subagents/dispatch.js +2 -2
- package/dist/runtime/subagents/org-agents.js +2 -1
- package/dist/runtime/subagents/roster.d.ts +1 -1
- package/dist/runtime/subagents/roster.js +1 -1
- package/dist/runtime/subagents/skill-fork.d.ts +2 -2
- package/dist/runtime/subagents/skill-fork.js +2 -2
- package/dist/runtime/subagents/subagents.service.d.ts +3 -3
- package/dist/runtime/subagents/subagents.service.js +3 -3
- package/dist/runtime/usage-report.d.ts +2 -1
- package/dist/runtime/usage-report.js +2 -1
- package/dist/runtime/waits.d.ts +13 -7
- package/dist/runtime/waits.js +42 -16
- 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/skills/catalog.d.ts +4 -4
- package/dist/skills/catalog.js +4 -4
- package/dist/skills/changes.service.d.ts +2 -2
- package/dist/skills/changes.service.js +5 -4
- package/dist/skills/invocation-keys.d.ts +1 -1
- package/dist/skills/invocation-keys.js +1 -1
- package/dist/skills/invoke.d.ts +1 -1
- package/dist/skills/invoke.js +5 -4
- package/dist/tools/approvals.service.d.ts +6 -0
- package/dist/tools/approvals.service.js +21 -1
- package/dist/tools/builtin-names.d.ts +91 -0
- package/dist/tools/builtin-names.js +143 -0
- package/dist/tools/builtins/agent.d.ts +7 -7
- package/dist/tools/builtins/agent.js +7 -11
- package/dist/tools/builtins/ask-user.d.ts +1 -1
- package/dist/tools/builtins/ask-user.js +2 -2
- package/dist/tools/builtins/builtin-tools.d.ts +17 -3
- package/dist/tools/builtins/builtin-tools.js +36 -4
- package/dist/tools/builtins/read-tool-output.d.ts +1 -1
- package/dist/tools/builtins/read-tool-output.js +2 -2
- package/dist/tools/builtins/sandbox/bash.d.ts +4 -4
- package/dist/tools/builtins/sandbox/bash.js +259 -29
- package/dist/tools/builtins/sandbox/common.d.ts +30 -7
- package/dist/tools/builtins/sandbox/common.js +9 -5
- package/dist/tools/builtins/sandbox/deliver.d.ts +1 -1
- package/dist/tools/builtins/sandbox/deliver.js +12 -12
- package/dist/tools/builtins/sandbox/edit.d.ts +1 -1
- package/dist/tools/builtins/sandbox/edit.js +41 -30
- package/dist/tools/builtins/sandbox/file-state.d.ts +34 -0
- package/dist/tools/builtins/sandbox/file-state.js +34 -0
- package/dist/tools/builtins/sandbox/glob-match.d.ts +1 -1
- package/dist/tools/builtins/sandbox/glob-match.js +1 -1
- package/dist/tools/builtins/sandbox/glob.d.ts +1 -1
- package/dist/tools/builtins/sandbox/glob.js +4 -4
- package/dist/tools/builtins/sandbox/grep.d.ts +7 -4
- package/dist/tools/builtins/sandbox/grep.js +147 -30
- package/dist/tools/builtins/sandbox/index.d.ts +3 -1
- package/dist/tools/builtins/sandbox/index.js +7 -1
- package/dist/tools/builtins/sandbox/read.d.ts +2 -2
- package/dist/tools/builtins/sandbox/read.js +21 -14
- 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/sandbox/write.d.ts +1 -1
- package/dist/tools/builtins/sandbox/write.js +37 -33
- package/dist/tools/builtins/skill-load.d.ts +3 -3
- package/dist/tools/builtins/skill-load.js +32 -16
- package/dist/tools/builtins/skill-write.d.ts +4 -4
- package/dist/tools/builtins/skill-write.js +10 -10
- package/dist/tools/builtins/tool-search.d.ts +13 -9
- package/dist/tools/builtins/tool-search.js +25 -14
- 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/connectors.service.js +4 -4
- package/dist/tools/display-names.d.ts +9 -0
- package/dist/tools/display-names.js +24 -0
- package/dist/tools/drift.js +5 -5
- package/dist/tools/elicitation.js +1 -1
- package/dist/tools/info.d.ts +20 -4
- package/dist/tools/info.js +55 -32
- package/dist/tools/legacy-data.d.ts +34 -0
- package/dist/tools/legacy-data.js +76 -0
- package/dist/tools/legacy-keys.d.ts +51 -0
- package/dist/tools/legacy-keys.js +115 -0
- package/dist/tools/naming.d.ts +1 -5
- package/dist/tools/naming.js +1 -7
- package/dist/tools/oauth/flow-paths.d.ts +1 -1
- package/dist/tools/oauth/flow-paths.js +1 -1
- package/dist/tools/outputs.js +1 -1
- package/dist/tools/pipeline.d.ts +13 -7
- package/dist/tools/pipeline.js +112 -39
- package/dist/tools/policy.d.ts +1 -14
- package/dist/tools/policy.js +13 -19
- package/dist/tools/questions.controller.d.ts +1 -1
- package/dist/tools/questions.controller.js +1 -1
- package/dist/tools/questions.service.d.ts +1 -1
- package/dist/tools/registry.d.ts +38 -24
- package/dist/tools/registry.js +54 -47
- package/dist/tools/resources.d.ts +1 -1
- package/dist/tools/resources.js +1 -1
- package/dist/tools/tools.module.js +13 -1
- package/dist/tools/validate.js +1 -1
- package/package.json +3 -3
- package/src/migrations/0063_tool_keys_claude_code.sql +220 -0
- 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
- package/dist/tools/builtins/sandbox/names.d.ts +0 -7
- package/dist/tools/builtins/sandbox/names.js +0 -20
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type Principal } from "../identity/principal.js";
|
|
2
|
+
import { type OrgContext } from "../orgs/org.guard.js";
|
|
3
|
+
import { ProviderRegistry } from "../runtime/providers.js";
|
|
4
|
+
import { AgentsService } from "./agents.service.js";
|
|
5
|
+
/**
|
|
6
|
+
* Whether the org can run (`/api/v1/orgs/:orgId/readiness`, web-order spec §3.8): the web app
|
|
7
|
+
* decides between the shell and the setup layout with it. `modelProvider` is `ready` when a
|
|
8
|
+
* model-provider extension is loaded (enabled and configured), from extension state only, never
|
|
9
|
+
* a vendor call; `agents` is the number of agents the caller can see (the agent list's
|
|
10
|
+
* visibility). Humans and service tokens (`org.read`).
|
|
11
|
+
*
|
|
12
|
+
* `modelProvider` is deployment-wide, not about this org: model providers are not org-scoped
|
|
13
|
+
* (an org's model purposes map onto the deployment's providers but cannot add one), so every org
|
|
14
|
+
* of the deployment reads the same value. It is read from this replica's provider registry, which
|
|
15
|
+
* follows the per-replica extension reconcile: right after a provider is enabled, a replica that
|
|
16
|
+
* has not reconciled yet may still answer `none` for a moment.
|
|
17
|
+
*/
|
|
18
|
+
export declare class ReadinessController {
|
|
19
|
+
private readonly registry;
|
|
20
|
+
private readonly agents;
|
|
21
|
+
constructor(registry: ProviderRegistry, agents: AgentsService);
|
|
22
|
+
get(principal: Principal, org: OrgContext): Promise<{
|
|
23
|
+
modelProvider: "ready" | "none";
|
|
24
|
+
agents: number;
|
|
25
|
+
}>;
|
|
26
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
8
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
9
|
+
};
|
|
10
|
+
import { Controller, Get, Inject } from "@nestjs/common";
|
|
11
|
+
import { CurrentPrincipal } from "../identity/principal.js";
|
|
12
|
+
import { CurrentOrg, OrgAction } from "../orgs/org.guard.js";
|
|
13
|
+
import { ProviderRegistry } from "../runtime/providers.js";
|
|
14
|
+
import { AgentsService } from "./agents.service.js";
|
|
15
|
+
/** Service tokens have no account: they see `visibility='org'` agents only (as the agent list). */
|
|
16
|
+
const viewerOf = (p) => (p.kind === "user" ? p.account.id : null);
|
|
17
|
+
/**
|
|
18
|
+
* Whether the org can run (`/api/v1/orgs/:orgId/readiness`, web-order spec §3.8): the web app
|
|
19
|
+
* decides between the shell and the setup layout with it. `modelProvider` is `ready` when a
|
|
20
|
+
* model-provider extension is loaded (enabled and configured), from extension state only, never
|
|
21
|
+
* a vendor call; `agents` is the number of agents the caller can see (the agent list's
|
|
22
|
+
* visibility). Humans and service tokens (`org.read`).
|
|
23
|
+
*
|
|
24
|
+
* `modelProvider` is deployment-wide, not about this org: model providers are not org-scoped
|
|
25
|
+
* (an org's model purposes map onto the deployment's providers but cannot add one), so every org
|
|
26
|
+
* of the deployment reads the same value. It is read from this replica's provider registry, which
|
|
27
|
+
* follows the per-replica extension reconcile: right after a provider is enabled, a replica that
|
|
28
|
+
* has not reconciled yet may still answer `none` for a moment.
|
|
29
|
+
*/
|
|
30
|
+
let ReadinessController = class ReadinessController {
|
|
31
|
+
registry;
|
|
32
|
+
agents;
|
|
33
|
+
constructor(registry, agents) {
|
|
34
|
+
this.registry = registry;
|
|
35
|
+
this.agents = agents;
|
|
36
|
+
}
|
|
37
|
+
async get(principal, org) {
|
|
38
|
+
const visible = await this.agents.listVisible(org.orgId, viewerOf(principal));
|
|
39
|
+
return {
|
|
40
|
+
modelProvider: this.registry.hasProvider() ? "ready" : "none",
|
|
41
|
+
agents: visible.length,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
__decorate([
|
|
46
|
+
Get(),
|
|
47
|
+
OrgAction("org.read"),
|
|
48
|
+
__param(0, CurrentPrincipal()),
|
|
49
|
+
__param(1, CurrentOrg())
|
|
50
|
+
], ReadinessController.prototype, "get", null);
|
|
51
|
+
ReadinessController = __decorate([
|
|
52
|
+
Controller("api/v1/orgs/:orgId/readiness"),
|
|
53
|
+
__param(0, Inject(ProviderRegistry)),
|
|
54
|
+
__param(1, Inject(AgentsService))
|
|
55
|
+
], ReadinessController);
|
|
56
|
+
export { ReadinessController };
|
package/dist/agents/templates.js
CHANGED
|
@@ -10,7 +10,9 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
|
10
10
|
import { randomUUID } from "node:crypto";
|
|
11
11
|
import { Inject, Injectable } from "@nestjs/common";
|
|
12
12
|
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
13
|
-
import { canonicalJson } from "../tools/
|
|
13
|
+
import { canonicalJson } from "../tools/canonical-json.js";
|
|
14
|
+
import { loadLegacyScope } from "../tools/legacy-data.js";
|
|
15
|
+
import { needsLegacyScope, normalizeToolKeys } from "./definition.js";
|
|
14
16
|
const baseQuery = (db, orgId) => db
|
|
15
17
|
.selectFrom("agent_templates as t")
|
|
16
18
|
.innerJoin("agent_template_versions as v", (j) => j.onRef("v.template_id", "=", "t.id").onRef("v.version", "=", "t.current_version"))
|
|
@@ -26,16 +28,27 @@ const baseQuery = (db, orgId) => db
|
|
|
26
28
|
])
|
|
27
29
|
.where("t.org_id", "=", orgId)
|
|
28
30
|
.where("t.deleted_at", "is", null);
|
|
29
|
-
|
|
31
|
+
/**
|
|
32
|
+
* A stored template row; its tool keys read as current keys (tool naming spec §8.3): old builtin
|
|
33
|
+
* names always, rc `<x>__<tool>` keys with `scope` (see {@link scopedRows}).
|
|
34
|
+
*/
|
|
35
|
+
const rowOf = (r, scope) => ({
|
|
30
36
|
id: r.id,
|
|
31
37
|
name: r.name,
|
|
32
38
|
plugin: r.plugin_name,
|
|
33
39
|
version: r.current_version,
|
|
34
|
-
template: r.definition,
|
|
40
|
+
template: normalizeToolKeys(r.definition, scope),
|
|
35
41
|
sourceFormat: r.source_format,
|
|
36
42
|
catalogItemId: r.catalog_item_id,
|
|
37
43
|
catalogVersion: r.catalog_version,
|
|
38
44
|
});
|
|
45
|
+
/** The rows of one org as {@link TemplateRow}s, reading the org's legacy scope once when needed. */
|
|
46
|
+
async function scopedRows(db, orgId, rows) {
|
|
47
|
+
const scope = rows.some((r) => needsLegacyScope(r.definition))
|
|
48
|
+
? await loadLegacyScope(db, orgId)
|
|
49
|
+
: undefined;
|
|
50
|
+
return rows.map((r) => rowOf(r, scope));
|
|
51
|
+
}
|
|
39
52
|
/** The live templates of `ids` in the org (deleted and foreign ids are left out), by id. */
|
|
40
53
|
export async function loadTemplates(db, orgId, ids) {
|
|
41
54
|
if (ids.length === 0)
|
|
@@ -43,7 +56,7 @@ export async function loadTemplates(db, orgId, ids) {
|
|
|
43
56
|
const rows = await baseQuery(db, orgId)
|
|
44
57
|
.where("t.id", "in", [...ids])
|
|
45
58
|
.execute();
|
|
46
|
-
return new Map(rows.map((
|
|
59
|
+
return new Map((await scopedRows(db, orgId, rows)).map((t) => [t.id, t]));
|
|
47
60
|
}
|
|
48
61
|
/** Reads the org's agent templates (consumed by the roster, the agent page and Task 19's API). */
|
|
49
62
|
let AgentTemplatesService = class AgentTemplatesService {
|
|
@@ -84,7 +97,9 @@ let AgentTemplatesService = class AgentTemplatesService {
|
|
|
84
97
|
const r = await q.executeTakeFirst();
|
|
85
98
|
if (!r)
|
|
86
99
|
return null;
|
|
87
|
-
const t =
|
|
100
|
+
const [t] = await scopedRows(this.db.db, orgId, [r]);
|
|
101
|
+
if (!t)
|
|
102
|
+
return null;
|
|
88
103
|
return { id: t.id, version: t.version, plugin: t.plugin, template: t.template };
|
|
89
104
|
}
|
|
90
105
|
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { type Principal } from "../identity/principal.js";
|
|
3
|
+
import { type OrgContext } from "../orgs/org.guard.js";
|
|
4
|
+
import { type BackgroundTaskOutput, BackgroundTasks, type BackgroundTaskView } from "./tasks.service.js";
|
|
5
|
+
declare const ConversationParams: z.ZodObject<{
|
|
6
|
+
id: z.ZodString;
|
|
7
|
+
}, z.core.$strip>;
|
|
8
|
+
declare const TaskParams: z.ZodObject<{
|
|
9
|
+
id: z.ZodString;
|
|
10
|
+
taskId: z.ZodString;
|
|
11
|
+
}, z.core.$strip>;
|
|
12
|
+
declare const OutputQuery: z.ZodObject<{
|
|
13
|
+
tail: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
14
|
+
}, z.core.$strip>;
|
|
15
|
+
/**
|
|
16
|
+
* Background Bash tasks of a conversation (spec 2026-11-05-background-bash-design §5). Visible to
|
|
17
|
+
* whoever sees the conversation, stoppable by whoever may abort its run (the owner): the same
|
|
18
|
+
* checks as `GET :id` and `POST :id/abort`.
|
|
19
|
+
*/
|
|
20
|
+
export declare class BackgroundController {
|
|
21
|
+
private readonly tasks;
|
|
22
|
+
constructor(tasks: BackgroundTasks);
|
|
23
|
+
/** The conversation's tasks and its sub-agents' (`subagentId`), newest first. */
|
|
24
|
+
list(principal: Principal, org: OrgContext, params: z.infer<typeof ConversationParams>): Promise<{
|
|
25
|
+
tasks: BackgroundTaskView[];
|
|
26
|
+
}>;
|
|
27
|
+
/**
|
|
28
|
+
* Stops a task like a person's stop: 200 `{ task }` with the row after the stop. A task that is
|
|
29
|
+
* still `running` with `stopping: true` is being stopped by another request (or is still
|
|
30
|
+
* launching); a stop that fails is an error and the task keeps running.
|
|
31
|
+
*/
|
|
32
|
+
stop(principal: Principal, org: OrgContext, params: z.infer<typeof TaskParams>): Promise<{
|
|
33
|
+
task: BackgroundTaskView;
|
|
34
|
+
}>;
|
|
35
|
+
/** The end of the task's output (`tail` bytes, at most 65536); 409 when it cannot be read now. */
|
|
36
|
+
output(principal: Principal, org: OrgContext, params: z.infer<typeof TaskParams>, q: z.infer<typeof OutputQuery>): Promise<BackgroundTaskOutput>;
|
|
37
|
+
}
|
|
38
|
+
export {};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
8
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
9
|
+
};
|
|
10
|
+
import { Controller, Get, HttpCode, Inject, Post } from "@nestjs/common";
|
|
11
|
+
import { z } from "zod";
|
|
12
|
+
import { CurrentPrincipal, patTokenId, requireUser, } from "../identity/principal.js";
|
|
13
|
+
import { createZodDto } from "../infra/validation/create-zod-dto.js";
|
|
14
|
+
import { ZParam, ZQuery } from "../infra/validation/zod-params.js";
|
|
15
|
+
import { CurrentOrg, OrgAction } from "../orgs/org.guard.js";
|
|
16
|
+
import { BackgroundTasks, OUTPUT_TAIL_MAX_BYTES, } from "./tasks.service.js";
|
|
17
|
+
const ConversationParams = z.object({ id: z.string().min(1).max(100) });
|
|
18
|
+
const TaskParams = z.object({ id: z.string().min(1).max(100), taskId: z.string().min(1).max(100) });
|
|
19
|
+
const OutputQuery = z.object({
|
|
20
|
+
tail: z.coerce.number().int().min(1).max(OUTPUT_TAIL_MAX_BYTES).default(OUTPUT_TAIL_MAX_BYTES),
|
|
21
|
+
});
|
|
22
|
+
class BackgroundConversationParamsDto extends createZodDto(ConversationParams, "BackgroundConversationParams") {
|
|
23
|
+
}
|
|
24
|
+
class BackgroundTaskParamsDto extends createZodDto(TaskParams, "BackgroundTaskParams") {
|
|
25
|
+
}
|
|
26
|
+
class BackgroundOutputQueryDto extends createZodDto(OutputQuery, "BackgroundOutputQuery") {
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Background Bash tasks of a conversation (spec 2026-11-05-background-bash-design §5). Visible to
|
|
30
|
+
* whoever sees the conversation, stoppable by whoever may abort its run (the owner): the same
|
|
31
|
+
* checks as `GET :id` and `POST :id/abort`.
|
|
32
|
+
*/
|
|
33
|
+
let BackgroundController = class BackgroundController {
|
|
34
|
+
tasks;
|
|
35
|
+
constructor(tasks) {
|
|
36
|
+
this.tasks = tasks;
|
|
37
|
+
}
|
|
38
|
+
/** The conversation's tasks and its sub-agents' (`subagentId`), newest first. */
|
|
39
|
+
async list(principal, org, params) {
|
|
40
|
+
const p = requireUser(principal);
|
|
41
|
+
return { tasks: await this.tasks.list(org.orgId, params.id, p.account.id) };
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Stops a task like a person's stop: 200 `{ task }` with the row after the stop. A task that is
|
|
45
|
+
* still `running` with `stopping: true` is being stopped by another request (or is still
|
|
46
|
+
* launching); a stop that fails is an error and the task keeps running.
|
|
47
|
+
*/
|
|
48
|
+
async stop(principal, org, params) {
|
|
49
|
+
const p = requireUser(principal);
|
|
50
|
+
return {
|
|
51
|
+
task: await this.tasks.stop(org.orgId, params.id, p.account.id, params.taskId, patTokenId(p)),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/** The end of the task's output (`tail` bytes, at most 65536); 409 when it cannot be read now. */
|
|
55
|
+
async output(principal, org, params, q) {
|
|
56
|
+
const p = requireUser(principal);
|
|
57
|
+
return this.tasks.output(org.orgId, params.id, p.account.id, params.taskId, q.tail);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
__decorate([
|
|
61
|
+
Get(":orgId/conversations/:id/background-tasks"),
|
|
62
|
+
OrgAction("agents.use"),
|
|
63
|
+
__param(0, CurrentPrincipal()),
|
|
64
|
+
__param(1, CurrentOrg()),
|
|
65
|
+
__param(2, ZParam(BackgroundConversationParamsDto))
|
|
66
|
+
], BackgroundController.prototype, "list", null);
|
|
67
|
+
__decorate([
|
|
68
|
+
Post(":orgId/conversations/:id/background-tasks/:taskId/stop"),
|
|
69
|
+
HttpCode(200),
|
|
70
|
+
OrgAction("agents.use"),
|
|
71
|
+
__param(0, CurrentPrincipal()),
|
|
72
|
+
__param(1, CurrentOrg()),
|
|
73
|
+
__param(2, ZParam(BackgroundTaskParamsDto))
|
|
74
|
+
], BackgroundController.prototype, "stop", null);
|
|
75
|
+
__decorate([
|
|
76
|
+
Get(":orgId/conversations/:id/background-tasks/:taskId/output"),
|
|
77
|
+
OrgAction("agents.use"),
|
|
78
|
+
__param(0, CurrentPrincipal()),
|
|
79
|
+
__param(1, CurrentOrg()),
|
|
80
|
+
__param(2, ZParam(BackgroundTaskParamsDto)),
|
|
81
|
+
__param(3, ZQuery(BackgroundOutputQueryDto))
|
|
82
|
+
], BackgroundController.prototype, "output", null);
|
|
83
|
+
BackgroundController = __decorate([
|
|
84
|
+
Controller("api/v1/orgs"),
|
|
85
|
+
__param(0, Inject(BackgroundTasks))
|
|
86
|
+
], BackgroundController);
|
|
87
|
+
export { BackgroundController };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Background Bash (spec 2026-11-05-background-bash-design): the task store (from
|
|
3
|
+
* {@link BackgroundStoreModule}, which `SandboxModule` imports too), the finish of a task with its
|
|
4
|
+
* event ({@link BackgroundTaskFinisher}) and the stops ({@link BackgroundTaskStopper}). The wake of
|
|
5
|
+
* an idle conversation (`BackgroundWake`) is the runtime's (`RuntimeModule`): it needs the run
|
|
6
|
+
* queue, and the runtime imports this module. The poll and prune jobs
|
|
7
|
+
* (`BackgroundTaskPollJob`) are the worker role's (`WorkerModule`).
|
|
8
|
+
*/
|
|
9
|
+
export declare class BackgroundModule {
|
|
10
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import { Module } from "@nestjs/common";
|
|
8
|
+
import { ConversationAccessModule } from "../conversations/access.js";
|
|
9
|
+
import { EventLogModule } from "../conversations/event-log.js";
|
|
10
|
+
import { SandboxModule } from "../sandbox/sandbox.module.js";
|
|
11
|
+
import { BackgroundTaskFinisher } from "./finish.js";
|
|
12
|
+
import { BackgroundTaskStopper } from "./stop.js";
|
|
13
|
+
import { BackgroundStoreModule } from "./store.module.js";
|
|
14
|
+
import { BackgroundTasks } from "./tasks.service.js";
|
|
15
|
+
/**
|
|
16
|
+
* Background Bash (spec 2026-11-05-background-bash-design): the task store (from
|
|
17
|
+
* {@link BackgroundStoreModule}, which `SandboxModule` imports too), the finish of a task with its
|
|
18
|
+
* event ({@link BackgroundTaskFinisher}) and the stops ({@link BackgroundTaskStopper}). The wake of
|
|
19
|
+
* an idle conversation (`BackgroundWake`) is the runtime's (`RuntimeModule`): it needs the run
|
|
20
|
+
* queue, and the runtime imports this module. The poll and prune jobs
|
|
21
|
+
* (`BackgroundTaskPollJob`) are the worker role's (`WorkerModule`).
|
|
22
|
+
*/
|
|
23
|
+
let BackgroundModule = class BackgroundModule {
|
|
24
|
+
};
|
|
25
|
+
BackgroundModule = __decorate([
|
|
26
|
+
Module({
|
|
27
|
+
imports: [BackgroundStoreModule, SandboxModule, EventLogModule, ConversationAccessModule],
|
|
28
|
+
providers: [BackgroundTaskFinisher, BackgroundTaskStopper, BackgroundTasks],
|
|
29
|
+
exports: [BackgroundStoreModule, BackgroundTaskFinisher, BackgroundTaskStopper, BackgroundTasks],
|
|
30
|
+
})
|
|
31
|
+
], BackgroundModule);
|
|
32
|
+
export { BackgroundModule };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { EventLog } from "../conversations/event-log.js";
|
|
2
|
+
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
3
|
+
import { KernelEvents } from "../infra/events/kernel-events.js";
|
|
4
|
+
import { type BackgroundTaskRow, BackgroundTaskStore } from "./store.js";
|
|
5
|
+
/** Where a task ends (as {@link BackgroundTaskStore.finish} takes it). */
|
|
6
|
+
export type BackgroundFinish = Parameters<BackgroundTaskStore["finish"]>[1];
|
|
7
|
+
/**
|
|
8
|
+
* Who stopped a task, when its finish must never wake the conversation: a person (who is looking at
|
|
9
|
+
* it) or the deletion of the conversation.
|
|
10
|
+
*/
|
|
11
|
+
export type BackgroundStoppedBy = "person" | "deletion";
|
|
12
|
+
/** The idempotency key of a task's `background_task_finished` event. */
|
|
13
|
+
export declare const finishedEventKey: (taskId: string) => string;
|
|
14
|
+
/**
|
|
15
|
+
* Takes a task out of `running` (spec 2026-11-05-background-bash-design §4.3): one transaction holds
|
|
16
|
+
* the conditional status update and the `background_task_finished` event of the owning
|
|
17
|
+
* conversation, so a crash between them cannot lose the event, and of two paths finishing the same
|
|
18
|
+
* task (two pollers, a poll and a stop) exactly one writes it. After the commit: the log's notify,
|
|
19
|
+
* the `changed: "background"` frame (to the owner and, for a sub-agent's task, the conversation
|
|
20
|
+
* that owns the sandbox, whose panel lists it too) and the `background_task.finished` domain event
|
|
21
|
+
* (`BackgroundWake` wakes an idle conversation; it lives in the runtime, above this module).
|
|
22
|
+
*
|
|
23
|
+
* Holds no lock and runs no sandbox command: the caller reads `lastLines` before.
|
|
24
|
+
*/
|
|
25
|
+
export declare class BackgroundTaskFinisher {
|
|
26
|
+
private readonly store;
|
|
27
|
+
private readonly eventLog;
|
|
28
|
+
private readonly events;
|
|
29
|
+
readonly txHost: TransactionHost;
|
|
30
|
+
constructor(store: Pick<BackgroundTaskStore, "finish">, eventLog: EventLog, events: KernelEvents, txHost: TransactionHost);
|
|
31
|
+
/**
|
|
32
|
+
* Finishes `task` as `to` with its last output lines (cut to their last 4 000 characters);
|
|
33
|
+
* `reported` when a tool call already told the model in its run. False (nothing written) when the
|
|
34
|
+
* task was no longer running, or, with `opts.unclaimed` (the poller), when a live stop claimed it:
|
|
35
|
+
* the stop's finish is then the only one. `opts.stoppedBy` marks a stop that must never wake the
|
|
36
|
+
* conversation: `"person"` (a person stopped it) or `"deletion"` (its conversation is being
|
|
37
|
+
* deleted).
|
|
38
|
+
*/
|
|
39
|
+
finish(task: BackgroundTaskRow, to: BackgroundFinish, lastLines: string, reported: boolean, opts?: {
|
|
40
|
+
unclaimed?: boolean;
|
|
41
|
+
stoppedBy?: BackgroundStoppedBy;
|
|
42
|
+
}): Promise<boolean>;
|
|
43
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
8
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
9
|
+
};
|
|
10
|
+
import { Inject, Injectable } from "@nestjs/common";
|
|
11
|
+
import { EventLog } from "../conversations/event-log.js";
|
|
12
|
+
import { afterCommit } from "../infra/database/after-commit.js";
|
|
13
|
+
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
14
|
+
import { Transactional } from "../infra/database/transactional.decorator.js";
|
|
15
|
+
import { KernelEvents } from "../infra/events/kernel-events.js";
|
|
16
|
+
import { LAST_LINES_MAX_CHARS } from "./session.js";
|
|
17
|
+
import { BackgroundTaskStore } from "./store.js";
|
|
18
|
+
/** The idempotency key of a task's `background_task_finished` event. */
|
|
19
|
+
export const finishedEventKey = (taskId) => `background_task_finished:${taskId}`;
|
|
20
|
+
/**
|
|
21
|
+
* Takes a task out of `running` (spec 2026-11-05-background-bash-design §4.3): one transaction holds
|
|
22
|
+
* the conditional status update and the `background_task_finished` event of the owning
|
|
23
|
+
* conversation, so a crash between them cannot lose the event, and of two paths finishing the same
|
|
24
|
+
* task (two pollers, a poll and a stop) exactly one writes it. After the commit: the log's notify,
|
|
25
|
+
* the `changed: "background"` frame (to the owner and, for a sub-agent's task, the conversation
|
|
26
|
+
* that owns the sandbox, whose panel lists it too) and the `background_task.finished` domain event
|
|
27
|
+
* (`BackgroundWake` wakes an idle conversation; it lives in the runtime, above this module).
|
|
28
|
+
*
|
|
29
|
+
* Holds no lock and runs no sandbox command: the caller reads `lastLines` before.
|
|
30
|
+
*/
|
|
31
|
+
let BackgroundTaskFinisher = class BackgroundTaskFinisher {
|
|
32
|
+
store;
|
|
33
|
+
eventLog;
|
|
34
|
+
events;
|
|
35
|
+
txHost;
|
|
36
|
+
constructor(store, eventLog, events, txHost) {
|
|
37
|
+
this.store = store;
|
|
38
|
+
this.eventLog = eventLog;
|
|
39
|
+
this.events = events;
|
|
40
|
+
this.txHost = txHost;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Finishes `task` as `to` with its last output lines (cut to their last 4 000 characters);
|
|
44
|
+
* `reported` when a tool call already told the model in its run. False (nothing written) when the
|
|
45
|
+
* task was no longer running, or, with `opts.unclaimed` (the poller), when a live stop claimed it:
|
|
46
|
+
* the stop's finish is then the only one. `opts.stoppedBy` marks a stop that must never wake the
|
|
47
|
+
* conversation: `"person"` (a person stopped it) or `"deletion"` (its conversation is being
|
|
48
|
+
* deleted).
|
|
49
|
+
*/
|
|
50
|
+
async finish(task, to, lastLines, reported, opts = {}) {
|
|
51
|
+
if (!(await this.store.finish(task.id, to, opts)))
|
|
52
|
+
return false;
|
|
53
|
+
const [event] = await this.eventLog.appendIn(task.conversationId, [
|
|
54
|
+
{
|
|
55
|
+
type: "background_task_finished",
|
|
56
|
+
idemKey: finishedEventKey(task.id),
|
|
57
|
+
payload: {
|
|
58
|
+
taskId: task.id,
|
|
59
|
+
description: task.description,
|
|
60
|
+
status: to.status,
|
|
61
|
+
exitCode: to.exitCode ?? null,
|
|
62
|
+
elapsedMs: Math.max(0, Date.now() - task.startedAt.getTime()),
|
|
63
|
+
lastLines: lastLines.length > LAST_LINES_MAX_CHARS
|
|
64
|
+
? lastLines.slice(-LAST_LINES_MAX_CHARS)
|
|
65
|
+
: lastLines,
|
|
66
|
+
outputPath: `/workspace/${task.outputPath}`,
|
|
67
|
+
reported,
|
|
68
|
+
...(to.lostReason !== undefined && { lostReason: to.lostReason }),
|
|
69
|
+
...(opts.stoppedBy !== undefined && { stoppedBy: opts.stoppedBy }),
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
]);
|
|
73
|
+
const conversations = [...new Set([task.conversationId, task.sandboxConversationId])];
|
|
74
|
+
afterCommit(async () => {
|
|
75
|
+
if (event)
|
|
76
|
+
await this.eventLog.notify(task.conversationId, event.seq);
|
|
77
|
+
for (const c of conversations)
|
|
78
|
+
await this.eventLog.publishChanged(c, "background");
|
|
79
|
+
await this.events.emit("background_task.finished", { conversationId: task.conversationId });
|
|
80
|
+
});
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
__decorate([
|
|
85
|
+
Transactional()
|
|
86
|
+
], BackgroundTaskFinisher.prototype, "finish", null);
|
|
87
|
+
BackgroundTaskFinisher = __decorate([
|
|
88
|
+
Injectable(),
|
|
89
|
+
__param(0, Inject(BackgroundTaskStore)),
|
|
90
|
+
__param(1, Inject(EventLog)),
|
|
91
|
+
__param(2, Inject(KernelEvents)),
|
|
92
|
+
__param(3, Inject(TransactionHost))
|
|
93
|
+
], BackgroundTaskFinisher);
|
|
94
|
+
export { BackgroundTaskFinisher };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
2
|
+
import { XactLock } from "../infra/database/xact-lock.js";
|
|
3
|
+
/**
|
|
4
|
+
* The launch lock of one sandbox (decision R9: one provider per transaction-scoped lock):
|
|
5
|
+
* serializes background task reservations in a sandbox, so the count of running tasks checked
|
|
6
|
+
* against `sandbox.background.maxTasks` and the insert of the new row are one step. Joins the
|
|
7
|
+
* caller's transaction (`mandatory`), which holds it until it ends.
|
|
8
|
+
*/
|
|
9
|
+
export declare class BackgroundLaunchLock {
|
|
10
|
+
private readonly xactLock;
|
|
11
|
+
readonly txHost: TransactionHost;
|
|
12
|
+
constructor(xactLock: XactLock, txHost: TransactionHost);
|
|
13
|
+
lock(sandboxConversationId: string): Promise<void>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
8
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
9
|
+
};
|
|
10
|
+
import { Inject, Injectable } from "@nestjs/common";
|
|
11
|
+
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
12
|
+
import { Transactional } from "../infra/database/transactional.decorator.js";
|
|
13
|
+
import { XactLock } from "../infra/database/xact-lock.js";
|
|
14
|
+
/**
|
|
15
|
+
* The launch lock of one sandbox (decision R9: one provider per transaction-scoped lock):
|
|
16
|
+
* serializes background task reservations in a sandbox, so the count of running tasks checked
|
|
17
|
+
* against `sandbox.background.maxTasks` and the insert of the new row are one step. Joins the
|
|
18
|
+
* caller's transaction (`mandatory`), which holds it until it ends.
|
|
19
|
+
*/
|
|
20
|
+
let BackgroundLaunchLock = class BackgroundLaunchLock {
|
|
21
|
+
xactLock;
|
|
22
|
+
txHost;
|
|
23
|
+
constructor(xactLock, txHost) {
|
|
24
|
+
this.xactLock = xactLock;
|
|
25
|
+
this.txHost = txHost;
|
|
26
|
+
}
|
|
27
|
+
async lock(sandboxConversationId) {
|
|
28
|
+
await this.xactLock.lock(`capybara.background-launch:${sandboxConversationId}`);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
__decorate([
|
|
32
|
+
Transactional({ propagation: "mandatory" })
|
|
33
|
+
], BackgroundLaunchLock.prototype, "lock", null);
|
|
34
|
+
BackgroundLaunchLock = __decorate([
|
|
35
|
+
Injectable(),
|
|
36
|
+
__param(0, Inject(XactLock)),
|
|
37
|
+
__param(1, Inject(TransactionHost))
|
|
38
|
+
], BackgroundLaunchLock);
|
|
39
|
+
export { BackgroundLaunchLock };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { KernelConfig } from "../config/schema.js";
|
|
2
|
+
/**
|
|
3
|
+
* Launching a background Bash task (spec 2026-11-05-background-bash-design §4.2): the task id, the
|
|
4
|
+
* one short exec that starts the command detached from the exec, and the model-visible result.
|
|
5
|
+
* Every path is relative to `/workspace` (the launch exec's `cwd`), so a provider that maps
|
|
6
|
+
* `/workspace` needs no rewriting inside the script.
|
|
7
|
+
*/
|
|
8
|
+
/** The launch exec's own time limit: it only writes a file and forks. */
|
|
9
|
+
export declare const LAUNCH_EXEC_TIMEOUT_MS = 30000;
|
|
10
|
+
/**
|
|
11
|
+
* The longest a background task may run, in seconds: `sandbox.background.maxSeconds`, at most
|
|
12
|
+
* `sandbox.limits.maxCommandSeconds`. It is also a task's default `timeout`.
|
|
13
|
+
*/
|
|
14
|
+
export declare const backgroundMaxSeconds: (cfg: Pick<KernelConfig, "sandbox">) => number;
|
|
15
|
+
/** The directory of the task files, relative to `/workspace`. */
|
|
16
|
+
export declare const TASKS_DIR = ".capybara/tasks";
|
|
17
|
+
/** A task's output file, relative to `/workspace` (`output_path`). */
|
|
18
|
+
export declare const taskOutputPath: (id: string) => string;
|
|
19
|
+
/** The file the launch writes the session id to, relative to `/workspace`. */
|
|
20
|
+
export declare const taskSidPath: (id: string) => string;
|
|
21
|
+
/** A new task id: `bt_` plus 6 lowercase base32 characters. */
|
|
22
|
+
export declare function newTaskId(): string;
|
|
23
|
+
/** A task id ({@link newTaskId}). */
|
|
24
|
+
export declare const TASK_ID_RE: RegExp;
|
|
25
|
+
/**
|
|
26
|
+
* The shell of the launch exec: writes `command` (base64 in the script, so nothing is quoted by
|
|
27
|
+
* hand) to `.capybara/tasks/<id>.sh` and starts it with bash (the shell a foreground `Bash` call
|
|
28
|
+
* runs its command in) in a session of its own under `timeout -s KILL <seconds>`, with its exit
|
|
29
|
+
* status written to `<id>.exit` and its output to `<id>.out`; writes the session id (`$!`, the pid
|
|
30
|
+
* `setsid` started) to `<id>.sid` and prints it. A sandbox without one of the needed programs fails with a readable error on
|
|
31
|
+
* stderr and exit code 127, before anything starts. `id` must be a task id and `seconds` a
|
|
32
|
+
* positive integer (both go into the script unquoted).
|
|
33
|
+
*/
|
|
34
|
+
export declare function launchScript(id: string, command: string, seconds: number): string;
|
|
35
|
+
/** The model-visible result of a launch (spec §2.1). */
|
|
36
|
+
export declare function startedText(id: string, description: string): string;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
/**
|
|
3
|
+
* Launching a background Bash task (spec 2026-11-05-background-bash-design §4.2): the task id, the
|
|
4
|
+
* one short exec that starts the command detached from the exec, and the model-visible result.
|
|
5
|
+
* Every path is relative to `/workspace` (the launch exec's `cwd`), so a provider that maps
|
|
6
|
+
* `/workspace` needs no rewriting inside the script.
|
|
7
|
+
*/
|
|
8
|
+
/** The launch exec's own time limit: it only writes a file and forks. */
|
|
9
|
+
export const LAUNCH_EXEC_TIMEOUT_MS = 30_000;
|
|
10
|
+
/**
|
|
11
|
+
* The longest a background task may run, in seconds: `sandbox.background.maxSeconds`, at most
|
|
12
|
+
* `sandbox.limits.maxCommandSeconds`. It is also a task's default `timeout`.
|
|
13
|
+
*/
|
|
14
|
+
export const backgroundMaxSeconds = (cfg) => Math.min(cfg.sandbox.background.maxSeconds, cfg.sandbox.limits.maxCommandSeconds);
|
|
15
|
+
/** The directory of the task files, relative to `/workspace`. */
|
|
16
|
+
export const TASKS_DIR = ".capybara/tasks";
|
|
17
|
+
/** A task's output file, relative to `/workspace` (`output_path`). */
|
|
18
|
+
export const taskOutputPath = (id) => `${TASKS_DIR}/${id}.out`;
|
|
19
|
+
/** The file the launch writes the session id to, relative to `/workspace`. */
|
|
20
|
+
export const taskSidPath = (id) => `${TASKS_DIR}/${id}.sid`;
|
|
21
|
+
const BASE32 = "abcdefghijklmnopqrstuvwxyz234567";
|
|
22
|
+
/** A new task id: `bt_` plus 6 lowercase base32 characters. */
|
|
23
|
+
export function newTaskId() {
|
|
24
|
+
const bytes = randomBytes(6);
|
|
25
|
+
let out = "bt_";
|
|
26
|
+
for (const b of bytes)
|
|
27
|
+
out += BASE32[b & 31];
|
|
28
|
+
return out;
|
|
29
|
+
}
|
|
30
|
+
/** The programs the launch and the later polls and stops need inside the sandbox. */
|
|
31
|
+
const REQUIRED = ["bash", "setsid", "nohup", "timeout", "pgrep", "pkill", "base64"];
|
|
32
|
+
/** A task id ({@link newTaskId}). */
|
|
33
|
+
export const TASK_ID_RE = /^bt_[a-z2-7]{6}$/;
|
|
34
|
+
/**
|
|
35
|
+
* The shell of the launch exec: writes `command` (base64 in the script, so nothing is quoted by
|
|
36
|
+
* hand) to `.capybara/tasks/<id>.sh` and starts it with bash (the shell a foreground `Bash` call
|
|
37
|
+
* runs its command in) in a session of its own under `timeout -s KILL <seconds>`, with its exit
|
|
38
|
+
* status written to `<id>.exit` and its output to `<id>.out`; writes the session id (`$!`, the pid
|
|
39
|
+
* `setsid` started) to `<id>.sid` and prints it. A sandbox without one of the needed programs fails with a readable error on
|
|
40
|
+
* stderr and exit code 127, before anything starts. `id` must be a task id and `seconds` a
|
|
41
|
+
* positive integer (both go into the script unquoted).
|
|
42
|
+
*/
|
|
43
|
+
export function launchScript(id, command, seconds) {
|
|
44
|
+
if (!TASK_ID_RE.test(id))
|
|
45
|
+
throw new Error(`invalid background task id: ${JSON.stringify(id)}`);
|
|
46
|
+
if (!Number.isSafeInteger(seconds) || seconds < 1) {
|
|
47
|
+
throw new Error(`invalid background task seconds: ${seconds}`);
|
|
48
|
+
}
|
|
49
|
+
const b64 = Buffer.from(command, "utf8").toString("base64");
|
|
50
|
+
const t = `${TASKS_DIR}/${id}`;
|
|
51
|
+
return [
|
|
52
|
+
"set -e",
|
|
53
|
+
`for c in ${REQUIRED.join(" ")}; do command -v "$c" >/dev/null 2>&1 || { echo "Background tasks need ${REQUIRED.join(", ")} in the sandbox; $c is missing." >&2; exit 127; }; done`,
|
|
54
|
+
`mkdir -p ${TASKS_DIR}`,
|
|
55
|
+
`printf '%s' '${b64}' | base64 -d > ${t}.sh`,
|
|
56
|
+
`setsid nohup sh -c 'timeout -s KILL ${seconds} bash ${t}.sh; echo $? > ${t}.exit' > ${t}.out 2>&1 < /dev/null &`,
|
|
57
|
+
// the session id goes to a file first: a launch that ends before its output comes back
|
|
58
|
+
// (aborted, timed out) still leaves the id of what it started
|
|
59
|
+
`echo $! > ${t}.sid`,
|
|
60
|
+
`cat ${t}.sid`,
|
|
61
|
+
].join("\n");
|
|
62
|
+
}
|
|
63
|
+
/** The model-visible result of a launch (spec §2.1). */
|
|
64
|
+
export function startedText(id, description) {
|
|
65
|
+
return [
|
|
66
|
+
`Started background task ${id} ("${description}").`,
|
|
67
|
+
`Output: /workspace/${taskOutputPath(id)}`,
|
|
68
|
+
"Read it with TaskOutput (or Read), stop it with TaskStop. You will be told when it finishes.",
|
|
69
|
+
].join("\n");
|
|
70
|
+
}
|