@sentinel-ai-solutions/agent-kit
v0.50.0
Published
Shared agent OS infrastructure for Sentinel AI Solutions' product family (Archer, future agents).
Downloads
2,662
Readme
Sentinel AI Agent Kit
Foundational primitives for building autonomous sub-agent systems
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
22 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 |
|---|--------|---------|---------|-------------------|--------|
| 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 |
F1 — Connections
| # | Module | Subpath | Shipped | What it gives you | Status |
|---|--------|---------|---------|-------------------|--------|
| 10 | 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 |
|---|--------|---------|---------|-------------------|--------|
| 11 | 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 |
|---|--------|---------|---------|-------------------|--------|
| 12 | 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 |
|---|--------|---------|---------|-------------------|--------|
| 13 | 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 |
|---|--------|---------|---------|-------------------|--------|
| 14 | 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 |
|---|--------|---------|---------|-------------------|--------|
| 15 | 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 |
|---|--------|---------|---------|-------------------|--------|
| 16 | 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 |
|---|--------|---------|---------|-------------------|--------|
| 17 | QA Audit (Maven) | /qa-audit | v0.45 | MavenAuditor + pluggable QualityRubric<T> system. Ships rubrics for: Olive triage, Hermes LinkedIn variant, cross-customer patterns, Trust Contract leak detection (deterministic, cross-cutting subAgentName: "ALL"). Composes with F0.10 surfacing. | LIVE |
Content & Voice
| # | Module | Subpath | Shipped | What it gives you | Status |
|---|--------|---------|---------|-------------------|--------|
| 18 | 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 |
|---|--------|---------|---------|-------------------|--------|
| 19 | Privacy | /privacy | v0.35 | PII redaction utilities (redactString, redactValue) for cross-agent and federated learning data sharing paths. | 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) and the capability modules they compose (/compliance-freshness, /relationship-cadence, /qa-audit, /hermes). Reference implementations and production starting points.
Customer-facing sub-agents
| Sub-agent | Role | Kit status |
|---|---|---|
| Olive | Email guru — inbox triage, classification, draft suggestion, vendor categorisation | 🔨 lives in prototype (atticus/ legacy folder name) — extraction candidate |
| Libra | Strategic knowledge curator — Universal Artifact model, GDrive + Obsidian connectors, restructure suggestions | 🔨 lives in prototype — extraction candidate (Universal Artifact data model is a strong kit-primitive candidate) |
| Vera (or Cora) | Regulatory compliance freshness — watches AHPRA, ASIC, ATO, AUSTRAC, Fair Work, NDIS, state regulators | ⚠️ 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 | ✅ shipped as kit module |
| Nyx | Relationship cadence — warm-lead detection, follow-up drafting, channel choice | ✅ shipped as kit module (F4 relationship-cadence) |
| Scout | Opportunity scanner — business development, events, target prospects | ✅ shipped as kit module |
| Pulse | Health (personal scope only) — sleep, HRV, daily Telegram check-in | 🔨 lives in prototype — personal-scope, Trust Contract enforced |
Meta-agents
| Sub-agent | Role | Kit status | |---|---|---| | Archer | Master orchestrator | ✅ primitives in kit (memory, telegram, tools, voice, approval, personality); customer-specific persona lives in deployment | | Conductor | Cross-agent synthesis + briefings | 🔨 lives in prototype — Layer 1 briefing synthesis pattern is extraction candidate | | Maven | Quality auditor + meta-rubric scoring | ✅ shipped as kit module (qa-audit) — local Maven primitive shipped; Federation Maven future | | Ledger | Cost monitoring per agent / customer / tier | ⚠️ 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 | 🔨 lives in prototype — auto-feedback-to-pricing pattern is a kit candidate | | Vigil (proposed) | Business risk + pricing sentinel | 🔴 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-kitPeer 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/reactMinimal 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 startOnly 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:
┌──────────────────────────────────────────────────────────────┐
│ SUB-AGENTS (scout · vera · cora) │
│ 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.48.0 — all 22 modules in production at Command Centre (BW Advisory). 95 test files 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 runAll 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 listSee 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.
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.
