@capybara-hq/kernel 0.1.0-rc.3 → 0.1.0-rc.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (226) hide show
  1. package/dist/admin/admin.controller.d.ts +9 -4
  2. package/dist/admin/admin.controller.js +13 -4
  3. package/dist/admin/extensions.controller.d.ts +8 -1
  4. package/dist/admin/extensions.controller.js +22 -2
  5. package/dist/agents/agent-lookup.d.ts +3 -0
  6. package/dist/agents/agent-lookup.js +10 -0
  7. package/dist/agents/agent-profile.service.d.ts +29 -0
  8. package/dist/agents/agent-profile.service.js +117 -0
  9. package/dist/agents/agents.controller.d.ts +93 -3
  10. package/dist/agents/agents.controller.js +114 -4
  11. package/dist/agents/agents.module.js +6 -2
  12. package/dist/agents/agents.service.d.ts +13 -5
  13. package/dist/agents/agents.service.js +30 -7
  14. package/dist/agents/definition.d.ts +4 -0
  15. package/dist/agents/definition.js +10 -0
  16. package/dist/agents/instruction-files.d.ts +60 -0
  17. package/dist/agents/instruction-files.js +111 -0
  18. package/dist/agents/models.controller.d.ts +9 -3
  19. package/dist/agents/models.controller.js +19 -4
  20. package/dist/agents/profile.d.ts +96 -0
  21. package/dist/agents/profile.js +51 -0
  22. package/dist/agents/readiness.controller.d.ts +26 -0
  23. package/dist/agents/readiness.controller.js +56 -0
  24. package/dist/agents/templates.js +1 -1
  25. package/dist/background/background.controller.d.ts +38 -0
  26. package/dist/background/background.controller.js +87 -0
  27. package/dist/background/background.module.d.ts +10 -0
  28. package/dist/background/background.module.js +32 -0
  29. package/dist/background/finish.d.ts +43 -0
  30. package/dist/background/finish.js +94 -0
  31. package/dist/background/launch-lock.d.ts +14 -0
  32. package/dist/background/launch-lock.js +39 -0
  33. package/dist/background/launch.d.ts +36 -0
  34. package/dist/background/launch.js +70 -0
  35. package/dist/background/poll.job.d.ts +53 -0
  36. package/dist/background/poll.job.js +182 -0
  37. package/dist/background/render.d.ts +23 -0
  38. package/dist/background/render.js +48 -0
  39. package/dist/background/session.d.ts +37 -0
  40. package/dist/background/session.js +66 -0
  41. package/dist/background/status.d.ts +35 -0
  42. package/dist/background/status.js +57 -0
  43. package/dist/background/stop.d.ts +62 -0
  44. package/dist/background/stop.js +225 -0
  45. package/dist/background/store.d.ts +150 -0
  46. package/dist/background/store.js +351 -0
  47. package/dist/background/store.module.d.ts +6 -0
  48. package/dist/background/store.module.js +22 -0
  49. package/dist/background/tasks.service.d.ts +67 -0
  50. package/dist/background/tasks.service.js +186 -0
  51. package/dist/background/wake-rules.d.ts +57 -0
  52. package/dist/background/wake-rules.js +83 -0
  53. package/dist/background/wake.d.ts +28 -0
  54. package/dist/background/wake.js +57 -0
  55. package/dist/catalog/claude/agents.js +2 -0
  56. package/dist/catalog/marketplaces.service.d.ts +1 -1
  57. package/dist/catalog/plugin-import.js +1 -1
  58. package/dist/catalog/plugin-upgrade.js +1 -1
  59. package/dist/channels/cards.js +1 -1
  60. package/dist/channels/connections.service.d.ts +16 -1
  61. package/dist/channels/connections.service.js +38 -4
  62. package/dist/channels/threads.d.ts +5 -0
  63. package/dist/channels/threads.js +8 -0
  64. package/dist/config/schema.d.ts +18 -0
  65. package/dist/config/schema.js +102 -0
  66. package/dist/conversations/access.js +2 -0
  67. package/dist/conversations/attachments.service.d.ts +30 -0
  68. package/dist/conversations/attachments.service.js +130 -5
  69. package/dist/conversations/conversations.controller.d.ts +36 -5
  70. package/dist/conversations/conversations.controller.js +62 -12
  71. package/dist/conversations/conversations.module.js +3 -0
  72. package/dist/conversations/conversations.service.d.ts +44 -2
  73. package/dist/conversations/conversations.service.js +140 -12
  74. package/dist/conversations/event-log.d.ts +1 -1
  75. package/dist/conversations/event-log.js +10 -1
  76. package/dist/conversations/events.d.ts +58 -0
  77. package/dist/conversations/events.js +57 -2
  78. package/dist/conversations/queue.d.ts +3 -1
  79. package/dist/conversations/queue.js +13 -2
  80. package/dist/conversations/queue.service.js +3 -1
  81. package/dist/conversations/titles.d.ts +7 -0
  82. package/dist/conversations/titles.js +15 -0
  83. package/dist/db/kernel-schema.d.ts +58 -2
  84. package/dist/db/pool.d.ts +5 -0
  85. package/dist/db/pool.js +3 -0
  86. package/dist/errors.d.ts +8 -0
  87. package/dist/errors.js +8 -0
  88. package/dist/evals/run-replay.d.ts +5 -2
  89. package/dist/evals/run-replay.js +30 -7
  90. package/dist/evals/step-replay.js +12 -6
  91. package/dist/evals/trajectory.js +1 -1
  92. package/dist/extensions/admin-ops.js +2 -0
  93. package/dist/extensions/admin-view.d.ts +3 -1
  94. package/dist/extensions/admin-view.js +10 -2
  95. package/dist/extensions/blobs.js +1 -0
  96. package/dist/extensions/config-issues.d.ts +13 -1
  97. package/dist/extensions/config-issues.js +22 -5
  98. package/dist/extensions/config-json-schema.d.ts +43 -7
  99. package/dist/extensions/config-json-schema.js +124 -58
  100. package/dist/extensions/deployment-config-store.d.ts +6 -0
  101. package/dist/extensions/deployment-config.d.ts +50 -3
  102. package/dist/extensions/deployment-config.js +125 -28
  103. package/dist/extensions/display-name.d.ts +22 -0
  104. package/dist/extensions/display-name.js +77 -0
  105. package/dist/extensions/extensions.module.js +6 -0
  106. package/dist/extensions/host.d.ts +26 -1
  107. package/dist/extensions/host.js +82 -9
  108. package/dist/extensions/org-config.controller.d.ts +11 -3
  109. package/dist/extensions/org-config.controller.js +14 -6
  110. package/dist/extensions/org-config.d.ts +6 -0
  111. package/dist/extensions/org-config.js +12 -13
  112. package/dist/extensions/package.d.ts +14 -1
  113. package/dist/extensions/package.js +51 -2
  114. package/dist/extensions/permission-view.d.ts +3 -0
  115. package/dist/extensions/point-defaults.controller.d.ts +34 -0
  116. package/dist/extensions/point-defaults.controller.js +71 -0
  117. package/dist/extensions/point-defaults.d.ts +64 -0
  118. package/dist/extensions/point-defaults.js +206 -0
  119. package/dist/extensions/registry.d.ts +1 -1
  120. package/dist/extensions/registry.js +2 -0
  121. package/dist/http/api.module.js +8 -0
  122. package/dist/http/nest-logger.d.ts +6 -5
  123. package/dist/http/nest-logger.js +24 -10
  124. package/dist/identity/auth.controller.js +9 -5
  125. package/dist/identity/cookies.d.ts +20 -5
  126. package/dist/identity/cookies.js +44 -6
  127. package/dist/identity/me.controller.d.ts +5 -1
  128. package/dist/identity/me.controller.js +12 -3
  129. package/dist/identity/sessions.d.ts +12 -0
  130. package/dist/identity/sessions.js +45 -0
  131. package/dist/index.d.ts +1 -1
  132. package/dist/infra/config/kernel-config.service.js +6 -0
  133. package/dist/infra/database/database.module.js +1 -0
  134. package/dist/infra/events/kernel-events.d.ts +8 -0
  135. package/dist/infra/lifecycle/process-guards.js +4 -0
  136. package/dist/infra/logging/logging.module.d.ts +4 -2
  137. package/dist/infra/logging/logging.module.js +24 -6
  138. package/dist/infra/queue/redis-connection.d.ts +6 -0
  139. package/dist/infra/queue/redis-connection.js +7 -0
  140. package/dist/infra/redis/redis.module.js +6 -2
  141. package/dist/infra/tokens.d.ts +6 -1
  142. package/dist/infra/tokens.js +6 -1
  143. package/dist/log.d.ts +13 -1
  144. package/dist/log.js +93 -17
  145. package/dist/models/purposes.d.ts +23 -0
  146. package/dist/models/purposes.js +31 -10
  147. package/dist/orgs/offboarding.d.ts +3 -1
  148. package/dist/orgs/offboarding.js +26 -16
  149. package/dist/orgs/settings.service.d.ts +5 -0
  150. package/dist/orgs/settings.service.js +13 -2
  151. package/dist/roles/worker.module.js +6 -0
  152. package/dist/runtime/context/build.d.ts +9 -7
  153. package/dist/runtime/context/build.js +27 -19
  154. package/dist/runtime/context/claude-tools.d.ts +2 -1
  155. package/dist/runtime/context/claude-tools.js +5 -4
  156. package/dist/runtime/context/compaction.js +4 -0
  157. package/dist/runtime/langgraph-loop.js +3 -2
  158. package/dist/runtime/loop.d.ts +4 -3
  159. package/dist/runtime/maintenance.d.ts +4 -2
  160. package/dist/runtime/maintenance.js +17 -5
  161. package/dist/runtime/model-call.d.ts +17 -1
  162. package/dist/runtime/model-call.js +3 -2
  163. package/dist/runtime/providers.d.ts +6 -0
  164. package/dist/runtime/providers.js +8 -0
  165. package/dist/runtime/run-queue.d.ts +25 -2
  166. package/dist/runtime/run-queue.js +129 -33
  167. package/dist/runtime/run-worker.js +16 -3
  168. package/dist/runtime/runtime.module.js +8 -3
  169. package/dist/runtime/step-log.d.ts +6 -0
  170. package/dist/runtime/step-log.js +16 -2
  171. package/dist/runtime/subagents/definition.js +3 -1
  172. package/dist/runtime/usage-report.d.ts +2 -1
  173. package/dist/runtime/usage-report.js +2 -1
  174. package/dist/runtime/waits.d.ts +8 -2
  175. package/dist/runtime/waits.js +37 -11
  176. package/dist/sandbox/sandbox.module.js +6 -1
  177. package/dist/sandbox/sandbox.service.d.ts +61 -3
  178. package/dist/sandbox/sandbox.service.js +141 -7
  179. package/dist/tools/approvals.service.js +2 -0
  180. package/dist/tools/builtin-names.d.ts +20 -2
  181. package/dist/tools/builtin-names.js +29 -2
  182. package/dist/tools/builtins/builtin-tools.d.ts +15 -1
  183. package/dist/tools/builtins/builtin-tools.js +34 -2
  184. package/dist/tools/builtins/sandbox/bash.d.ts +1 -1
  185. package/dist/tools/builtins/sandbox/bash.js +230 -3
  186. package/dist/tools/builtins/sandbox/common.d.ts +27 -4
  187. package/dist/tools/builtins/sandbox/common.js +7 -3
  188. package/dist/tools/builtins/sandbox/deliver.js +1 -1
  189. package/dist/tools/builtins/sandbox/index.d.ts +2 -0
  190. package/dist/tools/builtins/sandbox/index.js +6 -0
  191. package/dist/tools/builtins/sandbox/task-output.d.ts +63 -0
  192. package/dist/tools/builtins/sandbox/task-output.js +304 -0
  193. package/dist/tools/builtins/sandbox/task-stop.d.ts +10 -0
  194. package/dist/tools/builtins/sandbox/task-stop.js +112 -0
  195. package/dist/tools/builtins/web/errors.d.ts +8 -0
  196. package/dist/tools/builtins/web/errors.js +53 -0
  197. package/dist/tools/builtins/web/extract.d.ts +62 -0
  198. package/dist/tools/builtins/web/extract.js +267 -0
  199. package/dist/tools/builtins/web/fetch.d.ts +55 -0
  200. package/dist/tools/builtins/web/fetch.js +174 -0
  201. package/dist/tools/builtins/web/index.d.ts +13 -0
  202. package/dist/tools/builtins/web/index.js +12 -0
  203. package/dist/tools/builtins/web/page-cache.d.ts +25 -0
  204. package/dist/tools/builtins/web/page-cache.js +77 -0
  205. package/dist/tools/builtins/web/search.d.ts +25 -0
  206. package/dist/tools/builtins/web/search.js +209 -0
  207. package/dist/tools/builtins/web/url.d.ts +17 -0
  208. package/dist/tools/builtins/web/url.js +43 -0
  209. package/dist/tools/oauth/flow-paths.d.ts +1 -1
  210. package/dist/tools/oauth/flow-paths.js +1 -1
  211. package/dist/tools/pipeline.js +15 -0
  212. package/dist/tools/registry.d.ts +22 -3
  213. package/dist/tools/registry.js +26 -11
  214. package/dist/tools/tools.module.js +12 -0
  215. package/dist/tools/validate.js +1 -1
  216. package/package.json +3 -3
  217. package/src/migrations/0064_account_last_org.sql +7 -0
  218. package/src/migrations/0065_conversation_archive.sql +8 -0
  219. package/src/migrations/0066_extension_display_name.sql +4 -0
  220. package/src/migrations/0067_agent_profiles.sql +20 -0
  221. package/src/migrations/0068_agent_avatar_uploads.sql +7 -0
  222. package/src/migrations/0069_attachments_agent_idx.sql +3 -0
  223. package/src/migrations/0070_background_tasks.sql +37 -0
  224. package/src/migrations/0071_background_tasks_reserve.sql +6 -0
  225. package/src/migrations/0072_background_tasks_stopping.sql +5 -0
  226. package/src/migrations/0073_org_extension_point_defaults.sql +11 -0
