@capybara-hq/kernel 0.1.0-rc.3 → 0.1.0-rc.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/admin.controller.d.ts +9 -4
- package/dist/admin/admin.controller.js +13 -4
- package/dist/admin/extensions.controller.d.ts +8 -1
- package/dist/admin/extensions.controller.js +22 -2
- package/dist/agents/agent-lookup.d.ts +3 -0
- package/dist/agents/agent-lookup.js +10 -0
- package/dist/agents/agent-profile.service.d.ts +29 -0
- package/dist/agents/agent-profile.service.js +117 -0
- package/dist/agents/agents.controller.d.ts +93 -3
- package/dist/agents/agents.controller.js +114 -4
- package/dist/agents/agents.module.js +6 -2
- package/dist/agents/agents.service.d.ts +13 -5
- package/dist/agents/agents.service.js +30 -7
- package/dist/agents/definition.d.ts +4 -0
- package/dist/agents/definition.js +10 -0
- package/dist/agents/instruction-files.d.ts +60 -0
- package/dist/agents/instruction-files.js +111 -0
- package/dist/agents/models.controller.d.ts +9 -3
- package/dist/agents/models.controller.js +19 -4
- package/dist/agents/profile.d.ts +96 -0
- package/dist/agents/profile.js +51 -0
- package/dist/agents/readiness.controller.d.ts +26 -0
- package/dist/agents/readiness.controller.js +56 -0
- package/dist/agents/templates.js +1 -1
- package/dist/background/background.controller.d.ts +38 -0
- package/dist/background/background.controller.js +87 -0
- package/dist/background/background.module.d.ts +10 -0
- package/dist/background/background.module.js +32 -0
- package/dist/background/finish.d.ts +43 -0
- package/dist/background/finish.js +94 -0
- package/dist/background/launch-lock.d.ts +14 -0
- package/dist/background/launch-lock.js +39 -0
- package/dist/background/launch.d.ts +36 -0
- package/dist/background/launch.js +70 -0
- package/dist/background/poll.job.d.ts +53 -0
- package/dist/background/poll.job.js +182 -0
- package/dist/background/render.d.ts +23 -0
- package/dist/background/render.js +48 -0
- package/dist/background/session.d.ts +37 -0
- package/dist/background/session.js +66 -0
- package/dist/background/status.d.ts +35 -0
- package/dist/background/status.js +57 -0
- package/dist/background/stop.d.ts +62 -0
- package/dist/background/stop.js +225 -0
- package/dist/background/store.d.ts +150 -0
- package/dist/background/store.js +351 -0
- package/dist/background/store.module.d.ts +6 -0
- package/dist/background/store.module.js +22 -0
- package/dist/background/tasks.service.d.ts +67 -0
- package/dist/background/tasks.service.js +186 -0
- package/dist/background/wake-rules.d.ts +57 -0
- package/dist/background/wake-rules.js +83 -0
- package/dist/background/wake.d.ts +28 -0
- package/dist/background/wake.js +57 -0
- package/dist/catalog/claude/agents.js +2 -0
- package/dist/catalog/marketplaces.service.d.ts +1 -1
- package/dist/catalog/plugin-import.js +1 -1
- package/dist/catalog/plugin-upgrade.js +1 -1
- package/dist/channels/cards.js +1 -1
- package/dist/channels/connections.service.d.ts +16 -1
- package/dist/channels/connections.service.js +38 -4
- package/dist/channels/threads.d.ts +5 -0
- package/dist/channels/threads.js +8 -0
- package/dist/config/schema.d.ts +18 -0
- package/dist/config/schema.js +102 -0
- package/dist/conversations/access.js +2 -0
- package/dist/conversations/attachments.service.d.ts +30 -0
- package/dist/conversations/attachments.service.js +130 -5
- package/dist/conversations/conversations.controller.d.ts +36 -5
- package/dist/conversations/conversations.controller.js +62 -12
- package/dist/conversations/conversations.module.js +3 -0
- package/dist/conversations/conversations.service.d.ts +44 -2
- package/dist/conversations/conversations.service.js +140 -12
- package/dist/conversations/event-log.d.ts +1 -1
- package/dist/conversations/event-log.js +10 -1
- package/dist/conversations/events.d.ts +58 -0
- package/dist/conversations/events.js +57 -2
- package/dist/conversations/queue.d.ts +3 -1
- package/dist/conversations/queue.js +13 -2
- package/dist/conversations/queue.service.js +3 -1
- package/dist/conversations/titles.d.ts +7 -0
- package/dist/conversations/titles.js +15 -0
- package/dist/db/kernel-schema.d.ts +58 -2
- package/dist/db/pool.d.ts +5 -0
- package/dist/db/pool.js +3 -0
- package/dist/errors.d.ts +8 -0
- package/dist/errors.js +8 -0
- package/dist/evals/run-replay.d.ts +5 -2
- package/dist/evals/run-replay.js +30 -7
- package/dist/evals/step-replay.js +12 -6
- package/dist/evals/trajectory.js +1 -1
- package/dist/extensions/admin-ops.js +2 -0
- package/dist/extensions/admin-view.d.ts +3 -1
- package/dist/extensions/admin-view.js +10 -2
- package/dist/extensions/blobs.js +1 -0
- package/dist/extensions/config-issues.d.ts +13 -1
- package/dist/extensions/config-issues.js +22 -5
- package/dist/extensions/config-json-schema.d.ts +43 -7
- package/dist/extensions/config-json-schema.js +124 -58
- package/dist/extensions/deployment-config-store.d.ts +6 -0
- package/dist/extensions/deployment-config.d.ts +50 -3
- package/dist/extensions/deployment-config.js +125 -28
- package/dist/extensions/display-name.d.ts +22 -0
- package/dist/extensions/display-name.js +77 -0
- package/dist/extensions/extensions.module.js +6 -0
- package/dist/extensions/host.d.ts +26 -1
- package/dist/extensions/host.js +82 -9
- package/dist/extensions/org-config.controller.d.ts +11 -3
- package/dist/extensions/org-config.controller.js +14 -6
- package/dist/extensions/org-config.d.ts +6 -0
- package/dist/extensions/org-config.js +12 -13
- package/dist/extensions/package.d.ts +14 -1
- package/dist/extensions/package.js +51 -2
- package/dist/extensions/permission-view.d.ts +3 -0
- package/dist/extensions/point-defaults.controller.d.ts +34 -0
- package/dist/extensions/point-defaults.controller.js +71 -0
- package/dist/extensions/point-defaults.d.ts +64 -0
- package/dist/extensions/point-defaults.js +206 -0
- package/dist/extensions/registry.d.ts +1 -1
- package/dist/extensions/registry.js +2 -0
- package/dist/http/api.module.js +8 -0
- package/dist/http/nest-logger.d.ts +6 -5
- package/dist/http/nest-logger.js +24 -10
- package/dist/identity/auth.controller.js +9 -5
- package/dist/identity/cookies.d.ts +20 -5
- package/dist/identity/cookies.js +44 -6
- package/dist/identity/me.controller.d.ts +5 -1
- package/dist/identity/me.controller.js +12 -3
- package/dist/identity/sessions.d.ts +12 -0
- package/dist/identity/sessions.js +45 -0
- package/dist/index.d.ts +1 -1
- package/dist/infra/config/kernel-config.service.js +6 -0
- package/dist/infra/database/database.module.js +1 -0
- package/dist/infra/events/kernel-events.d.ts +8 -0
- package/dist/infra/lifecycle/process-guards.js +4 -0
- package/dist/infra/logging/logging.module.d.ts +4 -2
- package/dist/infra/logging/logging.module.js +24 -6
- package/dist/infra/queue/redis-connection.d.ts +6 -0
- package/dist/infra/queue/redis-connection.js +7 -0
- package/dist/infra/redis/redis.module.js +6 -2
- package/dist/infra/tokens.d.ts +6 -1
- package/dist/infra/tokens.js +6 -1
- package/dist/log.d.ts +13 -1
- package/dist/log.js +93 -17
- package/dist/models/purposes.d.ts +23 -0
- package/dist/models/purposes.js +31 -10
- package/dist/orgs/offboarding.d.ts +3 -1
- package/dist/orgs/offboarding.js +26 -16
- package/dist/orgs/settings.service.d.ts +5 -0
- package/dist/orgs/settings.service.js +13 -2
- package/dist/roles/worker.module.js +6 -0
- package/dist/runtime/context/build.d.ts +9 -7
- package/dist/runtime/context/build.js +27 -19
- package/dist/runtime/context/claude-tools.d.ts +2 -1
- package/dist/runtime/context/claude-tools.js +5 -4
- package/dist/runtime/context/compaction.js +4 -0
- package/dist/runtime/langgraph-loop.js +3 -2
- package/dist/runtime/loop.d.ts +4 -3
- package/dist/runtime/maintenance.d.ts +4 -2
- package/dist/runtime/maintenance.js +17 -5
- package/dist/runtime/model-call.d.ts +17 -1
- package/dist/runtime/model-call.js +3 -2
- package/dist/runtime/providers.d.ts +6 -0
- package/dist/runtime/providers.js +8 -0
- package/dist/runtime/run-queue.d.ts +25 -2
- package/dist/runtime/run-queue.js +129 -33
- package/dist/runtime/run-worker.js +16 -3
- package/dist/runtime/runtime.module.js +8 -3
- package/dist/runtime/step-log.d.ts +6 -0
- package/dist/runtime/step-log.js +16 -2
- package/dist/runtime/subagents/definition.js +3 -1
- package/dist/runtime/usage-report.d.ts +2 -1
- package/dist/runtime/usage-report.js +2 -1
- package/dist/runtime/waits.d.ts +8 -2
- package/dist/runtime/waits.js +37 -11
- package/dist/sandbox/sandbox.module.js +6 -1
- package/dist/sandbox/sandbox.service.d.ts +61 -3
- package/dist/sandbox/sandbox.service.js +141 -7
- package/dist/tools/approvals.service.js +2 -0
- package/dist/tools/builtin-names.d.ts +20 -2
- package/dist/tools/builtin-names.js +29 -2
- package/dist/tools/builtins/builtin-tools.d.ts +15 -1
- package/dist/tools/builtins/builtin-tools.js +34 -2
- package/dist/tools/builtins/sandbox/bash.d.ts +1 -1
- package/dist/tools/builtins/sandbox/bash.js +230 -3
- package/dist/tools/builtins/sandbox/common.d.ts +27 -4
- package/dist/tools/builtins/sandbox/common.js +7 -3
- package/dist/tools/builtins/sandbox/deliver.js +1 -1
- package/dist/tools/builtins/sandbox/index.d.ts +2 -0
- package/dist/tools/builtins/sandbox/index.js +6 -0
- package/dist/tools/builtins/sandbox/task-output.d.ts +63 -0
- package/dist/tools/builtins/sandbox/task-output.js +304 -0
- package/dist/tools/builtins/sandbox/task-stop.d.ts +10 -0
- package/dist/tools/builtins/sandbox/task-stop.js +112 -0
- package/dist/tools/builtins/web/errors.d.ts +8 -0
- package/dist/tools/builtins/web/errors.js +53 -0
- package/dist/tools/builtins/web/extract.d.ts +62 -0
- package/dist/tools/builtins/web/extract.js +267 -0
- package/dist/tools/builtins/web/fetch.d.ts +55 -0
- package/dist/tools/builtins/web/fetch.js +174 -0
- package/dist/tools/builtins/web/index.d.ts +13 -0
- package/dist/tools/builtins/web/index.js +12 -0
- package/dist/tools/builtins/web/page-cache.d.ts +25 -0
- package/dist/tools/builtins/web/page-cache.js +77 -0
- package/dist/tools/builtins/web/search.d.ts +25 -0
- package/dist/tools/builtins/web/search.js +209 -0
- package/dist/tools/builtins/web/url.d.ts +17 -0
- package/dist/tools/builtins/web/url.js +43 -0
- package/dist/tools/oauth/flow-paths.d.ts +1 -1
- package/dist/tools/oauth/flow-paths.js +1 -1
- package/dist/tools/pipeline.js +15 -0
- package/dist/tools/registry.d.ts +22 -3
- package/dist/tools/registry.js +26 -11
- package/dist/tools/tools.module.js +12 -0
- package/dist/tools/validate.js +1 -1
- package/package.json +3 -3
- package/src/migrations/0064_account_last_org.sql +7 -0
- package/src/migrations/0065_conversation_archive.sql +8 -0
- package/src/migrations/0066_extension_display_name.sql +4 -0
- package/src/migrations/0067_agent_profiles.sql +20 -0
- package/src/migrations/0068_agent_avatar_uploads.sql +7 -0
- package/src/migrations/0069_attachments_agent_idx.sql +3 -0
- package/src/migrations/0070_background_tasks.sql +37 -0
- package/src/migrations/0071_background_tasks_reserve.sql +6 -0
- package/src/migrations/0072_background_tasks_stopping.sql +5 -0
- package/src/migrations/0073_org_extension_point_defaults.sql +11 -0
|
@@ -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 {
|
|
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:
|
|
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
|
-
|
|
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,
|
|
108
|
-
definition:
|
|
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
|
-
|
|
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,
|
|
126
|
-
definition?:
|
|
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,
|
|
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,
|
|
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,
|
|
413
|
-
await assertPinnedSkills(t, orgId, agentId,
|
|
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
|
|
434
|
+
definition,
|
|
419
435
|
toolKeys: toolKeys ?? new Set(),
|
|
420
436
|
kept: current.definition.subagents,
|
|
421
437
|
});
|
|
422
|
-
if (canonical(current.definition) !== canonical(
|
|
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(
|
|
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
|
-
/**
|
|
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
|
-
|
|
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
|
-
/**
|
|
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
|
-
|
|
20
|
+
displayNames;
|
|
21
|
+
constructor(registry, displayNames) {
|
|
17
22
|
this.registry = registry;
|
|
23
|
+
this.displayNames = displayNames;
|
|
18
24
|
}
|
|
19
25
|
async list() {
|
|
20
|
-
|
|
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 };
|