@agentproto/corpus 0.1.0-alpha.3 → 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/dist/{chunk-3P23OX5X.mjs → chunk-JV6VRZ6U.mjs} +2 -2
- package/dist/chunk-JV6VRZ6U.mjs.map +1 -0
- package/dist/{chunk-UPX26MYH.mjs → chunk-WF5KGEL6.mjs} +7 -2
- package/dist/chunk-WF5KGEL6.mjs.map +1 -0
- package/dist/index.d.ts +373 -10
- package/dist/index.mjs +362 -38
- package/dist/index.mjs.map +1 -1
- package/dist/report/index.mjs +1 -1
- package/dist/sidecar-WZTZS2I6.mjs +3 -0
- package/dist/{sidecar-TTWQD5R3.mjs.map → sidecar-WZTZS2I6.mjs.map} +1 -1
- package/package.json +4 -4
- package/dist/chunk-3P23OX5X.mjs.map +0 -1
- package/dist/chunk-UPX26MYH.mjs.map +0 -1
- package/dist/sidecar-TTWQD5R3.mjs +0 -3
package/dist/report/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":[],"names":[],"mappings":"","file":"sidecar-
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"sidecar-WZTZS2I6.mjs"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentproto/corpus",
|
|
3
|
-
"version": "0.1.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"description": "@agentproto/corpus — canonical composition of AIP-10 KNOWLEDGE, AIP-12 PLAYBOOK, AIP-18 COLLECTION, AIP-9 OPERATOR, AIP-15 WORKFLOW, AIP-41 ROUTINE into an autonomous knowledge-improvement system. Pure: zero runtime, zero filesystem, zero HTTP — all I/O via injected ports.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agentproto",
|
|
@@ -61,10 +61,10 @@
|
|
|
61
61
|
"zod": "^4.4.3",
|
|
62
62
|
"@agentproto/collection": "0.1.0-alpha.1",
|
|
63
63
|
"@agentproto/knowledge": "0.1.0-alpha.1",
|
|
64
|
-
"@agentproto/registry": "0.1.0-alpha.1",
|
|
65
|
-
"@agentproto/playbook": "0.1.0-alpha.1",
|
|
66
64
|
"@agentproto/operator": "0.1.0-alpha.1",
|
|
67
|
-
"@agentproto/
|
|
65
|
+
"@agentproto/playbook": "0.1.0-alpha.1",
|
|
66
|
+
"@agentproto/registry": "0.1.0-alpha.1",
|
|
67
|
+
"@agentproto/routine": "0.1.0",
|
|
68
68
|
"@agentproto/workflow": "0.1.0-alpha.1"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/distill/types.ts","../src/knowledge/resolve.ts"],"names":["z"],"mappings":"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","file":"chunk-3P23OX5X.mjs","sourcesContent":["/**\n * Distill — raw source → refined knowledge entries (the KNOWLEDGE layer).\n *\n * The \"simple mode\": no graph engine. A refined entry is just an AIP-10\n * `knowledge.entry/v1` markdown file whose `sources: [<id>]` frontmatter\n * IS the `derivedFrom` provenance edge. The filesystem refs ARE the graph;\n * a graph engine (mind-graph / gbrain) is an optional index added later.\n *\n * The kit owns the contract + the runner (what to write, where, with what\n * provenance). The LLM that actually extracts insights is an injected\n * `DistillPort` — host-supplied (Claude/OpenAI), so the kit stays pure.\n */\n\nimport { z } from \"zod\"\n\n/** Generic AIP-10 refined kinds — NOT domain-specific. */\nexport const REFINED_KIND_SCHEMA = z.enum([\n \"principle\",\n \"pattern\",\n \"critique\",\n \"summary\",\n \"example\",\n])\n\nexport type RefinedKind = z.infer<typeof REFINED_KIND_SCHEMA>\n\n/** Runtime guard — narrows an untyped value to RefinedKind without a cast. */\nexport function isRefinedKind(value: unknown): value is RefinedKind {\n return REFINED_KIND_SCHEMA.safeParse(value).success\n}\n\nexport interface DistilledItem {\n readonly kind: RefinedKind\n readonly title: string\n /** The refined insight, markdown. Self-contained, not a quote dump. */\n readonly body: string\n /** 0–1 model confidence. */\n readonly confidence?: number\n readonly tags?: readonly string[]\n}\n\nexport interface DistillInput {\n readonly title: string\n readonly body: string\n readonly tags?: readonly string[]\n /** Hint for the kinds worth extracting (e.g. only principles). */\n readonly kinds?: readonly RefinedKind[]\n}\n\n/**\n * The LLM boundary. Given a raw source, return refined items. Injected by\n * the host (corpus-cli `AnthropicDistiller`, a Guilde operator, …) so the\n * kit takes no model dependency.\n */\nexport interface DistillPort {\n distill(input: DistillInput): Promise<readonly DistilledItem[]>\n}\n","/**\n * resolveKnowledge — the `knowledge:` binding resolver (KNOWLEDGE→SKILL link).\n *\n * Filesystem-first: a skill's binding (`{ tags, kinds }`) is resolved against\n * the refined `entries/**/*.md` on disk — no graph engine. Returns the\n * matching refined entries (with their `sources:` provenance), filtered by the\n * caller's access scope so an operator never sees knowledge above its clearance.\n *\n * A graph engine (mind-graph / gbrain) can later replace this scan with\n * traversal/activation behind the SAME signature — the binding contract is stable.\n */\n\nimport matter from \"gray-matter\"\nimport { z } from \"zod\"\nimport type { FsPort } from \"../ports/fs.port.js\"\nimport { isRefinedKind, type RefinedKind } from \"../distill/types.js\"\n\n/**\n * Lenient zod view of an AIP-10 entry's frontmatter. Every field `.catch`es to\n * undefined so a malformed value degrades gracefully (the entry is still\n * parsed, the bad field is simply absent) — matching the previous defensive\n * hand-narrowing, but typed and cast-free.\n */\nconst ENTRY_FRONTMATTER = z\n .object({\n schema: z.string().optional().catch(undefined),\n slug: z.string().optional().catch(undefined),\n kind: z.string().optional().catch(undefined),\n title: z.string().optional().catch(undefined),\n sources: z.array(z.string()).optional().catch(undefined),\n confidence: z.number().optional().catch(undefined),\n tags: z.array(z.string()).optional().catch(undefined),\n metadata: z\n .object({\n corpus: z\n .object({\n access: z.string().optional().catch(undefined),\n status: z.string().optional().catch(undefined),\n // Resolved provenance written at promote time — the origins this\n // entry was distilled from, so a recall can cite the real source.\n source_refs: z\n .array(\n z\n .object({\n id: z.string(),\n url: z.string().optional().catch(undefined),\n title: z.string().optional().catch(undefined),\n authority: z.string().optional().catch(undefined),\n language: z.string().optional().catch(undefined),\n })\n .loose()\n )\n .optional()\n .catch(undefined),\n })\n .loose()\n .optional()\n .catch(undefined),\n })\n .loose()\n .optional()\n .catch(undefined),\n })\n .loose()\n\nexport interface CorpusEntryQuery {\n /** Match entries sharing ANY of these tags. Empty/absent = no tag filter. */\n readonly tags?: readonly string[]\n /** Restrict to these refined kinds. Empty/absent = all kinds. */\n readonly kinds?: readonly RefinedKind[]\n /** Cap results (highest-confidence first). */\n readonly maxResults?: number\n}\n\n/** A resolved origin an entry was distilled from (id + where to find it). */\nexport interface SourceRef {\n readonly id: string\n readonly url?: string\n readonly title?: string\n readonly authority?: string\n readonly language?: string\n}\n\nexport interface ResolvedEntry {\n readonly slug: string\n readonly kind: string\n readonly title: string\n readonly body: string\n /** Provenance — the raw source ids this entry was derived from. */\n readonly sources: readonly string[]\n /** Resolved provenance — id + url + title for each source, when available. */\n readonly sourceRefs?: readonly SourceRef[]\n readonly confidence: number\n readonly tags: readonly string[]\n readonly access?: string\n readonly path: string\n}\n\nexport interface ResolveKnowledgeOptions {\n readonly fs: FsPort\n readonly query: CorpusEntryQuery\n /**\n * Access scopes the caller (operator) is cleared for. An entry passes if it\n * has no access (public) or its access ∈ this set. Omit = no access filter.\n */\n readonly allowedAccess?: ReadonlySet<string>\n}\n\nexport async function resolveKnowledge(\n opts: ResolveKnowledgeOptions\n): Promise<readonly ResolvedEntry[]> {\n const { fs, query, allowedAccess } = opts\n const wantTags = new Set((query.tags ?? []).map(t => t.toLowerCase()))\n const wantKinds = query.kinds ? new Set(query.kinds) : null\n\n let rels: readonly string[]\n try {\n rels = await fs.walk(\"entries\")\n } catch {\n return []\n }\n\n const hits: ResolvedEntry[] = []\n for (const rel of rels) {\n if (!rel.endsWith(\".md\")) continue\n // `walk(\"entries\")` yields paths relative to the entries/ dir; readFile\n // resolves against the workspace root, so re-prefix.\n const path = `entries/${rel}`\n let parsed\n try {\n parsed = matter(await fs.readFile(path))\n } catch {\n continue\n }\n const fm = ENTRY_FRONTMATTER.parse(parsed.data)\n if (fm.schema !== \"knowledge.entry/v1\") continue\n\n // Tombstone: a guild disables a pack entry by shadowing it (same path) with\n // status \"archived\". Filtered here so the overlaid archived entry hides the\n // pack's. Absent/active = shown — only an explicit archive subtracts.\n if (fm.metadata?.corpus?.status === \"archived\") continue\n\n const kind = fm.kind ?? \"\"\n if (wantKinds && !(isRefinedKind(kind) && wantKinds.has(kind))) continue\n\n const tags = fm.tags ?? []\n if (wantTags.size > 0 && !tags.some(t => wantTags.has(t.toLowerCase()))) continue\n\n const access = fm.metadata?.corpus?.access\n if (allowedAccess && access && !allowedAccess.has(access)) continue\n\n hits.push({\n slug: fm.slug ?? path,\n kind,\n title: fm.title ?? \"\",\n body: parsed.content.trim(),\n sources: fm.sources ?? [],\n ...(fm.metadata?.corpus?.source_refs\n ? { sourceRefs: fm.metadata.corpus.source_refs }\n : {}),\n confidence: fm.confidence ?? 0,\n tags,\n ...(access ? { access } : {}),\n path,\n })\n }\n\n hits.sort((a, b) => b.confidence - a.confidence)\n return query.maxResults !== undefined ? hits.slice(0, query.maxResults) : hits\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/workspace/sidecar.ts"],"names":["yamlParse","yamlStringify"],"mappings":"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","file":"chunk-UPX26MYH.mjs","sourcesContent":["/**\n * CandidatesSidecar — accessor for `collections/<name>/_candidates.yaml`.\n *\n * The plan calls out lazy materialization (so-so #6): the\n * \"discovered\" state has high volume and short lifespan; AIP-18 ITEM\n * files only materialize when a candidate transitions to `analyzed`.\n * Until then, candidates live in a YAML sidecar keyed by id.\n *\n * Sidecar shape:\n * candidates:\n * - id: <slug>\n * status: discovered # may also be transitioning (rare)\n * sourceUrl: ... # collection-specific fields are free-form\n * contentHash: ...\n * discoveredAt: ISO\n * discoveredBy: ws://operators/<slug>\n *\n * The accessor enforces:\n * - id uniqueness within the file\n * - atomic full-file writes (no partial sidecar)\n * - status transition discipline (a row stays here only while\n * `status === \"discovered\"`; transitions out remove the row and\n * return the data so the caller can materialize ITEM.md)\n */\n\nimport { parse as yamlParse, stringify as yamlStringify } from \"yaml\"\nimport type { FsPort } from \"../ports/fs.port.js\"\n\nexport interface CandidateRow {\n readonly id: string\n readonly status: \"discovered\" | string\n /** Open-ended — collection schema's `fields` defines what's allowed. */\n readonly [key: string]: unknown\n}\n\ninterface SidecarShape {\n readonly candidates: readonly CandidateRow[]\n}\n\nexport interface CandidatesSidecarOptions {\n readonly fs: FsPort\n /** Workspace-relative path of the sidecar file. */\n readonly path: string\n}\n\nexport class CandidatesSidecar {\n constructor(private readonly opts: CandidatesSidecarOptions) {}\n\n /**\n * Load every row from disk. Returns an empty list if the file\n * doesn't exist (a fresh corpus has no candidates yet).\n */\n async load(): Promise<readonly CandidateRow[]> {\n if (!(await this.opts.fs.exists(this.opts.path))) return []\n const content = await this.opts.fs.readFile(this.opts.path)\n if (!content.trim()) return []\n const parsed = yamlParse(content) as Partial<SidecarShape> | null\n if (!parsed || !Array.isArray(parsed.candidates)) return []\n return parsed.candidates as readonly CandidateRow[]\n }\n\n /**\n * Append a new candidate. Refuses if `id` already exists — sidecar\n * keys are unique. Returns the full updated list.\n */\n async append(row: CandidateRow): Promise<readonly CandidateRow[]> {\n const existing = await this.load()\n if (existing.some((r) => r.id === row.id)) {\n throw new SidecarDuplicateError(this.opts.path, row.id)\n }\n const next = [...existing, row]\n await this.write(next)\n return next\n }\n\n /**\n * Mutate one row in-place. Throws if not found.\n */\n async update(\n id: string,\n patch: Readonly<Record<string, unknown>>\n ): Promise<CandidateRow> {\n const existing = await this.load()\n let updated: CandidateRow | null = null\n const next = existing.map((r) => {\n if (r.id !== id) return r\n updated = { ...r, ...patch, id: r.id } as CandidateRow\n return updated\n })\n if (!updated) throw new SidecarNotFoundError(this.opts.path, id)\n await this.write(next)\n return updated\n }\n\n /**\n * Remove a row by id. Returns the removed row so the caller can\n * materialize an ITEM.md from its fields. Throws if not found.\n *\n * Use this when a candidate transitions out of `discovered`: pull\n * it from the sidecar, write the ITEM.md, append an event.\n */\n async take(id: string): Promise<CandidateRow> {\n const existing = await this.load()\n const idx = existing.findIndex((r) => r.id === id)\n if (idx === -1) throw new SidecarNotFoundError(this.opts.path, id)\n const removed = existing[idx]!\n const next = [...existing.slice(0, idx), ...existing.slice(idx + 1)]\n await this.write(next)\n return removed\n }\n\n /**\n * Whole-file replace. Used in tests and by the indexer for\n * bulk migrations. Prefer append/update/take for normal flow.\n */\n async write(candidates: readonly CandidateRow[]): Promise<void> {\n const shape: SidecarShape = { candidates }\n const content = yamlStringify(shape, {\n // Stable output: sort keys alphabetically across rows for\n // greppable diffs. Each row's id key is preserved by the\n // stringifier; the rest is dictionary order.\n sortMapEntries: false,\n })\n await this.opts.fs.writeFile(this.opts.path, content)\n }\n}\n\nexport class SidecarDuplicateError extends Error {\n constructor(\n readonly path: string,\n readonly id: string\n ) {\n super(\n `SidecarDuplicateError: candidate id \"${id}\" already exists in ${path}`\n )\n this.name = \"SidecarDuplicateError\"\n }\n}\n\nexport class SidecarNotFoundError extends Error {\n constructor(\n readonly path: string,\n readonly id: string\n ) {\n super(`SidecarNotFoundError: candidate id \"${id}\" not in ${path}`)\n this.name = \"SidecarNotFoundError\"\n }\n}\n"]}
|