@@ -0,0 +1,71 @@
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, Put } from "@nestjs/common";
11
+ import { z } from "zod";
12
+ import { CurrentPrincipal, requireUser } from "../identity/principal.js";
13
+ import { createZodDto } from "../infra/validation/create-zod-dto.js";
14
+ import { ZBody, ZParam } from "../infra/validation/zod-params.js";
15
+ import { CurrentOrg, OrgAction } from "../orgs/org.guard.js";
16
+ import { ExtensionPointDefaults } from "./point-defaults.js";
17
+ const PointParams = z.object({ point: z.string().min(1).max(100) });
18
+ const DefaultBody = z.object({ extension: z.string().min(1).max(214) }).strict();
19
+ class ExtensionPointParamsDto extends createZodDto(PointParams, "ExtensionPointParams") {
20
+ }
21
+ class ExtensionPointDefaultBodyDto extends createZodDto(DefaultBody, "ExtensionPointDefaultBody") {
22
+ }
23
+ /**
24
+ * The org default provider of `org-default` extension points (web tools spec §6):
25
+ * - `GET /api/v1/orgs/:orgId/extension-points` (`org.extensions.manage`) → `{ points }`: each
26
+ * point with its candidate providers, the stored default and the effective provider;
27
+ * - `PUT /api/v1/orgs/:orgId/extension-points/:point/default` `{ extension }`
28
+ * (`org.extensions.manage`) → 204;
29
+ * - `GET /api/v1/orgs/:orgId/extension-points/available` (`agents.use`) → `{ points }`: whether
30
+ * each point has a provider now, without extension names (for the agent form).
31
+ */
32
+ let ExtensionPointDefaultsController = class ExtensionPointDefaultsController {
33
+ points;
34
+ constructor(points) {
35
+ this.points = points;
36
+ }
37
+ async list(org) {
38
+ return { points: await this.points.list(org.orgId) };
39
+ }
40
+ async available(org) {
41
+ return { points: await this.points.available(org.orgId) };
42
+ }
43
+ async setDefault(principal, org, { point }, body) {
44
+ const p = requireUser(principal);
45
+ await this.points.setDefault(org.orgId, point, body.extension, p.account.id);
46
+ }
47
+ };
48
+ __decorate([
49
+ Get(),
50
+ OrgAction("org.extensions.manage"),
51
+ __param(0, CurrentOrg())
52
+ ], ExtensionPointDefaultsController.prototype, "list", null);
53
+ __decorate([
54
+ Get("available"),
55
+ OrgAction("agents.use"),
56
+ __param(0, CurrentOrg())
57
+ ], ExtensionPointDefaultsController.prototype, "available", null);
58
+ __decorate([
59
+ Put(":point/default"),
60
+ HttpCode(204),
61
+ OrgAction("org.extensions.manage"),
62
+ __param(0, CurrentPrincipal()),
63
+ __param(1, CurrentOrg()),
64
+ __param(2, ZParam(ExtensionPointParamsDto)),
65
+ __param(3, ZBody(ExtensionPointDefaultBodyDto))
66
+ ], ExtensionPointDefaultsController.prototype, "setDefault", null);
67
+ ExtensionPointDefaultsController = __decorate([
68
+ Controller("api/v1/orgs/:orgId/extension-points"),
69
+ __param(0, Inject(ExtensionPointDefaults))
70
+ ], ExtensionPointDefaultsController);
71
+ export { ExtensionPointDefaultsController };
@@ -0,0 +1,64 @@
1
+ import { KernelDb } from "../infra/database/kernel-db.js";
2
+ import { TransactionHost } from "../infra/database/transaction-host.js";
3
+ import type { KernelLogger } from "../log.js";
4
+ import { OrgExtensionConfigs } from "./org-config.js";
5
+ import { ExtensionRegistry } from "./registry.js";
6
+ /** The outcome of {@link ExtensionPointDefaults.resolve}. */
7
+ export type PointResolution<T> = {
8
+ ok: true;
9
+ extension: string;
10
+ impl: T;
11
+ orgConfig: unknown;
12
+ } | {
13
+ ok: false;
14
+ reason: "none" | "ambiguous";
15
+ };
16
+ /** One `org-default` extension point of an org (the admin view). */
17
+ export interface OrgPointDefaultView {
18
+ point: string;
19
+ /** The extensions that could serve the org now, by name. */
20
+ candidates: string[];
21
+ /** The stored default, also when it is not a candidate now. */
22
+ default: string | null;
23
+ /** The extension a call would use now; null when there is none or the choice is ambiguous. */
24
+ effective: string | null;
25
+ }
26
+ /**
27
+ * The org default provider of an `org-default` extension point (web tools spec §6). Per call,
28
+ * never cached in process: the candidates are the providers loaded on this replica that are
29
+ * enabled in the org, whose org config is valid and whose `configured()` (when present) does not
30
+ * return false. One candidate is used as is; several need the org's stored default
31
+ * (`capybara.org_extension_point_defaults`), else the choice is ambiguous. A stored default that
32
+ * is not a candidate (disabled, uninstalled, unconfigured) stays and counts again once it is one.
33
+ */
34
+ export declare class ExtensionPointDefaults {
35
+ private readonly kdb;
36
+ readonly txHost: TransactionHost;
37
+ private readonly registry;
38
+ private readonly orgConfigs;
39
+ private readonly log;
40
+ constructor(kdb: KernelDb, txHost: TransactionHost, registry: Pick<ExtensionRegistry, "list">, orgConfigs: Pick<OrgExtensionConfigs, "get">, log: KernelLogger);
41
+ /**
42
+ * The provider of `point` (the name without the major, e.g. `web-search`) for `orgId`, with the
43
+ * org's decrypted config to pass back to it. Throws when the point has no `selection`.
44
+ */
45
+ resolve<T>(orgId: string, point: string): Promise<PointResolution<T>>;
46
+ /** Every `org-default` point with its candidates, stored default and effective provider. */
47
+ list(orgId: string): Promise<OrgPointDefaultView[]>;
48
+ /** Whether each `org-default` point has a provider for the org now (no extension names). */
49
+ available(orgId: string): Promise<{
50
+ point: string;
51
+ available: boolean;
52
+ }[]>;
53
+ /**
54
+ * Stores `extension` as the org's default provider of `point`. 400 `validation_failed` unless
55
+ * the point is an `org-default` one and the extension provides it (loaded here) and is enabled
56
+ * in the org. Audited as `extension_point.default_set`.
57
+ */
58
+ setDefault(orgId: string, point: string, extension: string, actorId: string): Promise<void>;
59
+ /** The candidates of `point` in `orgId`, by extension name. */
60
+ private candidates;
61
+ private choose;
62
+ /** The org's stored defaults, point → extension. */
63
+ private storedDefaults;
64
+ }
@@ -0,0 +1,206 @@
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 { EXTENSION_POINTS } from "@capybara-hq/plugin-sdk";
11
+ import { Inject, Injectable } from "@nestjs/common";
12
+ import { ApiError } from "../errors.js";
13
+ import { audit } from "../identity/audit.js";
14
+ import { KernelDb } from "../infra/database/kernel-db.js";
15
+ import { TransactionHost } from "../infra/database/transaction-host.js";
16
+ import { Transactional } from "../infra/database/transactional.decorator.js";
17
+ import { KERNEL_LOGGER } from "../infra/tokens.js";
18
+ import { enabledExtensionsQuery, isExtensionEnabled } from "./enabled.js";
19
+ import { OrgExtensionConfigs } from "./org-config.js";
20
+ import { ExtensionRegistry } from "./registry.js";
21
+ /** The extension points whose provider each org chooses (`selection: "org-default"`). */
22
+ function selectionPoints() {
23
+ return Object.entries(EXTENSION_POINTS)
24
+ .filter(([, info]) => info.selection === "org-default")
25
+ .map(([name]) => name);
26
+ }
27
+ const hasSelection = (point) => EXTENSION_POINTS[point]?.selection === "org-default";
28
+ /**
29
+ * The org default provider of an `org-default` extension point (web tools spec §6). Per call,
30
+ * never cached in process: the candidates are the providers loaded on this replica that are
31
+ * enabled in the org, whose org config is valid and whose `configured()` (when present) does not
32
+ * return false. One candidate is used as is; several need the org's stored default
33
+ * (`capybara.org_extension_point_defaults`), else the choice is ambiguous. A stored default that
34
+ * is not a candidate (disabled, uninstalled, unconfigured) stays and counts again once it is one.
35
+ */
36
+ let ExtensionPointDefaults = class ExtensionPointDefaults {
37
+ kdb;
38
+ txHost;
39
+ registry;
40
+ orgConfigs;
41
+ log;
42
+ constructor(kdb, txHost, registry, orgConfigs, log) {
43
+ this.kdb = kdb;
44
+ this.txHost = txHost;
45
+ this.registry = registry;
46
+ this.orgConfigs = orgConfigs;
47
+ this.log = log;
48
+ }
49
+ /**
50
+ * The provider of `point` (the name without the major, e.g. `web-search`) for `orgId`, with the
51
+ * org's decrypted config to pass back to it. Throws when the point has no `selection`.
52
+ */
53
+ async resolve(orgId, point) {
54
+ if (!hasSelection(point)) {
55
+ throw new Error(`extension point ${point} has no org-default selection`);
56
+ }
57
+ const candidates = await this.candidates(orgId, point);
58
+ const chosen = await this.choose(orgId, point, candidates);
59
+ if (!chosen.ok)
60
+ return chosen;
61
+ const c = chosen.candidate;
62
+ return { ok: true, extension: c.extension, impl: c.impl, orgConfig: c.orgConfig };
63
+ }
64
+ /** Every `org-default` point with its candidates, stored default and effective provider. */
65
+ async list(orgId) {
66
+ const stored = await this.storedDefaults(orgId);
67
+ const out = [];
68
+ for (const point of selectionPoints()) {
69
+ const candidates = await this.candidates(orgId, point);
70
+ const chosen = pick(candidates, stored.get(point));
71
+ out.push({
72
+ point,
73
+ candidates: candidates.map((c) => c.extension),
74
+ default: stored.get(point) ?? null,
75
+ effective: chosen.ok ? chosen.candidate.extension : null,
76
+ });
77
+ }
78
+ return out;
79
+ }
80
+ /** Whether each `org-default` point has a provider for the org now (no extension names). */
81
+ async available(orgId) {
82
+ const out = [];
83
+ for (const point of selectionPoints()) {
84
+ out.push({ point, available: (await this.resolve(orgId, point)).ok });
85
+ }
86
+ return out;
87
+ }
88
+ /**
89
+ * Stores `extension` as the org's default provider of `point`. 400 `validation_failed` unless
90
+ * the point is an `org-default` one and the extension provides it (loaded here) and is enabled
91
+ * in the org. Audited as `extension_point.default_set`.
92
+ */
93
+ async setDefault(orgId, point, extension, actorId) {
94
+ const db = this.kdb.db;
95
+ if (!hasSelection(point)) {
96
+ throw new ApiError("validation_failed", `${point} is not an extension point with an organization default`);
97
+ }
98
+ const provides = this.registry.list(point).some((e) => e.plugin === extension);
99
+ if (!provides || !(await isExtensionEnabled(db, orgId, extension))) {
100
+ throw new ApiError("validation_failed", `Extension ${extension} does not provide ${point} or is not enabled in this organization`);
101
+ }
102
+ await db
103
+ .insertInto("org_extension_point_defaults")
104
+ .values({ org_id: orgId, point, extension, updated_by: actorId })
105
+ .onConflict((oc) => oc.columns(["org_id", "point"]).doUpdateSet({
106
+ extension,
107
+ updated_by: actorId,
108
+ updated_at: new Date(),
109
+ }))
110
+ .execute();
111
+ await audit(db, {
112
+ actorAccountId: actorId,
113
+ orgId,
114
+ action: "extension_point.default_set",
115
+ target: point,
116
+ detail: { point, extension },
117
+ });
118
+ }
119
+ /** The candidates of `point` in `orgId`, by extension name. */
120
+ async candidates(orgId, point) {
121
+ const db = this.kdb.db;
122
+ const enabled = new Set((await db
123
+ .selectFrom(enabledExtensionsQuery(db, orgId).as("en"))
124
+ .select("en.plugin")
125
+ .execute()).map((r) => r.plugin));
126
+ const providers = new Map();
127
+ for (const e of this.registry.list(point)) {
128
+ if (enabled.has(e.plugin) && !providers.has(e.plugin))
129
+ providers.set(e.plugin, e.impl);
130
+ }
131
+ const out = [];
132
+ for (const [extension, impl] of providers) {
133
+ let cfg;
134
+ try {
135
+ cfg = await this.orgConfigs.get(orgId, extension);
136
+ }
137
+ catch (err) {
138
+ this.log.warn("org default provider: provider excluded, its org config is invalid", {
139
+ orgId,
140
+ point,
141
+ extension,
142
+ err,
143
+ });
144
+ continue;
145
+ }
146
+ if (!cfg?.enabled)
147
+ continue;
148
+ const configured = impl.configured;
149
+ if (typeof configured === "function") {
150
+ let ok;
151
+ try {
152
+ ok = configured.call(impl, cfg.config) !== false;
153
+ }
154
+ catch (err) {
155
+ this.log.warn("org default provider: provider excluded, configured() threw", {
156
+ orgId,
157
+ point,
158
+ extension,
159
+ err,
160
+ });
161
+ continue;
162
+ }
163
+ if (!ok)
164
+ continue;
165
+ }
166
+ out.push({ extension, impl, orgConfig: cfg.config });
167
+ }
168
+ return out.sort((a, b) => (a.extension < b.extension ? -1 : a.extension > b.extension ? 1 : 0));
169
+ }
170
+ async choose(orgId, point, candidates) {
171
+ if (candidates.length < 2)
172
+ return pick(candidates, undefined);
173
+ return pick(candidates, (await this.storedDefaults(orgId)).get(point));
174
+ }
175
+ /** The org's stored defaults, point → extension. */
176
+ async storedDefaults(orgId) {
177
+ const rows = await this.kdb.db
178
+ .selectFrom("org_extension_point_defaults")
179
+ .select(["point", "extension"])
180
+ .where("org_id", "=", orgId)
181
+ .execute();
182
+ return new Map(rows.map((r) => [r.point, r.extension]));
183
+ }
184
+ };
185
+ __decorate([
186
+ Transactional()
187
+ ], ExtensionPointDefaults.prototype, "setDefault", null);
188
+ ExtensionPointDefaults = __decorate([
189
+ Injectable(),
190
+ __param(0, Inject(KernelDb)),
191
+ __param(1, Inject(TransactionHost)),
192
+ __param(2, Inject(ExtensionRegistry)),
193
+ __param(3, Inject(OrgExtensionConfigs)),
194
+ __param(4, Inject(KERNEL_LOGGER))
195
+ ], ExtensionPointDefaults);
196
+ export { ExtensionPointDefaults };
197
+ /** Rules 2-4 of spec §6: one candidate → it; several → the stored default if a candidate. */
198
+ function pick(candidates, stored) {
199
+ const [only] = candidates;
200
+ if (only === undefined)
201
+ return { ok: false, reason: "none" };
202
+ if (candidates.length === 1)
203
+ return { ok: true, candidate: only };
204
+ const chosen = candidates.find((c) => c.extension === stored);
205
+ return chosen ? { ok: true, candidate: chosen } : { ok: false, reason: "ambiguous" };
206
+ }
@@ -11,7 +11,7 @@ export interface ExtensionPointEntry {
11
11
  version: string;
12
12
  /** 部署配置摘要,见 `deploymentConfigHash`。 */
13
13
  configHash: string;
14
- /** 提供方实例的在途计数(connector / sandbox / model-provider 的 `impl` 已经包成计入它的代理)。 */
14
+ /** The provider instance's in-flight tracker (a tracked point's `impl`: connector, sandbox, model-provider, web-search, web-fetch, is already a proxy counted in it). */
15
15
  inflight: InflightTracker;
16
16
  }
