@agentproto/corpus-cli 0.1.0-alpha.1 → 0.1.0-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/cli.mjs +43 -8
  2. package/dist/cli.mjs.map +1 -1
  3. package/dist/index.d.ts +1 -5
  4. package/dist/index.mjs +15 -4
  5. package/dist/index.mjs.map +1 -1
  6. package/dist/specs/resources/aip-10/draft/KNOWLEDGE.schema.json +457 -0
  7. package/dist/specs/resources/aip-11/draft/LESSON.schema.json +149 -0
  8. package/dist/specs/resources/aip-12/draft/PLAYBOOK.schema.json +349 -0
  9. package/dist/specs/resources/aip-13/draft/WORK_ITEM.schema.json +547 -0
  10. package/dist/specs/resources/aip-14/draft/TOOL.schema.json +169 -0
  11. package/dist/specs/resources/aip-15/draft/WORKFLOW.schema.json +356 -0
  12. package/dist/specs/resources/aip-16/draft/IO.schema.json +64 -0
  13. package/dist/specs/resources/aip-17/draft/RUNNER.schema.json +81 -0
  14. package/dist/specs/resources/aip-18/draft/COLLECTION.schema.json +436 -0
  15. package/dist/specs/resources/aip-19/draft/SECRETS.schema.json +261 -0
  16. package/dist/specs/resources/aip-20/draft/WORK.schema.json +371 -0
  17. package/dist/specs/resources/aip-21/draft/AGENCY.schema.json +454 -0
  18. package/dist/specs/resources/aip-22/draft/OFFICE.schema.json +390 -0
  19. package/dist/specs/resources/aip-23/draft/IDENTITY.schema.json +358 -0
  20. package/dist/specs/resources/aip-24/draft/ASSEMBLY.schema.json +411 -0
  21. package/dist/specs/resources/aip-25/draft/PERSONA.schema.json +257 -0
  22. package/dist/specs/resources/aip-26/draft/CODE.schema.json +172 -0
  23. package/dist/specs/resources/aip-28/draft/INTENT.schema.json +215 -0
  24. package/dist/specs/resources/aip-29/draft/CLI.schema.json +339 -0
  25. package/dist/specs/resources/aip-3/draft/SKILL.schema.json +206 -0
  26. package/dist/specs/resources/aip-30/draft/DRIVER.schema.json +237 -0
  27. package/dist/specs/resources/aip-31/draft/HTTP.schema.json +80 -0
  28. package/dist/specs/resources/aip-32/draft/MCP.schema.json +88 -0
  29. package/dist/specs/resources/aip-33/draft/SDK.schema.json +60 -0
  30. package/dist/specs/resources/aip-34/draft/WORKSPACE.schema.json +252 -0
  31. package/dist/specs/resources/aip-35/draft/STORAGE.schema.json +237 -0
  32. package/dist/specs/resources/aip-36/draft/SANDBOX.schema.json +246 -0
  33. package/dist/specs/resources/aip-38/draft/POLICY.schema.json +217 -0
  34. package/dist/specs/resources/aip-39/draft/ACTION.schema.json +134 -0
  35. package/dist/specs/resources/aip-4/draft/DESIGN.schema.json +189 -0
  36. package/dist/specs/resources/aip-40/draft/EXTENSION.schema.json +116 -0
  37. package/dist/specs/resources/aip-41/draft/ROUTINE.schema.json +345 -0
  38. package/dist/specs/resources/aip-42/draft/AGENT.schema.json +268 -0
  39. package/dist/specs/resources/aip-44/draft/ACP.schema.json +170 -0
  40. package/dist/specs/resources/aip-45/draft/AGENT-CLI.schema.json +371 -0
  41. package/dist/specs/resources/aip-47/draft/ROLE.schema.json +199 -0
  42. package/dist/specs/resources/aip-48/draft/MultiAgentRuntime.schema.json +108 -0
  43. package/dist/specs/resources/aip-5/draft/TEMPLATE.schema.json +231 -0
  44. package/dist/specs/resources/aip-6/draft/COMPANY.schema.json +268 -0
  45. package/dist/specs/resources/aip-7/draft/GOVERNANCE.schema.json +581 -0
  46. package/dist/specs/resources/aip-8/draft/AGENCY.schema.json +518 -0
  47. package/dist/specs/resources/aip-9/draft/OPERATOR.schema.json +297 -0
  48. package/package.json +2 -2
package/dist/index.d.ts CHANGED
@@ -1,10 +1,6 @@
1
1
  export { N as NodeFsAdapter, a as NodeFsAdapterOptions, O as OsIdentityAdapter, b as OsIdentityAdapterOptions } from './os-identity.adapter-CLBmnRoD.js';
2
2
  import '@agentproto/corpus';
3
3
 
4
- /**
5
- * Version constant — pinned at build time. Stay in sync with
6
- * package.json manually until we wire a build-time inliner.
7
- */
8
- declare const VERSION: "0.1.0-alpha.0";
4
+ declare const VERSION: string;
9
5
 
10
6
  export { VERSION };
package/dist/index.mjs CHANGED
@@ -1,7 +1,8 @@
1
+ import { readFileSync, existsSync } from 'fs';
2
+ import path, { dirname, resolve } from 'path';
3
+ import { fileURLToPath } from 'url';
1
4
  import { randomBytes } from 'crypto';
2
5
  import { stat, readFile, mkdir, writeFile, rename, appendFile, readdir, rm } from 'fs/promises';
3
- import path from 'path';
4
- import { existsSync } from 'fs';
5
6
  import os from 'os';
6
7
 
7
8
  /**
@@ -9,8 +10,18 @@ import os from 'os';
9
10
  * The `corpus` binary — local-topology host for AIP-10 corpora.
10
11
  */
11
12
 
