@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
@@ -7,30 +7,59 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
7
7
  var __param = (this && this.__param) || function (paramIndex, decorator) {
8
8
  return function (target, key) { decorator(target, key, paramIndex); }
9
9
  };
10
- import { Controller, Delete, Get, HttpCode, Inject, Param, Patch, Post } from "@nestjs/common";
10
+ import { hasControlChars, MEDIA_NAME_MAX_CHARS } from "@capybara-hq/plugin-sdk";
11
+ import { Controller, Delete, Get, HttpCode, Inject, Param, Patch, Post, Res } from "@nestjs/common";
11
12
  import { z } from "zod";
13
+ import { AGENT_AVATAR_CONTENT_TYPES, AGENT_AVATAR_DOWNLOAD_TTL_SECONDS, AGENT_AVATAR_MAX_BYTES, AttachmentsService, } from "../conversations/attachments.service.js";
14
+ import { ApiError } from "../errors.js";
12
15
  import { CurrentPrincipal, patTokenId, requireUser, } from "../identity/principal.js";
13
16
  import { createZodDto } from "../infra/validation/create-zod-dto.js";
14
17
  import { ZBody } from "../infra/validation/zod-params.js";
15
18
  import { CurrentOrg, OrgAction } from "../orgs/org.guard.js";
19
+ import { AgentProfileService } from "./agent-profile.service.js";
16
20
  import { AGENT_VISIBILITIES, AgentsService, } from "./agents.service.js";
17
21
  import { AgentDefinitionSchema, AgentToolRef, DEFAULT_AGENT_TOOLS } from "./definition.js";
22
+ import { ProfileInputSchema } from "./profile.js";
18
23
  const MemberIds = z.array(z.string().min(1).max(100)).max(1000);
19
24
  const OwnerBody = z.object({ accountId: z.string().min(1).max(100) }).strict();
20
25
  /** 创建时未给 `tools` → 默认工具(Ruling P7);修改请求不注入默认值,版本里总是存显式的 tools。 */
26
+ /**
27
+ * `instructions` has no default in a request: with `instructionFiles` the kernel derives it, and a
28
+ * new agent given neither gets the `AGENTS.md` starter (`deriveInstructions`).
29
+ */
30
+ const RequestInstructions = z.string().max(100_000).optional();
21
31
  const CreateDefinition = AgentDefinitionSchema.extend({
32
+ instructions: RequestInstructions,
22
33
  tools: z.array(AgentToolRef).default(() => structuredClone(DEFAULT_AGENT_TOOLS)),
23
34
  });
35
+ const UpdateDefinition = AgentDefinitionSchema.extend({ instructions: RequestInstructions });
24
36
  const CreateBody = z
25
37
  .object({
26
38
  definition: CreateDefinition,
27
39
  visibility: z.enum(AGENT_VISIBILITIES),
28
40
  memberIds: MemberIds.optional(),
41
+ /** Without an avatar the agent gets a random preset. */
42
+ profile: ProfileInputSchema.optional(),
43
+ })
44
+ .strict();
45
+ const ProfileBody = ProfileInputSchema.extend({
46
+ /** The `profile.updatedAt` the client read; a different one → 409 `agent.profile_conflict`. */
47
+ expectedUpdatedAt: z.string().min(1).max(40),
48
+ }).strict();
49
+ const AvatarUploadBody = z
50
+ .object({
51
+ name: z
52
+ .string()
53
+ .min(1)
54
+ .max(MEDIA_NAME_MAX_CHARS)
55
+ .refine((s) => !hasControlChars(s), "name must not contain control characters"),
56
+ contentType: z.enum(AGENT_AVATAR_CONTENT_TYPES),
57
+ size: z.number().int().min(1).max(AGENT_AVATAR_MAX_BYTES),
29
58
  })
30
59
  .strict();
31
60
  const UpdateBody = z