17
17
  /**
@@ -12,6 +12,8 @@ const TRACKED_EXTENSION_POINTS = {
12
12
  connector: { sessions: ["open"] },
13
13
  sandbox: {},
14
14
  "model-provider": {},
15
+ "web-search": {},
16
+ "web-fetch": {},
15
17
  };
16
18
  /**
17
19
  * The extension instances in effect in this process (by extension name, one each). One provider
@@ -13,7 +13,10 @@ import { SetupController } from "../admin/setup.controller.js";
13
13
  import { AgentsController } from "../agents/agents.controller.js";
14
14
  import { AgentsModule } from "../agents/agents.module.js";
15
15
  import { ModelsController } from "../agents/models.controller.js";
16
+ import { ReadinessController } from "../agents/readiness.controller.js";
16
17
  import { AgentTemplatesController } from "../agents/templates.controller.js";
18
+ import { BackgroundController } from "../background/background.controller.js";
19
+ import { BackgroundModule } from "../background/background.module.js";
17
20
  import { AdminCatalogController } from "../catalog/admin-catalog.controller.js";
18
21
  import { CatalogModule } from "../catalog/catalog.module.js";
19
22
  import { MarketplacesController } from "../catalog/marketplaces.controller.js";
@@ -29,6 +32,7 @@ import { EvalSetsController } from "../evals/sets.controller.js";
29
32
  import { TrajectoryController } from "../evals/trajectory.controller.js";
30
33
  import { ExtensionsModule } from "../extensions/extensions.module.js";
31
34
  import { OrgExtensionConfigsController } from "../extensions/org-config.controller.js";
35
+ import { ExtensionPointDefaultsController } from "../extensions/point-defaults.controller.js";
32
36
  import { FilesModule } from "../files/files.module.js";
33
37
  import { FilePreviewersController } from "../files/previewers.controller.js";
34
38
  import { AuthController } from "../identity/auth.controller.js";
@@ -75,7 +79,9 @@ const CONTROLLERS = [
75
79
  AgentsController,
76
80
  AgentTemplatesController,
77
81
  ModelsController,
82
+ ReadinessController,
78
83
  ConversationsController,
84
+ BackgroundController,
79
85
  AttachmentsController,
80
86
  SkillsController,
81
87
  SkillChangesController,
@@ -97,6 +103,7 @@ const CONTROLLERS = [
97
103
  DirectorySyncController,
98
104
  UiController,
99
105
  FilePreviewersController,
106
+ ExtensionPointDefaultsController,
100
107
  TrajectoryController,
101
108
  EvalSetsController,
102
109
  EvalRunsController,
@@ -126,6 +133,7 @@ ApiModule = __decorate([
126
133
  ToolsModule,
127
134
  AgentsModule,
128
135
  ConversationsModule,
136
+ BackgroundModule,
129
137
  RuntimeModule,
130
138
  ChannelsModule,
131
139
  EvalsModule,
@@ -1,12 +1,13 @@
1
1
  import type { Logger } from "@capybara-hq/plugin-sdk";
2
2
  import type { LoggerService } from "@nestjs/common";
3
- /** 可选带 debug 的 logger(内核 logger 有 debug;插件契约的 Logger 没有)。 */
3
+ /** A logger with an optional debug (the kernel logger has one; the SDK Logger does not). */
4
4
  type MaybeDebugLogger = Logger & {
5
5
  debug?: (msg: string, fields?: Record<string, unknown>) => void;
6
6
  };