12
- // src/version.ts
13
- var VERSION = "0.1.0-alpha.0";
13
+ function readVersion() {
14
+ try {
15
+ const here = dirname(fileURLToPath(import.meta.url));
16
+ const pkg = JSON.parse(
17
+ readFileSync(resolve(here, "../package.json"), "utf8")
18
+ );
19
+ return pkg.version ?? "0.0.0";
20
+ } catch {
21
+ return "0.0.0";
22
+ }
23
+ }
24
+ var VERSION = readVersion();
14
25
  var NodeFsAdapter = class {
15
26
  root;
16
27
  constructor(opts) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/version.ts","../src/ports/local-fs.adapter.ts","../src/ports/os-identity.adapter.ts"],"names":["path"],"mappings":";;;;;;;;;;;;AAIO,IAAM,OAAA,GAAU;ACmChB,IAAM,gBAAN,MAAsC;AAAA,EAC1B,IAAA;AAAA,EAEjB,YAAY,IAAA,EAA4B;AACtC,IAAA,IAAA,CAAK,OAAO,IAAA,CAAK,IAAA;AAAA,EACnB;AAAA,EAEA,MAAM,OAAO,CAAA,EAA6B;AACxC,IAAA,IAAI;AACF,MAAA,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,CAAQ,CAAC,CAAC,CAAA;AAC1B,MAAA,OAAO,IAAA;AAAA,IACT,CAAA,CAAA,MAAQ;AACN,MAAA,OAAO,KAAA;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAM,SAAS,CAAA,EAA4B;AACzC,IAAA,OAAO,MAAM,QAAA,CAAS,IAAA,CAAK,OAAA,CAAQ,CAAC,GAAG,MAAM,CAAA;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,SAAA,CAAU,CAAA,EAAW,OAAA,EAAgC;AACzD,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,OAAA,CAAQ,CAAC,CAAA;AAC7B,IAAA,MAAM,KAAA,CAAM,KAAK,OAAA,CAAQ,MAAM,GAAG,EAAE,SAAA,EAAW,MAAM,CAAA;AACrD,IAAA,MAAM,GAAA,GAAM,GAAG,MAAM,CAAA,KAAA,EAAQ,YAAY,CAAC,CAAA,CAAE,QAAA,CAAS,KAAK,CAAC,CAAA,CAAA;AAC3D,IAAA,MAAM,SAAA,CAAU,GAAA,EAAK,OAAA,EAAS,MAAM,CAAA;AACpC,IAAA,MAAM,MAAA,CAAO,KAAK,MAAM,CAAA;AAAA,EAC1B;AAAA,EAEA,MAAM,UAAA,CAAW,CAAA,EAAW,OAAA,EAAgC;AAC1D,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,OAAA,CAAQ,CAAC,CAAA;AAC7B,IAAA,MAAM,KAAA,CAAM,KAAK,OAAA,CAAQ,MAAM,GAAG,EAAE,SAAA,EAAW,MAAM,CAAA;AACrD,IAAA,MAAM,UAAA,CAAW,MAAA,EAAQ,OAAA,EAAS,MAAM,CAAA;AAAA,EAC1C;AAAA,EAEA,MAAM,QAAQ,CAAA,EAAuC;AACnD,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,OAAA,CAAQ,CAAC,CAAA;AAC7B,IAAA,MAAM,OAAA,GAAU,MAAM,OAAA,CAAQ,MAAM,CAAA;AACpC,IAAA,OAAO,OAAA;AAAA,EACT;AAAA,EAEA,MAAM,KAAK,CAAA,EAAuC;AAChD,IAAA,MAAM,MAAgB,EAAC;AACvB,IAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,OAAA,CAAQ,CAAC,CAAA;AAC5B,IAAA,MAAM,eAAA,GAAkB,KAAA;AACxB,IAAA,MAAM,KAAA,GAAQ,OAAO,GAAA,KAA+B;AAClD,MAAA,IAAI,UAAoB,EAAC;AACzB,MAAA,IAAI;AACF,QAAA,OAAA,GAAU,MAAM,QAAQ,GAAG,CAAA;AAAA,MAC7B,CAAA,CAAA,MAAQ;AACN,QAAA;AAAA,MACF;AACA,MAAA,KAAA,MAAW,OAAO,OAAA,EAAS;AAEzB,QAAA,IAAI,GAAA,CAAI,UAAA,CAAW,GAAG,CAAA,EAAG;AACzB,QAAA,MAAM,IAAA,GAAO,IAAA,CAAK,IAAA,CAAK,GAAA,EAAK,GAAG,CAAA;AAC/B,QAAA,MAAM,CAAA,GAAI,MAAM,IAAA,CAAK,IAAI,CAAA;AACzB,QAAA,IAAI,CAAA,CAAE,aAAY,EAAG;AACnB,UAAA,MAAM,MAAM,IAAI,CAAA;AAAA,QAClB,CAAA,MAAA,IAAW,CAAA,CAAE,MAAA,EAAO,EAAG;AACrB,UAAA,MAAM,GAAA,GAAM,IAAA,CAAK,QAAA,CAAS,eAAA,EAAiB,IAAI,CAAA,CAAE,KAAA,CAAM,IAAA,CAAK,GAAG,CAAA,CAAE,IAAA,CAAK,GAAG,CAAA;AACzE,UAAA,GAAA,CAAI,KAAK,GAAG,CAAA;AAAA,QACd;AAAA,MACF;AAAA,IACF,CAAA;AACA,IAAA,MAAM,MAAM,KAAK,CAAA;AACjB,IAAA,OAAO,GAAA;AAAA,EACT;AAAA,EAEA,MAAM,KAAK,CAAA,EAAmC;AAC5C,IAAA,IAAI;AACF,MAAA,MAAM,IAAI,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,CAAQ,CAAC,CAAC,CAAA;AACpC,MAAA,OAAO;AAAA,QACL,IAAA,EAAM,CAAA,CAAE,WAAA,EAAY,GAAI,WAAA,GAAc,MAAA;AAAA,QACtC,KAAA,EAAO,CAAA,CAAE,MAAA,EAAO,GAAI,EAAE,IAAA,GAAO,KAAA,CAAA;AAAA,QAC7B,YAAY,CAAA,CAAE;AAAA,OAChB;AAAA,IACF,CAAA,CAAA,MAAQ;AACN,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,KAAK,CAAA,EAAkC;AAC3C,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,OAAA,CAAQ,CAAC,CAAA;AAC7B,IAAA,MAAM,UAAU,IAAA,CAAK,IAAA,CAAK,KAAK,OAAA,CAAQ,MAAM,GAAG,eAAe,CAAA;AAC/D,IAAA,MAAM,KAAA,CAAM,OAAA,EAAS,EAAE,SAAA,EAAW,MAAM,CAAA;AACxC,IAAA,MAAM,QAAA,GAAW,KAAK,IAAA,CAAK,OAAA,EAAS,KAAK,QAAA,CAAS,MAAM,IAAI,OAAO,CAAA;AACnE,IAAA,MAAM,OAAA,GAAU,CAAA,EAAG,OAAA,CAAQ,GAAG,CAAA,CAAA,EAAI,YAAY,CAAC,CAAA,CAAE,QAAA,CAAS,KAAK,CAAC,CAAA,CAAA;AAIhE,IAAA,OAAO,IAAA,EAAM;AACX,MAAA,IAAI,CAAC,UAAA,CAAW,QAAQ,CAAA,EAAG;AACzB,QAAA,IAAI;AACF,UAAA,MAAM,SAAA,CAAU,UAAU,OAAA,EAAS,EAAE,UAAU,MAAA,EAAQ,IAAA,EAAM,MAAM,CAAA;AAEnE,UAAA,MAAM,OAAA,GAAU,MAAM,QAAA,CAAS,QAAA,EAAU,MAAM,CAAA,CAAE,KAAA,CAAM,MAAM,EAAE,CAAA;AAC/D,UAAA,IAAI,YAAY,OAAA,EAAS;AAAA,QAC3B,CAAA,CAAA,MAAQ;AAAA,QAER;AAAA,MACF;AACA,MAAA,MAAM,IAAI,OAAA,CAAQ,CAAC,MAAM,UAAA,CAAW,CAAA,EAAG,EAAE,CAAC,CAAA;AAAA,IAC5C;AACA,IAAA,OAAO;AAAA,MACL,SAAS,YAAY;AACnB,QAAA,IAAI;AACF,UAAA,MAAM,EAAA,CAAG,QAAA,EAAU,EAAE,KAAA,EAAO,MAAM,CAAA;AAAA,QACpC,CAAA,CAAA,MAAQ;AAAA,QAER;AAAA,MACF;AAAA,KACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUQ,QAAQ,CAAA,EAAmB;AACjC,IAAA,MAAM,UAAA,GAAa,KAAK,KAAA,CAAM,SAAA,CAAU,MAAM,CAAA,CAAE,OAAA,CAAQ,MAAA,EAAQ,EAAE,CAAC,CAAA;AAEnE,IAAA,MAAM,GAAA,GAAM,UAAA,CAAW,KAAA,CAAM,CAAC,CAAA;AAC9B,IAAA,IAAI,GAAA,CAAI,UAAA,CAAW,IAAI,CAAA,EAAG;AACxB,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,qBAAA,EAAwB,CAAC,CAAA,8BAAA,EAAiC,IAAA,CAAK,IAAI,CAAA,CAAA;AAAA,OACrE;AAAA,IACF;AACA,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,IAAA,CAAK,IAAA,EAAM,GAAG,CAAA;AAAA,EACjC;AACF;ACzJO,IAAM,oBAAN,MAAgD;AAAA,EACpC,MAAA;AAAA,EAEjB,YAAY,IAAA,EAAgC;AAI1C,IAAA,MAAM,IAAA,GACJ,IAAA,CAAK,YAAA,IACL,OAAA,CAAQ,GAAA,CAAI,QACZ,OAAA,CAAQ,GAAA,CAAI,QAAA,IACZ,WAAA,EAAY,IACZ,WAAA;AACF,IAAA,MAAM,MAAA,GAASA,IAAAA,CAAK,QAAA,CAAS,IAAA,CAAK,aAAa,CAAA,IAAK,SAAA;AACpD,IAAA,IAAA,CAAK,MAAA,GAAS,OAAO,MAAA,CAAO;AAAA,MAC1B,SAAA,EAAW,CAAA,WAAA,EAAc,OAAA,CAAQ,IAAI,CAAC,CAAA,CAAA;AAAA,MACtC,YAAA,EAAc,OAAO,MAAA,CAAO;AAAA,QAC1B,CAAA,WAAA,EAAc,OAAA,CAAQ,IAAI,CAAC,CAAA,CAAA;AAAA,QAC3B,CAAA,gBAAA,EAAmB,OAAA,CAAQ,MAAM,CAAC,CAAA;AAAA,OACnC,CAAA;AAAA,MACD,WAAA,EAAa;AAAA,KACd,CAAA;AAAA,EACH;AAAA,EAEA,MAAM,OAAA,GAAmC;AACvC,IAAA,OAAO,IAAA,CAAK,MAAA;AAAA,EACd;AACF;AAEA,SAAS,WAAA,GAAkC;AACzC,EAAA,IAAI;AACF,IAAA,OAAO,EAAA,CAAG,UAAS,CAAE,QAAA;AAAA,EACvB,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,MAAA;AAAA,EACT;AACF;AAEA,SAAS,QAAQ,CAAA,EAAmB;AAClC,EAAA,OAAO,EACJ,WAAA,EAAY,CACZ,QAAQ,cAAA,EAAgB,GAAG,EAC3B,OAAA,CAAQ,UAAA,EAAY,EAAE,CAAA,CACtB,QAAQ,QAAA,EAAU,GAAG,EACrB,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA,IAAK,WAAA;AACrB","file":"index.mjs","sourcesContent":["/**\n * Version constant — pinned at build time. Stay in sync with\n * package.json manually until we wire a build-time inliner.\n */\nexport const VERSION = \"0.1.0-alpha.0\" as const\n","/**\n * NodeFsAdapter — `FsPort` implementation backed by node:fs.\n *\n * Atomic writes via temp + rename (the same posix pattern every\n * mature tool uses). Recursive `walk` skips hidden segments\n * (anything beginning with `.`). Advisory locks via lockfiles in\n * `.corpus-locks/` next to the target — single-process safe; for\n * multi-process the lockfile owner pid is recorded so callers can\n * see who holds the lock.\n */\n\nimport { randomBytes } from \"node:crypto\"\nimport {\n appendFile,\n mkdir,\n readFile,\n readdir,\n rename,\n rm,\n stat,\n writeFile,\n} from \"node:fs/promises\"\nimport path from \"node:path\"\nimport { existsSync } from \"node:fs\"\nimport type {\n FsPort,\n FsLockHandle,\n FsStat,\n} from \"@agentproto/corpus\"\n\nexport interface NodeFsAdapterOptions {\n /**\n * Absolute path to the workspace root. All FsPort paths are\n * resolved relative to this — the kit never sees host filesystem\n * paths.\n */\n readonly root: string\n}\n\nexport class NodeFsAdapter implements FsPort {\n private readonly root: string\n\n constructor(opts: NodeFsAdapterOptions) {\n this.root = opts.root\n }\n\n async exists(p: string): Promise<boolean> {\n try {\n await stat(this.resolve(p))\n return true\n } catch {\n return false\n }\n }\n\n async readFile(p: string): Promise<string> {\n return await readFile(this.resolve(p), \"utf8\")\n }\n\n /**\n * Atomic write: write to `${target}.tmp-{nonce}`, fsync, rename.\n * Rename is atomic on the same filesystem. Creates parent dirs as\n * needed (most callers expect mkdir -p semantics).\n */\n async writeFile(p: string, content: string): Promise<void> {\n const target = this.resolve(p)\n await mkdir(path.dirname(target), { recursive: true })\n const tmp = `${target}.tmp-${randomBytes(8).toString(\"hex\")}`\n await writeFile(tmp, content, \"utf8\")\n await rename(tmp, target)\n }\n\n async appendFile(p: string, content: string): Promise<void> {\n const target = this.resolve(p)\n await mkdir(path.dirname(target), { recursive: true })\n await appendFile(target, content, \"utf8\")\n }\n\n async readdir(p: string): Promise<readonly string[]> {\n const target = this.resolve(p)\n const entries = await readdir(target)\n return entries\n }\n\n async walk(p: string): Promise<readonly string[]> {\n const out: string[] = []\n const start = this.resolve(p)\n const rootForRelative = start\n const visit = async (dir: string): Promise<void> => {\n let entries: string[] = []\n try {\n entries = await readdir(dir)\n } catch {\n return\n }\n for (const ent of entries) {\n // Skip hidden segments — matches the FsPort contract.\n if (ent.startsWith(\".\")) continue\n const full = path.join(dir, ent)\n const s = await stat(full)\n if (s.isDirectory()) {\n await visit(full)\n } else if (s.isFile()) {\n const rel = path.relative(rootForRelative, full).split(path.sep).join(\"/\")\n out.push(rel)\n }\n }\n }\n await visit(start)\n return out\n }\n\n async stat(p: string): Promise<FsStat | null> {\n try {\n const s = await stat(this.resolve(p))\n return {\n kind: s.isDirectory() ? \"directory\" : \"file\",\n bytes: s.isFile() ? s.size : undefined,\n modifiedAt: s.mtime,\n }\n } catch {\n return null\n }\n }\n\n /**\n * Advisory lock via a lockfile. Spins waiting for the lockfile to\n * disappear, then creates it with exclusive flag. Released by\n * deleting it. Single-process safe; multi-process semi-safe (the\n * write is best-effort exclusive — adequate for the corpus's\n * promote-entry transaction which is rare and short-lived).\n */\n async lock(p: string): Promise<FsLockHandle> {\n const target = this.resolve(p)\n const lockDir = path.join(path.dirname(target), \".corpus-locks\")\n await mkdir(lockDir, { recursive: true })\n const lockFile = path.join(lockDir, path.basename(target) + \".lock\")\n const myToken = `${process.pid}-${randomBytes(4).toString(\"hex\")}`\n\n // Spin until we acquire. Cheap polling is fine for corpus\n // workloads — transactions are < 1s.\n while (true) {\n if (!existsSync(lockFile)) {\n try {\n await writeFile(lockFile, myToken, { encoding: \"utf8\", flag: \"wx\" })\n // Race: verify our token is the one that landed.\n const written = await readFile(lockFile, \"utf8\").catch(() => \"\")\n if (written === myToken) break\n } catch {\n // Lost the race — fall through and retry.\n }\n }\n await new Promise((r) => setTimeout(r, 25))\n }\n return {\n release: async () => {\n try {\n await rm(lockFile, { force: true })\n } catch {\n // Lockfile already gone — no-op.\n }\n },\n }\n }\n\n // ── Helpers ────────────────────────────────────────────────────\n\n /**\n * Resolve a workspace-relative path to an absolute host path.\n * Refuses to climb out of the workspace root (defense against\n * `../../etc/passwd`-style abuse) — the corpus kit's paths must\n * stay inside the workspace.\n */\n private resolve(p: string): string {\n const normalized = path.posix.normalize(\"/\" + p.replace(/^\\/+/, \"\"))\n // Drop the leading \"/\" so path.join joins correctly under root.\n const rel = normalized.slice(1)\n if (rel.startsWith(\"..\")) {\n throw new Error(\n `NodeFsAdapter: path \"${p}\" escapes the workspace root \"${this.root}\"`\n )\n }\n return path.join(this.root, rel)\n }\n}\n","/**\n * OsIdentityAdapter — `IdentityPort` resolved from OS user + cwd.\n *\n * Local-topology identity model:\n * - principal = `ws://users/{os-user}`\n * - identityTree = [\n * `ws://users/{os-user}`,\n * `ws://workspaces/{workspace-dir-name}`,\n * ]\n *\n * Roles + guilds + orgs don't exist locally — single-user, single-\n * workspace machine. Scope-policy in the corpus adapter matches\n * against this minimal tree, so AIP-10 `appliesTo` entries scoped\n * narrower than `ws://users/...` or `ws://workspaces/...` are\n * invisible to the local user. That's the intended local-topology\n * semantics: you only see what's scoped to you or your workspace.\n */\n\nimport os from \"node:os\"\nimport path from \"node:path\"\nimport type {\n CallerIdentity,\n IdentityPort,\n} from \"@agentproto/corpus\"\n\nexport interface OsIdentityAdapterOptions {\n readonly workspaceRoot: string\n /** Override the OS user (useful for tests). Falls back to os.userInfo(). */\n readonly userOverride?: string\n}\n\nexport class OsIdentityAdapter implements IdentityPort {\n private readonly cached: CallerIdentity\n\n constructor(opts: OsIdentityAdapterOptions) {\n // Env vars win over os.userInfo() — matches Unix convention (env is\n // explicit intent, OS detection is fallback). Also keeps tests\n // deterministic across CI environments where the OS user differs.\n const user =\n opts.userOverride ??\n process.env.USER ??\n process.env.USERNAME ??\n safeGetUser() ??\n \"anonymous\"\n const wsName = path.basename(opts.workspaceRoot) || \"default\"\n this.cached = Object.freeze({\n principal: `ws://users/${slugify(user)}`,\n identityTree: Object.freeze([\n `ws://users/${slugify(user)}`,\n `ws://workspaces/${slugify(wsName)}`,\n ]),\n displayName: user,\n })\n }\n\n async resolve(): Promise<CallerIdentity> {\n return this.cached\n }\n}\n\nfunction safeGetUser(): string | undefined {\n try {\n return os.userInfo().username\n } catch {\n return undefined\n }\n}\n\nfunction slugify(s: string): string {\n return s\n .toLowerCase()\n .replace(/[^a-z0-9-]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .replace(/-{2,}/g, \"-\")\n .slice(0, 64) || \"anonymous\"\n}\n"]}
