@capybara-hq/plugin-sdk 0.1.0-rc.1 → 0.1.0-rc.3

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 (54) hide show
  1. package/README.md +1 -1
  2. package/agent-docs/agent-templates.md +47 -0
  3. package/agent-docs/concepts.md +58 -0
  4. package/agent-docs/connector-templates.md +47 -0
  5. package/agent-docs/extension-points/channel.md +15 -0
  6. package/agent-docs/extension-points/config-source.md +15 -0
  7. package/agent-docs/extension-points/connector.md +15 -0
  8. package/agent-docs/extension-points/counter.md +15 -0
  9. package/agent-docs/extension-points/directory.md +15 -0
  10. package/agent-docs/extension-points/file-preview.md +15 -0
  11. package/agent-docs/extension-points/identity-source.md +15 -0
  12. package/agent-docs/extension-points/model-provider.md +15 -0
  13. package/agent-docs/extension-points/object-store.md +15 -0
  14. package/agent-docs/extension-points/sandbox.md +15 -0
  15. package/agent-docs/extension-points/secret-store.md +15 -0
  16. package/agent-docs/extension-points/web-fetch.md +15 -0
  17. package/agent-docs/extension-points/web-search.md +15 -0
  18. package/agent-docs/extensions.md +63 -0
  19. package/agent-docs/plugin-format.md +60 -0
  20. package/agent-docs/skills.md +44 -0
  21. package/agent-docs/tool-naming.md +21 -0
  22. package/dist/agent-template.d.ts +85 -0
  23. package/dist/agent-template.js +361 -2
  24. package/dist/config-ui.d.ts +131 -0
  25. package/dist/config-ui.js +602 -0
  26. package/dist/ctx.d.ts +3 -1
  27. package/dist/directory.d.ts +11 -2
  28. package/dist/extension-point.d.ts +9 -0
  29. package/dist/extension-point.js +17 -12
  30. package/dist/file-preview.d.ts +8 -0
  31. package/dist/hooks.d.ts +14 -4
  32. package/dist/index.d.ts +4 -1
  33. package/dist/index.js +4 -1
  34. package/dist/marketplace.d.ts +29 -2
  35. package/dist/marketplace.js +30 -2
  36. package/dist/org-access.d.ts +27 -0
  37. package/dist/org-access.js +35 -0
  38. package/dist/plugin-manifest.d.ts +25 -2
  39. package/dist/plugin-manifest.js +20 -1
  40. package/dist/sandbox.d.ts +140 -60
  41. package/dist/sandbox.js +30 -6
  42. package/dist/secret.d.ts +4 -1
  43. package/dist/secret.js +5 -2
  44. package/dist/skill-invocation.d.ts +8 -5
  45. package/dist/skill-invocation.js +17 -8
  46. package/dist/skill.d.ts +20 -3
  47. package/dist/skill.js +28 -6
  48. package/dist/tool.d.ts +8 -0
  49. package/dist/ui.d.ts +8 -0
  50. package/dist/web.d.ts +105 -0
  51. package/dist/web.js +135 -0
  52. package/package.json +4 -2
  53. package/schemas/marketplace.v1.schema.json +36 -0
  54. package/schemas/plugin.v1.schema.json +49 -4
