@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 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":";;;;;;;;;AAeO,IAAM,6BAA6B,CAAA,CAAE,MAAA,CAAO,EAAE,QAAA,EAAU,EAAE,OAAA,CAAQ,yBAAyB,CAAA,CAAE,QAAA,CAAS,kDAAkD,CAAA,EAAG,MAAA,EAAQ,CAAA,CAAE,MAAA,GAAS,KAAA,CAAM,IAAI,MAAA,CAAO,2CAA2C,CAAC,CAAA,CAAE,QAAA,CAAS,6HAAwH,CAAA,EAAG,SAAS,CAAA,CAAE,MAAA,EAAO,CAAE,GAAA,CAAI,CAAC,CAAA,CAAE,GAAA,CAAI,EAAE,CAAA,CAAE,SAAS,8BAA8B,CAAA,EAAG,aAAA,EAAe,CAAA,CAAE,QAAO,CAAE,GAAA,CAAI,CAAC,CAAA,CAAE,IAAI,GAAI,CAAA,CAAE,QAAA,CAAS,qDAAqD,GAAG,SAAA,EAAW,CAAA,CAAE,MAAA,EAAO,CAAE,MAAM,IAAI,MAAA,CAAO,6CAA6C,CAAC,EAAE,QAAA,CAAS,kDAAkD,CAAA,EAAG,QAAA,EAAU,EAAE,OAAA,CAAQ,OAAO,CAAA,CAAE,QAAA,CAAS,8GAAyG,CAAA,EAAG,SAAA,EAAW,CAAA,CAAE,KAAA,CAAM,CAAC,CAAA,CAAE,MAAA,EAAO,CAAE,KAAA,CAAM,IAAI,MAAA,CAAO,YAAY,CAAC,CAAA,CAAE,SAAS,gDAAgD,CAAA,EAAG,CAAA,CAAE,OAAA,CAAQ,MAAM,CAAA,CAAE,QAAA,CAAS,oCAAoC,CAAC,CAAC,CAAA,CAAE,QAAA,CAAS,wEAAwE,CAAA,EAAG,cAAc,CAAA,CAAE,MAAA,CAAO,EAAE,YAAA,EAAc,EAAE,MAAA,CAAO,CAAA,CAAE,MAAA,EAAO,EAAG,EAAE,GAAA,EAAK,CAAA,CAAE,QAAA,CAAS,mJAA8I,CAAA,CAAE,QAAA,EAAS,EAAG,UAAA,EAAY,EAAE,KAAA,CAAM,CAAA,CAAE,MAAA,EAAQ,EAAE,MAAA,CAAO,CAAC,GAAA,KAAQ,GAAA,CAAI,MAAM,CAAC,IAAA,EAAM,CAAA,KAAM,GAAA,CAAI,QAAQ,IAAI,CAAA,IAAK,CAAC,CAAA,EAAG,EAAE,OAAA,EAAS,2BAAA,EAA6B,CAAA,CAAE,SAAS,sHAAsH,CAAA,CAAE,OAAA,CAAQ,EAAW,CAAA,EAAG,CAAA,CAAE,MAAA,GAAS,QAAA,CAAS,gEAAgE,CAAA,CAAE,QAAA,IAAY,SAAA,EAAW,CAAA,CAAE,MAAA,CAAO,CAAA,CAAE,QAAO,EAAG,CAAA,CAAE,MAAA,CAAO,EAAE,WAAW,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,IAAY,MAAA,EAAQ,CAAA,CAAE,KAAA,CAAM,CAAA,CAAE,KAAK,CAAA,CAAE,QAAA,EAAS,EAAG,aAAa,CAAA,CAAE,MAAA,EAAO,CAAE,GAAA,GAAM,GAAA,CAAI,CAAC,CAAA,CAAE,QAAA,IAAY,WAAA,EAAa,CAAA,CAAE,MAAA,EAAO,CAAE,KAAI,CAAE,GAAA,CAAI,CAAC,CAAA,CAAE,UAAS,EAAG,SAAA,EAAW,CAAA,CAAE,MAAA,GAAS,QAAA,EAAS,EAAG,SAAA,EAAW,CAAA,CAAE,QAAO,CAAE,QAAA,EAAS,EAAG,EAAE,MAAA,EAAQ,CAAA,CAAE,QAAA,CAAS,8IAA8I,CAAA,CAAE,QAAA,EAAS,EAAG,UAAA,EAAY,EAAE,MAAA,CAAO,CAAA,CAAE,MAAA,EAAO,EAAG,EAAE,GAAA,EAAK,CAAA,CAAE,QAAA,CAAS,8HAA8H,CAAA,CAAE,QAAA,EAAS,EAAG,iBAAA,EAAmB,EAAE,MAAA,EAAO,CAAE,GAAA,CAAI,CAAC,EAAE,QAAA,CAAS,iMAAiM,CAAA,CAAE,QAAA,IAAY,UAAA,EAAY,CAAA,CAAE,KAAA,CAAM,CAAA,CAAE,QAAO,CAAE,GAAA,EAAI,CAAE,GAAA,CAAI,CAAC,CAAC,CAAA,CAAE,MAAA,CAAO,CAAC,QAAQ,GAAA,CAAI,KAAA,CAAM,CAAC,IAAA,EAAM,MAAM,GAAA,CAAI,OAAA,CAAQ,IAAI,CAAA,IAAK,CAAC,CAAA,EAAG,EAAE,OAAA,EAAS,2BAAA,EAA6B,CAAA,CAAE,QAAA,CAAS,kEAAkE,CAAA,CAAE,QAAQ,EAAW,CAAA,EAAG,UAAA,EAAY,EAAE,MAAA,CAAO,CAAA,CAAE,MAAA,EAAO,EAAG,EAAE,GAAA,EAAK,CAAA,CAAE,QAAA,CAAS,kFAAkF,CAAA,CAAE,OAAA,CAAQ,EAAW,GAAG,CAAA,CAAE,MAAA,EAAO,CAAE,SAAS,yPAAyP;ACMzvG,IAAM,kBAAkB,aAAA,CAAoD;AAAA,EACjF,GAAA,EAAK,EAAA;AAAA,EACL,IAAA,EAAM,WAAA;AAAA;AAAA;AAAA,EAGN,SAAA,EAAW,2CAAA;AAAA,EACX,YAAA,EAAc,CAAC,GAAA,KAAQ,GAAA,CAAI,IAAA;AAAA,EAC3B,SAAS,GAAA,EAAK;AACZ,IAAA,MAAM,MAAA,GAAS,0BAAA,CAA2B,SAAA,CAAU,GAAG,CAAA;AACvD,IAAA,IAAI,CAAC,OAAO,OAAA,EAAS;AACnB,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,0BAAA,EAA6B,OAAO,KAAA,CAAM,MAAA,CACvC,IAAI,CAAC,CAAA,KAAM,GAAG,CAAA,CAAE,IAAA,CAAK,KAAK,GAAG,CAAC,KAAK,CAAA,CAAE,OAAO,EAAE,CAAA,CAC9C,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA,OACf;AAAA,IACF;AAAA,EAIF,CAAA;AAAA,EACA,MAAM,GAAA,EAAK;AAKT,IAAA,OAAO,EAAE,GAAG,GAAA,EAAI;AAAA,EAClB;AACF,CAAC;;;AClBM,SAAS,uBAAuB,MAAA,EAAmC;AACxE,EAAA,MAAM,MAAA,GAAS,OAAO,MAAM,CAAA;AAC5B,EAAA,IAAI,OAAO,IAAA,CAAK,MAAA,CAAO,IAAI,CAAA,CAAE,WAAW,CAAA,EAAG;AACzC,IAAA,MAAM,IAAI,MAAM,sDAAsD,CAAA;AAAA,EACxE;AACA,EAAA,MAAM,MAAA,GAAS,0BAAA,CAA2B,SAAA,CAAU,MAAA,CAAO,IAAI,CAAA;AAC/D,EAAA,IAAI,CAAC,OAAO,OAAA,EAAS;AACnB,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,mDAAA,EAAiD,OAAO,KAAA,CAAM,MAAA,CAC3D,IAAI,CAAC,CAAA,KAAM,GAAG,CAAA,CAAE,IAAA,CAAK,KAAK,GAAG,CAAC,KAAK,CAAA,CAAE,OAAO,EAAE,CAAA,CAC9C,IAAA,CAAK,IAAI,CAAC,CAAA;AAAA,KACf;AAAA,EACF;AACA,EAAA,OAAO,EAAE,WAAA,EAAa,MAAA,CAAO,IAAA,EAAM,IAAA,EAAM,OAAO,OAAA,EAAQ;AAC1D;AAEO,SAAS,sBAAsB,QAAA,EAA8C;AAKlF,EAAA,OAAO,eAAA,CAAgB,SAAS,WAA6C,CAAA;AAC/E","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 };
@@ -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":"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","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,2 @@
1
+ export { a as ExtensionFrontmatter, c as ExtensionManifest, e as extensionFromManifest, d as extensionFrontmatterSchema, p as parseExtensionManifest } from '../index-C2uTNMnb.js';
2
+ import 'zod';
@@ -0,0 +1,3 @@
1
+ export { extensionFromManifest, extensionFrontmatterSchema, parseExtensionManifest } from '../chunk-XIW4DKUM.mjs';
2
+ //# sourceMappingURL=index.mjs.map
3
+ //# sourceMappingURL=index.mjs.map
@@ -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
+ }