1
+ {"version":3,"sources":["../src/version.ts","../src/ports/local-fs.adapter.ts","../src/ports/os-identity.adapter.ts"],"names":["path"],"mappings":";;;;;;;;;;;;AAYA,SAAS,WAAA,GAAsB;AAC7B,EAAA,IAAI;AACF,IAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,aAAA,CAAc,MAAA,CAAA,IAAA,CAAY,GAAG,CAAC,CAAA;AACnD,IAAA,MAAM,MAAM,IAAA,CAAK,KAAA;AAAA,MACf,YAAA,CAAa,OAAA,CAAQ,IAAA,EAAM,iBAAiB,GAAG,MAAM;AAAA,KACvD;AACA,IAAA,OAAO,IAAI,OAAA,IAAW,OAAA;AAAA,EACxB,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,OAAA;AAAA,EACT;AACF;AAEO,IAAM,UAAkB,WAAA;ACexB,IAAM,gBAAN,MAAsC;AAAA,EAC1B,IAAA;AAAA,EAEjB,YAAY,IAAA,EAA4B;AACtC,IAAA,IAAA,CAAK,OAAO,IAAA,CAAK,IAAA;AAAA,EACnB;AAAA,EAEA,MAAM,OAAO,CAAA,EAA6B;AACxC,IAAA,IAAI;AACF,MAAA,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,CAAQ,CAAC,CAAC,CAAA;AAC1B,MAAA,OAAO,IAAA;AAAA,IACT,CAAA,CAAA,MAAQ;AACN,MAAA,OAAO,KAAA;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAM,SAAS,CAAA,EAA4B;AACzC,IAAA,OAAO,MAAM,QAAA,CAAS,IAAA,CAAK,OAAA,CAAQ,CAAC,GAAG,MAAM,CAAA;AAAA,EAC/C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,SAAA,CAAU,CAAA,EAAW,OAAA,EAAgC;AACzD,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,OAAA,CAAQ,CAAC,CAAA;AAC7B,IAAA,MAAM,KAAA,CAAM,KAAK,OAAA,CAAQ,MAAM,GAAG,EAAE,SAAA,EAAW,MAAM,CAAA;AACrD,IAAA,MAAM,GAAA,GAAM,GAAG,MAAM,CAAA,KAAA,EAAQ,YAAY,CAAC,CAAA,CAAE,QAAA,CAAS,KAAK,CAAC,CAAA,CAAA;AAC3D,IAAA,MAAM,SAAA,CAAU,GAAA,EAAK,OAAA,EAAS,MAAM,CAAA;AACpC,IAAA,MAAM,MAAA,CAAO,KAAK,MAAM,CAAA;AAAA,EAC1B;AAAA,EAEA,MAAM,UAAA,CAAW,CAAA,EAAW,OAAA,EAAgC;AAC1D,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,OAAA,CAAQ,CAAC,CAAA;AAC7B,IAAA,MAAM,KAAA,CAAM,KAAK,OAAA,CAAQ,MAAM,GAAG,EAAE,SAAA,EAAW,MAAM,CAAA;AACrD,IAAA,MAAM,UAAA,CAAW,MAAA,EAAQ,OAAA,EAAS,MAAM,CAAA;AAAA,EAC1C;AAAA,EAEA,MAAM,QAAQ,CAAA,EAAuC;AACnD,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,OAAA,CAAQ,CAAC,CAAA;AAC7B,IAAA,MAAM,OAAA,GAAU,MAAM,OAAA,CAAQ,MAAM,CAAA;AACpC,IAAA,OAAO,OAAA;AAAA,EACT;AAAA,EAEA,MAAM,KAAK,CAAA,EAAuC;AAChD,IAAA,MAAM,MAAgB,EAAC;AACvB,IAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,OAAA,CAAQ,CAAC,CAAA;AAC5B,IAAA,MAAM,eAAA,GAAkB,KAAA;AACxB,IAAA,MAAM,KAAA,GAAQ,OAAO,GAAA,KAA+B;AAClD,MAAA,IAAI,UAAoB,EAAC;AACzB,MAAA,IAAI;AACF,QAAA,OAAA,GAAU,MAAM,QAAQ,GAAG,CAAA;AAAA,MAC7B,CAAA,CAAA,MAAQ;AACN,QAAA;AAAA,MACF;AACA,MAAA,KAAA,MAAW,OAAO,OAAA,EAAS;AAEzB,QAAA,IAAI,GAAA,CAAI,UAAA,CAAW,GAAG,CAAA,EAAG;AACzB,QAAA,MAAM,IAAA,GAAO,IAAA,CAAK,IAAA,CAAK,GAAA,EAAK,GAAG,CAAA;AAC/B,QAAA,MAAM,CAAA,GAAI,MAAM,IAAA,CAAK,IAAI,CAAA;AACzB,QAAA,IAAI,CAAA,CAAE,aAAY,EAAG;AACnB,UAAA,MAAM,MAAM,IAAI,CAAA;AAAA,QAClB,CAAA,MAAA,IAAW,CAAA,CAAE,MAAA,EAAO,EAAG;AACrB,UAAA,MAAM,GAAA,GAAM,IAAA,CAAK,QAAA,CAAS,eAAA,EAAiB,IAAI,CAAA,CAAE,KAAA,CAAM,IAAA,CAAK,GAAG,CAAA,CAAE,IAAA,CAAK,GAAG,CAAA;AACzE,UAAA,GAAA,CAAI,KAAK,GAAG,CAAA;AAAA,QACd;AAAA,MACF;AAAA,IACF,CAAA;AACA,IAAA,MAAM,MAAM,KAAK,CAAA;AACjB,IAAA,OAAO,GAAA;AAAA,EACT;AAAA,EAEA,MAAM,KAAK,CAAA,EAAmC;AAC5C,IAAA,IAAI;AACF,MAAA,MAAM,IAAI,MAAM,IAAA,CAAK,IAAA,CAAK,OAAA,CAAQ,CAAC,CAAC,CAAA;AACpC,MAAA,OAAO;AAAA,QACL,IAAA,EAAM,CAAA,CAAE,WAAA,EAAY,GAAI,WAAA,GAAc,MAAA;AAAA,QACtC,KAAA,EAAO,CAAA,CAAE,MAAA,EAAO,GAAI,EAAE,IAAA,GAAO,KAAA,CAAA;AAAA,QAC7B,YAAY,CAAA,CAAE;AAAA,OAChB;AAAA,IACF,CAAA,CAAA,MAAQ;AACN,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,KAAK,CAAA,EAAkC;AAC3C,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,OAAA,CAAQ,CAAC,CAAA;AAC7B,IAAA,MAAM,UAAU,IAAA,CAAK,IAAA,CAAK,KAAK,OAAA,CAAQ,MAAM,GAAG,eAAe,CAAA;AAC/D,IAAA,MAAM,KAAA,CAAM,OAAA,EAAS,EAAE,SAAA,EAAW,MAAM,CAAA;AACxC,IAAA,MAAM,QAAA,GAAW,KAAK,IAAA,CAAK,OAAA,EAAS,KAAK,QAAA,CAAS,MAAM,IAAI,OAAO,CAAA;AACnE,IAAA,MAAM,OAAA,GAAU,CAAA,EAAG,OAAA,CAAQ,GAAG,CAAA,CAAA,EAAI,YAAY,CAAC,CAAA,CAAE,QAAA,CAAS,KAAK,CAAC,CAAA,CAAA;AAIhE,IAAA,OAAO,IAAA,EAAM;AACX,MAAA,IAAI,CAAC,UAAA,CAAW,QAAQ,CAAA,EAAG;AACzB,QAAA,IAAI;AACF,UAAA,MAAM,SAAA,CAAU,UAAU,OAAA,EAAS,EAAE,UAAU,MAAA,EAAQ,IAAA,EAAM,MAAM,CAAA;AAEnE,UAAA,MAAM,OAAA,GAAU,MAAM,QAAA,CAAS,QAAA,EAAU,MAAM,CAAA,CAAE,KAAA,CAAM,MAAM,EAAE,CAAA;AAC/D,UAAA,IAAI,YAAY,OAAA,EAAS;AAAA,QAC3B,CAAA,CAAA,MAAQ;AAAA,QAER;AAAA,MACF;AACA,MAAA,MAAM,IAAI,OAAA,CAAQ,CAAC,MAAM,UAAA,CAAW,CAAA,EAAG,EAAE,CAAC,CAAA;AAAA,IAC5C;AACA,IAAA,OAAO;AAAA,MACL,SAAS,YAAY;AACnB,QAAA,IAAI;AACF,UAAA,MAAM,EAAA,CAAG,QAAA,EAAU,EAAE,KAAA,EAAO,MAAM,CAAA;AAAA,QACpC,CAAA,CAAA,MAAQ;AAAA,QAER;AAAA,MACF;AAAA,KACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUQ,QAAQ,CAAA,EAAmB;AACjC,IAAA,MAAM,UAAA,GAAa,KAAK,KAAA,CAAM,SAAA,CAAU,MAAM,CAAA,CAAE,OAAA,CAAQ,MAAA,EAAQ,EAAE,CAAC,CAAA;AAEnE,IAAA,MAAM,GAAA,GAAM,UAAA,CAAW,KAAA,CAAM,CAAC,CAAA;AAC9B,IAAA,IAAI,GAAA,CAAI,UAAA,CAAW,IAAI,CAAA,EAAG;AACxB,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,CAAA,qBAAA,EAAwB,CAAC,CAAA,8BAAA,EAAiC,IAAA,CAAK,IAAI,CAAA,CAAA;AAAA,OACrE;AAAA,IACF;AACA,IAAA,OAAO,IAAA,CAAK,IAAA,CAAK,IAAA,CAAK,IAAA,EAAM,GAAG,CAAA;AAAA,EACjC;AACF;ACzJO,IAAM,oBAAN,MAAgD;AAAA,EACpC,MAAA;AAAA,EAEjB,YAAY,IAAA,EAAgC;AAI1C,IAAA,MAAM,IAAA,GACJ,IAAA,CAAK,YAAA,IACL,OAAA,CAAQ,GAAA,CAAI,QACZ,OAAA,CAAQ,GAAA,CAAI,QAAA,IACZ,WAAA,EAAY,IACZ,WAAA;AACF,IAAA,MAAM,MAAA,GAASA,IAAAA,CAAK,QAAA,CAAS,IAAA,CAAK,aAAa,CAAA,IAAK,SAAA;AACpD,IAAA,IAAA,CAAK,MAAA,GAAS,OAAO,MAAA,CAAO;AAAA,MAC1B,SAAA,EAAW,CAAA,WAAA,EAAc,OAAA,CAAQ,IAAI,CAAC,CAAA,CAAA;AAAA,MACtC,YAAA,EAAc,OAAO,MAAA,CAAO;AAAA,QAC1B,CAAA,WAAA,EAAc,OAAA,CAAQ,IAAI,CAAC,CAAA,CAAA;AAAA,QAC3B,CAAA,gBAAA,EAAmB,OAAA,CAAQ,MAAM,CAAC,CAAA;AAAA,OACnC,CAAA;AAAA,MACD,WAAA,EAAa;AAAA,KACd,CAAA;AAAA,EACH;AAAA,EAEA,MAAM,OAAA,GAAmC;AACvC,IAAA,OAAO,IAAA,CAAK,MAAA;AAAA,EACd;AACF;AAEA,SAAS,WAAA,GAAkC;AACzC,EAAA,IAAI;AACF,IAAA,OAAO,EAAA,CAAG,UAAS,CAAE,QAAA;AAAA,EACvB,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,MAAA;AAAA,EACT;AACF;AAEA,SAAS,QAAQ,CAAA,EAAmB;AAClC,EAAA,OAAO,EACJ,WAAA,EAAY,CACZ,QAAQ,cAAA,EAAgB,GAAG,EAC3B,OAAA,CAAQ,UAAA,EAAY,EAAE,CAAA,CACtB,QAAQ,QAAA,EAAU,GAAG,EACrB,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA,IAAK,WAAA;AACrB","file":"index.mjs","sourcesContent":["/**\n * CLI version — derived from the package's own package.json at runtime,\n * so it never drifts from the published version on a release bump.\n *\n * `import.meta.url` resolves to dist/cli.mjs in the published bundle and\n * to src/version.ts under tsx/vitest; in both, package.json sits one\n * directory up. A defensive fallback keeps --version from ever throwing.\n */\nimport { readFileSync } from \"node:fs\"\nimport { dirname, resolve } from \"node:path\"\nimport { fileURLToPath } from \"node:url\"\n\nfunction readVersion(): string {\n try {\n const here = dirname(fileURLToPath(import.meta.url))\n const pkg = JSON.parse(\n readFileSync(resolve(here, \"../package.json\"), \"utf8\")\n ) as { version?: string }\n return pkg.version ?? \"0.0.0\"\n } catch {\n return \"0.0.0\"\n }\n}\n\nexport const VERSION: string = readVersion()\n","/**\n * NodeFsAdapter — `FsPort` implementation backed by node:fs.\n *\n * Atomic writes via temp + rename (the same posix pattern every\n * mature tool uses). Recursive `walk` skips hidden segments\n * (anything beginning with `.`). Advisory locks via lockfiles in\n * `.corpus-locks/` next to the target — single-process safe; for\n * multi-process the lockfile owner pid is recorded so callers can\n * see who holds the lock.\n */\n\nimport { randomBytes } from \"node:crypto\"\nimport {\n appendFile,\n mkdir,\n readFile,\n readdir,\n rename,\n rm,\n stat,\n writeFile,\n} from \"node:fs/promises\"\nimport path from \"node:path\"\nimport { existsSync } from \"node:fs\"\nimport type {\n FsPort,\n FsLockHandle,\n FsStat,\n} from \"@agentproto/corpus\"\n\nexport interface NodeFsAdapterOptions {\n /**\n * Absolute path to the workspace root. All FsPort paths are\n * resolved relative to this — the kit never sees host filesystem\n * paths.\n */\n readonly root: string\n}\n\nexport class NodeFsAdapter implements FsPort {\n private readonly root: string\n\n constructor(opts: NodeFsAdapterOptions) {\n this.root = opts.root\n }\n\n async exists(p: string): Promise<boolean> {\n try {\n await stat(this.resolve(p))\n return true\n } catch {\n return false\n }\n }\n\n async readFile(p: string): Promise<string> {\n return await readFile(this.resolve(p), \"utf8\")\n }\n\n /**\n * Atomic write: write to `${target}.tmp-{nonce}`, fsync, rename.\n * Rename is atomic on the same filesystem. Creates parent dirs as\n * needed (most callers expect mkdir -p semantics).\n */\n async writeFile(p: string, content: string): Promise<void> {\n const target = this.resolve(p)\n await mkdir(path.dirname(target), { recursive: true })\n const tmp = `${target}.tmp-${randomBytes(8).toString(\"hex\")}`\n await writeFile(tmp, content, \"utf8\")\n await rename(tmp, target)\n }\n\n async appendFile(p: string, content: string): Promise<void> {\n const target = this.resolve(p)\n await mkdir(path.dirname(target), { recursive: true })\n await appendFile(target, content, \"utf8\")\n }\n\n async readdir(p: string): Promise<readonly string[]> {\n const target = this.resolve(p)\n const entries = await readdir(target)\n return entries\n }\n\n async walk(p: string): Promise<readonly string[]> {\n const out: string[] = []\n const start = this.resolve(p)\n const rootForRelative = start\n const visit = async (dir: string): Promise<void> => {\n let entries: string[] = []\n try {\n entries = await readdir(dir)\n } catch {\n return\n }\n for (const ent of entries) {\n // Skip hidden segments — matches the FsPort contract.\n if (ent.startsWith(\".\")) continue\n const full = path.join(dir, ent)\n const s = await stat(full)\n if (s.isDirectory()) {\n await visit(full)\n } else if (s.isFile()) {\n const rel = path.relative(rootForRelative, full).split(path.sep).join(\"/\")\n out.push(rel)\n }\n }\n }\n await visit(start)\n return out\n }\n\n async stat(p: string): Promise<FsStat | null> {\n try {\n const s = await stat(this.resolve(p))\n return {\n kind: s.isDirectory() ? \"directory\" : \"file\",\n bytes: s.isFile() ? s.size : undefined,\n modifiedAt: s.mtime,\n }\n } catch {\n return null\n }\n }\n\n /**\n * Advisory lock via a lockfile. Spins waiting for the lockfile to\n * disappear, then creates it with exclusive flag. Released by\n * deleting it. Single-process safe; multi-process semi-safe (the\n * write is best-effort exclusive — adequate for the corpus's\n * promote-entry transaction which is rare and short-lived).\n */\n async lock(p: string): Promise<FsLockHandle> {\n const target = this.resolve(p)\n const lockDir = path.join(path.dirname(target), \".corpus-locks\")\n await mkdir(lockDir, { recursive: true })\n const lockFile = path.join(lockDir, path.basename(target) + \".lock\")\n const myToken = `${process.pid}-${randomBytes(4).toString(\"hex\")}`\n\n // Spin until we acquire. Cheap polling is fine for corpus\n // workloads — transactions are < 1s.\n while (true) {\n if (!existsSync(lockFile)) {\n try {\n await writeFile(lockFile, myToken, { encoding: \"utf8\", flag: \"wx\" })\n // Race: verify our token is the one that landed.\n const written = await readFile(lockFile, \"utf8\").catch(() => \"\")\n if (written === myToken) break\n } catch {\n // Lost the race — fall through and retry.\n }\n }\n await new Promise((r) => setTimeout(r, 25))\n }\n return {\n release: async () => {\n try {\n await rm(lockFile, { force: true })\n } catch {\n // Lockfile already gone — no-op.\n }\n },\n }\n }\n\n // ── Helpers ────────────────────────────────────────────────────\n\n /**\n * Resolve a workspace-relative path to an absolute host path.\n * Refuses to climb out of the workspace root (defense against\n * `../../etc/passwd`-style abuse) — the corpus kit's paths must\n * stay inside the workspace.\n */\n private resolve(p: string): string {\n const normalized = path.posix.normalize(\"/\" + p.replace(/^\\/+/, \"\"))\n // Drop the leading \"/\" so path.join joins correctly under root.\n const rel = normalized.slice(1)\n if (rel.startsWith(\"..\")) {\n throw new Error(\n `NodeFsAdapter: path \"${p}\" escapes the workspace root \"${this.root}\"`\n )\n }\n return path.join(this.root, rel)\n }\n}\n","/**\n * OsIdentityAdapter — `IdentityPort` resolved from OS user + cwd.\n *\n * Local-topology identity model:\n * - principal = `ws://users/{os-user}`\n * - identityTree = [\n * `ws://users/{os-user}`,\n * `ws://workspaces/{workspace-dir-name}`,\n * ]\n *\n * Roles + guilds + orgs don't exist locally — single-user, single-\n * workspace machine. Scope-policy in the corpus adapter matches\n * against this minimal tree, so AIP-10 `appliesTo` entries scoped\n * narrower than `ws://users/...` or `ws://workspaces/...` are\n * invisible to the local user. That's the intended local-topology\n * semantics: you only see what's scoped to you or your workspace.\n */\n\nimport os from \"node:os\"\nimport path from \"node:path\"\nimport type {\n CallerIdentity,\n IdentityPort,\n} from \"@agentproto/corpus\"\n\nexport interface OsIdentityAdapterOptions {\n readonly workspaceRoot: string\n /** Override the OS user (useful for tests). Falls back to os.userInfo(). */\n readonly userOverride?: string\n}\n\nexport class OsIdentityAdapter implements IdentityPort {\n private readonly cached: CallerIdentity\n\n constructor(opts: OsIdentityAdapterOptions) {\n // Env vars win over os.userInfo() — matches Unix convention (env is\n // explicit intent, OS detection is fallback). Also keeps tests\n // deterministic across CI environments where the OS user differs.\n const user =\n opts.userOverride ??\n process.env.USER ??\n process.env.USERNAME ??\n safeGetUser() ??\n \"anonymous\"\n const wsName = path.basename(opts.workspaceRoot) || \"default\"\n this.cached = Object.freeze({\n principal: `ws://users/${slugify(user)}`,\n identityTree: Object.freeze([\n `ws://users/${slugify(user)}`,\n `ws://workspaces/${slugify(wsName)}`,\n ]),\n displayName: user,\n })\n }\n\n async resolve(): Promise<CallerIdentity> {\n return this.cached\n }\n}\n\nfunction safeGetUser(): string | undefined {\n try {\n return os.userInfo().username\n } catch {\n return undefined\n }\n}\n\nfunction slugify(s: string): string {\n return s\n .toLowerCase()\n .replace(/[^a-z0-9-]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\")\n .replace(/-{2,}/g, \"-\")\n .slice(0, 64) || \"anonymous\"\n}\n"]}
@@ -0,0 +1,457 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://agentproto.sh/schemas/aip-10/KNOWLEDGE.schema.json",
4
+ "title": "agentknowledge/v1 frontmatter (AIP-10)",
5
+ "description": "Validates the YAML frontmatter portion of an AIP-10 entry, source, or workspace manifest. The doctype is selected via the `schema` discriminator: 'knowledge.entry/v1' (curated, mutable), 'knowledge.source/v1' (raw, immutable), or 'knowledge.workspace/v1' (workspace manifest or per-context view).",
6
+ "oneOf": [
7
+ { "$ref": "#/$defs/entry" },
8
+ { "$ref": "#/$defs/source" },
9
+ { "$ref": "#/$defs/workspace" }
10
+ ],
11
+ "$defs": {
12
+ "entry": {
13
+ "type": "object",
14
+ "required": ["schema", "slug", "kind", "title", "updated_at"],
15
+ "additionalProperties": false,
16
+ "properties": {
17
+ "schema": {
18
+ "const": "knowledge.entry/v1",
19
+ "description": "Discriminator for curated wiki entries. Mutable doctype."
20
+ },
21
+ "slug": {
22
+ "type": "string",
23
+ "minLength": 2,
24
+ "maxLength": 96,
25
+ "pattern": "^[a-z][a-z0-9-]*[a-z0-9]$",
26
+ "description": "Stable kebab-case identifier. Used as the wikilink target ([[slug]]) and as the entry's path stem."
27
+ },
28
+ "kind": {
29
+ "type": "string",
30
+ "minLength": 2,
31
+ "maxLength": 32,
32
+ "pattern": "^[a-z][a-z0-9-]*$",
33
+ "description": "Entry kind. The wiki's KNOWLEDGE.md (entityTypes) and AGENTS.md schema together define the allowed values; common ones are 'entity', 'concept', 'summary', 'comparison', 'timeline'."
34
+ },
35
+ "title": {
36
+ "type": "string",
37
+ "minLength": 1,
38
+ "maxLength": 200,
39
+ "description": "Human-readable page title."
40
+ },
41
+ "sources": {
42
+ "type": "array",
43
+ "items": {
44
+ "type": "string",
45
+ "minLength": 2,
46
+ "maxLength": 96,
47
+ "pattern": "^[a-z0-9][a-z0-9-]*$",
48
+ "description": "Source id (NOT path). The host resolves ids against the source registry."
49
+ },
50
+ "default": [],
51
+ "description": "Provenance — list of source ids that back the claims in this entry's body."
52
+ },
53
+ "confidence": {
54
+ "type": "number",
55
+ "minimum": 0,
56
+ "maximum": 1,
57
+ "default": 1.0,
58
+ "description": "Curation agent's confidence in the entry's claims. Advisory; downstream consumers may weight it against source authority and recency."
59
+ },
60
+ "updated_at": {
61
+ "type": "string",
62
+ "format": "date-time",
63
+ "description": "ISO 8601 timestamp of the last patch to this entry. MUST be set on every write."
64
+ },
65
+ "supersedes": {
66
+ "type": "array",
67
+ "items": {
68
+ "type": "string",
69
+ "pattern": "^[a-z][a-z0-9-]*[a-z0-9]$"
70
+ },
71
+ "default": [],
72
+ "description": "Slugs of earlier entries that this one replaces. Resolution rules live in the wiki's KNOWLEDGE.md curation.conflictResolution policy."
73
+ },
74
+ "contradicts": {
75
+ "type": "array",
76
+ "items": {
77
+ "type": "string",
78
+ "pattern": "^[a-z][a-z0-9-]*[a-z0-9]$"
79
+ },
80
+ "default": [],
81
+ "description": "Slugs of entries whose claims conflict with this one and that the contradiction policy could not auto-resolve. Surfaces in lint."
82
+ },
83
+ "links": {
84
+ "type": "array",
85
+ "items": {
86
+ "type": "string",
87
+ "pattern": "^[a-z][a-z0-9-]*[a-z0-9]$"
88
+ },
89
+ "default": [],
90
+ "description": "OPTIONAL hint to the link resolver — slugs this entry links to. The resolver also walks the body for [[slug]] and markdown links; this field is for entries whose links are computed."
91
+ },
92
+ "tags": {
93
+ "type": "array",
94
+ "items": {
95
+ "type": "string",
96
+ "pattern": "^[a-z][a-z0-9-]*$"
97
+ },
98
+ "default": [],
99
+ "description": "Free-form tags; consumed by retrieval, search, and grouping in _index.md."
100
+ },
101
+ "metadata": {
102
+ "type": "object",
103
+ "additionalProperties": true,
104
+ "default": {},
105
+ "description": "Vendor-specific extensions. Hosts MUST tolerate unknown keys; the spec's normative fields MUST NOT change meaning."
106
+ }
107
+ }
108
+ },
109
+ "source": {
110
+ "type": "object",
111
+ "required": [
112
+ "schema",
113
+ "id",
114
+ "path",
115
+ "title",
116
+ "captured_at",
117
+ "content_hash"
118
+ ],
119
+ "additionalProperties": false,
120
+ "properties": {
121
+ "schema": {
122
+ "const": "knowledge.source/v1",
123
+ "description": "Discriminator for raw sources. Immutable doctype — once registered, the host MUST refuse mutations to the file's bytes."
124
+ },
125
+ "id": {
126
+ "type": "string",
127
+ "minLength": 2,
128
+ "maxLength": 96,
129
+ "pattern": "^[a-z0-9][a-z0-9-]*$",
130
+ "description": "Stable kebab-case source id, ISO-prefixed when possible (e.g. '2026-04-27-investor-call'). Entries reference sources by id, never by path."
131
+ },
132
+ "path": {
133
+ "type": "string",
134
+ "pattern": "^sources/",
135
+ "description": "Path to the raw bytes, relative to the wiki root. MUST start with 'sources/'. The host pins this file once defineSource succeeds."
136
+ },
137
+ "title": {
138
+ "type": "string",
139
+ "minLength": 1,
140
+ "maxLength": 200,
141
+ "description": "Human-readable source title."
142
+ },
143
+ "captured_at": {
144
+ "type": "string",
145
+ "format": "date-time",
146
+ "description": "ISO 8601 timestamp of when the source entered the wiki."
147
+ },
148
+ "captured_by": {
149
+ "type": "string",
150
+ "maxLength": 200,
151
+ "description": "OPTIONAL — author or operator who pinned the source. May be a user identifier, an agent id, or an upstream system name."
152
+ },
153
+ "content_hash": {
154
+ "type": "string",
155
+ "pattern": "^(sha256|sha512|blake3):[A-Fa-f0-9]+$",
156
+ "description": "Cryptographic hash of the file bytes. sha256 is required by the spec; other algorithms MAY appear in additional sidecar fields. Once set, the file is pinned."
157
+ },
158
+ "authority": {
159
+ "enum": ["primary", "secondary", "rumour"],
160
+ "default": "secondary",
161
+ "description": "Source authority class. 'primary' = first-party document or recording; 'secondary' = derived analysis; 'rumour' = unverified claim. Consumed by contradiction policy."
162
+ },
163
+ "language": {
164
+ "type": "string",
165
+ "pattern": "^[a-z]{2}(-[A-Z]{2})?$",
166
+ "description": "OPTIONAL — BCP-47 language code of the source. Useful for ingest pipelines that pick a translator."
167
+ },
168
+ "superseded_by": {
169
+ "type": "string",
170
+ "pattern": "^[a-z0-9][a-z0-9-]*$",
171
+ "description": "OPTIONAL — id of a newer source that replaces this one. The host writes this; agents do not edit the source file's bytes to 'correct' it. Tombstones the source for contradiction-policy purposes while preserving the original bytes."
172
+ },
173
+ "tags": {
174
+ "type": "array",
175
+ "items": {
176
+ "type": "string",
177
+ "pattern": "^[a-z][a-z0-9-]*$"
178
+ },
179
+ "default": []
180
+ },
181
+ "metadata": {
182
+ "type": "object",
183
+ "additionalProperties": true,
184
+ "default": {},
185
+ "description": "Vendor-specific extensions. Hosts MUST tolerate unknown keys."
186
+ }
187
+ }
188
+ },
189
+ "workspace": {
190
+ "type": "object",
191
+ "required": ["schema", "name", "title", "description", "version"],
192
+ "additionalProperties": false,
193
+ "description": "Workspace manifest doctype. Used both as the root manifest of a wiki (no `extends`) and as a per-consumer view (with `extends`). The same schema validates both modes; the host distinguishes by checking whether `extends` is set.",
194
+ "properties": {
195
+ "schema": {
196
+ "const": "knowledge.workspace/v1",
197
+ "description": "Discriminator for the workspace manifest doctype."
198
+ },
199
+ "name": {
200
+ "type": "string",
201
+ "minLength": 2,
202
+ "maxLength": 96,
203
+ "pattern": "^[a-z][a-z0-9-]*[a-z0-9]$",
204
+ "description": "Stable kebab-case identifier for the workspace or view."
205
+ },
206
+ "title": {
207
+ "type": "string",
208
+ "minLength": 1,
209
+ "maxLength": 200,
210
+ "description": "Human-readable workspace title."
211
+ },
212
+ "description": {
213
+ "type": "string",
214
+ "minLength": 1,
215
+ "maxLength": 2000,
216
+ "description": "One-paragraph statement of purpose: what this workspace (or view) is for and who uses it."
217
+ },
218
+ "version": {
219
+ "type": "string",
220
+ "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+(-[A-Za-z0-9.-]+)?$",
221
+ "description": "Semantic version of the WORKSPACE shape. Bump on entityTypes / lints / source / curation changes. Independent of the wiki's content version."
222
+ },
223
+ "extends": {
224
+ "type": "string",
225
+ "minLength": 1,
226
+ "maxLength": 512,
227
+ "pattern": "^(\\.\\./|\\./)[^\\s]+/KNOWLEDGE\\.md$",
228
+ "description": "OPTIONAL — relative path to a parent KNOWLEDGE.md. Presence of this field makes the manifest a VIEW; absence makes it a WORKSPACE ROOT. Recursive composition: parents may themselves declare `extends`. Maximum chain depth is 8."
229
+ },
230
+ "appliesTo": {
231
+ "type": "array",
232
+ "items": {
233
+ "type": "string",
234
+ "pattern": "^(ws://(operators|companies|skills)/[a-z][a-z0-9-]*|\\.\\./[^\\s]+)$",
235
+ "description": "Either a ws:// ref to an AIP-9 operator / AIP-6 company / AIP-3 skill, or a relative path to a consumer workspace folder."
236
+ },
237
+ "uniqueItems": true,
238
+ "description": "OPTIONAL — list of consumers this VIEW adapts the workspace for. Hosts MUST refuse the view if any binding does not resolve. Not inherited; views declare their own scope."
239
+ },
240
+ "curator": {
241
+ "type": "string",
242
+ "pattern": "^ws://operators/[a-z][a-z0-9-]*$",
243
+ "description": "OPTIONAL — AIP-9 operator that curates this workspace. The host activates this operator for ingest, curation, and lint passes."
244
+ },
245
+ "governance": {
246
+ "type": "string",
247
+ "minLength": 1,
248
+ "maxLength": 512,
249
+ "description": "OPTIONAL — AIP-7 policy or audit binding. May be a path to an AIP-7 policy file or a ws:// ref. Schema-poisoning mitigations and source-mutation audits flow through this ref."
250
+ },
251
+ "entityTypes": {
252
+ "type": "array",
253
+ "items": {
254
+ "type": "object",
255
+ "required": ["name"],
256
+ "additionalProperties": false,
257
+ "properties": {
258
+ "name": {
259
+ "type": "string",
260
+ "pattern": "^[A-Z][A-Za-z0-9]*$",
261
+ "description": "PascalCase type name. Merge key when composing with extends parent."
262
+ },
263
+ "fields": {
264
+ "type": "array",
265
+ "items": {
266
+ "type": "string",
267
+ "pattern": "^[a-z][A-Za-z0-9_]*$"
268
+ },
269
+ "default": [],
270
+ "description": "Canonical fields for this entity type. Child views append fields to parent's set."
271
+ },
272
+ "icon": {
273
+ "type": "string",
274
+ "maxLength": 8,
275
+ "description": "OPTIONAL — display hint, typically a single emoji."
276
+ },
277
+ "description": {
278
+ "type": "string",
279
+ "maxLength": 1000,
280
+ "description": "OPTIONAL — prose describing what entries of this type capture."
281
+ },
282
+ "parent": {
283
+ "type": "string",
284
+ "pattern": "^[A-Z][A-Za-z0-9]*$",
285
+ "description": "OPTIONAL — name of another LOCAL entity type that this one extends. Used for subtyping (e.g. Investor extends Person)."
286
+ }
287
+ }
288
+ },
289
+ "default": [],
290
+ "description": "Entity types this workspace recognizes. Merge-by-name against the extends parent: a child entry with the same name replaces the parent's; new names are appended."
291
+ },
292
+ "lints": {
293
+ "type": "array",
294
+ "items": {
295
+ "type": "object",
296
+ "required": ["id", "kind", "appliesTo", "severity"],
297
+ "additionalProperties": false,
298
+ "properties": {
299
+ "id": {
300
+ "type": "string",
301
+ "pattern": "^[a-z][a-z0-9-]*$",
302
+ "description": "Stable kebab-case lint id. Merge key when composing with extends parent."
303
+ },
304
+ "kind": {
305
+ "enum": [
306
+ "require-source",
307
+ "max-age",
308
+ "min-confidence",
309
+ "broken-ref",
310
+ "orphan",
311
+ "custom"
312
+ ],
313
+ "description": "Lint algorithm. 'custom' delegates to a host-defined check identified by `id`."
314
+ },
315
+ "appliesTo": {
316
+ "type": "string",
317
+ "pattern": "^([A-Z][A-Za-z0-9]*|\\*)$",
318
+ "description": "Entity type name (e.g. 'Concept') or '*' for all entries."
319
+ },
320
+ "severity": {
321
+ "enum": ["error", "warn", "info"],
322
+ "description": "Lint severity. Child views may soften (warn -> info) but governance policies may forbid that."
323
+ },
324
+ "params": {
325
+ "type": "object",
326
+ "additionalProperties": true,
327
+ "default": {},
328
+ "description": "Kind-specific parameters. e.g. { days: 90 } for 'max-age'; { min: 0.6 } for 'min-confidence'."
329
+ }
330
+ }
331
+ },
332
+ "default": [],
333
+ "description": "Lint rules. Merge-by-id against the extends parent: a child entry with the same id replaces the parent's; new ids are appended."
334
+ },
335
+ "sources": {
336
+ "type": "object",
337
+ "additionalProperties": false,
338
+ "properties": {
339
+ "retention": {
340
+ "type": "string",
341
+ "pattern": "^(forever|days:[0-9]+)$",
342
+ "description": "Source retention. 'forever' (default) or 'days:<n>' for time-limited."
343
+ },
344
+ "signing": {
345
+ "enum": ["required", "optional", "none"],
346
+ "description": "Whether sources must carry an AIP-7 signature. Composes with AIP-7 governance."
347
+ },
348
+ "hashAlgo": {
349
+ "enum": ["sha256", "sha512", "blake3"],
350
+ "description": "Default content-hash algorithm for new sources."
351
+ },
352
+ "authorityDefault": {
353
+ "enum": ["primary", "secondary", "rumour"],
354
+ "description": "Default authority assigned to new sources when defineSource omits the field."
355
+ }
356
+ },
357
+ "description": "Source registry policy. Each leaf field overrides independently across the extends chain."
358
+ },
359
+ "curation": {
360
+ "type": "object",
361
+ "additionalProperties": false,
362
+ "properties": {
363
+ "tone": {
364
+ "type": "string",
365
+ "maxLength": 200,
366
+ "description": "Free-form tone hint for the curator agent (e.g. 'academic', 'sales', 'neutral')."
367
+ },
368
+ "depth": {
369
+ "enum": ["shallow", "medium", "deep"],
370
+ "description": "How exhaustive the curator agent should be when distilling sources into entries."
371
+ },
372
+ "autoLink": {
373
+ "enum": ["byName", "manual", "off"],
374
+ "description": "Auto-linking policy: 'byName' walks bodies for entity names and inserts wikilinks; 'manual' leaves links to the agent; 'off' disables auto-linking entirely."
375
+ },
376
+ "conflictResolution": {
377
+ "enum": [
378
+ "defer",
379
+ "recency",
380
+ "authority",
381
+ "observation-count",
382
+ "keep-both"
383
+ ],
384
+ "description": "How the curator agent resolves contradictions when sources disagree."
385
+ },
386
+ "newEntryThreshold": {
387
+ "type": "string",
388
+ "maxLength": 1000,
389
+ "description": "Prose hint for when to promote a mention into a full entry (vs leaving it as a body reference)."
390
+ }
391
+ },
392
+ "description": "Curation policy. Each leaf field overrides independently across the extends chain."
393
+ },
394
+ "queryHints": {
395
+ "type": "object",
396
+ "additionalProperties": false,
397
+ "properties": {
398
+ "preferRecent": {
399
+ "type": "boolean",
400
+ "description": "When ranking results, weight recency higher."
401
+ },
402
+ "preferAuthoritative": {
403
+ "type": "boolean",
404
+ "description": "When ranking results, weight authority='primary' higher."
405
+ },
406
+ "scopeTo": {
407
+ "type": "array",
408
+ "items": {
409
+ "type": "string",
410
+ "pattern": "^[A-Z][A-Za-z0-9]*$"
411
+ },
412
+ "uniqueItems": true,
413
+ "description": "Default query scope — entity types this view focuses on. Replaced wholesale by child if present."
414
+ }
415
+ },
416
+ "description": "Hints for how consumers should retrieve from this view."
417
+ },
418
+ "display": {
419
+ "type": "object",
420
+ "additionalProperties": false,
421
+ "properties": {
422
+ "homePage": {
423
+ "type": "string",
424
+ "pattern": "^[a-z][a-z0-9-]*[a-z0-9]$",
425
+ "description": "OPTIONAL — slug of the entry to use as the workspace landing page."
426
+ },
427
+ "defaultGrouping": {
428
+ "enum": ["kind", "tag", "source"],
429
+ "description": "Default _index.md grouping."
430
+ }
431
+ },
432
+ "description": "Display hints for UIs that render the workspace. Runtime-agnostic."
433
+ },
434
+ "metadata": {
435
+ "type": "object",
436
+ "additionalProperties": true,
437
+ "default": {},
438
+ "description": "Vendor-specific extensions, namespaced under <vendor>. Deep-merged across the extends chain."
439
+ }
440
+ },
441
+ "allOf": [
442
+ {
443
+ "description": "A view (any manifest with appliesTo set) MUST extend a parent. A workspace-root manifest has neither field.",
444
+ "if": {
445
+ "required": ["appliesTo"],
446
+ "properties": {
447
+ "appliesTo": { "type": "array", "minItems": 1 }
448
+ }
449
+ },
450
+ "then": {
451
+ "required": ["extends"]
452
+ }
453
+ }
454
+ ]
455
+ }
456
+ }
457
+ }