7
7
  /**
8
- * 把 Nest 自身的日志转给内核 logger:`log`(启动时的模块加载、路由映射等,每次启动几十行)降为 debug,
9
- * `warn` / `error` 原级别转发,`debug` / `verbose` 丢弃。logger 没有 debug 时 `log` 也丢弃。
8
+ * Forwards Nest's own log lines to the kernel logger, as octok does: `log` (module loading,
9
+ * the `Mapped {/api/v1/…, GET} route` lines at startup) at info, `warn` / `error` as they are,
10
+ * `debug` / `verbose` at debug. Nest's context name becomes the record's `context`.
10
11
  */
11
12
  export declare class NestLogAdapter implements LoggerService {
12
13
  private readonly logger;
@@ -14,7 +15,7 @@ export declare class NestLogAdapter implements LoggerService {
14
15
  log(message: unknown, ...params: unknown[]): void;
15
16
  warn(message: unknown, ...params: unknown[]): void;
16
17
  error(message: unknown, ...params: unknown[]): void;
17
- debug(): void;
18
- verbose(): void;
18
+ debug(message: unknown, ...params: unknown[]): void;
19
+ verbose(message: unknown, ...params: unknown[]): void;
19
20
  }
20
21
  export {};
@@ -1,6 +1,7 @@
1
1
  /**
2
- * 把 Nest 自身的日志转给内核 logger:`log`(启动时的模块加载、路由映射等,每次启动几十行)降为 debug,
3
- * `warn` / `error` 原级别转发,`debug` / `verbose` 丢弃。logger 没有 debug 时 `log` 也丢弃。
2
+ * Forwards Nest's own log lines to the kernel logger, as octok does: `log` (module loading,
3
+ * the `Mapped {/api/v1/…, GET} route` lines at startup) at info, `warn` / `error` as they are,
4
+ * `debug` / `verbose` at debug. Nest's context name becomes the record's `context`.
4
5
  */
5
6
  export class NestLogAdapter {
6
7
  logger;
@@ -8,7 +9,7 @@ export class NestLogAdapter {
8
9
  this.logger = logger;
9
10
  }
10
11
  log(message, ...params) {
11
- this.logger.debug?.(String(message), fields(params));
12
+ this.logger.info(String(message), fields(params));
12
13
  }
13
14
  warn(message, ...params) {
14
15
  this.logger.warn(String(message), fields(params));
@@ -16,13 +17,26 @@ export class NestLogAdapter {
16
17
  error(message, ...params) {
17
18
  this.logger.error(String(message), fields(params));
18
19
  }
19
- debug() { }
20
- verbose() { }
20
+ debug(message, ...params) {
21
+ this.logger.debug?.(String(message), fields(params));
22
+ }
23
+ verbose(message, ...params) {
24
+ this.logger.debug?.(String(message), fields(params));
25
+ }
21
26
  }
22
- /** Nest 把上下文名(如 `RoutesResolver`)、错误堆栈作为额外参数传入。 */
27
+ /**
28
+ * Nest passes its context name (e.g. `RoutesResolver`) last and, for errors, the stack before it. The
29
+ * name becomes `context` (the pretty `[context=…]`), anything else `nest`.
30
+ */
23
31
  function fields(params) {
24
- const ctx = params.filter((p) => p !== undefined);
25
- if (ctx.length === 0)
26
- return undefined;
27
- return { nest: ctx.length === 1 ? ctx[0] : ctx };
32
+ const rest = params.filter((p) => p !== undefined);
33
+ const last = rest.at(-1);
34
+ const out = {};
35
+ if (typeof last === "string" && /^[A-Za-z][\w.-]*$/.test(last)) {
36
+ out.context = last;
37
+ rest.pop();
38
+ }
39
+ if (rest.length > 0)
40
+ out.nest = rest.length === 1 ? rest[0] : rest;
41
+ return Object.keys(out).length > 0 ? out : undefined;
28
42
  }
@@ -15,7 +15,7 @@ import { CredentialThrottler } from "../infra/throttler/credential-throttler.js"
15
15
  import { createZodDto } from "../infra/validation/create-zod-dto.js";
16
16
  import { ZBody } from "../infra/validation/zod-params.js";
17
17
  import { AccountsService } from "./accounts.js";
18
- import { ACCESS_COOKIE, clearSessionCookies, REFRESH_COOKIE, setSessionCookies, } from "./cookies.js";
18
+ import { ACCESS_COOKIE, clearSessionCookies, refreshCookieValues, setSessionCookies, } from "./cookies.js";
19
19
  import { Public } from "./principal.js";
20
20
  import { SessionService } from "./sessions.js";
21
21
  const LoginBodySchema = z.object({ login: z.string().max(256), password: z.string() });
@@ -74,9 +74,13 @@ let AuthController = class AuthController {
74
74
  */
75
75
  async refresh(req, reply) {
76
76
  try {
77
- const rt = req.cookies[REFRESH_COOKIE];
78
- if (!rt)
77
+ // the header may carry several cap_rt (legacy Path and Path=/): the current one of a live session wins
78
+ const candidates = refreshCookieValues(req.headers.cookie);
79
+ if (candidates.length === 0) {
79
80
  throw new ApiError("auth.refresh_invalid", "Refresh token is missing");
81
+ }
82
+ // none is current: the last one (the Path=/ cookie) goes through the usual replay handling
83
+ const rt = (await this.sessions.pickCurrentRefresh(candidates)) ?? candidates.at(-1);
80
84
  const session = await this.sessions.refresh(rt, clientMeta(req));
81
85
  const account = await this.accounts.get(session.accountId);
82
86
  if (!account)
@@ -94,13 +98,13 @@ let AuthController = class AuthController {
94
98
  async logout(req, reply) {
95
99
  clearSessionCookies(reply);
96
100
  const at = req.cookies[ACCESS_COOKIE];
97
- const rt = req.cookies[REFRESH_COOKIE];
101
+ const rts = refreshCookieValues(req.headers.cookie);
98
102
  if (at) {
99
103
  const sid = await this.sessions.sessionIdFromAccess(at);
100
104
  if (sid !== undefined)
101
105
  await this.sessions.revoke(sid);
102
106
  }
103
- if (rt)
107
+ for (const rt of rts)
104
108
  await this.sessions.revokeByRefresh(rt);
105
109
  }
106
110
  };
@@ -3,14 +3,29 @@ import type { KernelConfig } from "../config/schema.js";
3
3
  import type { IssuedSession } from "./sessions.js";
4
4
  /** access token Cookie:随所有请求发送。 */
5
5
  export declare const ACCESS_COOKIE = "cap_at";
6
- /** refresh token Cookie:只发往认证端点。 */
6
+ /**
7
+ * The refresh-token cookie: SameSite=Lax so a top-level navigation from another site (an IM card
8
+ * link, an email, an OAuth return) carries it and the web tier can renew the session. The refresh
9
+ * endpoint is POST-only and requires `X-Capybara-Csrf`, and Lax cookies are not sent on cross-site
10
+ * POSTs, so this does not widen CSRF exposure.
11
+ */
7
12
  export declare const REFRESH_COOKIE = "cap_rt";
8
- /** refresh Cookie 的 Path。 */
9
- export declare const REFRESH_COOKIE_PATH = "/api/v1/auth";
10
13
  /**
11
- * 写会话 Cookie。两个 Cookie 都 HttpOnly、都**不设 Domain**(只属于当前主机);
12
- * `cap_at` 为 SameSite=Lax、Path=/;`cap_rt` 为 SameSite=Strict、只发往 `/api/v1/auth`。
14
+ * Path of the refresh Cookie: `/`, so the web tier sees it on page requests and can renew the
15
+ * session while rendering on the server.
16
+ */
17
+ export declare const REFRESH_COOKIE_PATH = "/";
18
+ /** The Path earlier releases set `cap_rt` on; browsers may still hold it, so it is cleared too. */
19
+ export declare const LEGACY_REFRESH_COOKIE_PATH = "/api/v1/auth";
20
+ /**
21
+ * Writes the session cookies. Both are HttpOnly, host-only (no Domain), SameSite=Lax and Path=/;
22
+ * the `cap_rt` on the legacy Path is cleared at the same time.
13
23
  */
14
24
  export declare function setSessionCookies(reply: FastifyReply, s: IssuedSession, cfg: KernelConfig): void;
15
25
  /** 清除会话 Cookie(Path 必须与设置时一致才能覆盖)。 */
16
26
  export declare function clearSessionCookies(reply: FastifyReply): void;
27
+ /**
28
+ * Every `cap_rt` value of a raw `cookie` header, in header order. A browser holding the legacy
29
+ * Path cookie and the new one sends both, the more specific Path first.
30
+ */
31
+ export declare function refreshCookieValues(header: string | undefined): string[];
@@ -1,12 +1,22 @@
1
1
  /** access token Cookie:随所有请求发送。 */
2
2
  export const ACCESS_COOKIE = "cap_at";
3
- /** refresh token Cookie:只发往认证端点。 */
3
+ /**
4
+ * The refresh-token cookie: SameSite=Lax so a top-level navigation from another site (an IM card
5
+ * link, an email, an OAuth return) carries it and the web tier can renew the session. The refresh
6
+ * endpoint is POST-only and requires `X-Capybara-Csrf`, and Lax cookies are not sent on cross-site
7
+ * POSTs, so this does not widen CSRF exposure.
8
+ */
4
9
  export const REFRESH_COOKIE = "cap_rt";
5
- /** refresh Cookie 的 Path。 */
6
- export const REFRESH_COOKIE_PATH = "/api/v1/auth";
7
10
  /**
8
- * 写会话 Cookie。两个 Cookie 都 HttpOnly、都**不设 Domain**(只属于当前主机);
9
- * `cap_at` 为 SameSite=Lax、Path=/;`cap_rt` 为 SameSite=Strict、只发往 `/api/v1/auth`。
11
+ * Path of the refresh Cookie: `/`, so the web tier sees it on page requests and can renew the
12
+ * session while rendering on the server.
13
+ */
14
+ export const REFRESH_COOKIE_PATH = "/";
15
+ /** The Path earlier releases set `cap_rt` on; browsers may still hold it, so it is cleared too. */
16
+ export const LEGACY_REFRESH_COOKIE_PATH = "/api/v1/auth";
17
+ /**
18
+ * Writes the session cookies. Both are HttpOnly, host-only (no Domain), SameSite=Lax and Path=/;
19
+ * the `cap_rt` on the legacy Path is cleared at the same time.
10
20
  */
11
21
  export function setSessionCookies(reply, s, cfg) {
12
22
  const secure = cfg.auth.cookieSecure;
@@ -20,13 +30,41 @@ export function setSessionCookies(reply, s, cfg) {
20
30
  reply.setCookie(REFRESH_COOKIE, s.refreshToken, {
21
31
  path: REFRESH_COOKIE_PATH,
22
32
  httpOnly: true,
23
- sameSite: "strict",
33
+ sameSite: "lax",
24
34
  secure,
25
35
  maxAge: cfg.auth.refreshTokenTtlDays * 86400,
26
36
  });
37
+ reply.clearCookie(REFRESH_COOKIE, { path: LEGACY_REFRESH_COOKIE_PATH });
27
38
  }
28
39
  /** 清除会话 Cookie(Path 必须与设置时一致才能覆盖)。 */
29
40
  export function clearSessionCookies(reply) {
30
41
  reply.clearCookie(ACCESS_COOKIE, { path: "/" });
31
42
  reply.clearCookie(REFRESH_COOKIE, { path: REFRESH_COOKIE_PATH });
43
+ reply.clearCookie(REFRESH_COOKIE, { path: LEGACY_REFRESH_COOKIE_PATH });
44
+ }
45
+ /**
46
+ * Every `cap_rt` value of a raw `cookie` header, in header order. A browser holding the legacy
47
+ * Path cookie and the new one sends both, the more specific Path first.
48
+ */
49
+ export function refreshCookieValues(header) {
50
+ if (!header)
51
+ return [];
52
+ const out = [];
53
+ for (const part of header.split(";")) {
54
+ const eq = part.indexOf("=");
55
+ if (eq < 0 || part.slice(0, eq).trim() !== REFRESH_COOKIE)
56
+ continue;
57
+ let v = part.slice(eq + 1).trim();
58
+ if (v.startsWith('"') && v.endsWith('"') && v.length >= 2)
59
+ v = v.slice(1, -1);
60
+ try {
61
+ v = decodeURIComponent(v);
62
+ }
63
+ catch {
64
+ // keep the raw value
65
+ }
66
+ if (v)
67
+ out.push(v);
68
+ }
69
+ return out;
32
70
  }
@@ -8,6 +8,7 @@ import { type Account, AccountsService } from "./accounts.js";
8
8
  import { type ApiTokenRecord, ApiTokenService } from "./api-tokens.js";
9
9
  import { type Principal } from "./principal.js";
10
10
  import { SessionService } from "./sessions.js";
11
+ import { SettingsService } from "./settings.js";
11
12
  declare const ChangePasswordSchema: z.ZodObject<{
12
13
  currentPassword: z.ZodString;
13
14
  newPassword: z.ZodString;
@@ -33,12 +34,15 @@ export declare class MeController {
33
34
  private readonly tokens;
34
35
  private readonly throttler;
35
36
  private readonly log;
36
- constructor(accounts: AccountsService, sessions: SessionService, orgs: OrgsService, tokens: ApiTokenService, throttler: CredentialThrottler, log: Logger);
37
+ private readonly settings;
38
+ constructor(accounts: AccountsService, sessions: SessionService, orgs: OrgsService, tokens: ApiTokenService, throttler: CredentialThrottler, log: Logger, settings: SettingsService);
37
39
  me(principal: Principal): Promise<{
38
40
  account: Account;
39
41
  via: "session" | "pat";
40
42
  currentOrgId: string | null;
41
43
  memberships: MembershipView[];
44
+ /** Whether `POST /api/v1/orgs` would accept this account (the `orgCreation` setting). */
45
+ canCreateOrg: boolean;
42
46
  }>;
43
47
  /** 切换当前组织(仅 Web 会话);`null` 清空。非成员或已软删除的组织 → `org.not_found`。 */
44
48
  setCurrentOrg(principal: Principal, body: z.infer<typeof CurrentOrgSchema>): Promise<void>;