npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@sentinel-ai-solutions/agent-kit

v0.58.0

Published

Shared agent OS infrastructure for Sentinel AI Solutions' product family (Archer, future agents).

Readme

Sentinel AI Agent Kit

Foundational primitives for building autonomous sub-agent systems

version license


What is it?

@sentinel-ai-solutions/agent-kit is the shared infrastructure layer behind Sentinel AI Solutions' commercial multi-agent platform. It packages the reusable primitives — memory, messaging, tool dispatch, OAuth connections, MCP integration, compliance freshness, cross-agent learning, cost monitoring, pattern analysis, and quality assurance — that any vertical sub-agent can compose rather than rebuild.

The kit ships as a published npm package consumed today by Command Centre (BW Advisory's AI-driven consulting back-office). Within a single deployment the kit maintains strict credential separation enforced at registration time by the tool dispatcher — business-scope agents never hold personal financial credentials, and vice versa. That separation contract (the Trust Contract) is the non-negotiable design principle that runs through every module.

In the commercial product, customer-facing deployments use the same primitive stack: a financial advisory compliance bot composes compliance-freshness + cross-agent-learning + qa-audit; a GP clinic process bot composes connections + proactive-surfacing + relationship-cadence; an accounting AML monitor composes tools + cost-monitoring + compliance-freshness. The kit is the foundation — each vertical sub-agent is an opinionated composition on top of it.

💸 Before you write an LLM call site, read COST-DISCIPLINE.md. Cache stable prompts and tool defs, default to Haiku (Sonnet needs a // COST-JUSTIFICATION: comment), bound conversation history, project cost in every new-producer PR, cap per-customer spend. Per-customer API spend is direct margin — token efficiency is the moat, not a tuning pass. These rules are mandatory for every contributor and are enforced in the consuming product's CI. This expectation also governs the v4 briefing producers and every other producer that fires on a schedule.


Module Inventory

26 named export paths. Every module ships in-memory test adapters so you can write tests without a real database or third-party service.

F0 — Foundational Primitives

| # | Module | Subpath | Shipped | What it gives you | Status | |---|--------|---------|---------|-------------------|--------| | 0 | LLM client | /llm | v0.51 | callModel() / createLlmClient() — the single Anthropic API chokepoint every module uses. Built-in: Haiku default, LlmCostJustificationError for Sonnet/Opus without justification, automatic cache_control: ephemeral on every system prompt, optional SpendGuardHook per-customer cap enforcement, optional LlmSpendTracker ledger recording. MODEL_IDS canonical model-id map. | LIVE | | 1 | AgentMemory | /memory | v0.1 | Six-discriminator record type (EPISODE, PREFERENCE, BELIEF, WORKFLOW, WATCHITEM, CALIBRATION) + typed write/read helpers. Prisma schema ships as copyable schema text. | LIVE | | 2 | Telegram dispatcher | /telegram | v0.2 | Webhook validation, typed bot client, TelegramRouter with handler registration, HTML formatting, PendingProposal<T> for the Yes/Edit/Discard approval keyboard pattern. | LIVE | | 3 | Tool dispatcher | /tools | v0.3 | ToolDefinition<TIn,TOut>, ToolRegistry with credential-scope enforcement at registration time, runWithTools multi-turn Claude loop. | LIVE | | 4 | Photo orchestrator | /photo | v0.4 | processPhoto fetches + persists + runs Claude vision in parallel; returns Zod-validated PhotoExtractionResult<T> with confidence field. MemoryPhotoStorage for tests. | LIVE | | 5 | Voice ingress | /voice | v0.5 | processVoice fetches + persists + runs caller-supplied STTProvider in parallel. Returns typed Transcript with segments. MockSTTProvider for tests. | LIVE | | 6 | Approval flow | /approval | v0.6 | ProposalRecord lifecycle: PENDING → APPROVED / EDITED / DISMISSED / COMPLETED. editProposal uses Haiku to merge free-text corrections. Full ProposalAuditLogger trail. | LIVE | | 7 | Personality framework | /personality | v0.7 | Composable PersonalityDefinition, deterministic buildSystemPrompt, channel-aware tone overrides, runtime calibration adjustments via CALIBRATION memory records. | LIVE |

F0.10 — Proactive Surfacing

| # | Module | Subpath | Shipped | What it gives you | Status | |---|--------|---------|---------|-------------------|--------| | 8 | Proactive Surfacing | /proactive-surfacing | v0.10 | TriggerSource → RelevanceScorer → Debouncer → SurfacingChannel pipeline. Ships RuleBasedRelevanceScorer, LLMAugmentedRelevanceScorer (with 60s result cache), TelegramSurfacingChannel, DashboardSurfacingChannel, CalibrationLearner, rate-limit + debounce primitives, Prisma schemas. Has own README. | LIVE | | 8a | SurfaceConnector | /proactive-surfacing | v0.50 | Unified one-call push primitive every sub-agent inherits. createSurfaceConnector({ prisma, telegram }) → priority routing (CRITICAL → immediate Telegram, HIGH → next briefing + dashboard, MEDIUM/LOW → dashboard only), Trust Contract scope isolation (PERSONAL ↔ BUSINESS never cross channels), dedupe on (agentId + sourceType + dedupeKey) within a 30-day window, Prisma-backed ProactiveSurfacingItem persistence, optional onSurface hook. InMemorySurfaceStore for tests. | LIVE | | 9 | Proactive Surfacing React | /proactive-surfacing/react | v0.15 | React UI helpers for rendering surfacing messages in a portal inbox. Tree-shaken subpath — does not pull React into non-React consumers. | LIVE |

F0.11 — Capability Introspection

| # | Module | Subpath | Shipped | What it gives you | Status | |---|--------|---------|---------|-------------------|--------| | 10 | Capability Introspection | /capability-introspection | v0.50 | The primitive that lets a master agent answer "what can you do for me?" from its own tool registry — no hand-maintained prose list that drifts. Two surfaces: live registry (describeRegistryTools, describeBySubAgent) reads the actual callable tools, so a new tool appears immediately; curated sidecar (describeCapabilities, describeCapability, whatsNew) reads per-tool metadata grouped by consumer-owned categories joined with a growth log. Trust Contract scope filtering built in. natural-language.ts renders either surface to deterministic Markdown — no LLM between the registry and the answer. See MIGRATION.md for the Command Centre / Archer migration guide. | LIVE |

F1 — Connections

| # | Module | Subpath | Shipped | What it gives you | Status | |---|--------|---------|---------|-------------------|--------| | 11 | Connections | /connections | v0.11 | Vendor-agnostic OAuth orchestration. ConnectionManager (connect, complete, getAccessToken, disconnect, list) + PrismaConnectionStore with AES-256-GCM token encryption at rest. 9 providers: Google, Microsoft Graph, Slack, HubSpot, Xero, Notion, GitHub, Linear, Atlassian. Has own README. | LIVE |

F2 — MCP

| # | Module | Subpath | Shipped | What it gives you | Status | |---|--------|---------|---------|-------------------|--------| | 12 | MCP | /mcp | v0.19 | MCPClient + ToolRegistryBridge (v0.19), ResourceProviderBridge for MCP resource reads, MCPLifecycleManager with retry/reconnect, per-tenant MCPManager, MCPObserver for cost/trace observability, reference connectors for filesystem/PostgreSQL/GitHub. | LIVE |

F3 — Cross-Agent Learning

| # | Module | Subpath | Shipped | What it gives you | Status | |---|--------|---------|---------|-------------------|--------| | 13 | Cross-Agent Learning | /cross-agent-learning | v0.35 | ErrorClassifier, ErrorAggregator (deduplication + frequency tracking), FixProposer (Haiku-drafted fixes, approval-gated), CapabilityEmergenceDetector, SkillRecommendationEngine, ImprovementRolloutOrchestrator. ErrorLearningClient for single-call integration. | LIVE |

F4 — Relationship Cadence

| # | Module | Subpath | Shipped | What it gives you | Status | |---|--------|---------|---------|-------------------|--------| | 14 | Relationship Cadence | /relationship-cadence | v0.22 | Silence detectors, cadence policies per vertical (professional services, healthcare, etc.), PolicyInterventionRecommender, ClaudeInterventionDrafter. Composes with F0.6 approval flow — drafts never auto-send. | LIVE |

F5 — Compliance Freshness

| # | Module | Subpath | Shipped | What it gives you | Status | |---|--------|---------|---------|-------------------|--------| | 15 | Compliance Freshness | /compliance-freshness | v0.30 | RegulatorWatcher, DriftDetector, CrossReferenceMap (inter-regulator cascade rules), InterventionDrafter, VersionHistory. Pre-configured with 13 Australian regulatory sources + cross-reference rules. | LIVE |

F6 — Cost Monitoring

| # | Module | Subpath | Shipped | What it gives you | Status | |---|--------|---------|---------|-------------------|--------| | 16 | Cost Monitoring | /cost-monitoring | v0.24 | CostCalculator, DefaultSpendTracker, MarginMonitor (per-customer margin alerts), AnomalyDetector (Opus spikes, cache ratio drops), ModelRecommender (all swaps approval-gated), PricingChangeAdapter. | LIVE |

F7 — Pattern Analysis

| # | Module | Subpath | Shipped | What it gives you | Status | |---|--------|---------|---------|-------------------|--------| | 17 | Pattern Analysis | /pattern-analysis | v0.40 | PatternDetector (six algorithms: temporal, co-occurrence, industry cluster, relationship shift, sentiment trend, cross-channel gap), InsightRanker, DataAggregator, PatternAnalysisTriggerSource (F0.10 adapter), MarketIntelligenceScanner, cross-customer detector with privacy contract enforcement. | LIVE |

F8 — QA Audit

| # | Module | Subpath | Shipped | What it gives you | Status | |---|--------|---------|---------|-------------------|--------| | 18 | QA Audit (Maven) | /qa-audit | v0.52 | MavenAuditor + pluggable QualityRubric<T> system. Ships 7 rubrics: Olive triage, Hermes LinkedIn, cross-customer patterns, Trust Contract (deterministic, cross-cutting subAgentName: "ALL"), Argo concierge, Ledger CFO, Juno calendar. Federation data layer: FederationQualityStore, anonymiseSnapshot(), InMemoryFederationStore (transport deferred). DriftFinding input/cost detection. TrustDashboardViewModel. Composes with F0.10 surfacing. | LIVE |

Content & Voice

| # | Module | Subpath | Shipped | What it gives you | Status | |---|--------|---------|---------|-------------------|--------| | 19 | Hermes | /hermes | v0.35 | LinkedIn content curator + draft writer + engagement watcher. Consumes signals from Vera, Scout, and Pattern Analysis. Every draft goes through F0.6 approval — never auto-posts. Maven-scored on the hermes-linkedin-variant rubric. | LIVE |

Privacy

| # | Module | Subpath | Shipped | What it gives you | Status | |---|--------|---------|---------|-------------------|--------| | 20 | Privacy | /privacy | v0.35 | PII redaction utilities (redactString, redactValue) for cross-agent and federated learning data sharing paths. | LIVE |

Calendar

| # | Module | Subpath | Shipped | What it gives you | Status | |---|--------|---------|---------|-------------------|--------| | 21 | Juno | /juno | v0.53 | Calendar sub-agent (Customer-Layer). Pure, $0 core — composeEventFromHandoff (handoff → write-ready event), routeEventToCalendar (resolve the calendar for a (TrustScope, CalendarScope) pair, fallback-flagged), detectClashes (real overlaps only — all-day markers and back-to-backs never clash). JunoStorage adapter boundary (consumer owns the calendar I/O + writes route by Trust scope). surfaceCalendarProposal (SurfaceConnector / CALENDAR_PROPOSAL). runCalendarHandoff end-to-end orchestrator (write-direct or propose-for-approval). JUNO_PERSONALITY. Opt-in deterministic-first Haiku classifyCalendarScope — the module's only LLM call site. InMemoryJunoStorage for tests. Maven-scored on the juno-clash rubric. | LIVE |

Library

| # | Module | Subpath | Shipped | What it gives you | Status | |---|--------|---------|---------|-------------------|--------| | 22 | Libra | /libra | v0.57 | Artifact-library sub-agent (Customer-Layer) — the customer's document store ("Artifact Library" tab). Pure, $0 core — composeArtifact (raw drop → typed artifact, derives title + dedup contentHash), routeArtifactToDomain (resolve the collection for a (TrustScope, ArtifactDomain) pair, fallback-flagged), detectDuplicates (content-hash → URL → title, strongest-first). LibraStorage adapter boundary (consumer owns file I/O + search; writes route by Trust scope). surfaceArtifactProposal (SurfaceConnector / ARTIFACT_PROPOSAL). runArtifactIngestion end-to-end orchestrator (file-direct or propose-for-approval). LIBRA_PERSONALITY. Opt-in deterministic-first Haiku classifyArtifact — the module's only LLM call site. InMemoryLibraStorage for tests. Distinct from Curator (internal library hygiene). | LIVE |

Email

| # | Module | Subpath | Shipped | What it gives you | Status | |---|--------|---------|---------|-------------------|--------| | 23 | Olive | /olive | v0.58 | Email sub-agent (Customer-Layer) — the biggest extraction. Pure, $0 spine: classifyEmail (deterministic-first — vendor-prior → receipt → keyword → cost-justified Haiku tail), proposeArchiveAction (conservative archive routing + the Olive/Archived/<Category>/<Auto\|Confirmed> label hierarchy; protected mail is never a candidate), detectPatterns (five deterministic detectors — vendor_batch, bill_cluster_due, thread_escalation, response_overdue, new_vendor), aggregateVendorIntel + buildVendorDb (recurring-sender knowledge), computeRuleSnapshots (counts-only federation telemetry), detectReceipt + vendorSimilarity (receipt recognition + reconciliation maths), gateConfidence (the 0.70 auto/review gate). OliveStorage adapter boundary (consumer owns mail/DB I/O + Trust scope routing). Three SurfaceConnector kinds: OLIVE_PROPOSAL (drafts), OLIVE_ALERT (patterns), OLIVE_BILL_FOUND (a Juno calendar handoff). composeDraftReply — cost-justified, cached Sonnet, never auto-sends. runOliveTriage orchestrator. OLIVE_PERSONALITY. InMemoryOliveStorage for tests. Maven-scored on the olive-triage rubric. | LIVE |

Orchestrator

| # | Module | Subpath | Shipped | What it gives you | Status | |---|--------|---------|---------|-------------------|--------| | 24 | Archer | /archer | v0.56 | THE ORCHESTRATOR — the operator's primary AI interface and the pattern that conducts a whole agent fleet. Pure composition of existing primitives, not new machinery: runArcherTurn (one Sonnet-default strategic turn — cached system prompt + /tools runWithTools dispatch + proposal/resolution side-channels), runArcherChat (read-memory → turn → write-episode), composeSystemPrompt (personality + memory digest + live tool roster + consumer live-context; deterministic, $0), proposeFromIntent + the proposal lifecycle (ARCHER_PROPOSAL surfaces — propose-don't-fire), recordEpisode (deterministic $0; opt-in Haiku extractEpisodeSummary), ArcherStorage + InMemoryArcherStorage, ARCHER_PERSONALITY. BUSINESS scope onlyArcherScope is a single-member union; the Personal Agent is separate. | LIVE |

Sub-Agents

agent-kit ships the framework primitives and a growing library of sub-agent modules. The full Sentinel AI sub-agent fleet is described in the Contributor Briefing; some sub-agents currently live in the Sentinel AI Command Centre prototype and are extraction candidates for the kit. Status flags below show what's in agent-kit today vs what's still in the prototype.

Status legend: ✅ shipped as kit module · ⚠️ partial (some primitives in kit, full sub-agent logic in prototype) · 🔨 lives in prototype (Command Centre), extraction candidate · 🔴 concept only.

Pre-built sub-agent modules already in the kit ship under ./src/sub-agents/ (Scout, Vera, Cora), at the top level (/juno, /hermes), and as the capability modules they compose (/compliance-freshness, /relationship-cadence, /qa-audit). Reference implementations and production starting points.

Customer-facing sub-agents

Deploymentper_seat: one instance per operator; shared: one firm-wide instance. See src/sub-agents/scope.ts + ARCHITECTURE.md §5a for the pricing multiplier.

| Sub-agent | Role | Deployment | Kit status | |---|---|---|---| | Olive | Email guru — inbox triage, classification, draft suggestion, vendor categorisation | per_seat | ✅ shipped as kit module (/olive) — Customer-Layer; consumer implements OliveStorage over its mail provider + DB | | Libra | Artifact library — document intake (URL/upload/screenshot/voice/email), domain classification, dedup, retrieval; one-tap filing | per_seat | ✅ shipped as kit module (/libra) — Customer-Layer; consumer implements LibraStorage over its document store. Distinct from Curator (internal library hygiene — retention/supersession — stays CC-side) | | Vera (or Cora) | Regulatory compliance freshness — watches AHPRA, ASIC, ATO, AUSTRAC, Fair Work, NDIS, state regulators | shared | ⚠️ partial — F5 compliance-freshness module in kit (regulator watchers, scanners, citations, cross-ref matrix); full persona pending | | Hermes | LinkedIn content curator + draft writer + engagement watcher; voice-learning drafts, approval-gated | per_seat | ✅ shipped as kit module | | Nyx | Relationship cadence — warm-lead detection, follow-up drafting, channel choice | shared | ✅ shipped as kit module (F4 relationship-cadence) | | Scout | Opportunity scanner — business development, events, target prospects | shared | ✅ shipped as kit module | | Juno | Calendar — event routing, clash detection, Olive bill/invite handoffs, one-tap approval | per_seat | ✅ shipped as kit module (/juno) — Customer-Layer; consumer implements JunoStorage over its calendar | | Pulse | Health (personal scope only) — sleep, HRV, daily Telegram check-in | per_seat | 🔨 lives in prototype — personal-scope, Trust Contract enforced |

Meta-agents

| Sub-agent | Role | Deployment | Kit status | |---|---|---|---| | Archer | Master orchestrator | per_seat | ✅ shipped as kit module (/archer, v0.56) — the orchestrator pattern (chat loop, system-prompt composition, proposal lifecycle, memory wiring) composing the kit primitives; the 108 concrete tools + live-context queries stay CC-side | | Conductor | Cross-agent synthesis + briefings | shared | 🔨 lives in prototype — Layer 1 briefing synthesis pattern is extraction candidate | | Maven | Quality auditor + meta-rubric scoring | shared | ✅ shipped as kit module (qa-audit) — local Maven primitive shipped; Federation Maven future | | Ledger | Cost monitoring per agent / customer / tier | shared | ⚠️ partial — F6 cost-monitoring primitives in kit (calculator, trackers, budget, anomaly); provider admin API integrations (Anthropic, Google, OpenAI admin credit-sync) are per-customer infra, lives in product by design | | Atlas | Efficiency audits + scaffolding + cost feedback | shared | 🔨 lives in prototype — auto-feedback-to-pricing pattern is a kit candidate | | Vigil (proposed) | Business risk + pricing sentinel | shared | 🔴 concept only — design pending Jevon/James input |

Other names in the registry — Lex, Owen, Helix, Tally, Mira, Pippa, Lynnea, SDL — are reserved, scaffolded, or parked at varying stages; see the Contributor Briefing's fleet field guide and glossary for the authoritative per-agent status.


Quick Start

npm install @sentinel-ai-solutions/agent-kit

Peer dependencies (install the ones you use):

npm install @anthropic-ai/sdk              # required for LLM calls
npm install @prisma/client                 # required for Prisma-backed stores
npm install @modelcontextprotocol/sdk      # required for MCP module
npm install react                          # required for proactive-surfacing/react

Minimal example — tool dispatcher + Claude loop

import Anthropic from "@anthropic-ai/sdk";
import { Tools } from "@sentinel-ai-solutions/agent-kit";
import { z } from "zod";

const client = new Anthropic();
const registry = new Tools.ToolRegistry("business");

registry.register({
  name: "get_weather",
  description: "Get the current weather for a city.",
  scope: "public",
  inputSchema: z.object({ city: z.string() }),
  handler: async ({ city }) => ({ temp: 22, condition: "sunny", city }),
});

const result = await Tools.runWithTools({
  client,
  model: "claude-sonnet-4-6",
  system: "You are a helpful assistant.",
  messages: [{ role: "user", content: "What's the weather in Sydney?" }],
  registry,
  ctx: { agentId: "my-agent", agentScope: "business", sessionId: "s1", log: console },
});

console.log(`Done in ${result.iterations} round trips`);

Try the portal quickstart

A full end-to-end demo wiring connections/, proactive-surfacing/, personality/, memory/, and approval/ together:

cd examples/portal-quickstart
npm install
npm start

Only requires ANTHROPIC_API_KEY — all storage uses in-memory adapters.

See examples/ for more reference implementations.


Architecture Overview

The kit is organised in a phase ladder. Each phase adds a new capability tier that higher phases can compose:

  ┌──────────────────────────────────────────────────────────────┐
  │  ORCHESTRATOR  (archer — conducts the fleet)                  │
  │  SUB-AGENTS  (scout · vera · cora · juno · libra · olive)     │
  │  Content Creation  (hermes)                                   │
  │  QA Audit  (maven)                                            │
  └───────────────────────────┬──────────────────────────────────┘
                              │ composes
  ┌───────────────────────────▼──────────────────────────────────┐
  │  INTELLIGENCE              │  COMPLIANCE + COST + LEARNING   │
  │  pattern-analysis          │  compliance-freshness           │
  │  cross-agent-learning      │  cost-monitoring                │
  │  relationship-cadence      │  privacy                        │
  └───────────────────────────┬──────────────────────────────────┘
                              │ composes
  ┌───────────────────────────▼──────────────────────────────────┐
  │  INTEGRATION + SURFACING                                      │
  │  connections (F1)    ·    mcp (F2)                            │
  │  proactive-surfacing (F0.10) + react                          │
  └───────────────────────────┬──────────────────────────────────┘
                              │ composes
  ┌───────────────────────────▼──────────────────────────────────┐
  │  DECISION / DISPATCH                                          │
  │  approval · personality                                       │
  └───────────────────────────┬──────────────────────────────────┘
                              │ composes
  ┌───────────────────────────▼──────────────────────────────────┐
  │  FOUNDATIONAL PRIMITIVES                                      │
  │  memory · tools · telegram · photo · voice                    │
  └──────────────────────────────────────────────────────────────┘

The foundational primitives are deliberately thin — no business logic, no opinionated storage. Each layer up adds orchestration over the layer below. Sub-agents at the top are full implementations that compose the entire stack.

See docs/architecture.md for the full architectural narrative — Trust Contract model, per-tenant MCP, federation pattern, Maven quality loop, and per-module composition guide.


Status

v0.57.0 — all modules in production at Command Centre (BW Advisory), now including Archer the orchestrator (/archer) and Libra the artifact library (/libra). 1735+ tests across the full module surface. Three gates must pass before any merge to main:

npm run typecheck   # tsc --noEmit
npm run build       # tsup → dist/
npm test            # vitest run

All foundational modules (F0 through F0.7) are stable with no breaking API changes since their initial release. Integration-layer modules (F1, F2, F0.10) and intelligence-layer modules (F3–F7) follow semantic versioning — additive releases only within a major version.


Environment Variables

| Variable | Required for | Description | |----------|--------------|-------------| | ANTHROPIC_API_KEY | LLMAugmentedRelevanceScorer, runWithTools, any LLM call | Anthropic API key | | CONNECTION_ENCRYPTION_KEY | PrismaConnectionStore | 32-byte secret for AES-256-GCM token encryption | | GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET | GoogleProvider | OAuth 2.0 credentials from Google Cloud Console | | MICROSOFT_CLIENT_ID / MICROSOFT_CLIENT_SECRET | MicrosoftGraphProvider | Azure AD app registration credentials | | SLACK_CLIENT_ID / SLACK_CLIENT_SECRET | SlackProvider | Slack OAuth app credentials | | HUBSPOT_CLIENT_ID / HUBSPOT_CLIENT_SECRET | HubSpotProvider | HubSpot developer app credentials | | XERO_CLIENT_ID / XERO_CLIENT_SECRET | XeroProvider | Xero developer app credentials | | NOTION_CLIENT_ID / NOTION_CLIENT_SECRET | NotionProvider | Notion public integration credentials | | GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRET | GitHubProvider | GitHub OAuth app credentials | | LINEAR_CLIENT_ID / LINEAR_CLIENT_SECRET | LinearProvider | Linear workspace application credentials | | ATLASSIAN_CLIENT_ID / ATLASSIAN_CLIENT_SECRET | AtlassianProvider | Atlassian developer console credentials | | TELEGRAM_BOT_TOKEN | createTelegramBot | Token from @BotFather | | TELEGRAM_WEBHOOK_SECRET | validateTelegramWebhook | Any random string set as webhook secret |

The examples/portal-quickstart demo only requires ANTHROPIC_API_KEY.


Module Deep-Dives

The following foundational modules have inline documentation and code examples below. For integration-layer and intelligence-layer modules, see the per-module source directories linked in the Module Inventory table.

AgentMemory

import { writeEpisode, findRecentEpisodes } from "@sentinel-ai-solutions/agent-kit/memory";
import { prisma } from "./db"; // consumer's Prisma client

await writeEpisode(prisma, {
  scope: "user:brad",
  payload: { kind: "telegram_message_in", text: "..." },
  source: "telegram",
});

const recent = await findRecentEpisodes(prisma, {
  scope: "user:brad",
  sinceHours: 24,
  limit: 50,
});

src/memory/schema.prisma ships as schema text, not a runnable file. Copy the AgentMemoryRecordType enum and AgentMemoryRecord model into your consumer's prisma/schema.prisma. Each consumer's database holds its own records — schemas are shared, data is isolated.

Telegram dispatcher

import { Telegram } from "@sentinel-ai-solutions/agent-kit";

const bot = Telegram.createTelegramBot({
  token: process.env.TELEGRAM_BOT_TOKEN!,
  webhookSecret: process.env.TELEGRAM_WEBHOOK_SECRET!,
});

const router = new Telegram.TelegramRouter(bot);

router.onSlashCommand("inbox", async (ctx) => {
  await ctx.bot.sendMessage(ctx.chatId, "Reading your inbox…", { parseMode: "HTML" });
});

router.onCallback("approve", async (ctx) => {
  await ctx.bot.answerCallbackQuery(ctx.callbackQueryId, { text: "Saved" });
});

router.onVoice(async (ctx) => {
  const file = await Telegram.downloadTelegramFile(ctx.bot, ctx.voice.file_id);
  // pass to your STT provider
});

export async function POST(req: Request) {
  const body = await req.text();
  const ok = Telegram.validateTelegramWebhook(
    Object.fromEntries(req.headers),
    body,
    process.env.TELEGRAM_WEBHOOK_SECRET!,
  );
  if (!ok) return new Response("Invalid signature", { status: 401 });
  await router.dispatch(JSON.parse(body));
  return new Response("OK");
}

Tool dispatcher

The framework ships the typed-tool shape, the registry, and the multi-turn loop. Domain tools (log_transaction, send_email) live in the consuming product.

import { z } from "zod";
import Anthropic from "@anthropic-ai/sdk";
import { Tools } from "@sentinel-ai-solutions/agent-kit";

// Define a tool
const logTransaction: Tools.ToolDefinition<
  { amountAud: number; category: string; description: string },
  { transactionId: string }
> = {
  name: "log_transaction",
  description: "Record a business expense.",
  scope: "business",
  inputSchema: z.object({
    amountAud: z.number().positive(),
    category: z.string(),
    description: z.string(),
  }),
  handler: async (input, ctx) => {
    const tx = await db.transaction.create({ data: input });
    return { transactionId: tx.id };
  },
};

// Register and run
const registry = new Tools.ToolRegistry("business");
registry.register(logTransaction);
// registry.register(personalScopeTool) would throw ToolScopeMismatchError

const result = await Tools.runWithTools({
  client: new Anthropic(),
  model: "claude-sonnet-4-6",
  system: "You are Archer, a business operations agent.",
  messages: [{ role: "user", content: "Log $42.50 for Adobe Creative Cloud." }],
  registry,
  ctx: { agentId: "archer", agentScope: "business", sessionId: "s1", log: console },
});

The credential-scope check at registration time is the runtime guardrail for the Personal/Business separation contract. See SECURITY.md for the full model.

Photo orchestrator

import { Photo, Telegram } from "@sentinel-ai-solutions/agent-kit";
import Anthropic from "@anthropic-ai/sdk";
import { z } from "zod";

const receiptSchema = z.object({
  vendor: z.string(),
  total: z.number(),
  currency: z.string().length(3),
  date: z.string(),
  confidence: z.number().min(0).max(1),
});

const source: Photo.PhotoSource = {
  channel: "telegram",
  externalId: photo.file_id,
  mimeType: "image/jpeg",
  fetchBytes: () => Telegram.downloadTelegramFile(bot, photo.file_id),
  caption: msg.caption,
};

const result = await Photo.processPhoto({
  source,
  storage: new Photo.MemoryPhotoStorage(),
  vision: { client: new Anthropic(), model: "claude-sonnet-4-6" },
  schema: receiptSchema,
  systemPrompt: "Extract structured receipt data from photos.",
  userPrompt: "Extract vendor, total, currency, date.",
  ctx: { agentId: "archer", userId: "u_brad" },
});

if (result.data && result.confidence > 0.8) {
  await logTransaction(result.data, { photoUrl: result.photoUrl });
}

Voice ingress

import { Voice, Telegram } from "@sentinel-ai-solutions/agent-kit";

const source: Voice.VoiceSource = {
  channel: "telegram-voice",
  externalId: voice.file_id,
  mimeType: "audio/ogg",
  fetchBytes: () => Telegram.downloadTelegramFile(bot, voice.file_id),
  durationSec: voice.duration,
};

const transcript = await Voice.processVoice({
  source,
  storage: new Voice.MemoryVoiceStorage(),
  stt: new YourSTTProvider(), // implements Voice.STTProvider
  language: "en-AU",
  ctx: { agentId: "archer", userId: "u_brad" },
});

await classifyIntent(transcript.text, { audioUrl: transcript.audioUrl });

Use Voice.MockSTTProvider for tests — returns scripted transcripts and records every call.

Approval flow

import Anthropic from "@anthropic-ai/sdk";
import { Approval, Telegram } from "@sentinel-ai-solutions/agent-kit";

const storage: Approval.ProposalStorage<EmailPayload> = makePrismaStorage(prisma);
const audit: Approval.ProposalAuditLogger = {
  record: (event) => writeEpisode(prisma, { scope: `agent:archer:approval`, payload: event, source: "approval-flow" }),
};

// Propose an action
const proposal = await Approval.createProposal({
  storage, audit, ctx,
  record: {
    agentId: "archer",
    userId: "u_brad",
    type: "send_email",
    source: "AGENT_INITIATED",
    title: "Send invoice reminder",
    rationale: "Invoice #42 is 7 days overdue.",
    payload: { to: "[email protected]", subject: "Friendly reminder", body: "…" },
  },
});

const { text, inlineKeyboard } = Approval.buildApprovalMessage(proposal);
await bot.sendMessage(chatId, text, { reply_markup: inlineKeyboard });

// Handle responses
router.onCallback("approve", async (ctx) => {
  const parsed = Approval.parseApprovalCallback(ctx.callbackData);
  if (!parsed) return;
  await Approval.approveProposal({ id: parsed.proposalId, storage, audit, ctx: dispatchCtx, executor: sendEmail });
});

editProposal runs the user's free-text correction through Haiku to merge it with the original payload. Every state transition writes a structured ProposalAuditEvent.

Personality framework

import type { Personality } from "@sentinel-ai-solutions/agent-kit";

const archerPersonality: Personality.PersonalityDefinition = {
  name: "Archer",
  role: "BW Advisory's business operations agent",
  about: "A direct, useful assistant focused on running Brad's consulting business.",
  tone: { register: "professional", energy: "measured", humor: "dry", directness: "direct" },
  style: { sentenceLength: "mixed", listsAllowed: true, emojiPolicy: "never", addressTheUserAs: "Brad", selfReference: "first-person" },
  preferredPhrasing: ["I'd suggest", "happy to dig in"],
  avoidPhrasing: ["as an AI", "I'm just a"],
  doList: ["answer the actual question", "say when you don't know"],
  dontList: ["pad replies with filler", "invent capabilities you don't have"],
  channels: {
    telegram: { style: { sentenceLength: "short", emojiPolicy: "sparingly" } },
    email: { tone: { register: "formal" }, style: { sentenceLength: "medium", listsAllowed: false } },
  },
  version: "1.0.0",
  updatedAt: new Date("2026-05-22T00:00:00Z"),
};

const systemPrompt = Personality.buildSystemPrompt({
  personality: archerPersonality,
  channel: "telegram",
  task: "Reply to the user's message about last week's expenses.",
  recentCalibrations,
  includeCapabilitiesHint: true,
});

buildSystemPrompt is deterministic — same inputs always produce the same output, which keeps it prompt-cache-friendly. Runtime tone corrections are captured as CALIBRATION memory records and layered at composition time, not baked into the stored personality.

Connections module

import {
  ConnectionManager,
  GoogleProvider,
  GoogleScopes,
  MemoryConnectionStore,
} from "@sentinel-ai-solutions/agent-kit/connections";

const manager = new ConnectionManager({
  store: new MemoryConnectionStore(),
  providers: new Map([["google", new GoogleProvider()]]),
  credentials: new Map([["google", { clientId: process.env.GOOGLE_CLIENT_ID!, clientSecret: process.env.GOOGLE_CLIENT_SECRET! }]]),
  defaultRedirectUri: "https://myapp.com/auth/callback",
});

const { authorizationUrl } = await manager.connect("operator_123", "google", {
  scopes: [GoogleScopes.GMAIL_SEND, GoogleScopes.CALENDAR_EVENTS],
});

await manager.completeConnection("operator_123", "google", req.query.code);

// Token refresh happens transparently within 5 min of expiry
const accessToken = await manager.getAccessToken("operator_123", "google");

See src/connections/README.md for provider-specific setup, scope bundles, encryption details, and the add-a-provider guide.

MCP module

import { MCPClient, ToolRegistryBridge } from "@sentinel-ai-solutions/agent-kit/mcp";
import { ToolRegistry } from "@sentinel-ai-solutions/agent-kit/tools";

const registry = new ToolRegistry("business");
const bridge = new ToolRegistryBridge(registry);

const server = new MCPClient({
  name: "my-agent",
  version: "1.0.0",
  transport: { type: "stdio", command: "npx", args: ["-y", "@my-org/mcp-server"] },
});
await server.connect();
await bridge.registerMcpServer(server);

const tools = bridge.toAnthropicTools(); // native + MCP tools as one merged list

See src/mcp/README.md for resource providers, lifecycle management, per-tenant isolation, and reference connectors.

SurfaceConnector

The single push entry point every sub-agent uses. Where the Proactive Surfacing pipeline collects and ranks candidates, SurfaceConnector emits one already-decided surface — routed by priority, scope-isolated, deduped, and persisted in one call.

import {
  createSurfaceConnector,
} from "@sentinel-ai-solutions/agent-kit/proactive-surfacing";
import { PrismaClient } from "@prisma/client";

const connector = createSurfaceConnector({
  prisma: new PrismaClient(),                 // needs the ProactiveSurfacingItem model
  telegram: {
    client: bot,                              // any { sendMessage(chatId, text) }
    personalChatId: PERSONAL_CHAT_ID,
    businessChatId: BUSINESS_CHAT_ID,
  },
  // dedupeWindowDays defaults to 30
  onSurface: async ({ id, channel, deduped }) => {
    // optional sub-agent-specific side effects
  },
});

const { id, channel } = await connector.surface({
  agentId: "vera",
  category: "ALERT",
  priority: "CRITICAL",                       // → immediate Telegram + dashboard
  title: "ASIC RG 271 update affects 3 clients",
  body: "Review the updated guidance before EOFY.",
  attribution: { sourceType: "regulator-watch", sourceId: "asic-rg271" },
  dedupeKey: "asic-rg271-2026-06",            // repeat within 30d → same record, no re-send
  scope: "BUSINESS",                          // never reaches the personal chat
});

await connector.acknowledge(id); // or connector.dismiss(id)

Priority routing. CRITICAL → immediate Telegram message and dashboard record (channel: "BOTH"); HIGH → queued for the next briefing and dashboard ("BOTH", no immediate send); MEDIUM/LOW → dashboard only ("DASHBOARD").

Trust Contract. A PERSONAL surface can only ever resolve to personalChatId and a BUSINESS surface only to businessChatId. If a scope has no configured chat, the immediate Telegram leg degrades to dashboard-only rather than crossing over — it never leaks. scope is persisted on every row so dashboard and briefing jobs filter cleanly.

Idempotency. Dedupe identity is (agentId + attribution.sourceType + dedupeKey) within a rolling 30-day window (configurable). A repeat returns the original record's id and channel; no second Telegram message fires.

Copy the ProactiveSurfacingItem model from src/proactive-surfacing/schema.prisma.example and migrate. For tests and single-process reference use, InMemorySurfaceStore satisfies the same structural contract — no database required.

Juno (calendar)

Juno turns a structured handoff into a calendar event: she composes it, routes it to the right calendar, detects real clashes, and either writes it directly (deterministic due dates) or surfaces it for one-tap approval. The core is pure and $0; the calendar I/O lives behind a JunoStorage adapter the consumer implements.

Two scope axes, kept strictly apart:

  • TrustScope (BUSINESS | PERSONAL) — the credential world. Juno never decides this — it arrives on the handoff and the storage adapter routes the write by it.
  • CalendarScope (FAMILY | TIMETABLE | BILLS | BUSINESS) — which logical calendar. Routing maps a (TrustScope, CalendarScope) pair to a concrete calendar id; this is the kit's only decision, and it is deterministic.
import {
  runCalendarHandoff,
  InMemoryJunoStorage,
  type CalendarHandoff,
  type JunoConfig,
} from "@sentinel-ai-solutions/agent-kit/juno";
import { createSurfaceConnector } from "@sentinel-ai-solutions/agent-kit/proactive-surfacing";

const config: JunoConfig = {
  calendars: {
    PERSONAL: { BILLS: "[email protected]", FAMILY: "c_family@…" },
    BUSINESS: { BUSINESS: "c_work@…" },
  },
  fallbackCalendarId: "primary", // used + flagged when a pair has no mapping
};

// Olive detects a bill and hands the due date to Juno. Deterministic date →
// write directly, no approval (Command Centre's bills path).
const billHandoff: CalendarHandoff = {
  source: "olive-bill",
  scope: "PERSONAL",            // from the mailbox — Juno just honours it
  calendarScope: "BILLS",
  operation: "CREATE",
  title: "Synergy bill — $198.45",
  startISO: "2026-08-20T00:00:00+08:00",
  allDay: true,
  payload: { vendor: "Synergy", amount: 198.45 }, // rides along; the kit never reads it
};

const storage = new InMemoryJunoStorage(); // swap for your Google/Graph adapter
const { route, written } = await runCalendarHandoff(billHandoff, {
  config, storage, mode: "write",
});
// route.usedFallback === false → the consumer knows the Bills calendar is configured.

// An open-ended request instead surfaces a proposal for one-tap approval.
const surface = createSurfaceConnector({ prisma /* ProactiveSurfacingItem */ });
const { clashes, surfaced } = await runCalendarHandoff(
  { source: "nl-request", scope: "BUSINESS", calendarScope: "BUSINESS",
    operation: "CREATE", title: "Coffee with Joy", startISO: "2026-06-18T14:00:00+08:00" },
  { config, storage, surface, mode: "propose" },
);
// A clash makes the surface HIGH priority; a clean proposal is MEDIUM. Juno
// never self-escalates to CRITICAL — a proposed event is not an interrupt.

Pure pieces, used alone. composeEventFromHandoff, routeEventToCalendar, and detectClashes are exported directly — a consumer that wants to own its own dispatch can skip the orchestrator and call them. detectClashes treats all-day markers (term dates, "Book Week") as context, not clashes, and lets back-to-back events pass — the discipline Maven's juno-clash rubric scores.

The one LLM call site. Everything above is deterministic. classifyCalendarScope is the sole opt-in LLM helper (Haiku, deterministic keyword pass first → $0 on the common cases) for consumers that can't determine CalendarScope at the source. It decides the routing axis only — never the Trust Contract scope.

InMemoryJunoStorage satisfies the JunoStorage contract for tests and single-process use — no calendar API required.

Archer (orchestrator)

Archer is the orchestrator — the operator's primary AI interface and the pattern that conducts the whole fleet. She is the first module that is pure composition: the orchestration layer is the chat loop, the system-prompt build, the proposal lifecycle, and the memory wiring, and everything else is a primitive she already had — /tools runWithTools for dispatch, /llm callModel, /personality buildSystemPrompt, /capability-introspection for "what can you do?", SurfaceConnector for approval cards, /memory for episodes.

She is BUSINESS scope only. ArcherScope is a single-member union ("business") so she cannot be stood up against personal credentials — the Personal Agent is a separate orchestrator with its own store.

import { runArcherChat, InMemoryArcherStorage } from "@sentinel-ai-solutions/agent-kit/archer";
import { ToolRegistry } from "@sentinel-ai-solutions/agent-kit/tools";

const tools = new ToolRegistry("business");
tools.registerAll(myBusinessTools);         // your concrete tools (kit-side: the framework; CC-side: the 108)

const result = await runArcherChat({
  session: { id: "s1", userId: "u1", scope: "business", channel: "telegram", history: [] },
  message: "What's outstanding with Gatha, and draft a nudge?",
  tools,
  storage: new InMemoryArcherStorage(),     // or your Prisma adapter
  config: { anthropic, surface: myConnector },
});

result.text;              // Archer's reply
result.pendingApprovals;  // proposals awaiting a one-tap approval — nothing fired

Two cost tiers, each where it belongs. The orchestrator turn defaults to Sonnet — multi-step strategic reasoning across the whole business picture is the canonical cost-justified case. Writing the turn to memory is Haiku (extractEpisodeSummary), and the default episode path is $0 (deterministic, no LLM). The large, stable system prompt is cached so it's paid for once per session.

Propose, don't fire. Every outward action becomes an ArcherProposal surfaced as an ARCHER_PROPOSAL card; only the consumer's executor acts on it once the operator approves. Archer never self-escalates a proposal to CRITICAL.

What stays CC-side. The kit ships the pattern. Command Centre keeps the 108 concrete tools, the Next.js streaming route, and buildLiveContext's 20+ business queries — the consumer hands Archer a ToolRegistry, an ArcherStorage, and a liveContext string. See src/archer/README.md and ARCHITECTURE.md §6d.

Libra (artifact library)

Libra runs the customer's document library. A customer drops something in; she composes it into a typed artifact, classifies its domain, routes it to the right collection, dedups it against the shelf, and either files it directly or surfaces it for one-tap approval. The core is pure and $0; the file I/O + search live behind a LibraStorage adapter the consumer implements. Archer reads from the library Libra curates.

Two scope axes, kept strictly apart (the same discipline as Juno):

  • TrustScope (BUSINESS | PERSONAL) — the credential world. Libra never decides this — it arrives on the intake and the storage adapter routes the write by it.
  • ArtifactDomain (PRICING | COMPLIANCE | METHODOLOGY | ENGAGEMENT | MARKETING | BRAND | OTHER) — the ContentScope: which logical collection. Routing maps a (TrustScope, ArtifactDomain) pair to a concrete collection id; this is the kit's only routing decision, and it is deterministic.

Libra is not Curator. Same store, different question. Libra is the customer-facing intake/retrieval agent — "is this filed and findable?". Curator is the internal library-hygiene agent (canonical/superseded lineage, retention, ATO/ASIC obligations) and stays Sentinel-side.

import {
  runArtifactIngestion,
  InMemoryLibraStorage,
  type LibraConfig,
  type RawArtifactInput,
} from "@sentinel-ai-solutions/agent-kit/libra";
import { createSurfaceConnector } from "@sentinel-ai-solutions/agent-kit/proactive-surfacing";

const config: LibraConfig = {
  collections: {
    BUSINESS: { PRICING: "col_pricing", COMPLIANCE: "col_compliance" },
    PERSONAL: { OTHER: "col_personal_misc" },
  },
  fallbackCollectionId: "library", // used + flagged when a pair has no mapping
};

const storage = new InMemoryLibraStorage(); // swap for your Document/Drive/S3 adapter

// The operator pastes a URL (the consumer already fetched the page text). The
// domain is known at the source, so no LLM is touched — propose for one-tap filing.
const drop: RawArtifactInput = {
  source: "URL",
  url: "https://bwadvisory.com.au/2026-rate-card",
  text: "BW Advisory 2026 rate card — senior day rate $1,950.",
  domain: "PRICING",
};

const surface = createSurfaceConnector({ prisma /* ProactiveSurfacingItem */ });
const { artifact, route, duplicates, surfaced } = await runArtifactIngestion(drop, {
  config, storage, scope: "BUSINESS", surface, // mode defaults to "propose"
});
// route.collectionId === "col_pricing"; a clean artifact surfaces MEDIUM, a
// suspected duplicate surfaces LOW. Libra never escalates to HIGH/CRITICAL.

// A trusted intake the consumer opts into files directly, skipping a re-drop:
const { written } = await runArtifactIngestion(
  { source: "UPLOAD", filename: "rate-card.pdf", text: "BW Advisory 2026 rate card — senior day rate $1,950." },
  { config, storage, scope: "BUSINESS", mode: "write" },
);
// written.wasDuplicate === true — the same content arriving via a different drop
// surface is caught by the normalised content hash; one entry per thing.

Pure pieces, used alone. composeArtifact, routeArtifactToDomain, and detectDuplicates are exported directly — a consumer that wants to own its own dispatch can skip the orchestrator. detectDuplicates matches strongest-first (content hash → URL → title), and dedup is scope-isolated: identical content in PERSONAL vs BUSINESS is not a duplicate.

The one LLM call site. Everything above is deterministic. classifyArtifact is the sole opt-in LLM helper (Haiku, deterministic keyword pass first → $0 on the common cases, bounded excerpt so cost stays flat regardless of file size) for intakes that arrive with no domain. It decides the ContentScope (domain) only — never the Trust scope.

InMemoryLibraStorage satisfies the LibraStorage contract for tests and single-process use — no document API required.

Olive (email)

Olive triages an incoming email, clears plain clutter into a labelled archive (never touching protected mail), recognises receipts and bills, drafts candidate replies in the operator's own voice (and never sends them), and surfaces the cross-email patterns a single message can't show. The core is pure and $0; the mail/DB I/O lives behind an OliveStorage adapter the consumer implements. See src/olive/README.md for the full surface.

One scope axis runs through it — TrustScope (BUSINESS | PERSONAL), the credential world. Olive never decides it: it arrives on the email's mailbox and the storage adapter routes every read and write by it, so a personal email can never touch business knowledge.

import { runOliveTriage, InMemoryOliveStorage } from "@sentinel-ai-solutions/agent-kit/olive";

const storage = new InMemoryOliveStorage(); // swap for your Gmail/Graph + DB adapter

// Triage one email: classify → propose archive → gate on confidence → record.
const r = await runOliveTriage(
  {
    messageId: "m-1",
    from: "Deals <[email protected]>",
    subject: "50% off — shop now",
    scope: "PERSONAL",        // from the mailbox — Olive just honours it
  },
  { storage },                // pass `anthropic` only if the Haiku tail is needed
);
// r.action === "ARCHIVE"  (confident, low-value promo → cleared + labelled)
// r.action === "SURFACE"  (an archive candidate Olive isn't sure about → held for review)
// r.action === "TRIAGE"   (protected / non-archive mail → recorded, untouched)

Deterministic-first classification. classifyEmail resolves the overwhelming majority for $0: a confident vendor prior (a known recurring sender) → receipt recognition → a keyword table. Only the genuinely ambiguous tail reaches Haiku, and only when an Anthropic client is supplied. Everything is cost-justified; Haiku is the floor, never Sonnet/Opus.

Drafts are Sonnet, and proposals only. composeDraftReply is the one Sonnet call site — customer-facing reply generation in the operator's voice, with the voice rules cached as the system prefix and a documented cost justification. It returns a candidate; the consumer surfaces it as an OLIVE_PROPOSAL for one-tap approval. Olive never auto-sends.

Patterns, vendor intel, federation telemetry — wrapped, not duplicated. detectPatterns (five deterministic detectors), aggregateVendorIntel/buildVendorDb, and computeRuleSnapshots are the canonical kit-side implementations of primitives the Command Centre already computed kit-side. The extraction lifts the computation here; the consumer still owns persistence via OliveStorage.

InMemoryOliveStorage satisfies the OliveStorage contract for tests and single-process use — strictly scope-isolated, no mail API required.


Releases

Bump version in package.json as part of any PR. On merge to main, GitHub Actions runs typecheck + tests + build and publishes the new version to npmjs.com, then pushes the matching vX.Y.Z tag. If package.json is not bumped, publish is skipped.


Contributing

See CONTRIBUTING.md for development setup, branch conventions, the PR process, and the sub-agent creation guide.

This is a proprietary package. External contributions are accepted only under a signed Contributor Agreement. Contact the repository owner before starting substantial work.


License

All rights reserved. See LICENSE.

Copyright © 2026 BW Advisory Solutions Pty Ltd / Sentinel AI Solutions Pty Ltd.