@@ -21,6 +21,62 @@ export interface AgentTemplate {
21
21
  maxSteps?: number;
22
22
  /** Skill names preloaded (pinned) in the sub-agent. */
23
23
  skills?: string[];
24
+ /** Further skill names the agent uses (not preloaded; P4-1). */
25
+ selectedSkills?: string[];
26
+ /** Connectors the agent needs: a connector template of the plugin, or a described need. */
27
+ connectors?: AgentTemplateConnector[];
28
+ /** The sub-agents the agent gets in its roster. */
29
+ subagents?: AgentTemplateSubagents;
30
+ /** `provider/model`: the concrete model an org template was saved with. */
31
+ modelRef?: string;
32
+ /** The agent profile's quick actions (same shape and limits). */
33
+ quickActions?: AgentTemplateQuickAction[];
34
+ /** The agent's instruction files. */
35
+ instructionFiles?: AgentTemplateInstructionFile[];
36
+ }
37
+ /** A quick action of the agent profile. */
38
+ export interface AgentTemplateQuickAction {
39
+ title: string;
40
+ body: string;
41
+ }
42
+ /** An instruction file of the agent. */
43
+ export interface AgentTemplateInstructionFile {
44
+ name: string;
45
+ content: string;
46
+ }
47
+ /**
48
+ * A connector entry of a template: a connector template id (`<id>` or `<plugin>:<id>`) or a need.
49
+ * `policies` tighten the connector's tools by tool name (`ask` / `deny`), applied once it is bound.
50
+ */
51
+ export type AgentTemplateConnector = {
52
+ template: string;
53
+ tools?: string[];
54
+ policies?: Record<string, "ask" | "deny">;
55
+ } | {
56
+ needs: {
57
+ protocol: "mcp" | "openapi";
58
+ endpoint: string;
59
+ tools: string[];
60
+ label: string;
61
+ };
62
+ policies?: Record<string, "ask" | "deny">;
63
+ };
64
+ /** A sub-agent type described inside a template. */
65
+ export interface AgentTemplateInlineSubagent {
66
+ name: string;
67
+ description: string;
68
+ instructions: string;
69
+ /** Tool keys allowed (an allowlist; `[]` = no tools); absent = all of the agent's tools. */
70
+ tools?: string[];
71
+ /** Skill names. */
72
+ skills: string[];
73
+ model: AgentTemplateTier;
74
+ }
75
+ export interface AgentTemplateSubagents {
76
+ general?: boolean;
77
+ /** Template types as the roster writes them (`<plugin>:<name>` or `<name>`). */
78
+ types?: string[];
79
+ inline?: AgentTemplateInlineSubagent[];
24
80
  }
25
81
  /** Limits of agent templates. */
26
82
  export declare const AGENT_TEMPLATE_LIMITS: {
@@ -33,9 +89,38 @@ export declare const AGENT_TEMPLATE_LIMITS: {
33
89
  readonly skills: 3;
34
90
  /** Entries of `tools` and of `disallowedTools`. */
35
91
  readonly tools: 200;
92
+ /** Entries of `selectedSkills`. */
93
+ readonly selectedSkills: 32;
94
+ /** Entries of `connectors`. */
95
+ readonly connectors: 16;
96
+ /** `subagents.types` plus `subagents.inline`. */
97
+ readonly subagentTypes: 16;
98
+ /** Characters of one inline sub-agent's instructions. */
99
+ readonly inlineInstructionsChars: 100000;
100
+ /** Tools named by a connector entry (its `tools`, and the keys of its `policies`). */
101
+ readonly connectorTools: 200;
102
+ /** Quick actions, as the agent profile limits them. */
103
+ readonly quickActions: 9;
104
+ readonly quickActionTitleChars: 40;
105
+ readonly quickActionBodyChars: 20000;
106
+ /** Instruction files and their joined size, as the agent limits them. */
107
+ readonly instructionFiles: 20;
108
+ readonly instructionFilesChars: 100000;
36
109
  };
110
+ /** One frontmatter key of an agent template, for documentation (served to agents by `Platform`). */
111
+ export interface AgentTemplateField {
112
+ name: string;
113
+ type: string;
114
+ required: boolean;
115
+ /** What it does, with its limit. */
116
+ description: string;
117
+ }
118
+ /** The frontmatter keys `parseAgentTemplate` reads (the body is the instructions). */
119
+ export declare const AGENT_TEMPLATE_FIELDS: readonly AgentTemplateField[];
37
120
  /** The name rule of a template (the catalog item name rule). */
38
121
  export declare const AGENT_TEMPLATE_NAME_RE: RegExp;
122
+ /** The name rule of an instruction file (the agent's rule, mirrored). */
123
+ export declare const AGENT_TEMPLATE_INSTRUCTION_FILE_NAME_RE: RegExp;
39
124
  /**
40
125
  * Parses an agent template file. Returns the template, or every problem found (English, for the
41
126
  * plugin author or an admin). Keys the format does not define are ignored. Never throws.
@@ -17,14 +17,130 @@ export const AGENT_TEMPLATE_LIMITS = {
17
17
  skills: 3,
18
18
  /** Entries of `tools` and of `disallowedTools`. */
19
19
  tools: 200,
20
+ /** Entries of `selectedSkills`. */
21
+ selectedSkills: 32,
22
+ /** Entries of `connectors`. */
23
+ connectors: 16,
24
+ /** `subagents.types` plus `subagents.inline`. */
25
+ subagentTypes: 16,
26
+ /** Characters of one inline sub-agent's instructions. */
27
+ inlineInstructionsChars: 100_000,
28
+ /** Tools named by a connector entry (its `tools`, and the keys of its `policies`). */
29
+ connectorTools: 200,
30
+ /** Quick actions, as the agent profile limits them. */
31
+ quickActions: 9,
32
+ quickActionTitleChars: 40,
33
+ quickActionBodyChars: 20_000,
34
+ /** Instruction files and their joined size, as the agent limits them. */
35
+ instructionFiles: 20,
36
+ instructionFilesChars: 100_000,
20
37
  };
