@agentproto/extension 0.1.0
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/LICENSE +21 -0
- package/README.md +23 -0
- package/dist/chunk-XIW4DKUM.mjs +51 -0
- package/dist/chunk-XIW4DKUM.mjs.map +1 -0
- package/dist/index-C2uTNMnb.d.ts +157 -0
- package/dist/index.d.ts +79 -0
- package/dist/index.mjs +112 -0
- package/dist/index.mjs.map +1 -0
- package/dist/manifest/index.d.ts +2 -0
- package/dist/manifest/index.mjs +3 -0
- package/dist/manifest/index.mjs.map +1 -0
- package/package.json +69 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 agentproto contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# @agentproto/extension
|
|
2
|
+
|
|
3
|
+
AIP-40 `EXTENSION.md` reference implementation. A meta-doctype that lets a workspace declare its own custom doctype as an extension of an existing AIP — adding fields, tightening constraints, overriding defaults, and choosing a path convention — without going through the public AIP process. The runtime (@agentproto/manifest verbs, MCP server, scaffolder) treats local extensions identically to public AIPs.
|
|
4
|
+
|
|
5
|
+
> **Status: 0.1.0-alpha.** Generated by `scripts/scaffold-aip.mjs` — `build()` and `validate()` bodies are TODOs.
|
|
6
|
+
|
|
7
|
+
Spec: <https://agentproto.sh/docs/aip-40>
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { defineExtension } from "@agentproto/extension"
|
|
13
|
+
|
|
14
|
+
const x = defineExtension({
|
|
15
|
+
id: "my-extension",
|
|
16
|
+
description: "Short purpose.",
|
|
17
|
+
// ...
|
|
18
|
+
})
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## License
|
|
22
|
+
|
|
23
|
+
MIT — see [LICENSE](./LICENSE).
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import matter from 'gray-matter';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { createDoctype } from '@agentproto/define-doctype';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @agentproto/extension v0.1.0-alpha
|
|
7
|
+
* AIP-40 EXTENSION.md `defineExtension` reference implementation.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
var extensionFrontmatterSchema = z.object({ "schema": z.literal("agentproto/extension/v1").describe("Pins the spec version this manifest conforms to."), "slug": z.string().regex(new RegExp("^[a-z][a-z0-9-]*:[a-z][a-z0-9-]*[a-z0-9]$")).describe("Namespaced identifier \u2014 `<namespace>:<name>`. Lowercase, digits, dashes; single colon separator. Example: `acme:deal`."), "title": z.string().min(1).max(80).describe("Human-readable display name."), "description": z.string().min(1).max(2e3).describe("One-paragraph statement of the extension's purpose."), "version": z.string().regex(new RegExp("^\\d+\\.\\d+\\.\\d+(?:[-+][a-zA-Z0-9.-]+)?$")).describe("Extension's own semver. Bump on breaking change."), "status": z.literal("Local").describe("Extensions are perpetually Local \u2014 they never enter the public registry's Draft/Review/Final lifecycle."), "extends": z.union([z.string().regex(new RegExp("^aip-\\d+$")).describe("Inherit a public AIP's schema (e.g. `aip-14`)."), z.literal("none").describe("Brand-new doctype, no inheritance.")]).describe("Parent AIP this extension inherits from, or `none` for a root doctype."), "add_fields": z.object({ "properties": z.record(z.string(), z.any()).describe("JSON Schema property definitions to merge into the parent's properties. Collisions are an error \u2014 use `tighten` to narrow an existing field.").optional(), "required": z.array(z.string()).refine((arr) => arr.every((item, i) => arr.indexOf(item) == i), { message: "All items must be unique!" }).describe("Field names that become required in addition to parent's required[]. Unioned with parent's required, never replaces.").default([]) }).strict().describe("Schema-level additions: new properties + new required entries.").optional(), "tighten": z.record(z.string(), z.object({ "pattern": z.string().optional(), "enum": z.array(z.any()).optional(), "minLength": z.number().int().gte(0).optional(), "maxLength": z.number().int().gte(0).optional(), "minimum": z.number().optional(), "maximum": z.number().optional() }).strict()).describe("Per-field constraint overrides. Each entry MUST tighten (not loosen) the parent's constraints; runtimes verify monotonicity at registration.").optional(), "defaults": z.record(z.string(), z.any()).describe("Default values applied when a field is omitted. Layered on top of parent's defaults; extension's value wins on the same key.").optional(), "path_convention": z.string().min(1).describe("Filesystem convention template, e.g. `\"deals/<slug>/DEAL.md\"`. Tokens: `<slug>` (doctype identity), `<DOCTYPE>` (extension's slug name part). Falls back to parent's convention when omitted.").optional(), "requires": z.array(z.number().int().gte(1)).refine((arr) => arr.every((item, i) => arr.indexOf(item) == i), { message: "All items must be unique!" }).describe("Public AIPs the extension depends on, in addition to its parent.").default([]), "metadata": z.record(z.string(), z.any()).describe("Free-form vendor extensions under namespaced keys (`metadata.<vendor>.<field>`).").default({}) }).strict().describe("Validates the YAML frontmatter portion of an AIP-40 EXTENSION.md manifest. An extension declares a workspace-local doctype that inherits a public AIP's schema and may add fields, tighten constraints, set defaults, and override the path convention.");
|
|
11
|
+
var defineExtension = createDoctype({
|
|
12
|
+
aip: 40,
|
|
13
|
+
name: "extension",
|
|
14
|
+
// Extension slugs are namespaced: `<namespace>:<name>` (e.g.
|
|
15
|
+
// `acme:deal`). Override the default kebab-only pattern.
|
|
16
|
+
idPattern: /^[a-z][a-z0-9-]*:[a-z][a-z0-9-]*[a-z0-9]$/,
|
|
17
|
+
readIdentity: (def) => def.slug,
|
|
18
|
+
validate(def) {
|
|
19
|
+
const result = extensionFrontmatterSchema.safeParse(def);
|
|
20
|
+
if (!result.success) {
|
|
21
|
+
throw new Error(
|
|
22
|
+
`defineExtension (AIP-40): ${result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")}`
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
build(def) {
|
|
27
|
+
return { ...def };
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// src/manifest/index.ts
|
|
32
|
+
function parseExtensionManifest(source) {
|
|
33
|
+
const parsed = matter(source);
|
|
34
|
+
if (Object.keys(parsed.data).length === 0) {
|
|
35
|
+
throw new Error("parseExtensionManifest: missing or empty frontmatter");
|
|
36
|
+
}
|
|
37
|
+
const result = extensionFrontmatterSchema.safeParse(parsed.data);
|
|
38
|
+
if (!result.success) {
|
|
39
|
+
throw new Error(
|
|
40
|
+
`parseExtensionManifest: invalid frontmatter \u2014 ${result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ")}`
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
return { frontmatter: result.data, body: parsed.content };
|
|
44
|
+
}
|
|
45
|
+
function extensionFromManifest(manifest) {
|
|
46
|
+
return defineExtension(manifest.frontmatter);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export { defineExtension, extensionFromManifest, extensionFrontmatterSchema, parseExtensionManifest };
|
|
50
|
+
//# sourceMappingURL=chunk-XIW4DKUM.mjs.map
|
|
51
|
+
//# sourceMappingURL=chunk-XIW4DKUM.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/schema.ts","../src/define-extension.ts","../src/manifest/index.ts"],"names":[],"mappings":"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","file":"chunk-XIW4DKUM.mjs","sourcesContent":["/**\n * AIP-40 EXTENSION.md frontmatter zod schema.\n *\n * Generated from `resources/aip-40/draft/EXTENSION.schema.json` via\n * json-schema-to-zod. Imported by both `define-extension.ts` (TS path\n * validation) and `manifest/index.ts` (.md path validation) so every\n * field-level constraint runs in both authoring paths from a single\n * source of truth — re-run scaffold-aip to refresh after spec changes.\n *\n * Cross-field rules (if/then/allOf in JSON Schema) don't translate\n * cleanly and live in `define-extension.ts`'s `validate(def)` instead.\n */\n\nimport { z } from \"zod\"\n\nexport const extensionFrontmatterSchema = z.object({ \"schema\": z.literal(\"agentproto/extension/v1\").describe(\"Pins the spec version this manifest conforms to.\"), \"slug\": z.string().regex(new RegExp(\"^[a-z][a-z0-9-]*:[a-z][a-z0-9-]*[a-z0-9]$\")).describe(\"Namespaced identifier — `<namespace>:<name>`. Lowercase, digits, dashes; single colon separator. Example: `acme:deal`.\"), \"title\": z.string().min(1).max(80).describe(\"Human-readable display name.\"), \"description\": z.string().min(1).max(2000).describe(\"One-paragraph statement of the extension's purpose.\"), \"version\": z.string().regex(new RegExp(\"^\\\\d+\\\\.\\\\d+\\\\.\\\\d+(?:[-+][a-zA-Z0-9.-]+)?$\")).describe(\"Extension's own semver. Bump on breaking change.\"), \"status\": z.literal(\"Local\").describe(\"Extensions are perpetually Local — they never enter the public registry's Draft/Review/Final lifecycle.\"), \"extends\": z.union([z.string().regex(new RegExp(\"^aip-\\\\d+$\")).describe(\"Inherit a public AIP's schema (e.g. `aip-14`).\"), z.literal(\"none\").describe(\"Brand-new doctype, no inheritance.\")]).describe(\"Parent AIP this extension inherits from, or `none` for a root doctype.\"), \"add_fields\": z.object({ \"properties\": z.record(z.string(), z.any()).describe(\"JSON Schema property definitions to merge into the parent's properties. Collisions are an error — use `tighten` to narrow an existing field.\").optional(), \"required\": z.array(z.string()).refine((arr) => arr.every((item, i) => arr.indexOf(item) == i), { message: \"All items must be unique!\" }).describe(\"Field names that become required in addition to parent's required[]. Unioned with parent's required, never replaces.\").default([] as never) }).strict().describe(\"Schema-level additions: new properties + new required entries.\").optional(), \"tighten\": z.record(z.string(), z.object({ \"pattern\": z.string().optional(), \"enum\": z.array(z.any()).optional(), \"minLength\": z.number().int().gte(0).optional(), \"maxLength\": z.number().int().gte(0).optional(), \"minimum\": z.number().optional(), \"maximum\": z.number().optional() }).strict()).describe(\"Per-field constraint overrides. Each entry MUST tighten (not loosen) the parent's constraints; runtimes verify monotonicity at registration.\").optional(), \"defaults\": z.record(z.string(), z.any()).describe(\"Default values applied when a field is omitted. Layered on top of parent's defaults; extension's value wins on the same key.\").optional(), \"path_convention\": z.string().min(1).describe(\"Filesystem convention template, e.g. `\\\"deals/<slug>/DEAL.md\\\"`. Tokens: `<slug>` (doctype identity), `<DOCTYPE>` (extension's slug name part). Falls back to parent's convention when omitted.\").optional(), \"requires\": z.array(z.number().int().gte(1)).refine((arr) => arr.every((item, i) => arr.indexOf(item) == i), { message: \"All items must be unique!\" }).describe(\"Public AIPs the extension depends on, in addition to its parent.\").default([] as never), \"metadata\": z.record(z.string(), z.any()).describe(\"Free-form vendor extensions under namespaced keys (`metadata.<vendor>.<field>`).\").default({} as never) }).strict().describe(\"Validates the YAML frontmatter portion of an AIP-40 EXTENSION.md manifest. An extension declares a workspace-local doctype that inherits a public AIP's schema and may add fields, tighten constraints, set defaults, and override the path convention.\")\n\nexport type ExtensionFrontmatter = z.infer<typeof extensionFrontmatterSchema>\n","import { createDoctype } from \"@agentproto/define-doctype\"\nimport { extensionFrontmatterSchema } from \"./schema.js\"\nimport type { ExtensionDefinition, ExtensionHandle } from \"./types.js\"\n\n/**\n * AIP-40 reference implementation of `defineExtension`.\n *\n * Built on `createDoctype` so the cross-AIP invariants (id pattern,\n * description length, top-level freeze, \"defineExtension (AIP-40): …\"\n * error prefix) run uniformly with every other AIP defineX.\n *\n * Field-level validation runs the schema-derived zod from\n * `./schema.ts` against the input. Same source of truth as the .md\n * path uses (`parseExtensionManifest`), so a malformed TS-authored\n * definition fails with the same diagnostic as a malformed manifest.\n * Cross-field rules go in `validate(def)` after the zod check.\n *\n * Identity / description extractors detected from the JSON Schema:\n * readIdentity: def.slug\n * readDescription: def.description.\n */\nexport const defineExtension = createDoctype<ExtensionDefinition, ExtensionHandle>({\n aip: 40,\n name: \"extension\",\n // Extension slugs are namespaced: `<namespace>:<name>` (e.g.\n // `acme:deal`). Override the default kebab-only pattern.\n idPattern: /^[a-z][a-z0-9-]*:[a-z][a-z0-9-]*[a-z0-9]$/,\n readIdentity: (def) => def.slug,\n validate(def) {\n const result = extensionFrontmatterSchema.safeParse(def)\n if (!result.success) {\n throw new Error(\n `defineExtension (AIP-40): ${result.error.issues\n .map((i) => `${i.path.join(\".\")}: ${i.message}`)\n .join(\"; \")}`,\n )\n }\n // TODO: spec-40-specific cross-field rules (if/then/allOf in\n // the JSON Schema) — those don't translate to zod cleanly and\n // belong here. See @agentproto/operator's autonomy=gated rule.\n },\n build(def) {\n // Default build: spread the validated definition into a fresh object.\n // Hand-tune for nested freezing (Object.freeze on arrays/objects) and\n // for fields that need defaults applied — see @agentproto/operator\n // for a reference shape.\n return { ...def } as ExtensionHandle\n },\n})\n","/**\n * AIP-40 EXTENSION.md sidecar parser + manifest-to-handle constructor.\n *\n * Mirror of `@agentproto/tool/manifest` and `@agentproto/driver/manifest`:\n * the .md provides metadata; the TS module supplies any spec-specific\n * runtime bits (schemas, execute bodies, …) that can't live in\n * frontmatter. Both inputs end up in `defineExtension` so the cross-AIP\n * invariants run uniformly.\n *\n *\n * The frontmatter zod schema below was generated from\n * `resources/aip-40/draft/EXTENSION.schema.json` via json-schema-to-zod.\n * Re-run scaffold-aip to refresh after spec changes (or hand-tune\n * any constraint the converter doesn't capture cleanly).\n */\n\nimport matter from \"gray-matter\"\nimport { extensionFrontmatterSchema, type ExtensionFrontmatter } from \"../schema.js\"\nimport { defineExtension } from \"../define-extension.js\"\nimport type { ExtensionDefinition, ExtensionHandle } from \"../types.js\"\n\n// Re-export so consumers can import the schema + inferred type either\n// from \"@@agentproto/extension/manifest\" or directly from \"@@agentproto/extension/schema\".\nexport { extensionFrontmatterSchema, type ExtensionFrontmatter }\n\nexport interface ExtensionManifest {\n frontmatter: ExtensionFrontmatter\n body: string\n}\n\nexport function parseExtensionManifest(source: string): ExtensionManifest {\n const parsed = matter(source)\n if (Object.keys(parsed.data).length === 0) {\n throw new Error(\"parseExtensionManifest: missing or empty frontmatter\")\n }\n const result = extensionFrontmatterSchema.safeParse(parsed.data)\n if (!result.success) {\n throw new Error(\n `parseExtensionManifest: invalid frontmatter — ${result.error.issues\n .map((i) => `${i.path.join(\".\")}: ${i.message}`)\n .join(\"; \")}`,\n )\n }\n return { frontmatter: result.data, body: parsed.content }\n}\n\nexport function extensionFromManifest(manifest: ExtensionManifest): ExtensionHandle {\n // The zod-validated frontmatter is structurally compatible with\n // ExtensionDefinition; the cast pins the typing once the manifest\n // schema and the TS interface diverge (e.g. handle has frozen fields\n // a literal config doesn't carry yet).\n return defineExtension(manifest.frontmatter as unknown as ExtensionDefinition)\n}\n"]}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* AIP-40 ExtensionDefinition + ExtensionHandle.
|
|
5
|
+
*
|
|
6
|
+
* `ExtensionDefinition` was generated from
|
|
7
|
+
* `resources/aip-40/draft/EXTENSION.schema.json` via json-schema-to-typescript.
|
|
8
|
+
* `ExtensionHandle` is the readonly view of the same shape; tighten it
|
|
9
|
+
* by hand for fields that get defaults applied in build().
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Validates the YAML frontmatter portion of an AIP-40 EXTENSION.md manifest. An extension declares a workspace-local doctype that inherits a public AIP's schema and may add fields, tighten constraints, set defaults, and override the path convention.
|
|
13
|
+
*/
|
|
14
|
+
interface ExtensionDefinition {
|
|
15
|
+
/**
|
|
16
|
+
* Pins the spec version this manifest conforms to.
|
|
17
|
+
*/
|
|
18
|
+
schema: "agentproto/extension/v1";
|
|
19
|
+
/**
|
|
20
|
+
* Namespaced identifier — `<namespace>:<name>`. Lowercase, digits, dashes; single colon separator. Example: `acme:deal`.
|
|
21
|
+
*/
|
|
22
|
+
slug: string;
|
|
23
|
+
/**
|
|
24
|
+
* Human-readable display name.
|
|
25
|
+
*/
|
|
26
|
+
title: string;
|
|
27
|
+
/**
|
|
28
|
+
* One-paragraph statement of the extension's purpose.
|
|
29
|
+
*/
|
|
30
|
+
description: string;
|
|
31
|
+
/**
|
|
32
|
+
* Extension's own semver. Bump on breaking change.
|
|
33
|
+
*/
|
|
34
|
+
version: string;
|
|
35
|
+
/**
|
|
36
|
+
* Extensions are perpetually Local — they never enter the public registry's Draft/Review/Final lifecycle.
|
|
37
|
+
*/
|
|
38
|
+
status: "Local";
|
|
39
|
+
/**
|
|
40
|
+
* Parent AIP this extension inherits from, or `none` for a root doctype.
|
|
41
|
+
*/
|
|
42
|
+
extends: string | "none";
|
|
43
|
+
/**
|
|
44
|
+
* Schema-level additions: new properties + new required entries.
|
|
45
|
+
*/
|
|
46
|
+
add_fields?: {
|
|
47
|
+
/**
|
|
48
|
+
* JSON Schema property definitions to merge into the parent's properties. Collisions are an error — use `tighten` to narrow an existing field.
|
|
49
|
+
*/
|
|
50
|
+
properties?: {
|
|
51
|
+
[k: string]: unknown;
|
|
52
|
+
};
|
|
53
|
+
/**
|
|
54
|
+
* Field names that become required in addition to parent's required[]. Unioned with parent's required, never replaces.
|
|
55
|
+
*/
|
|
56
|
+
required?: string[];
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Per-field constraint overrides. Each entry MUST tighten (not loosen) the parent's constraints; runtimes verify monotonicity at registration.
|
|
60
|
+
*/
|
|
61
|
+
tighten?: {
|
|
62
|
+
[k: string]: {
|
|
63
|
+
pattern?: string;
|
|
64
|
+
enum?: unknown[];
|
|
65
|
+
minLength?: number;
|
|
66
|
+
maxLength?: number;
|
|
67
|
+
minimum?: number;
|
|
68
|
+
maximum?: number;
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Default values applied when a field is omitted. Layered on top of parent's defaults; extension's value wins on the same key.
|
|
73
|
+
*/
|
|
74
|
+
defaults?: {
|
|
75
|
+
[k: string]: unknown;
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Filesystem convention template, e.g. `"deals/<slug>/DEAL.md"`. Tokens: `<slug>` (doctype identity), `<DOCTYPE>` (extension's slug name part). Falls back to parent's convention when omitted.
|
|
79
|
+
*/
|
|
80
|
+
path_convention?: string;
|
|
81
|
+
/**
|
|
82
|
+
* Public AIPs the extension depends on, in addition to its parent.
|
|
83
|
+
*/
|
|
84
|
+
requires?: number[];
|
|
85
|
+
/**
|
|
86
|
+
* Free-form vendor extensions under namespaced keys (`metadata.<vendor>.<field>`).
|
|
87
|
+
*/
|
|
88
|
+
metadata?: {
|
|
89
|
+
[k: string]: unknown;
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
type ExtensionHandle = Readonly<ExtensionDefinition>;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* AIP-40 EXTENSION.md frontmatter zod schema.
|
|
96
|
+
*
|
|
97
|
+
* Generated from `resources/aip-40/draft/EXTENSION.schema.json` via
|
|
98
|
+
* json-schema-to-zod. Imported by both `define-extension.ts` (TS path
|
|
99
|
+
* validation) and `manifest/index.ts` (.md path validation) so every
|
|
100
|
+
* field-level constraint runs in both authoring paths from a single
|
|
101
|
+
* source of truth — re-run scaffold-aip to refresh after spec changes.
|
|
102
|
+
*
|
|
103
|
+
* Cross-field rules (if/then/allOf in JSON Schema) don't translate
|
|
104
|
+
* cleanly and live in `define-extension.ts`'s `validate(def)` instead.
|
|
105
|
+
*/
|
|
106
|
+
|
|
107
|
+
declare const extensionFrontmatterSchema: z.ZodObject<{
|
|
108
|
+
schema: z.ZodLiteral<"agentproto/extension/v1">;
|
|
109
|
+
slug: z.ZodString;
|
|
110
|
+
title: z.ZodString;
|
|
111
|
+
description: z.ZodString;
|
|
112
|
+
version: z.ZodString;
|
|
113
|
+
status: z.ZodLiteral<"Local">;
|
|
114
|
+
extends: z.ZodUnion<readonly [z.ZodString, z.ZodLiteral<"none">]>;
|
|
115
|
+
add_fields: z.ZodOptional<z.ZodObject<{
|
|
116
|
+
properties: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
|
|
117
|
+
required: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
118
|
+
}, z.core.$strict>>;
|
|
119
|
+
tighten: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
120
|
+
pattern: z.ZodOptional<z.ZodString>;
|
|
121
|
+
enum: z.ZodOptional<z.ZodArray<z.ZodAny>>;
|
|
122
|
+
minLength: z.ZodOptional<z.ZodNumber>;
|
|
123
|
+
maxLength: z.ZodOptional<z.ZodNumber>;
|
|
124
|
+
minimum: z.ZodOptional<z.ZodNumber>;
|
|
125
|
+
maximum: z.ZodOptional<z.ZodNumber>;
|
|
126
|
+
}, z.core.$strict>>>;
|
|
127
|
+
defaults: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
|
|
128
|
+
path_convention: z.ZodOptional<z.ZodString>;
|
|
129
|
+
requires: z.ZodDefault<z.ZodArray<z.ZodNumber>>;
|
|
130
|
+
metadata: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodAny>>;
|
|
131
|
+
}, z.core.$strict>;
|
|
132
|
+
type ExtensionFrontmatter = z.infer<typeof extensionFrontmatterSchema>;
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* AIP-40 EXTENSION.md sidecar parser + manifest-to-handle constructor.
|
|
136
|
+
*
|
|
137
|
+
* Mirror of `@agentproto/tool/manifest` and `@agentproto/driver/manifest`:
|
|
138
|
+
* the .md provides metadata; the TS module supplies any spec-specific
|
|
139
|
+
* runtime bits (schemas, execute bodies, …) that can't live in
|
|
140
|
+
* frontmatter. Both inputs end up in `defineExtension` so the cross-AIP
|
|
141
|
+
* invariants run uniformly.
|
|
142
|
+
*
|
|
143
|
+
*
|
|
144
|
+
* The frontmatter zod schema below was generated from
|
|
145
|
+
* `resources/aip-40/draft/EXTENSION.schema.json` via json-schema-to-zod.
|
|
146
|
+
* Re-run scaffold-aip to refresh after spec changes (or hand-tune
|
|
147
|
+
* any constraint the converter doesn't capture cleanly).
|
|
148
|
+
*/
|
|
149
|
+
|
|
150
|
+
interface ExtensionManifest {
|
|
151
|
+
frontmatter: ExtensionFrontmatter;
|
|
152
|
+
body: string;
|
|
153
|
+
}
|
|
154
|
+
declare function parseExtensionManifest(source: string): ExtensionManifest;
|
|
155
|
+
declare function extensionFromManifest(manifest: ExtensionManifest): ExtensionHandle;
|
|
156
|
+
|
|
157
|
+
export { type ExtensionDefinition as E, type ExtensionFrontmatter as a, type ExtensionHandle as b, type ExtensionManifest as c, extensionFrontmatterSchema as d, extensionFromManifest as e, parseExtensionManifest as p };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { E as ExtensionDefinition } from './index-C2uTNMnb.js';
|
|
2
|
+
export { a as ExtensionFrontmatter, b as ExtensionHandle, c as ExtensionManifest, e as extensionFromManifest, d as extensionFrontmatterSchema, p as parseExtensionManifest } from './index-C2uTNMnb.js';
|
|
3
|
+
import { DoctypeSpec } from '@agentproto/manifest';
|
|
4
|
+
import 'zod';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* AIP-40 reference implementation of `defineExtension`.
|
|
8
|
+
*
|
|
9
|
+
* Built on `createDoctype` so the cross-AIP invariants (id pattern,
|
|
10
|
+
* description length, top-level freeze, "defineExtension (AIP-40): …"
|
|
11
|
+
* error prefix) run uniformly with every other AIP defineX.
|
|
12
|
+
*
|
|
13
|
+
* Field-level validation runs the schema-derived zod from
|
|
14
|
+
* `./schema.ts` against the input. Same source of truth as the .md
|
|
15
|
+
* path uses (`parseExtensionManifest`), so a malformed TS-authored
|
|
16
|
+
* definition fails with the same diagnostic as a malformed manifest.
|
|
17
|
+
* Cross-field rules go in `validate(def)` after the zod check.
|
|
18
|
+
*
|
|
19
|
+
* Identity / description extractors detected from the JSON Schema:
|
|
20
|
+
* readIdentity: def.slug
|
|
21
|
+
* readDescription: def.description.
|
|
22
|
+
*/
|
|
23
|
+
declare const defineExtension: (def: ExtensionDefinition) => Readonly<ExtensionDefinition>;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* `specFromExtension(handle, parent)` — turn a parsed EXTENSION.md
|
|
27
|
+
* into a runtime `DoctypeSpec` consumable by
|
|
28
|
+
* `@agentproto/manifest.createVerbs`.
|
|
29
|
+
*
|
|
30
|
+
* The parent argument is the public AIP's spec (e.g. `toolSpec` from
|
|
31
|
+
* `@agentproto/tool`). The function composes:
|
|
32
|
+
*
|
|
33
|
+
* schema parent.schema ∪ ext.add_fields, then ext.tighten
|
|
34
|
+
* path ext.path_convention ?? parent.pathOf
|
|
35
|
+
* defaults parent defaults ⨁ ext.defaults
|
|
36
|
+
* define wraps parent.define with default-application
|
|
37
|
+
* parse parent.parse (frontmatter shape stays compatible)
|
|
38
|
+
*
|
|
39
|
+
* Tightening monotonicity is verified by-field: pattern subset
|
|
40
|
+
* (cheap heuristic — full regex-language inclusion is undecidable),
|
|
41
|
+
* enum subset (set membership), minLength/minimum ≥, maxLength/maximum ≤.
|
|
42
|
+
*
|
|
43
|
+
* For `extends: none`, `parent` is null/undefined and the extension
|
|
44
|
+
* acts as a root doctype: the `add_fields` becomes its full schema,
|
|
45
|
+
* `path_convention` is required (no parent fallback).
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
interface SpecFromExtensionOptions<TParams extends {
|
|
49
|
+
id?: string;
|
|
50
|
+
slug?: string;
|
|
51
|
+
name?: string;
|
|
52
|
+
}, THandle> {
|
|
53
|
+
/**
|
|
54
|
+
* Parent doctype spec. Required when `extension.extends !== "none"`.
|
|
55
|
+
* Pass e.g. `toolSpec` from `@agentproto/tool`.
|
|
56
|
+
*/
|
|
57
|
+
parent?: DoctypeSpec<TParams, THandle>;
|
|
58
|
+
}
|
|
59
|
+
declare function specFromExtension<TParams extends {
|
|
60
|
+
id?: string;
|
|
61
|
+
slug?: string;
|
|
62
|
+
name?: string;
|
|
63
|
+
}, THandle>(extension: ExtensionDefinition, opts?: SpecFromExtensionOptions<TParams, THandle>): DoctypeSpec<TParams, THandle>;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @agentproto/extension — AIP-40 EXTENSION.md `defineExtension` reference impl.
|
|
67
|
+
*
|
|
68
|
+
* A meta-doctype that lets a workspace declare its own custom doctype as an extension of an existing AIP — adding fields, tightening constraints, overriding defaults, and choosing a path convention — without going through the public AIP process. The runtime (@agentproto/manifest verbs, MCP server, scaffolder) treats local extensions identically to public AIPs.
|
|
69
|
+
*
|
|
70
|
+
* Spec: https://agentproto.sh/docs/aip-40
|
|
71
|
+
*
|
|
72
|
+
* Authoring paths:
|
|
73
|
+
* - TS: `defineExtension({...})` → `ExtensionHandle`
|
|
74
|
+
* - MD: `parseExtensionManifest(src) → extensionFromManifest({...})` → `ExtensionHandle`
|
|
75
|
+
*/
|
|
76
|
+
declare const SPEC_NAME: "agentextension/v1";
|
|
77
|
+
declare const SPEC_VERSION: "1.0.0-alpha";
|
|
78
|
+
|
|
79
|
+
export { ExtensionDefinition, SPEC_NAME, SPEC_VERSION, type SpecFromExtensionOptions, defineExtension, specFromExtension };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
export { defineExtension, extensionFromManifest, extensionFrontmatterSchema, parseExtensionManifest } from './chunk-XIW4DKUM.mjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @agentproto/extension v0.1.0-alpha
|
|
5
|
+
* AIP-40 EXTENSION.md `defineExtension` reference implementation.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// src/spec-from-extension.ts
|
|
9
|
+
function specFromExtension(extension, opts = {}) {
|
|
10
|
+
const { parent } = opts;
|
|
11
|
+
const isRoot = extension.extends === "none";
|
|
12
|
+
if (!isRoot && !parent) {
|
|
13
|
+
throw new Error(
|
|
14
|
+
`specFromExtension (AIP-40): extension '${extension.slug}' extends '${extension.extends}' but no parent spec was provided in opts.parent`
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
if (isRoot && !extension.path_convention) {
|
|
18
|
+
throw new Error(
|
|
19
|
+
`specFromExtension (AIP-40): extension '${extension.slug}' is a root doctype (extends: none) and MUST declare path_convention`
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
if (parent && extension.tighten) {
|
|
23
|
+
verifyTightening(extension);
|
|
24
|
+
}
|
|
25
|
+
const slugName = extension.slug.split(":")[1] ?? extension.slug;
|
|
26
|
+
const pathTemplate = extension.path_convention ?? null;
|
|
27
|
+
const extensionKeys = new Set(
|
|
28
|
+
Object.keys(extension.add_fields?.properties ?? {})
|
|
29
|
+
);
|
|
30
|
+
const define = (params) => {
|
|
31
|
+
const withDefaults = { ...params };
|
|
32
|
+
if (extension.defaults) {
|
|
33
|
+
for (const [key, value] of Object.entries(extension.defaults)) {
|
|
34
|
+
if (withDefaults[key] === void 0 && value !== void 0) {
|
|
35
|
+
withDefaults[key] = value;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (isRoot) {
|
|
40
|
+
return Object.freeze(withDefaults);
|
|
41
|
+
}
|
|
42
|
+
const parentOnly = {};
|
|
43
|
+
const extensionOnly = {};
|
|
44
|
+
for (const [key, value] of Object.entries(withDefaults)) {
|
|
45
|
+
if (extensionKeys.has(key)) extensionOnly[key] = value;
|
|
46
|
+
else parentOnly[key] = value;
|
|
47
|
+
}
|
|
48
|
+
const parentHandle = parent.define(parentOnly);
|
|
49
|
+
return Object.freeze({
|
|
50
|
+
...parentHandle,
|
|
51
|
+
...extensionOnly
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
const parse = isRoot ? rootParse(extension) : parent.parse;
|
|
55
|
+
const pathOf = (handle) => {
|
|
56
|
+
if (pathTemplate) {
|
|
57
|
+
return resolvePathTemplate(
|
|
58
|
+
pathTemplate,
|
|
59
|
+
handle,
|
|
60
|
+
slugName
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
return parent.pathOf(handle);
|
|
64
|
+
};
|
|
65
|
+
return {
|
|
66
|
+
name: extension.slug,
|
|
67
|
+
aip: 40,
|
|
68
|
+
schemaLiteral: parent?.schemaLiteral ?? `agentproto/extension/v1`,
|
|
69
|
+
pathOf,
|
|
70
|
+
define,
|
|
71
|
+
parse
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function verifyTightening(extension, parent) {
|
|
75
|
+
const t = extension.tighten ?? {};
|
|
76
|
+
for (const [field, override] of Object.entries(t)) {
|
|
77
|
+
if (typeof override.minLength === "number" && typeof override.maxLength === "number" && override.minLength > override.maxLength) {
|
|
78
|
+
throw new Error(
|
|
79
|
+
`specFromExtension (AIP-40): extension '${extension.slug}' tighten.${field}: minLength (${override.minLength}) > maxLength (${override.maxLength})`
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
if (typeof override.minimum === "number" && typeof override.maximum === "number" && override.minimum > override.maximum) {
|
|
83
|
+
throw new Error(
|
|
84
|
+
`specFromExtension (AIP-40): extension '${extension.slug}' tighten.${field}: minimum (${override.minimum}) > maximum (${override.maximum})`
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
if (override.enum !== void 0 && !Array.isArray(override.enum)) {
|
|
88
|
+
throw new Error(
|
|
89
|
+
`specFromExtension (AIP-40): extension '${extension.slug}' tighten.${field}: enum must be an array`
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function rootParse(extension) {
|
|
95
|
+
return (source) => {
|
|
96
|
+
throw new Error(
|
|
97
|
+
`specFromExtension (AIP-40): extension '${extension.slug}' is a root doctype \u2014 supply your own parser or extend a public AIP for parser inheritance`
|
|
98
|
+
);
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function resolvePathTemplate(template, handle, doctypeSlug) {
|
|
102
|
+
const identity = typeof handle.id === "string" && handle.id || typeof handle.slug === "string" && handle.slug || typeof handle.name === "string" && handle.name || "unknown";
|
|
103
|
+
return template.replace(/<slug>/g, identity).replace(/<DOCTYPE>/g, doctypeSlug.toUpperCase());
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// src/index.ts
|
|
107
|
+
var SPEC_NAME = "agentextension/v1";
|
|
108
|
+
var SPEC_VERSION = "1.0.0-alpha";
|
|
109
|
+
|
|
110
|
+
export { SPEC_NAME, SPEC_VERSION, specFromExtension };
|
|
111
|
+
//# sourceMappingURL=index.mjs.map
|
|
112
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/spec-from-extension.ts","../src/index.ts"],"names":[],"mappings":";;;;;;;;AAqCO,SAAS,iBAAA,CAId,SAAA,EACA,IAAA,GAAmD,EAAC,EACrB;AAC/B,EAAA,MAAM,EAAE,QAAO,GAAI,IAAA;AACnB,EAAA,MAAM,MAAA,GAAS,UAAU,OAAA,KAAY,MAAA;AAErC,EAAA,IAAI,CAAC,MAAA,IAAU,CAAC,MAAA,EAAQ;AACtB,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,uCAAA,EAA0C,SAAA,CAAU,IAAI,CAAA,WAAA,EAAc,UAAU,OAAO,CAAA,gDAAA;AAAA,KACzF;AAAA,EACF;AACA,EAAA,IAAI,MAAA,IAAU,CAAC,SAAA,CAAU,eAAA,EAAiB;AACxC,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,uCAAA,EAA0C,UAAU,IAAI,CAAA,oEAAA;AAAA,KAC1D;AAAA,EACF;AAGA,EAAA,IAAI,MAAA,IAAU,UAAU,OAAA,EAAS;AAC/B,IAAA,gBAAA,CAAiB,SAAiB,CAAA;AAAA,EACpC;AAEA,EAAA,MAAM,QAAA,GAAW,UAAU,IAAA,CAAK,KAAA,CAAM,GAAG,CAAA,CAAE,CAAC,KAAK,SAAA,CAAU,IAAA;AAC3D,EAAA,MAAM,YAAA,GAAe,UAAU,eAAA,IAAmB,IAAA;AAKlD,EAAA,MAAM,gBAAgB,IAAI,GAAA;AAAA,IACxB,OAAO,IAAA,CAAK,SAAA,CAAU,UAAA,EAAY,UAAA,IAAc,EAAE;AAAA,GACpD;AAEA,EAAA,MAAM,MAAA,GAAS,CAAC,MAAA,KAAoB;AAGlC,IAAA,MAAM,YAAA,GAAe,EAAE,GAAG,MAAA,EAAO;AACjC,IAAA,IAAI,UAAU,QAAA,EAAU;AACtB,MAAA,KAAA,MAAW,CAAC,KAAK,KAAK,CAAA,IAAK,OAAO,OAAA,CAAQ,SAAA,CAAU,QAAQ,CAAA,EAAG;AAC7D,QAAA,IAAI,YAAA,CAAa,GAAG,CAAA,KAAM,MAAA,IAAa,UAAU,MAAA,EAAW;AAC1D,UAAA,YAAA,CAAa,GAAG,CAAA,GAAI,KAAA;AAAA,QACtB;AAAA,MACF;AAAA,IACF;AACA,IAAA,IAAI,MAAA,EAAQ;AAMV,MAAA,OAAO,MAAA,CAAO,OAAO,YAAY,CAAA;AAAA,IACnC;AASA,IAAA,MAAM,aAAsC,EAAC;AAC7C,IAAA,MAAM,gBAAyC,EAAC;AAChD,IAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,YAAY,CAAA,EAAG;AACvD,MAAA,IAAI,cAAc,GAAA,CAAI,GAAG,CAAA,EAAG,aAAA,CAAc,GAAG,CAAA,GAAI,KAAA;AAAA,WAC5C,UAAA,CAAW,GAAG,CAAA,GAAI,KAAA;AAAA,IACzB;AACA,IAAA,MAAM,YAAA,GAAe,MAAA,CAAQ,MAAA,CAAO,UAAgC,CAAA;AACpE,IAAA,OAAO,OAAO,MAAA,CAAO;AAAA,MACnB,GAAI,YAAA;AAAA,MACJ,GAAG;AAAA,KACJ,CAAA;AAAA,EACH,CAAA;AAEA,EAAA,MAAM,KAAA,GAAQ,MAAA,GAAS,SAAA,CAAU,SAAS,IAAI,MAAA,CAAQ,KAAA;AAEtD,EAAA,MAAM,MAAA,GAAS,CAAC,MAAA,KAAoB;AAClC,IAAA,IAAI,YAAA,EAAc;AAChB,MAAA,OAAO,mBAAA;AAAA,QACL,YAAA;AAAA,QACA,MAAA;AAAA,QACA;AAAA,OACF;AAAA,IACF;AACA,IAAA,OAAO,MAAA,CAAQ,OAAO,MAAM,CAAA;AAAA,EAC9B,CAAA;AAEA,EAAA,OAAO;AAAA,IACL,MAAM,SAAA,CAAU,IAAA;AAAA,IAChB,GAAA,EAAK,EAAA;AAAA,IACL,aAAA,EAAe,QAAQ,aAAA,IAAiB,CAAA,uBAAA,CAAA;AAAA,IACxC,MAAA;AAAA,IACA,MAAA;AAAA,IACA;AAAA,GACF;AACF;AAEA,SAAS,gBAAA,CAIP,WACA,MAAA,EACM;AAMN,EAAA,MAAM,CAAA,GAAI,SAAA,CAAU,OAAA,IAAW,EAAC;AAChC,EAAA,KAAA,MAAW,CAAC,KAAA,EAAO,QAAQ,KAAK,MAAA,CAAO,OAAA,CAAQ,CAAC,CAAA,EAAG;AACjD,IAAA,IACE,OAAO,QAAA,CAAS,SAAA,KAAc,QAAA,IAC9B,OAAO,QAAA,CAAS,SAAA,KAAc,QAAA,IAC9B,QAAA,CAAS,SAAA,GAAY,QAAA,CAAS,SAAA,EAC9B;AACA,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,uCAAA,EAA0C,SAAA,CAAU,IAAI,CAAA,UAAA,EAAa,KAAK,gBAAgB,QAAA,CAAS,SAAS,CAAA,eAAA,EAAkB,QAAA,CAAS,SAAS,CAAA,CAAA;AAAA,OAClJ;AAAA,IACF;AACA,IAAA,IACE,OAAO,QAAA,CAAS,OAAA,KAAY,QAAA,IAC5B,OAAO,QAAA,CAAS,OAAA,KAAY,QAAA,IAC5B,QAAA,CAAS,OAAA,GAAU,QAAA,CAAS,OAAA,EAC5B;AACA,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,uCAAA,EAA0C,SAAA,CAAU,IAAI,CAAA,UAAA,EAAa,KAAK,cAAc,QAAA,CAAS,OAAO,CAAA,aAAA,EAAgB,QAAA,CAAS,OAAO,CAAA,CAAA;AAAA,OAC1I;AAAA,IACF;AACA,IAAA,IAAI,QAAA,CAAS,SAAS,MAAA,IAAa,CAAC,MAAM,OAAA,CAAQ,QAAA,CAAS,IAAI,CAAA,EAAG;AAChE,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,uCAAA,EAA0C,SAAA,CAAU,IAAI,CAAA,UAAA,EAAa,KAAK,CAAA,uBAAA;AAAA,OAC5E;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,UAAU,SAAA,EAAgC;AACjD,EAAA,OAAO,CAAC,MAAA,KAAmB;AAQzB,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,uCAAA,EAA0C,UAAU,IAAI,CAAA,+FAAA;AAAA,KAC1D;AAAA,EACF,CAAA;AACF;AAEA,SAAS,mBAAA,CACP,QAAA,EACA,MAAA,EACA,WAAA,EACQ;AAIR,EAAA,MAAM,WACH,OAAO,MAAA,CAAO,OAAO,QAAA,IAAY,MAAA,CAAO,MACxC,OAAO,MAAA,CAAO,IAAA,KAAS,QAAA,IAAY,OAAO,IAAA,IAC1C,OAAO,OAAO,IAAA,KAAS,QAAA,IAAY,OAAO,IAAA,IAC3C,SAAA;AACF,EAAA,OAAO,QAAA,CACJ,QAAQ,SAAA,EAAW,QAAQ,EAC3B,OAAA,CAAQ,YAAA,EAAc,WAAA,CAAY,WAAA,EAAa,CAAA;AACpD;;;ACnMO,IAAM,SAAA,GAAY;AAClB,IAAM,YAAA,GAAe","file":"index.mjs","sourcesContent":["/**\n * `specFromExtension(handle, parent)` — turn a parsed EXTENSION.md\n * into a runtime `DoctypeSpec` consumable by\n * `@agentproto/manifest.createVerbs`.\n *\n * The parent argument is the public AIP's spec (e.g. `toolSpec` from\n * `@agentproto/tool`). The function composes:\n *\n * schema parent.schema ∪ ext.add_fields, then ext.tighten\n * path ext.path_convention ?? parent.pathOf\n * defaults parent defaults ⨁ ext.defaults\n * define wraps parent.define with default-application\n * parse parent.parse (frontmatter shape stays compatible)\n *\n * Tightening monotonicity is verified by-field: pattern subset\n * (cheap heuristic — full regex-language inclusion is undecidable),\n * enum subset (set membership), minLength/minimum ≥, maxLength/maximum ≤.\n *\n * For `extends: none`, `parent` is null/undefined and the extension\n * acts as a root doctype: the `add_fields` becomes its full schema,\n * `path_convention` is required (no parent fallback).\n */\n\nimport type { DoctypeSpec } from \"@agentproto/manifest\"\nimport type { ExtensionDefinition } from \"./types.js\"\n\nexport interface SpecFromExtensionOptions<\n TParams extends { id?: string; slug?: string; name?: string },\n THandle,\n> {\n /**\n * Parent doctype spec. Required when `extension.extends !== \"none\"`.\n * Pass e.g. `toolSpec` from `@agentproto/tool`.\n */\n parent?: DoctypeSpec<TParams, THandle>\n}\n\nexport function specFromExtension<\n TParams extends { id?: string; slug?: string; name?: string },\n THandle,\n>(\n extension: ExtensionDefinition,\n opts: SpecFromExtensionOptions<TParams, THandle> = {},\n): DoctypeSpec<TParams, THandle> {\n const { parent } = opts\n const isRoot = extension.extends === \"none\"\n\n if (!isRoot && !parent) {\n throw new Error(\n `specFromExtension (AIP-40): extension '${extension.slug}' extends '${extension.extends}' but no parent spec was provided in opts.parent`,\n )\n }\n if (isRoot && !extension.path_convention) {\n throw new Error(\n `specFromExtension (AIP-40): extension '${extension.slug}' is a root doctype (extends: none) and MUST declare path_convention`,\n )\n }\n\n // Tightening monotonicity check — see Specification §\"Composition rules\" #3.\n if (parent && extension.tighten) {\n verifyTightening(extension, parent)\n }\n\n const slugName = extension.slug.split(\":\")[1] ?? extension.slug\n const pathTemplate = extension.path_convention ?? null\n\n // Keys the extension is authoritative over — anything declared in\n // `add_fields.properties`. Computed once at spec-composition time so\n // the per-call `define` is a hot-path lookup, not a re-derivation.\n const extensionKeys = new Set(\n Object.keys(extension.add_fields?.properties ?? {}),\n )\n\n const define = (params: TParams) => {\n // Layered defaults: parent defaults are baked into parent.define;\n // extension defaults apply on top, only for keys the user omitted.\n const withDefaults = { ...params } as Record<string, unknown>\n if (extension.defaults) {\n for (const [key, value] of Object.entries(extension.defaults)) {\n if (withDefaults[key] === undefined && value !== undefined) {\n withDefaults[key] = value\n }\n }\n }\n if (isRoot) {\n // Root doctype — no parent.define to delegate to. Return the\n // params verbatim, frozen. (A more disciplined runtime would\n // compile the add_fields schema to zod and validate; deferred\n // because root doctypes are rare and the manifest layer's\n // schema-level zod catches malformed input.)\n return Object.freeze(withDefaults) as unknown as THandle\n }\n // Split params before delegating: parent specs typically validate\n // through `zod.strict()` (e.g. AIP-42 `agentFrontmatterSchema`),\n // which rejects extension-owned keys as \"Unrecognized\". Hand the\n // parent only what it owns, then re-attach extension fields onto\n // the resulting handle. Extension-side validation of these fields\n // is the manifest layer's responsibility (compiled from\n // `add_fields` JSON Schema at parse time); the runtime trusts\n // input it received via that path.\n const parentOnly: Record<string, unknown> = {}\n const extensionOnly: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(withDefaults)) {\n if (extensionKeys.has(key)) extensionOnly[key] = value\n else parentOnly[key] = value\n }\n const parentHandle = parent!.define(parentOnly as unknown as TParams)\n return Object.freeze({\n ...(parentHandle as object),\n ...extensionOnly,\n }) as unknown as THandle\n }\n\n const parse = isRoot ? rootParse(extension) : parent!.parse\n\n const pathOf = (handle: THandle) => {\n if (pathTemplate) {\n return resolvePathTemplate(\n pathTemplate,\n handle as unknown as Record<string, unknown>,\n slugName,\n )\n }\n return parent!.pathOf(handle)\n }\n\n return {\n name: extension.slug,\n aip: 40,\n schemaLiteral: parent?.schemaLiteral ?? `agentproto/extension/v1`,\n pathOf,\n define,\n parse,\n }\n}\n\nfunction verifyTightening<\n TParams extends { id?: string; slug?: string; name?: string },\n THandle,\n>(\n extension: ExtensionDefinition,\n parent: DoctypeSpec<TParams, THandle>,\n): void {\n // Best-effort: the manifest layer doesn't expose the parent's raw\n // JSON Schema, so we can only verify what the spec ships in\n // `extension.tighten` against itself for self-consistency. Full\n // parent-vs-extension monotonicity verification belongs to a future\n // version once parent specs expose `schema:` introspection.\n const t = extension.tighten ?? {}\n for (const [field, override] of Object.entries(t)) {\n if (\n typeof override.minLength === \"number\" &&\n typeof override.maxLength === \"number\" &&\n override.minLength > override.maxLength\n ) {\n throw new Error(\n `specFromExtension (AIP-40): extension '${extension.slug}' tighten.${field}: minLength (${override.minLength}) > maxLength (${override.maxLength})`,\n )\n }\n if (\n typeof override.minimum === \"number\" &&\n typeof override.maximum === \"number\" &&\n override.minimum > override.maximum\n ) {\n throw new Error(\n `specFromExtension (AIP-40): extension '${extension.slug}' tighten.${field}: minimum (${override.minimum}) > maximum (${override.maximum})`,\n )\n }\n if (override.enum !== undefined && !Array.isArray(override.enum)) {\n throw new Error(\n `specFromExtension (AIP-40): extension '${extension.slug}' tighten.${field}: enum must be an array`,\n )\n }\n }\n}\n\nfunction rootParse(extension: ExtensionDefinition) {\n return (source: string) => {\n // Root doctypes don't have a parent parser — fall back to bare\n // gray-matter. The manifest layer applies `define` afterwards.\n // We don't import gray-matter here to keep this module dependency-\n // light; the caller wires their own parse if they really want a\n // root doctype, or uses the standard parse from a sibling package.\n void source\n void extension\n throw new Error(\n `specFromExtension (AIP-40): extension '${extension.slug}' is a root doctype — supply your own parser or extend a public AIP for parser inheritance`,\n )\n }\n}\n\nfunction resolvePathTemplate(\n template: string,\n handle: Record<string, unknown>,\n doctypeSlug: string,\n): string {\n // Tokens supported in v1: <slug> (the doctype's identity field —\n // tries id, slug, name in that order) and <DOCTYPE> (uppercase\n // version of the extension's slug name part, e.g. \"DEAL\").\n const identity =\n (typeof handle.id === \"string\" && handle.id) ||\n (typeof handle.slug === \"string\" && handle.slug) ||\n (typeof handle.name === \"string\" && handle.name) ||\n \"unknown\"\n return template\n .replace(/<slug>/g, identity)\n .replace(/<DOCTYPE>/g, doctypeSlug.toUpperCase())\n}\n","/**\n * @agentproto/extension — AIP-40 EXTENSION.md `defineExtension` reference impl.\n *\n * A meta-doctype that lets a workspace declare its own custom doctype as an extension of an existing AIP — adding fields, tightening constraints, overriding defaults, and choosing a path convention — without going through the public AIP process. The runtime (@agentproto/manifest verbs, MCP server, scaffolder) treats local extensions identically to public AIPs.\n *\n * Spec: https://agentproto.sh/docs/aip-40\n *\n * Authoring paths:\n * - TS: `defineExtension({...})` → `ExtensionHandle`\n * - MD: `parseExtensionManifest(src) → extensionFromManifest({...})` → `ExtensionHandle`\n */\n\nexport const SPEC_NAME = \"agentextension/v1\" as const\nexport const SPEC_VERSION = \"1.0.0-alpha\" as const\n\nexport { defineExtension } from \"./define-extension.js\"\nexport type { ExtensionDefinition, ExtensionHandle } from \"./types.js\"\nexport {\n specFromExtension,\n type SpecFromExtensionOptions,\n} from \"./spec-from-extension.js\"\nexport {\n parseExtensionManifest,\n extensionFromManifest,\n extensionFrontmatterSchema,\n type ExtensionFrontmatter,\n type ExtensionManifest,\n} from \"./manifest/index.js\"\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"index.mjs"}
|
package/package.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@agentproto/extension",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "@agentproto/extension — AIP-40 EXTENSION.md reference implementation. A meta-doctype that lets a workspace declare its own custom doctype as an extension of an existing AIP — adding fields, tightening constraints, overriding defaults, and choosing a path convention — without going through the public AIP process. The runtime (@agentproto/manifest verbs, MCP server, scaffolder) treats local extensions identically to public AIPs.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"agentproto",
|
|
7
|
+
"aip-40",
|
|
8
|
+
"extension",
|
|
9
|
+
"defineExtension",
|
|
10
|
+
"open-standard",
|
|
11
|
+
"agentic"
|
|
12
|
+
],
|
|
13
|
+
"homepage": "https://agentproto.sh/docs/aip-40",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/agentproto/ts",
|
|
17
|
+
"directory": "packages/extension"
|
|
18
|
+
},
|
|
19
|
+
"bugs": {
|
|
20
|
+
"url": "https://github.com/agentproto/ts/issues"
|
|
21
|
+
},
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"type": "module",
|
|
24
|
+
"main": "dist/index.mjs",
|
|
25
|
+
"module": "dist/index.mjs",
|
|
26
|
+
"types": "dist/index.d.ts",
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"import": "./dist/index.mjs",
|
|
31
|
+
"default": "./dist/index.mjs"
|
|
32
|
+
},
|
|
33
|
+
"./manifest": {
|
|
34
|
+
"types": "./dist/manifest/index.d.ts",
|
|
35
|
+
"import": "./dist/manifest/index.mjs",
|
|
36
|
+
"default": "./dist/manifest/index.mjs"
|
|
37
|
+
},
|
|
38
|
+
"./package.json": "./package.json"
|
|
39
|
+
},
|
|
40
|
+
"files": [
|
|
41
|
+
"dist",
|
|
42
|
+
"README.md",
|
|
43
|
+
"LICENSE"
|
|
44
|
+
],
|
|
45
|
+
"publishConfig": {
|
|
46
|
+
"access": "public"
|
|
47
|
+
},
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"gray-matter": "^4.0.3",
|
|
50
|
+
"zod": "^4.4.3",
|
|
51
|
+
"@agentproto/define-doctype": "0.1.0",
|
|
52
|
+
"@agentproto/manifest": "0.1.0"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@types/node": "^25.6.2",
|
|
56
|
+
"tsup": "^8.5.1",
|
|
57
|
+
"typescript": "^5.9.3",
|
|
58
|
+
"vitest": "^3.2.4",
|
|
59
|
+
"@agentproto/tooling": "0.1.0-alpha.0"
|
|
60
|
+
},
|
|
61
|
+
"scripts": {
|
|
62
|
+
"dev": "tsup --watch",
|
|
63
|
+
"build": "tsup",
|
|
64
|
+
"clean": "rm -rf dist",
|
|
65
|
+
"check-types": "tsc --noEmit",
|
|
66
|
+
"test": "vitest run --passWithNoTests",
|
|
67
|
+
"test:watch": "vitest"
|
|
68
|
+
}
|
|
69
|
+
}
|