32
61
  .object({
33
- definition: AgentDefinitionSchema.optional(),
62
+ definition: UpdateDefinition.optional(),
34
63
  visibility: z.enum(AGENT_VISIBILITIES).optional(),
35
64
  memberIds: MemberIds.optional(),
36
65
  /**
@@ -46,6 +75,10 @@ class AgentCreateBodyDto extends createZodDto(CreateBody, "AgentCreateBody") {
46
75
  }
47
76
  class AgentUpdateBodyDto extends createZodDto(UpdateBody, "AgentUpdateBody") {
48
77
  }
78
+ class AgentProfileBodyDto extends createZodDto(ProfileBody, "AgentProfileBody") {
79
+ }
80
+ class AgentAvatarUploadBodyDto extends createZodDto(AvatarUploadBody, "AgentAvatarUploadBody") {
81
+ }
49
82
  class AgentOwnerBodyDto extends createZodDto(OwnerBody, "AgentOwnerBody") {
50
83
  }
51
84
  /**
@@ -54,8 +87,12 @@ class AgentOwnerBodyDto extends createZodDto(OwnerBody, "AgentOwnerBody") {
54
87
  */
55
88
  let AgentsController = class AgentsController {
56
89
  agents;
57
- constructor(agents) {
90
+ profiles;
91
+ attachments;
92
+ constructor(agents, profiles, attachments) {
58
93
  this.agents = agents;
94
+ this.profiles = profiles;
95
+ this.attachments = attachments;
59
96
  }
60
97
  async create(principal, org, body) {
61
98
  const p = requireUser(principal);
@@ -63,6 +100,7 @@ let AgentsController = class AgentsController {
63
100
  definition: body.definition,
64
101
  visibility: body.visibility,
65
102
  ...(body.memberIds !== undefined && { memberIds: body.memberIds }),
103
+ ...(body.profile !== undefined && { profile: body.profile }),
66
104
  }, patTokenId(p));
67
105
  }
68
106
  async list(principal, org) {
@@ -75,6 +113,52 @@ let AgentsController = class AgentsController {
75
113
  const p = requireUser(principal);
76
114
  return this.agents.update(org.orgId, agentId, p.account.id, body, patTokenId(p));
77
115
  }
116
+ /** Display data only: no agent version. Same permission as `PATCH :agentId`. */
117
+ async updateProfile(principal, org, agentId, body) {
118
+ const p = requireUser(principal);
119
+ const { expectedUpdatedAt, ...patch } = body;
120
+ return {
121
+ profile: await this.profiles.update(org.orgId, agentId, p.account.id, patch, expectedUpdatedAt, patTokenId(p)),
122
+ };
123
+ }
124
+ /**
125
+ * Registers an avatar image upload (presigned, like chat attachments and skill packages): the
126
+ * browser uploads straight to the object store, then saves `{ kind: "upload", uploadId }` with
127
+ * the profile PATCH, which finalizes it. 503 `storage.unavailable` without an object store.
128
+ */
129
+ async createAvatarUpload(principal, org, agentId, body) {
130
+ const p = requireUser(principal);
131
+ // the agent must exist and be visible to the caller (writing is checked when it is saved)
132
+ await this.agents.get(org.orgId, agentId, viewerOf(p));
133
+ return this.attachments.create(org.orgId, p.account.id, body, "agent-avatar");
134
+ }
135
+ /**
136
+ * The avatar image: 302 to a short-lived presigned URL for an upload avatar, or to the stored
137
+ * URL of an external avatar when it is https; 404 for presets, no avatar, or an agent the
138
+ * reader cannot see.
139
+ */
140
+ async avatar(principal, org, agentId, reply) {
141
+ const view = await this.agents.get(org.orgId, agentId, viewerOf(principal));
142
+ const avatar = view.profile.avatar;
143
+ let location = null;
144
+ if (avatar?.kind === "upload") {
145
+ location = await this.attachments.agentAvatarUrl(org.orgId, agentId, avatar.uploadId);
146
+ }
147
+ else if (avatar?.kind === "external") {
148
+ try {
149
+ if (new URL(avatar.url).protocol === "https:")
150
+ location = avatar.url;
151
+ }
152
+ catch {
153
+ // a malformed migrated URL: no image
154
+ }
155
+ }
156
+ if (location === null)
157
+ throw new ApiError("agent.not_found", "The agent has no avatar image");
158
+ await reply
159
+ .header("cache-control", `private, max-age=${Math.floor(AGENT_AVATAR_DOWNLOAD_TTL_SECONDS / 5)}`)
160
+ .redirect(location, 302);
161
+ }
78
162
  async remove(principal, org, agentId) {
79
163
  const p = requireUser(principal);
80
164
  await this.agents.softDelete(org.orgId, agentId, p.account.id, patTokenId(p));
@@ -119,6 +203,30 @@ __decorate([
119
203
  __param(2, Param("agentId")),
120
204
  __param(3, ZBody(AgentUpdateBodyDto))
121
205
  ], AgentsController.prototype, "update", null);
206
+ __decorate([
207
+ Patch(":agentId/profile"),
208
+ OrgAction("agents.create"),
209
+ __param(0, CurrentPrincipal()),
210
+ __param(1, CurrentOrg()),
211
+ __param(2, Param("agentId")),
212
+ __param(3, ZBody(AgentProfileBodyDto))
213
+ ], AgentsController.prototype, "updateProfile", null);
214
+ __decorate([
215
+ Post(":agentId/avatar/uploads"),
216
+ OrgAction("agents.create"),
217
+ __param(0, CurrentPrincipal()),
218
+ __param(1, CurrentOrg()),
219
+ __param(2, Param("agentId")),
220
+ __param(3, ZBody(AgentAvatarUploadBodyDto))
221
+ ], AgentsController.prototype, "createAvatarUpload", null);
222
+ __decorate([
223
+ Get(":agentId/avatar"),
224
+ OrgAction("agents.use"),
225
+ __param(0, CurrentPrincipal()),
226
+ __param(1, CurrentOrg()),
227
+ __param(2, Param("agentId")),
228
+ __param(3, Res())
229
+ ], AgentsController.prototype, "avatar", null);
122
230
  __decorate([
123
231
  Delete(":agentId"),
124
232
  HttpCode(204),
@@ -145,6 +253,8 @@ __decorate([
145
253
  ], AgentsController.prototype, "versions", null);
146
254
  AgentsController = __decorate([
147
255
  Controller("api/v1/orgs/:orgId/agents"),
148
- __param(0, Inject(AgentsService))
256
+ __param(0, Inject(AgentsService)),
257
+ __param(1, Inject(AgentProfileService)),
258
+ __param(2, Inject(AttachmentsService))
149
259
  ], AgentsController);
150
260
  export { AgentsController };
@@ -5,6 +5,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
5
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
7
  import { Module } from "@nestjs/common";
8
+ import { AttachmentsModule } from "../conversations/attachments.module.js";
8
9
  import { EvalBudgetModule } from "../evals/budget.js";
9
10
  import { OrgLockModule } from "../orgs/org-lock.js";
10
11
  import { ProvidersModule } from "../runtime/providers.js";
@@ -12,6 +13,7 @@ import { SandboxModule } from "../sandbox/sandbox.module.js";
12
13
  import { SkillsModule } from "../skills/skills.module.js";
13
14
  import { ToolsModule } from "../tools/tools.module.js";
14
15
  import { AgentLookupModule } from "./agent-lookup.js";
16
+ import { AgentProfileService } from "./agent-profile.service.js";
15
17
  import { AgentsService } from "./agents.service.js";
16
18
  import { AgentTemplatesService } from "./templates.js";
17
19
  /**
@@ -25,6 +27,8 @@ AgentsModule = __decorate([
25
27
  Module({
26
28
  imports: [
27
29
  AgentLookupModule,
30
+ // avatar uploads
31
+ AttachmentsModule,
28
32
  OrgLockModule,
29
33
  ProvidersModule,
30
34
  SkillsModule,
@@ -34,8 +38,8 @@ AgentsModule = __decorate([
34
38
  // cancels the deleted agent's eval runs
35
39
  EvalBudgetModule,
36
40
  ],
37
- providers: [AgentsService, AgentTemplatesService],
38
- exports: [AgentLookupModule, AgentsService, AgentTemplatesService],
41
+ providers: [AgentsService, AgentProfileService, AgentTemplatesService],
42
+ exports: [AgentLookupModule, AgentsService, AgentProfileService, AgentTemplatesService],
39
43
  })
40
44
  ], AgentsModule);
41
45
  export { AgentsModule };
@@ -10,6 +10,8 @@ import { SkillChangesService } from "../skills/changes.service.js";
10
10
  import { ToolRegistry } from "../tools/registry.js";
11
11
  import { AgentLookup, type AgentView, type AgentVisibility } from "./agent-lookup.js";
12
12
  import { type AgentDefinition } from "./definition.js";
13
+ import { type AgentDefinitionInput } from "./instruction-files.js";
14
+ import { type ProfileInput } from "./profile.js";
13
15
  export { AGENT_VISIBILITIES, type AgentView, type AgentVisibility, isAgentVisibleTo, visibleTo, } from "./agent-lookup.js";
14
16
  export { paramIssues, SUBAGENT_ROSTER_MAX } from "./definition.js";
15
17
  export interface AgentVersion {
@@ -21,6 +23,8 @@ export interface AgentVersion {
21
23
  export type AgentVersionSummary = Omit<AgentVersion, "definition">;
22
24
  /** 创建 / 修改的提示(不拒绝):`fallback_same_provider` = 兜底与主模型同一供应商(spec 4.5)。 */
23
25
  export type AgentWarning = "fallback_same_provider";
26
+ /** A new agent has no id to bind an upload to yet: a client can only choose a preset on create. */
27
+ export declare function assertProfileWritable(profile: ProfileInput | undefined): void;
24
28
  /** Why a `subagents` block is refused (`agent.subagents_invalid` details). */
25
29
  export type SubagentsProblem = "not_found" | "invalid_name" | "duplicate" | "tool_not_selected" | "skill_not_selected" | "agent_not_available" | "self" | "too_many";
26
30
  /**
@@ -104,10 +108,11 @@ export declare class AgentsService {
104
108
  * 新建 agent(版本 1):在组织行锁内授权 `agents.create`;主模型与兜底都必须可解析
105
109
  * (`agent.model_unavailable`);兜底与主模型同一供应商只提示 `fallback_same_provider`。
106
110
  */
107
- create(orgId: string, actorAccountId: string, input: {
108
- definition: AgentDefinition;
111
+ create(orgId: string, actorAccountId: string, request: {
112
+ definition: AgentDefinitionInput;
109
113
  visibility: AgentVisibility;
110
114
  memberIds?: string[];
115
+ profile?: ProfileInput;
111
116
  }, actorTokenId?: string): Promise<{
112
117
  agent: AgentView;
113
118
  warnings: AgentWarning[];
@@ -117,13 +122,16 @@ export declare class AgentsService {
117
122
  * 锁组织行并复核发起人,再锁 agent 行:发起人须仍具备 `agents.create`(member 以上),且是负责人或组织管理员以上;
118
123
  * 对发起人不可见的 agent(非管理员)→ `agent.not_found`,可见但无权 → `forbidden`。
119
124
  */
120
- private lockForWrite;
125
+ lockForWrite(orgId: string, agentId: string, actorAccountId: string): Promise<{
126
+ visibility: AgentVisibility;
127
+ current_version: number;
128
+ }>;
121
129
  /**
122
130
  * 修改 agent:`definition` 变化时写新版本(`current_version + 1`),只改 visibility / memberIds 不产生新版本。
123
131
  * 只有负责人或组织管理员以上可改,在组织行锁内复核。
124
132
  */
125
- update(orgId: string, agentId: string, actorAccountId: string, patch: {
126
- definition?: AgentDefinition | undefined;
133
+ update(orgId: string, agentId: string, actorAccountId: string, request: {
134
+ definition?: AgentDefinitionInput | undefined;
127
135
  visibility?: AgentVisibility | undefined;
128
136
  memberIds?: string[] | undefined;
129
137
  /**
@@ -28,10 +28,18 @@ import { legacyKeySaveText } from "../tools/legacy-keys.js";
28
28
  import { ToolRegistry } from "../tools/registry.js";
29
29
  import { AgentLookup, isAgentVisibleTo, visibleTo, } from "./agent-lookup.js";
30
30
  import { normalizeStoredDefinition, paramIssues, SUBAGENT_NAME_RE, SUBAGENT_ROSTER_MAX, } from "./definition.js";
31
+ import { deriveInstructions } from "./instruction-files.js";
32
+ import { randomPreset } from "./profile.js";
31
33
  export { AGENT_VISIBILITIES, isAgentVisibleTo, visibleTo, } from "./agent-lookup.js";
32
34
  export { paramIssues, SUBAGENT_ROSTER_MAX } from "./definition.js";
33
35
  /** 删除 agent 时每个销毁任务带的对话数。 */
34
36
  const SANDBOX_DESTROY_BATCH = 500;
37
+ /** A new agent has no id to bind an upload to yet: a client can only choose a preset on create. */
38
+ export function assertProfileWritable(profile) {
39
+ if (profile?.avatar?.kind === "upload") {
40
+ throw new ApiError("validation_failed", "Choose a preset avatar when creating an agent; upload one afterwards", [{ path: "avatar.kind", message: "upload is not supported on create" }]);
41
+ }
42
+ }
35
43
  const notFound = () => new ApiError("agent.not_found", "Agent not found");
36
44
  /** 键排序后的 JSON:jsonb 不保留键顺序,比较定义是否变化时用。 */
37
45
  function canonical(v) {
@@ -292,13 +300,15 @@ let AgentsService = class AgentsService {
292
300
  * 新建 agent(版本 1):在组织行锁内授权 `agents.create`;主模型与兜底都必须可解析
293
301
  * (`agent.model_unavailable`);兜底与主模型同一供应商只提示 `fallback_same_provider`。
294
302
  */
295
- async create(orgId, actorAccountId, input, actorTokenId) {
303
+ async create(orgId, actorAccountId, request, actorTokenId) {
304
+ const input = { ...request, definition: deriveInstructions(request.definition, null) };
296
305
  // 先解析模型(可能访问供应商),不在持有组织行锁时做
297
306
  const warnings = await this.checkModels(input.definition);
298
307
  const toolKeys = await this.checkTools(orgId, input.definition);
299
308
  if (input.visibility !== "members" && (input.memberIds?.length ?? 0) > 0) {
300
309
  throw new ApiError("validation_failed", "memberIds requires visibility 'members'");
301
310
  }
311
+ assertProfileWritable(input.profile);
302
312
  const agent = await this.createIn(orgId, actorAccountId, input, toolKeys, actorTokenId);
303
313
  return { agent, warnings };
304
314
  }
@@ -326,6 +336,9 @@ let AgentsService = class AgentsService {
326
336
  owner_account_id: actorAccountId,
327
337
  visibility: input.visibility,
328
338
  current_version: 1,
339
+ profile_avatar: JSON.stringify(input.profile?.avatar ?? { kind: "preset", id: randomPreset() }),
340
+ profile_description: input.profile?.description ?? "",
341
+ profile_quick_actions: JSON.stringify(input.profile?.quickActions ?? []),
329
342
  })
330
343
  .execute();
331
344
  await t
@@ -383,7 +396,8 @@ let AgentsService = class AgentsService {
383
396
  * 修改 agent:`definition` 变化时写新版本(`current_version + 1`),只改 visibility / memberIds 不产生新版本。
384
397
  * 只有负责人或组织管理员以上可改,在组织行锁内复核。
385
398
  */
386
- async update(orgId, agentId, actorAccountId, patch, actorTokenId) {
399
+ async update(orgId, agentId, actorAccountId, request, actorTokenId) {
400
+ const patch = request;
387
401
  const warnings = patch.definition ? await this.checkModels(patch.definition) : [];
388
402
  const toolKeys = patch.definition ? await this.checkTools(orgId, patch.definition) : null;
389
403
  const agent = await this.updateIn(orgId, agentId, actorAccountId, patch, toolKeys, actorTokenId);
@@ -407,26 +421,28 @@ let AgentsService = class AgentsService {
407
421
  .where("agent_id", "=", agentId)
408
422
  .where("version", "=", before.current_version)
409
423
  .executeTakeFirstOrThrow();
424
+ // the prompt: instructionFiles give instructions; a save given neither keeps the current one
425
+ const definition = deriveInstructions(patch.definition, normalizeStoredDefinition(current.definition));
410
426
  // 当前版本已有的技能引用保存时合法,不再核对(被删的技能不挡住其他改动);新加的照常核对
411
427
  const kept = new Set(current.definition.skills?.orgSkills ?? []);
412
- await assertOrgSkills(t, orgId, patch.definition.skills?.orgSkills ?? [], kept);
413
- await assertPinnedSkills(t, orgId, agentId, patch.definition.skills, current.definition.skills?.pinned ?? []);
428
+ await assertOrgSkills(t, orgId, definition.skills?.orgSkills ?? [], kept);
429
+ await assertPinnedSkills(t, orgId, agentId, definition.skills, current.definition.skills?.pinned ?? []);
414
430
  await assertSubagents(t, {
415
431
  orgId,
416
432
  agentId,
417
433
  actorAccountId,
418
- definition: patch.definition,
434
+ definition,
419
435
  toolKeys: toolKeys ?? new Set(),
420
436
  kept: current.definition.subagents,
421
437
  });
422
- if (canonical(current.definition) !== canonical(patch.definition)) {
438
+ if (canonical(current.definition) !== canonical(definition)) {
423
439
  toVersion = before.current_version + 1;
424
440
  await t
425
441
  .insertInto("agent_versions")
426
442
  .values({
427
443
  agent_id: agentId,
428
444
  version: toVersion,
429
- definition: JSON.stringify(patch.definition),
445
+ definition: JSON.stringify(definition),
430
446
  created_by: actorAccountId,
431
447
  })
432
448
  .execute();
@@ -473,6 +489,13 @@ let AgentsService = class AgentsService {
473
489
  .set({ deleted_at: sql `now()`, updated_at: sql `now()` })
474
490
  .where("id", "=", agentId)
475
491
  .execute();
492
+ // the avatar upload is garbage now: the cleanup job deletes it (and its object) once expired
493
+ await t
494
+ .updateTable("attachments")
495
+ .set({ expires_at: sql `now()` })
496
+ .where("agent_id", "=", agentId)
497
+ .where("purpose", "=", "agent-avatar")
498
+ .execute();
476
499
  await audit(t, {
477
500
  actorAccountId,
478
501
  ...(actorTokenId !== undefined && { actorTokenId }),
@@ -117,6 +117,10 @@ export declare const AgentDefinitionSchema: z.ZodObject<{
117
117
  name: z.ZodString;
118
118
  avatarUrl: z.ZodDefault<z.ZodNullable<z.ZodURL>>;
119
119
  instructions: z.ZodDefault<z.ZodString>;
120
+ instructionFiles: z.ZodOptional<z.ZodArray<z.ZodObject<{
121
+ name: z.ZodString;
122
+ content: z.ZodString;
123
+ }, z.core.$strict>>>;
120
124
  model: z.ZodObject<{
121
125
  provider: z.ZodString;
122
126
  model: z.ZodString;
@@ -1,6 +1,7 @@
1
1
  import { validateParams } from "@capybara-hq/plugin-sdk";
2
2
  import { z } from "zod";
3
3
  import { isLegacyBuiltinKey, isLegacyPrefixedKey, legacyToolKey, } from "../tools/legacy-keys.js";
4
+ import { InstructionFilesSchema } from "./instruction-files.js";
4
5
  /** 模型引用:供应商插件名 + 模型 id。 */
5
6
  export const ModelRef = z.object({ provider: z.string(), model: z.string() });
6
7
  /**
@@ -94,7 +95,16 @@ export const SubagentsSchema = z
94
95
  export const AgentDefinitionSchema = z.object({
95
96
  name: z.string().min(1).max(80),
96
97
  avatarUrl: z.url().nullable().default(null),
98
+ /**
99
+ * The model-visible system prompt. Derived on save from `instructionFiles` when they are given
100
+ * (`deriveInstructions`); every reader (build.ts, the sub-agent copy, evals, replay) reads this.
101
+ */
97
102
  instructions: z.string().max(100_000).default(""),
103
+ /**
104
+ * The prompt files, the authoring form of `instructions` (spec 2026-11-04 §3.3). No default:
105
+ * definitions without it (every older version) read as one `AGENTS.md` and gain no version.
106
+ */
107
+ instructionFiles: InstructionFilesSchema.optional(),
98
108
  model: ModelRef,
99
109
  fallback: ModelRef.nullable().default(null),
100
110
  /** 主模型的参数(按主模型的声明校验)。 */
@@ -0,0 +1,60 @@
1
+ import { z } from "zod";
2
+ import type { AgentDefinition } from "./definition.js";
3
+ /**
4
+ * Prompt files (spec 2026-11-04 §3.3): the authoring form of an agent's system prompt. The kernel
5
+ * joins them into `instructions` on save, and `instructions` stays the one model-visible field
6
+ * (build.ts, the sub-agent copy, eval candidates and replay keep reading it), so no log event
7
+ * changes shape and replay needs no new `DriftReason`.
8
+ */
9
+ export type InstructionFile = {
10
+ name: string;
11
+ content: string;
12
+ };
13
+ /** The default and first file. */
14
+ export declare const AGENTS_MD = "AGENTS.md";
15
+ export declare const INSTRUCTION_FILE_LIMITS: {
16
+ /** At most this many files, at least one. */
17
+ readonly count: 20;
18
+ /** The joined text, the same bound as `instructions`. */
19
+ readonly joinedChars: 100000;
20
+ };
21
+ /** A file name: 1–64 characters without `/` or `\`, then `.md`. */
22
+ export declare const INSTRUCTION_FILE_NAME_RE: RegExp;
23
+ /**
24
+ * The starter prompt of a new agent created without one (model-visible once saved; English like
25
+ * every model-facing text).
26
+ */
27
+ export declare const AGENTS_MD_STARTER: string;
28
+ /**
29
+ * The model-visible join: one file gives exactly its content (so an old agent edited without
30
+ * adding files sends the same text as before); several files give `# <name>\n\n<content>` each,
31
+ * in order, joined by `\n\n`.
32
+ */
33
+ export declare function joinInstructionFiles(files: readonly InstructionFile[]): string;
34
+ /** The files of a definition; a definition without them (every old version) is one `AGENTS.md`. */
35
+ export declare function readInstructionFiles(def: Pick<AgentDefinition, "instructions" | "instructionFiles">): InstructionFile[];
36
+ export declare const InstructionFileSchema: z.ZodObject<{
37
+ name: z.ZodString;
38
+ content: z.ZodString;
39
+ }, z.core.$strict>;
40
+ /**
41
+ * 1–20 files, names unique ignoring case (`AGENTS.md` and `agents.md` would collide on a
42
+ * case-insensitive file system), a joined total of at most 100 000 characters.
43
+ */
44
+ export declare const InstructionFilesSchema: z.ZodArray<z.ZodObject<{
45
+ name: z.ZodString;
46
+ content: z.ZodString;
47
+ }, z.core.$strict>>;
48
+ /** A definition as a save request gives it: `instructions` may be left out. */
49
+ export type AgentDefinitionInput = Omit<AgentDefinition, "instructions"> & {
50
+ instructions?: string | undefined;
51
+ };
52
+ /**
53
+ * Derives the stored definition of a save. With `instructionFiles`, `instructions` is their join
54
+ * (a differing `instructions` given with them → 400 `agent.instructions_mismatch`). With only
55
+ * `instructions` (old clients) the current files are kept while it equals their join, else the
56
+ * definition is stored as given, without files. Given neither,
57
+ * an update keeps the `current` version's prompt, and a new agent (`current` null) gets the
58
+ * `AGENTS.md` starter.
59
+ */
60
+ export declare function deriveInstructions(def: AgentDefinitionInput, current: Pick<AgentDefinition, "instructions" | "instructionFiles"> | null): AgentDefinition;
@@ -0,0 +1,111 @@
1
+ import { z } from "zod";
2
+ import { ApiError } from "../errors.js";
3
+ import { CONTROL_OR_HIDDEN_RE } from "../text.js";
4
+ /** The default and first file. */
5
+ export const AGENTS_MD = "AGENTS.md";
6
+ export const INSTRUCTION_FILE_LIMITS = {
7
+ /** At most this many files, at least one. */
8
+ count: 20,
9
+ /** The joined text, the same bound as `instructions`. */
10
+ joinedChars: 100_000,
11
+ };
12
+ /** A file name: 1–64 characters without `/` or `\`, then `.md`. */
13
+ export const INSTRUCTION_FILE_NAME_RE = /^[^/\\]{1,64}\.md$/;
14
+ /**
15
+ * The starter prompt of a new agent created without one (model-visible once saved; English like
16
+ * every model-facing text).
17
+ */
18
+ export const AGENTS_MD_STARTER = [
19
+ "You are an assistant for this team. Help people get their work done accurately and efficiently.",
20
+ "",
21
+ "- Tone: clear, friendly and concise. Use plain language, keep answers short, and add detail when asked.",
22
+ "- When a request is unclear or missing key details, ask one short clarifying question before acting.",
23
+ " If you go ahead anyway, say what you assumed.",
24
+ "- If you don't know something or can't do it, say so instead of guessing.",
25
+ ].join("\n");
26
+ /**
27
+ * The model-visible join: one file gives exactly its content (so an old agent edited without
28
+ * adding files sends the same text as before); several files give `# <name>\n\n<content>` each,
29
+ * in order, joined by `\n\n`.
30
+ */
31
+ export function joinInstructionFiles(files) {
32
+ if (files.length === 1)
33
+ return files[0]?.content ?? "";
34
+ return files.map((f) => `# ${f.name}\n\n${f.content}`).join("\n\n");
35
+ }
36
+ /** The files of a definition; a definition without them (every old version) is one `AGENTS.md`. */
37
+ export function readInstructionFiles(def) {
38
+ return def.instructionFiles ?? [{ name: AGENTS_MD, content: def.instructions }];
39
+ }
40
+ export const InstructionFileSchema = z
41
+ .object({
42
+ name: z
43
+ .string()
44
+ .regex(INSTRUCTION_FILE_NAME_RE, "a file name is 1–64 characters without / or \\, then .md")
45
+ .refine((n) => !CONTROL_OR_HIDDEN_RE.test(n), "a file name must not contain control or format characters"),
46
+ content: z.string().max(INSTRUCTION_FILE_LIMITS.joinedChars),
47
+ })
48
+ .strict();
49
+ /**
50
+ * 1–20 files, names unique ignoring case (`AGENTS.md` and `agents.md` would collide on a
51
+ * case-insensitive file system), a joined total of at most 100 000 characters.
52
+ */
53
+ export const InstructionFilesSchema = z
54
+ .array(InstructionFileSchema)
55
+ .min(1)
56
+ .max(INSTRUCTION_FILE_LIMITS.count)
57
+ .superRefine((files, ctx) => {
58
+ const seen = new Set();
59
+ files.forEach((f, i) => {
60
+ const key = f.name.toLowerCase();
61
+ if (seen.has(key)) {
62
+ ctx.addIssue({ code: "custom", path: [i, "name"], message: `duplicate file ${f.name}` });
63
+ }
64
+ seen.add(key);
65
+ });
66
+ const joined = joinInstructionFiles(files).length;
67
+ if (joined > INSTRUCTION_FILE_LIMITS.joinedChars) {
68
+ ctx.addIssue({
69
+ code: "custom",
70
+ message: `the joined files are ${joined} characters, more than ${INSTRUCTION_FILE_LIMITS.joinedChars}`,
71
+ });
72
+ }
73
+ });
74
+ /**
75
+ * Derives the stored definition of a save. With `instructionFiles`, `instructions` is their join
76
+ * (a differing `instructions` given with them → 400 `agent.instructions_mismatch`). With only
77
+ * `instructions` (old clients) the current files are kept while it equals their join, else the
78
+ * definition is stored as given, without files. Given neither,
79
+ * an update keeps the `current` version's prompt, and a new agent (`current` null) gets the
80
+ * `AGENTS.md` starter.
81
+ */
82
+ export function deriveInstructions(def, current) {
83
+ const { instructions, instructionFiles, ...rest } = def;
84
+ if (instructionFiles !== undefined) {
85
+ const joined = joinInstructionFiles(instructionFiles);
86
+ if (instructions !== undefined && instructions !== joined) {
87
+ throw new ApiError("agent.instructions_mismatch", "instructions differs from the join of instructionFiles; send only instructionFiles");
88
+ }
89
+ return { ...rest, instructions: joined, instructionFiles };
90
+ }
91
+ if (instructions !== undefined) {
92
+ // an old client re-sending the same joined text (an unrelated edit) keeps the files
93
+ const files = current?.instructionFiles;
94
+ if (files !== undefined && instructions === joinInstructionFiles(files)) {
95
+ return { ...rest, instructions, instructionFiles: files };
96
+ }
97
+ return { ...rest, instructions };
98
+ }
99
+ if (current !== null) {
100
+ return {
101
+ ...rest,
102
+ instructions: current.instructions,
103
+ ...(current.instructionFiles !== undefined && { instructionFiles: current.instructionFiles }),
104
+ };
105
+ }
106
+ return {
107
+ ...rest,
108
+ instructions: AGENTS_MD_STARTER,
109
+ instructionFiles: [{ name: AGENTS_MD, content: AGENTS_MD_STARTER }],
110
+ };
111
+ }
@@ -1,12 +1,18 @@
1
- import type { ModelInfo } from "@capybara-hq/plugin-sdk";
1
+ import type { LocalizedName, ModelInfo } from "@capybara-hq/plugin-sdk";
2
+ import { ExtensionDisplayNames } from "../extensions/display-name.js";
2
3
  import { ProviderRegistry } from "../runtime/providers.js";
3
- /** 可选模型(`/api/v1/orgs/:orgId/models`):已安装的模型供应商及其模型,列表缓存在 Redis。 */
4
+ /**
5
+ * 可选模型(`/api/v1/orgs/:orgId/models`):已安装的模型供应商及其模型,列表缓存在 Redis。Each provider
6
+ * carries its extension's display name (null when it has none).
7
+ */
4
8
  export declare class ModelsController {
5
9
  private readonly registry;
6
- constructor(registry: ProviderRegistry);
10
+ private readonly displayNames;
11
+ constructor(registry: ProviderRegistry, displayNames: ExtensionDisplayNames);
7
12
  list(): Promise<{
8
13
  providers: {
9
14
  provider: string;
15
+ displayName: LocalizedName | null;
10
16
  models: ModelInfo[];
11
17
  }[];
12
18
  }>;
@@ -8,16 +8,30 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
8
8
  return function (target, key) { decorator(target, key, paramIndex); }
9
9
  };
10
10
  import { Controller, Get, Inject } from "@nestjs/common";
11
+ import { ExtensionDisplayNames } from "../extensions/display-name.js";
11
12
  import { OrgAction } from "../orgs/org.guard.js";
12
13
  import { ProviderRegistry } from "../runtime/providers.js";
13
- /** 可选模型(`/api/v1/orgs/:orgId/models`):已安装的模型供应商及其模型,列表缓存在 Redis。 */
14
+ /**
15
+ * 可选模型(`/api/v1/orgs/:orgId/models`):已安装的模型供应商及其模型,列表缓存在 Redis。Each provider
16
+ * carries its extension's display name (null when it has none).
17
+ */
14
18
  let ModelsController = class ModelsController {
15
19
  registry;
16
- constructor(registry) {
20
+ displayNames;
21
+ constructor(registry, displayNames) {
17
22
  this.registry = registry;
23
+ this.displayNames = displayNames;
18
24
  }
19
25
  async list() {
20
- return { providers: await this.registry.list() };
26
+ const providers = await this.registry.list();
27
+ const names = await this.displayNames.many(providers.map((p) => p.provider));
28
+ return {
29
+ providers: providers.map((p) => ({
30
+ provider: p.provider,
31
+ displayName: names.get(p.provider) ?? null,
32
+ models: p.models,
33
+ })),
34
+ };
21
35
  }
22
36
  };
23
37
  __decorate([
@@ -26,6 +40,7 @@ __decorate([
26
40
  ], ModelsController.prototype, "list", null);
27
41
  ModelsController = __decorate([
28
42
  Controller("api/v1/orgs/:orgId/models"),
29
- __param(0, Inject(ProviderRegistry))
43
+ __param(0, Inject(ProviderRegistry)),
44
+ __param(1, Inject(ExtensionDisplayNames))
30
45
  ], ModelsController);
31
46
  export { ModelsController };