38
+ /** The frontmatter keys `parseAgentTemplate` reads (the body is the instructions). */
39
+ export const AGENT_TEMPLATE_FIELDS = [
40
+ {
41
+ name: "name",
42
+ type: "string",
43
+ required: true,
44
+ description: "`^[a-z0-9][a-z0-9-]{0,63}$`; the type name is `<plugin>:<name>`.",
45
+ },
46
+ {
47
+ name: "description",
48
+ type: "string",
49
+ required: true,
50
+ description: "When to use it; at most 4096 characters.",
51
+ },
52
+ {
53
+ name: "tools",
54
+ type: "array of tool keys",
55
+ required: false,
56
+ description: "Allowlist (at most 200); omitted = the parent's tools, `[]` = none.",
57
+ },
58
+ {
59
+ name: "disallowedTools",
60
+ type: "array of tool keys",
61
+ required: false,
62
+ description: "Tool keys removed (at most 200).",
63
+ },
64
+ {
65
+ name: "toolPolicies",
66
+ type: "object",
67
+ required: false,
68
+ description: "Tool key to `ask` or `deny`; can only tighten.",
69
+ },
70
+ {
71
+ name: "model",
72
+ type: "`inherit` \\| `fast` \\| `standard` \\| `capable`",
73
+ required: false,
74
+ description: "A purpose tier, never a model id; default `inherit`.",
75
+ },
76
+ {
77
+ name: "effort",
78
+ type: "`low` \\| `medium` \\| `high` \\| `xhigh` \\| `max`",
79
+ required: false,
80
+ description: "Reasoning effort.",
81
+ },
82
+ {
83
+ name: "maxSteps",
84
+ type: "integer",
85
+ required: false,
86
+ description: "Step limit; the kernel caps it.",
87
+ },
88
+ {
89
+ name: "skills",
90
+ type: "array of skill names",
91
+ required: false,
92
+ description: "At most 3, preloaded (pinned).",
93
+ },
94
+ {
95
+ name: "selectedSkills",
96
+ type: "array of skill names",
97
+ required: false,
98
+ description: "At most 32 further skills the agent may use.",
99
+ },
100
+ {
101
+ name: "connectors",
102
+ type: "array of objects",
103
+ required: false,
104
+ description: "At most 16: `{ template, tools?, policies? }` for a connector template (`<id>` or `<plugin>:<id>`), or `{ needs: { protocol, endpoint, tools, label }, policies? }` for a described need.",
105
+ },
106
+ {
107
+ name: "subagents",
108
+ type: "object",
109
+ required: false,
110
+ description: "`{ general?, types?, inline? }`: the sub-agents in its roster (at most 16 types plus inline).",
111
+ },
112
+ {
113
+ name: "modelRef",
114
+ type: "string",
115
+ required: false,
116
+ description: "`provider/model`; set on org templates saved from an agent.",
117
+ },
118
+ {
119
+ name: "quickActions",
120
+ type: "array of objects",
121
+ required: false,
122
+ description: "At most 9 `{ title, body }` shortcuts.",
123
+ },
124
+ {
125
+ name: "instructionFiles",
126
+ type: "array of objects",
127
+ required: false,
128
+ description: "At most 20 `{ name, content }` files.",
129
+ },
130
+ ];
21
131
  /** The name rule of a template (the catalog item name rule). */
