AI adapter interface

Provider boundaries for optional model calls

Org2's core parsing, formatting, linting, publishing, corpus compilation, query, and manifest validation work without an LLM provider. AI workflows that need model output call a small adapter boundary, keeping vendor SDKs out of the rest of Org2.

The TypeScript contract lives in src/aiAdapter.ts. It defines:

Request contract

An adapter request packages context already selected and prepared by Org2:

{
  schema: "org2:ai-adapter-request:v1",
  jobId: "weekly-summary",
  task: {
    type: "summarize-meeting",
    template: "weekly-summary@v1",
    instructions: "Summarize decisions with citations."
  },
  prompt: [
    { role: "system", content: "Use only the supplied Org2 context." },
    { role: "user", content: "Create a weekly summary." }
  ],
  context: [
    {
      id: "meeting-1",
      type: "org-headline",
      title: "Team Sync",
      text: "Decision: ship the adapter interface first.",
      sourceRefs: [{ file: "notes/team.org", id: "abc", line: 12, endLine: 16 }]
    }
  ],
  output: { contentType: "text+json", schemaHint: "weekly-summary@v1" },
  provenance: { requireSourceRefs: true, promptTemplateVersion: "weekly-summary@v1" }
}

The adapter receives already-curated context. It should not crawl notes, mutate canonical files, or decide review policy.

Response contract

Adapters return text and/or structured JSON plus metadata:

{
  schema: "org2:ai-adapter-response:v1",
  text: "Decisions: ...",
  json: { decisions: [] },
  citations: [{ source: { file: "notes/team.org", line: 12, endLine: 16 } }],
  metadata: {
    adapterName: "work-summary",
    model: "summary-profile",
    provider: "local-or-hosted-provider",
    invocationId: "optional-provider-run-id",
    usage: { inputTokens: 1200, outputTokens: 300 }
  }
}

Job orchestration can write this metadata into generated artifacts without knowing which provider produced it.

Adapter responsibilities

Adapters should:

  • accept AiAdapterRequest and return AiAdapterResponse

  • keep provider credentials in local config/environment, never in notes, manifests, or generated artifacts

  • report provider/model metadata needed for provenance

  • support deterministic mock behavior in tests

  • avoid changing non-AI Org2 command behavior

Adapters should not:

  • modify canonical notes directly

  • embed secrets in response metadata

  • require provider configuration for parser/compiler/lint/publish/query commands

  • bypass manifest review/provenance policies

Mock adapter

MockAiAdapter gives future job orchestration deterministic tests before real providers exist:

import { MockAiAdapter, createAiAdapterRequest } from "./dist/aiAdapter.js";

const adapter = new MockAiAdapter({ name: "local-test", model: "deterministic-v1" });
const response = await adapter.generate(createAiAdapterRequest({
  task: { type: "summarize-meeting" },
  prompt: [{ role: "user", content: "Summarize." }],
  context: [{ id: "a", type: "text", text: "Decision: ship it." }],
  output: { contentType: "text+json" }
}));

The default mock response records calls, echoes task/context counts, and emits citations from supplied context source refs. A custom responder can simulate structured model output, failures, or provider metadata without changing orchestration code.