Skip to content
Night Owls.dev
Jump to a page

@nightowlsdev/agent-kit

Pre-built agents

The shared foundation for the pre-built agent family: curated skills.sh manifests + governed batch import, fail-loud tool contracts, progressive-disclosure skill-library tools, and persisted-store lifecycle helpers.

What it does

@nightowlsdev/agent-kit is what the pre-built agent packages (builder/researcher/marketer/designer/writer) are made of, and what you use to build your own. A `CuratedSkillSet` manifest pins external skills.sh refs to reviewed snapshot hashes; `importCuratedSkills` batch-imports them through the governed skills seam (default `pinMode: 'strict'`: a drifted upstream is skipped for review, a renamed upstream is rejected in every mode, failures are isolated per ref) and `verifyGrants` makes the silent-miss mode legible (a granted stored name with nothing behind it injects nothing). `skillLibraryTools` solves the 18-skills-without-prompt-explosion problem: an agent holds a big curated library at ~zero prompt cost and reads one skill on demand as a FENCED tool result, scoped to an explicit name list (never whole-tenant). `assertToolRequirements` makes capability contracts fail loud at factory time. `ensurePrebuiltAgent` (boot-safe seed-if-absent) and `publishPrebuiltAgent` (the deliberate upgrade) speak core repo contracts only, the 'evolve once, upgrade downstream' lifecycle on a persisted store.

Install

pnpm add @nightowlsdev/agent-kit

Key exports

  • importCuratedSkills / verifyGrants (strict pins, rename rejection, per-ref isolation)
  • skillLibraryTools (skill_library_list / skill_library_read, fenced, scoped)
  • assertToolRequirements (fail-loud capability contracts)
  • ensurePrebuiltAgent / publishPrebuiltAgent (persisted-store lifecycle)
  • buildAgentSpec / PrebuiltAgentManifest / PrebuiltAgentOpts
  • ALL_PREBUILT_TOOL_NAMES / PREBUILT_READONLY_TOOL_NAMES

Usage

agent-kit.ts
import { importCuratedSkills, verifyGrants, ensurePrebuiltAgent } from "@nightowlsdev/agent-kit";
import { skillsShProvider } from "@nightowlsdev/skills";
import { createResearcher, manifest } from "@nightowlsdev/agent-researcher";

// 1. Import an agent package's curated manifest (strict pins: drift is skipped, renames rejected).
await importCuratedSkills({
  sets: manifest.curatedSkills,
  providers: { "skills.sh": skillsShProvider() },
  storage,                                   // { skills, skillsWritable }, createSupabaseStorage has both
  tenantId,
  actor: { type: "service", serviceId: "seed", tenantId },
});

// 2. Verify the grants resolve (a granted stored name with nothing behind it injects nothing).
const { missing } = await verifyGrants(storage.skills, tenantId, ["deep-research", "research-synthesis"]);

// 3. Seed the factory's agent on a persisted store (publishPrebuiltAgent is the deliberate upgrade).
const researcher = createResearcher({ tools: { webSearch: myWebSearchTool } });
await ensurePrebuiltAgent({
  agents: storage.agents, agentsWritable: storage.agentsWritable,
  def: researcher, tenantId, actor: { type: "service", serviceId: "boot", tenantId },
});

// The full journey (tier config, dynamicSkills, approvals): /docs/adopt-prebuilt-agents

What it provides

agent-kit is the shared toolkit under every pre-built agent package (builder/researcher/marketer/designer/writer/diagnostics) — and what you use to build your own. It gives you curated skills.sh manifests plus a governed batch import (importCuratedSkills / verifyGrants), fail-loud capability contracts (assertToolRequirements), progressive-disclosure skill-library tools (skillLibraryTools), persisted-store lifecycle helpers (ensurePrebuiltAgent / publishPrebuiltAgent), and the one manifest+persona→AgentSpec shape (buildAgentSpec / PrebuiltAgentManifest). It is not itself an agent; it is the machinery a pre-built agent is assembled from.

When to use it

  • You are adopting any pre-built agent — you go through agent-kit to import its curated skills once per tenant and (on a persisted store) seed it.
  • You are authoring your own pre-built agent package and want the same manifest shape, curated-import governance, and library tools the shipped ones use.
  • You want to hold a large curated skill library at ~zero prompt cost and read one skill on demand, fenced and name-scoped.
  • You want capability contracts that fail at factory time with a named missing tool rather than a silently half-configured agent at run time.

When not to

  • You are defining agents inline with defineAgent and need neither external-skill import, a persisted catalog, nor the library-read pattern — core alone is enough.
  • You want to author skill text yourself rather than import pinned external refs — reach for @nightowlsdev/skills directly.
  • You just want a run to execute — agent-kit lands rows in the store; it does not inject them at runtime by itself.

Alternatives

  • @nightowlsdev/skills directlyYou already have your own skill store and grant flow and just want the SkillProvider import primitives, without the curated-manifest, verify, and lifecycle helpers layered on top.
  • defineAgent in @nightowlsdev/coreA single hand-authored agent with inline skills, no external-skill import, no persisted lifecycle, and no on-demand library — the kit's helpers would be unused.