22
132
  export const AGENT_TEMPLATE_NAME_RE = /^[a-z0-9][a-z0-9-]{0,63}$/;
23
133
  const TIERS = ["inherit", "fast", "standard", "capable"];
24
134
  const EFFORTS = ["low", "medium", "high", "xhigh", "max"];
25
- const FRONTMATTER_MAX_CHARS = 64 * 1024;
135
+ /** Room for sub-agents described inline (up to 16 x 100 000 characters of instructions, P4-1). */
136
+ const FRONTMATTER_MAX_CHARS = 2 * 1024 * 1024;
26
137
  /** The kernel's tool key rule (model-visible keys): no dots, no whitespace, at most 64 chars. */
27
138
  const TOOL_KEY_RE = /^[a-zA-Z0-9_-]{1,64}$/;
139
+ const distinct = (v) => new Set(v).size === v.length;
140
+ const NAME_REF_RE = /^[a-z0-9][a-z0-9-]{0,63}(:[a-z0-9][a-z0-9-]{0,63})?$/;
141
+ const MODEL_REF_RE = /^[^/\s]+\/\S+$/;
142
+ /** The name rule of an instruction file (the agent's rule, mirrored). */
143
+ export const AGENT_TEMPLATE_INSTRUCTION_FILE_NAME_RE = /^[^/\\]{1,64}\.md$/;
28
144
  const chars = (s) => [...s].length;
29
145
  const isToolKey = (v) => typeof v === "string" && TOOL_KEY_RE.test(v);
30
146
  const toolList = (v) => Array.isArray(v) &&
@@ -112,12 +228,255 @@ export function parseAgentTemplate(text) {
112
228
  }
113
229
  if (fm.skills !== undefined) {
114
230
  const s = fm.skills;
115
- if (Array.isArray(s) && s.length <= L.skills && s.every(isSkillName))
231
+ if (Array.isArray(s) && s.length <= L.skills && s.every(isSkillName) && distinct(s))
116
232
  out.skills = s;
117
233
  else
118
234
  errors.push(`skills must list at most ${L.skills} skill names`);
119
235
  }
236
+ if (fm.selectedSkills !== undefined) {
237
+ const s = fm.selectedSkills;
238
+ if (Array.isArray(s) && s.length <= L.selectedSkills && s.every(isSkillName) && distinct(s)) {
239
+ out.selectedSkills = s;
240
+ }
241
+ else
242
+ errors.push(`selectedSkills must list at most ${L.selectedSkills} skill names`);
243
+ }
244
+ if (fm.connectors !== undefined) {
245
+ const c = readConnectors(fm.connectors, errors);
246
+ if (c)
247
+ out.connectors = c;
248
+ }
249
+ if (fm.subagents !== undefined) {
250
+ const s = readSubagents(fm.subagents, errors);
251
+ if (s)
252
+ out.subagents = s;
253
+ }
254
+ if (fm.modelRef !== undefined) {
255
+ if (typeof fm.modelRef === "string" &&
256
+ fm.modelRef.length <= 200 &&
257
+ MODEL_REF_RE.test(fm.modelRef)) {
258
+ out.modelRef = fm.modelRef;
259
+ }
260
+ else
261
+ errors.push("modelRef must be <provider>/<model>");
262
+ }
263
+ if (fm.quickActions !== undefined) {
264
+ const q = readQuickActions(fm.quickActions, errors);
265
+ if (q)
266
+ out.quickActions = q;
267
+ }
268
+ if (fm.instructionFiles !== undefined) {
269
+ const f = readInstructionFiles(fm.instructionFiles, errors);
270
+ if (f)
271
+ out.instructionFiles = f;
272
+ }
120
273
  if (errors.length > 0)
121
274
  return { errors };
122
275
  return { template: out };
123
276
  }