Strengths

  • Strict pins by default: importCuratedSkills runs pinMode 'strict' — a drifted upstream is skipped for review, a rename is rejected in every mode, and per-ref failures are isolated so one bad ref never sinks the batch.
  • verifyGrants makes the silent-miss legible — a granted stored name with nothing behind it injects nothing, and this is the check that surfaces it before a run quietly under-performs.
  • skillLibraryTools solves prompt explosion: a big library held at ~zero prompt cost, one skill read on demand as a FENCED tool result, scoped to an explicit name list (never whole-tenant).
  • Fail-loud contracts: assertToolRequirements lists every missing tool with its purpose at factory time.
  • Boot-safe lifecycle: ensurePrebuiltAgent seeds-if-absent without version churn; publishPrebuiltAgent is the deliberate immutable upgrade, speaking core repo contracts only.

Limits & trade-offs

  • It does not inject skills at runtime — importCuratedSkills only lands rows; you still wire dynamicSkills: materializeSkillStore(...) on defineSwarm or the grants are inert.
  • Library scoping filters by NAME only in v1 — there is no tag-based scoping, so you pass the explicit names you want in scope.
  • The curated import is a per-tenant seed step, not a runtime call — it needs a live SkillProvider (e.g. skillsShProvider()) and a writable skill store.
  • No turnkey eval suites yet (blocked on FR-018).

How it works

A CuratedSkillSet manifest pins external skills.sh refs to reviewed snapshot hashes — the packages ship refs plus pins, never third-party text. importCuratedSkills batch-imports them through the governed @nightowlsdev/skills seam under an actor, isolating per-ref failures and reporting name-mismatch / unpinned statuses; verifyGrants then confirms the granted names actually resolve to stored skills. buildAgentSpec merges a PrebuiltPersona with your PrebuiltAgentOpts into the AgentSpec each factory passes to defineAgent (modelId defaults to 'tier:'). On a persisted store, ensurePrebuiltAgent seeds the definition boot-safely and publishPrebuiltAgent cuts a new immutable version, both over core's repo contracts.

Examples

Import a package's curated skills, then verify the grants

Strict pins by default; verifyGrants surfaces a granted name with nothing behind it.

agent-kit-example-1.ts
import { importCuratedSkills, verifyGrants } from "@nightowlsdev/agent-kit";
import { skillsShProvider } from "@nightowlsdev/skills";
import { manifest } from "@nightowlsdev/agent-researcher";

await importCuratedSkills({
  sets: manifest.curatedSkills,
  providers: { "skills.sh": skillsShProvider() },
  storage,                       // { skills, skillsWritable }
  tenantId,
  actor: { type: "service", serviceId: "seed", tenantId },
});

const { missing } = await verifyGrants(storage.skills, tenantId, ["deep-research"]);
if (missing.length) throw new Error("granted names with nothing behind them: " + missing.join(", "));

Hold a big library at ~zero prompt cost, read on demand

skill_library_list / skill_library_read, scoped to an explicit name list — never whole-tenant.

agent-kit-example-2.ts
import { skillLibraryTools } from "@nightowlsdev/agent-kit";
import { defineAgent } from "@nightowlsdev/core";

const libraryTools = skillLibraryTools(storage.skills, {
  names: ["impeccable", "ui-ux-pro-max", "design-taste-frontend"],
});

const agent = defineAgent({
  slug: "designer",
  role: "specialist",
  personality: "Survey the library, then read only the skills the task needs.",
  skills: libraryTools,
  modelId: "tier:",
});

Seed a factory's agent on a persisted store, boot-safe

ensurePrebuiltAgent is seed-if-absent (no version churn); publishPrebuiltAgent is the deliberate upgrade.

agent-kit-example-3.ts
import { ensurePrebuiltAgent } from "@nightowlsdev/agent-kit";
import { createWriter } from "@nightowlsdev/agent-writer";

await ensurePrebuiltAgent({
  agents: storage.agents,
  agentsWritable: storage.agentsWritable,
  def: createWriter(),
  tenantId,
  actor: { type: "service", serviceId: "boot", tenantId },
});

Doing the parts it doesn't support

  • Injecting the imported skills into a runimportCuratedSkills only lands rows in the store. Wire dynamicSkills: materializeSkillStore(storage.skills) (from @nightowlsdev/skills) on defineSwarm so a granted stored name resolves to instructions at runtime — without it the grant is inert.
  • Tag-based library scopingTag-based scoping is not in v1 — SkillLibraryOpts takes only a names list. Scope skillLibraryTools by an explicit names list; build that list from the manifest's refs if you want a curated subset.
  • Upgrading a seeded agent deliberatelyensurePrebuiltAgent is seed-if-absent. To cut a new immutable version call publishPrebuiltAgent, which leaves rollback available.

Related

  • skillsThe governed skill store + SkillProvider import primitives the curated import runs on; materializeSkillStore injects the grants.
  • coreThe defineAgent / defineSwarm primitives and repo contracts every kit helper speaks.
  • adopt-prebuilt-agentsThe full host-wiring journey (storage, tier config, approvals) the four steps expand into.
  • capability-bundlesPackage a whole crew (agents + grants) for reuse — the bundle layer above the kit.
  • approval-modesWhy PREBUILT_READONLY_TOOL_NAMES go on toolApproval.readOnly so library reads don't suspend.