277
+ const isObj = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
278
+ const isStr = (v, max) => typeof v === "string" && v.trim() !== "" && chars(v) <= max;
279
+ const strList = (v, max, itemMax) => Array.isArray(v) && v.length <= max && v.every((x) => isStr(x, itemMax));
280
+ const connectorPolicies = (v) => isObj(v) &&
281
+ Object.keys(v).length <= AGENT_TEMPLATE_LIMITS.connectorTools &&
282
+ Object.entries(v).every(([k, p]) => isStr(k, 128) && (p === "ask" || p === "deny"));
283
+ const connectorPoliciesRule = `must map at most ${AGENT_TEMPLATE_LIMITS.connectorTools} tool names to ask or deny`;
284
+ function readConnectors(v, errors) {
285
+ const L = AGENT_TEMPLATE_LIMITS;
286
+ if (!Array.isArray(v) || v.length > L.connectors) {
287
+ errors.push(`connectors must be a list of at most ${L.connectors} entries`);
288
+ return undefined;
289
+ }
290
+ const out = [];
291
+ let ok = true;
292
+ v.forEach((e, i) => {
293
+ const at = `connectors[${i}]`;
294
+ if (!isObj(e)) {
295
+ errors.push(`${at} must be { template } or { needs }`);
296
+ ok = false;
297
+ }
298
+ else if ("template" in e) {
299
+ if (typeof e.template !== "string" || !NAME_REF_RE.test(e.template)) {
300
+ errors.push(`${at}.template must be a connector template id (<id> or <plugin>:<id>)`);
301
+ ok = false;
302
+ }
303
+ else if (e.tools !== undefined && !strList(e.tools, L.connectorTools, 128)) {
304
+ errors.push(`${at}.tools must list at most ${L.connectorTools} tool names`);
305
+ ok = false;
306
+ }
307
+ else if (e.policies !== undefined && !connectorPolicies(e.policies)) {
308
+ errors.push(`${at}.policies ${connectorPoliciesRule}`);
309
+ ok = false;
310
+ }
311
+ else {
312
+ out.push({
313
+ template: e.template,
314
+ ...(e.tools !== undefined && { tools: e.tools }),
315
+ ...(e.policies !== undefined && {
316
+ policies: { ...e.policies },
317
+ }),
318
+ });
319
+ }
320
+ }
321
+ else if ("needs" in e) {
322
+ const n = e.needs;
323
+ if (!isObj(n)) {
324
+ errors.push(`${at}.needs must be an object`);
325
+ ok = false;
326
+ }
327
+ else if (n.protocol !== "mcp" && n.protocol !== "openapi") {
328
+ errors.push(`${at}.needs.protocol must be mcp or openapi`);
329
+ ok = false;
330
+ }
331
+ else if (!isStr(n.endpoint, 2048)) {
332
+ errors.push(`${at}.needs.endpoint must be a non-empty string of at most 2048 characters`);
333
+ ok = false;
334
+ }
335
+ else if (!strList(n.tools, L.connectorTools, 128)) {
336
+ errors.push(`${at}.needs.tools must list at most ${L.connectorTools} tool names`);
337
+ ok = false;
338
+ }
339
+ else if (!isStr(n.label, 80)) {
340
+ errors.push(`${at}.needs.label must be a non-empty string of at most 80 characters`);
341
+ ok = false;
342
+ }
343
+ else if (e.policies !== undefined && !connectorPolicies(e.policies)) {
344
+ errors.push(`${at}.policies ${connectorPoliciesRule}`);
345
+ ok = false;
346
+ }
347
+ else {
348
+ out.push({
349
+ needs: {
350
+ protocol: n.protocol,
351
+ endpoint: n.endpoint,
352
+ tools: n.tools,
353
+ label: n.label,
354
+ },
355
+ ...(e.policies !== undefined && {
356
+ policies: { ...e.policies },
357
+ }),
358
+ });
359
+ }
360
+ }
361
+ else {
362
+ errors.push(`${at} must have template or needs`);
363
+ ok = false;
364
+ }
365
+ });
366
+ return ok ? out : undefined;
367
+ }
368
+ function readSubagents(v, errors) {
369
+ const L = AGENT_TEMPLATE_LIMITS;
370
+ if (!isObj(v)) {
371
+ errors.push("subagents must be an object");
372
+ return undefined;
373
+ }
374
+ const out = {};
375
+ const n0 = errors.length;
376
+ if (v.general !== undefined) {
377
+ if (typeof v.general === "boolean")
378
+ out.general = v.general;
379
+ else
380
+ errors.push("subagents.general must be true or false");
381
+ }
382
+ if (v.types !== undefined) {
383
+ if (Array.isArray(v.types) &&
384
+ v.types.every((t) => typeof t === "string" && NAME_REF_RE.test(t)) &&
385
+ distinct(v.types)) {
386
+ out.types = v.types;
387
+ }
388
+ else
389
+ errors.push("subagents.types must list sub-agent type names");
390
+ }
391
+ if (v.inline !== undefined) {
392
+ if (!Array.isArray(v.inline))
393
+ errors.push("subagents.inline must be a list");
394
+ else {
395
+ const inline = [];
396
+ v.inline.forEach((e, i) => {
397
+ const at = `subagents.inline[${i}]`;
398
+ if (!isObj(e)) {
399
+ errors.push(`${at} must be an object`);
400
+ return;
401
+ }
402
+ const before = errors.length;
403
+ if (typeof e.name !== "string" || !AGENT_TEMPLATE_NAME_RE.test(e.name)) {
404
+ errors.push(`${at}.name must match ^[a-z0-9][a-z0-9-]{0,63}$`);
405
+ }
406
+ if (!isStr(e.description, L.descriptionChars)) {
407
+ errors.push(`${at}.description must be a non-empty string of at most ${L.descriptionChars} characters`);
408
+ }
409
+ if (typeof e.instructions !== "string" ||
410
+ e.instructions.trim() === "" ||
411
+ chars(e.instructions) > L.inlineInstructionsChars) {
412
+ errors.push(`${at}.instructions must be a non-empty string of at most ${L.inlineInstructionsChars} characters`);
413
+ }
414
+ if (e.tools !== undefined && !toolList(e.tools)) {
415
+ errors.push(`${at}.tools must be ${toolListRule}`);
416
+ }
417
+ if (!(Array.isArray(e.skills) &&
418
+ e.skills.length <= L.skills &&
419
+ e.skills.every(isSkillName) &&
420
+ distinct(e.skills))) {
421
+ errors.push(`${at}.skills must list at most ${L.skills} skill names`);
422
+ }
423
+ if (!TIERS.includes(e.model)) {
424
+ errors.push(`${at}.model must be one of ${TIERS.join(", ")}`);
425
+ }
426
+ if (errors.length === before)
427
+ inline.push(e);
428
+ });
429
+ out.inline = inline;
430
+ }
431
+ }
432
+ if ((out.types?.length ?? 0) + (out.inline?.length ?? 0) > L.subagentTypes) {
433
+ errors.push(`subagents.types and subagents.inline together must be at most ${L.subagentTypes}`);
434
+ }
435
+ return errors.length === n0 ? out : undefined;
436
+ }
437
+ function readQuickActions(v, errors) {
438
+ const L = AGENT_TEMPLATE_LIMITS;
439
+ if (!Array.isArray(v) || v.length > L.quickActions) {
440
+ errors.push(`quickActions must be a list of at most ${L.quickActions} entries`);
441
+ return undefined;
442
+ }
443
+ const n0 = errors.length;
444
+ const out = [];
445
+ v.forEach((e, i) => {
446
+ if (isObj(e) &&
447
+ isStr(e.title, L.quickActionTitleChars) &&
448
+ isStr(e.body, L.quickActionBodyChars)) {
449
+ out.push({ title: e.title, body: e.body });
450
+ }
451
+ else {
452
+ errors.push(`quickActions[${i}] must have a title (at most ${L.quickActionTitleChars} characters) and a body (at most ${L.quickActionBodyChars})`);
453
+ }
454
+ });
455
+ return errors.length === n0 ? out : undefined;
456
+ }
457
+ function readInstructionFiles(v, errors) {
458
+ const L = AGENT_TEMPLATE_LIMITS;
459
+ if (!Array.isArray(v) || v.length > L.instructionFiles) {
460
+ errors.push(`instructionFiles must be a list of at most ${L.instructionFiles} entries`);
461
+ return undefined;
462
+ }
463
+ const n0 = errors.length;
464
+ const out = [];
465
+ let total = 0;
466
+ v.forEach((e, i) => {
467
+ if (isObj(e) &&
468
+ typeof e.name === "string" &&
469
+ AGENT_TEMPLATE_INSTRUCTION_FILE_NAME_RE.test(e.name) &&
470
+ typeof e.content === "string") {
471
+ total += chars(e.content);
472
+ out.push({ name: e.name, content: e.content });
473
+ }
474
+ else {
475
+ errors.push(`instructionFiles[${i}] must have a name (<name>.md) and a content string`);
476
+ }
477
+ });
478
+ if (errors.length === n0 && total > L.instructionFilesChars) {
479
+ errors.push(`instructionFiles must total at most ${L.instructionFilesChars} characters`);
480
+ }
481
+ return errors.length === n0 ? out : undefined;
482
+ }
@@ -0,0 +1,131 @@
1
+ import { z } from "zod";
2
+ import type { LocalizedName } from "./plugin-package.js";
3
+ /**
4
+ * Config UI hints (extension config forms spec §3): declarative presentation hints attached to an
5
+ * extension's config schema with {@link ui}, serialized as `x-ui` into the JSON Schema
6
+ * ({@link configJsonSchema}) and drawn by one generic renderer in the web app. Hints never change
7
+ * what validates: the zod schema stays the single source of truth for shape and validation.
8
+ */
9
+ /** A JSON Schema object (as produced by {@link configJsonSchema}). */
10
+ export type JsonSchema = Record<string, unknown>;
11
+ /** The input widget of a field. */
12
+ export type ConfigWidget = "text" | "textarea" | "url" | "number" | "select" | "radio" | "switch" | "checkbox" | "tristate" | "tags" | "json";
13
+ /** A labelled value of an enum (or boolean) field. */
14
+ export interface ConfigUiOption {
15
+ value: string | number | boolean;
16
+ label: LocalizedName;
17
+ help?: LocalizedName;
18
+ }
19
+ /** A root-level section that fields join with `section`. */
20
+ export interface ConfigUiSection {
21
+ id: string;
22
+ label: LocalizedName;
23
+ help?: LocalizedName;
24
+ }
25
+ /** A prefilled item offered when adding an item to an array of objects. */
26
+ export interface ConfigUiPreset {
27
+ id: string;
28
+ label: LocalizedName;
29
+ help?: LocalizedName;
30
+ /** Prefilled item fields; never a secret. */
31
+ values: Record<string, unknown>;
32
+ }
33
+ /** A secret drawn inside each item of an array, stored at a fixed record path keyed by an item field. */
34
+ export interface ConfigUiItemSecret {
35
+ /** Dot path, from the schema root, of a `secret(z.record(z.string(), z.string()))` field. */
36
+ path: string;
37
+ /** The item's string field whose value is the record key (e.g. `name`). */
38
+ keyBy: string;
39
+ label: LocalizedName;
40
+ help?: LocalizedName;
41
+ placeholder?: string | LocalizedName;
42
+ /** The item field the input is drawn after (default: last). */
43
+ after?: string;
44
+ }
45
+ /** Presentation hints of one config schema node (zod meta `ui`, JSON Schema `x-ui`). */
46
+ export interface ConfigUiHints {
47
+ label?: LocalizedName;
48
+ help?: LocalizedName;
49
+ placeholder?: string | LocalizedName;
50
+ /** Shown after a number input ("tokens", "ms"). */
51
+ unit?: LocalizedName;
52
+ /** Sort key among siblings (default: declaration order). */
53
+ order?: number;
54
+ /** Root-level section id (`sections` on the root). */
55
+ section?: string;
56
+ /** Collapsed under "Advanced" in its object or item. */
57
+ advanced?: boolean;
58
+ widget?: ConfigWidget;
59
+ options?: ConfigUiOption[];
60
+ /** Objects (and array items): fields stacked (default) or one compact row. */
61
+ layout?: "stack" | "row";
62
+ /** Arrays: how each item is drawn and added. */
63
+ item?: {
64
+ /** `{field}` template from the item's own scalar fields, e.g. "{name}". */
65
+ title?: string;
66
+ subtitle?: string;
67
+ addLabel?: LocalizedName;
68
+ reorder?: boolean;
69
+ };
70
+ presets?: ConfigUiPreset[];
71
+ secrets?: ConfigUiItemSecret[];
72
+ /** Root only. */
73
+ sections?: ConfigUiSection[];
74
+ /** Root only: localized messages for custom issues (`params: { ui: key }`). */
75
+ messages?: Record<string, LocalizedName>;
76
+ }
77
+ /** Limits of config UI hints and of the JSON Schemas a plugin package carries. */
78
+ export declare const CONFIG_UI_LIMITS: {
79
+ /** Characters per language of a `label` (also `unit`, `item.addLabel`). */
80
+ readonly labelChars: 100;
81
+ /** Characters per language of a `help` (also `messages` values). */
82
+ readonly helpChars: 500;
83
+ /** Characters (per language) of a `placeholder`. */
84
+ readonly placeholderChars: 200;
85
+ /** UTF-8 bytes of all `x-ui` JSON of one schema. */
86
+ readonly hintsBytes: number;
87
+ /** Options per node. */
88
+ readonly options: 200;
89
+ /** Presets per node. */
90
+ readonly presets: 20;
91
+ /** Root sections. */
92
+ readonly sections: 20;
93
+ /**
94
+ * UTF-8 bytes of each JSON Schema in `plugin.json` `extension.configSchemas` (96 KiB: two of
95
+ * them must fit in the 256 KiB `plugin.json` text with the rest of the manifest).
96
+ */
97
+ readonly configSchemaBytes: number;
98
+ };
99
+ /**
100
+ * Attaches config UI hints to a schema (zod meta `ui`), merging with existing meta (`secret`,
101
+ * `hot`, `description`). Returns the same schema type. Convention: put `ui()` outermost, after
102
+ * `.default()` / `.optional()`.
103
+ */
104
+ export declare function ui<T extends z.ZodTypeAny>(schema: T, hints: ConfigUiHints): T;
105
+ /**
106
+ * An extension config schema as a JSON Schema for forms (the input side, defaults included, no
107
+ * `$schema`):
108
+ * - meta `secret: true` becomes `"x-secret": true`, `hot: true` becomes `"x-hot": true`, `org`
109
+ * (`orgAccess`) becomes `"x-org"`, `ui`
110
+ * becomes `"x-ui"` (a JSON copy of the hints); `title` / `description` are filled from
111
+ * `x-ui.label.en` / `x-ui.help.en` when the node has none;
112
+ * - unrepresentable types become `{ "x-unsupported": true }` instead of throwing;
113
+ * - under a secret node no value is published: `default` / `examples` / `const` / `enum` and
114
+ * `x-ui.options` / `x-ui.presets` are removed from it and all its descendants.
115
+ */
116
+ export declare function configJsonSchema(schema: z.ZodTypeAny): JsonSchema;
117
+ /** One problem of the hints of a config schema. */
118
+ export interface ConfigUiProblem {
119
+ /**
120
+ * Dot path of the node holding the hint: property names, with `items` / `additionalProperties`
121
+ * for array items and record values (`endpoints.items.models`); `""` is the root.
122
+ */
123
+ path: string;
124
+ message: string;
125
+ }
126
+ /**
127
+ * Validates the `x-ui` hints of a JSON Schema produced by {@link configJsonSchema} (spec §3.3) at
128
+ * a config level. Pure; used by the packer, `capybara-plugin check` and the kernel. Returns every
129
+ * problem (empty = valid); never throws. Problem paths are dot paths of the node holding the hint.
130
+ */
131
+ export declare function validateConfigUi(schema: JsonSchema, level: "deployment" | "org" | "connection"): ConfigUiProblem[];