@agstack/plugin-sdk
v0.1.0
Published
Foundation SDK for the AGStack plugin ecosystem — interfaces, contracts, types, and base classes for every AGStack plugin
Maintainers
Readme
@agstack/plugin-sdk
Foundation SDK for the AGStack plugin ecosystem — interfaces, contracts, types, and base classes for every AGStack plugin.
Zero runtime dependencies. TypeScript-first. Framework-agnostic.
Features
- Plugin interfaces — 13 typed plugin interfaces (
IPlugin,IStoragePlugin,IAIPlugin,INotificationPlugin,IFrameworkAdapter,ISecurityPlugin,IPayloadPlugin,IDatabaseObserver,IMetricsPlugin,ICloudPlugin,ITracingPlugin,IAnalyticsPlugin,IEnricherPlugin) - Base classes — 9 abstract base classes with lifecycle management (
BasePlugin,BaseStoragePlugin,BaseAIPlugin,BaseNotificationPlugin,BaseSecurityPlugin,BasePayloadPlugin,BaseDatabaseObserver,BaseMetricsPlugin,BaseEnricherPlugin) - Event system — 38 typed runtime events with discriminated union payload map
- Hook system — 15 hook points with typed payload maps
- Error classes — 9 semantic error types with
codeandsuggestionproperties - Versioning — SemVer utilities, version range constraints, compatibility checks
- Validation — Plugin metadata validation, configuration validation, dependency validation
- Transaction model — Full data model:
RawTransaction,EnrichedTransaction,ClientInfo,GeoInfo,PerformanceInfo,SecurityInfo,DatabaseOperationData,ExternalCallData,PayloadInfo - Utilities —
delay,withTimeout,createRetryPolicy,DisposableGroup,generateId,circularSafeStringify,truncate,maskField - Configuration — Typed configs for plugins, storage, AI, notifications, payload, security
- Runtime context —
IRuntimeContextwith event bus, logger, lifecycle, health reporter
Installation
npm install @agstack/plugin-sdkQuick Start
1. Create a plugin
import {
BasePlugin,
type PluginMetadata,
type IRuntimeContext,
type PluginConfiguration,
} from "@agstack/plugin-sdk";
export class MyPlugin extends BasePlugin {
readonly metadata: PluginMetadata = {
name: "@org/my-plugin",
version: "1.0.0",
description: "Does something useful",
type: "custom",
capabilities: ["custom_action"],
priority: 50,
supportedRuntimeVersions: {
pluginSdk: { min: "0.1.0" },
runtime: { min: "0.2.0" },
},
};
async initialize(context: IRuntimeContext, config?: PluginConfiguration): Promise<void> {
await super.initialize(context, config);
this.logger.info("Plugin initialized");
}
async start(): Promise<void> {
await super.start();
this.logger.info("Plugin started");
}
async shutdown(): Promise<void> {
await super.shutdown();
this.logger.info("Plugin stopped");
}
}2. Implement a storage plugin
import { BaseStoragePlugin, type RawTransaction } from "@agstack/plugin-sdk";
export class MyStoragePlugin extends BaseStoragePlugin {
readonly metadata = {
name: "@org/storage",
version: "1.0.0",
description: "Custom storage backend",
type: "storage" as const,
capabilities: ["store", "retrieve"],
priority: 60,
supportedRuntimeVersions: {
pluginSdk: { min: "0.1.0" },
runtime: { min: "0.2.0" },
},
};
private transactions: RawTransaction[] = [];
async save(transaction: unknown): Promise<void> {
this.transactions.push(transaction as RawTransaction);
}
async saveBatch(transactions: unknown[]): Promise<void> {
this.transactions.push(...(transactions as RawTransaction[]));
}
async flush(): Promise<void> {
// Persist to database
}
}3. Validate a plugin
import { validatePluginMetadata, isValidPluginName } from "@agstack/plugin-sdk";
const errors = validatePluginMetadata(myPlugin.metadata);
if (errors.length > 0) {
console.error("Plugin validation failed:", errors);
}
if (!isValidPluginName("@org/my-plugin")) {
console.error("Invalid plugin name format");
}4. Check version compatibility
import { checkPluginCompatibility, AGSTACK_SDK_VERSION } from "@agstack/plugin-sdk";
const result = checkPluginCompatibility(AGSTACK_SDK_VERSION, {
pluginSdk: { min: "0.1.0", max: "1.0.0" },
runtime: { min: "0.2.0" },
});
if (!result.compatible) {
console.error(result.reason);
}Architecture
The plugin-sdk defines the contract between AGStack runtime and all ecosystem plugins:
┌─────────────────────────────────────────────────────┐
│ Runtime Kernel │
│ (@agstack/logger or other runtime) │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌────────────┐ │
│ │ EventBus │ │ Lifecycle │ │ Plugin │ │
│ │ │ │ Manager │ │ Manager │ │
│ └──────────────┘ └──────────────┘ └─────┬──────┘ │
│ │ │
├────────────────────────────────────────────┼──────────┤
│ @agstack/plugin-sdk Contract │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ IPlugin.initialize(context, config) │ │
│ │ IPlugin.start() / .pause() / .resume() │ │
│ │ IPlugin.shutdown() / .dispose() │ │
│ │ IPlugin.health() / .validate() │ │
│ └─────────────────────────────────────────────────┘ │
│ │ │
├────────────────────────────────────────────┼──────────┤
│ Plugin Packages │ │
│ ▼ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Storage │ │ AI │ │ Notif. │ │ Security │ │
│ │ Plugin │ │ Plugin │ │ Plugin │ │ Plugin │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │Payload │ │Metrics │ │Database │ │Enricher │ │
│ │ Plugin │ │ Plugin │ │Observer │ │ Plugin │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────┘Plugin Development Guide
Plugin Lifecycle
Every plugin goes through these states:
created → initializing → initialized → starting → running
→ pausing → paused → resuming → running
→ stopping → stoppedErrors during any phase transition to error state.
Using Base Classes
Extend BasePlugin to get automatic lifecycle management:
| Method | Description |
|--------|-------------|
| initialize() | Sets up context, validates metadata, publishes plugin.initialized event |
| start() | Transitions to running |
| pause() | Transitions to paused |
| resume() | Transitions back to running |
| shutdown() | Transitions to stopped |
| dispose() | Cleans up error state |
| validate() | Checks capabilities are non-empty |
| health() | Returns HealthCheckResult based on current state |
| recordError() | Publishes plugin.error event |
Direct Interface Implementation
If you prefer not to extend base classes:
import type { IPlugin, PluginMetadata, PluginState, IRuntimeContext, PluginConfiguration } from "@agstack/plugin-sdk";
export class MyPlugin implements IPlugin {
readonly metadata: PluginMetadata = { ... };
state: PluginState = "created";
// Implement all IPlugin methods...
}Event System
Publishing Events
import type { EventPayloadMap, RuntimeEventType } from "@agstack/plugin-sdk";
// The runtime publishes these events; plugins subscribe to themSubscribing to Events
Via IRuntimeContext.eventBus:
context.eventBus.subscribe("transaction.created", (event) => {
console.log("New transaction:", event.payload.transactionId);
});
context.eventBus.subscribe("queue.backpressure", (event) => {
console.warn("Queue pressure:", event.payload.queueSize);
}, { filter: (e) => e.payload.queueSize > 1000 });All Event Types
| Event | Payload |
|-------|---------|
| runtime.started | RuntimeStartedPayload |
| runtime.shutdown | RuntimeShutdownPayload |
| runtime.booted | {} |
| runtime.stopping | { reason?: string } |
| runtime.stopped | { uptimeMs: number } |
| runtime.error | { message: string; stack?: string } |
| transaction.created | TransactionCreatedPayload |
| transaction.completed | TransactionCompletedPayload |
| transaction.enriched | TransactionEnrichedPayload |
| transaction.dropped | { transactionId: string; reason: string } |
| queue.enqueued | { transactionId: string; queueSize: number } |
| queue.dequeued | { transactionId: string; queueSize: number } |
| queue.backpressure | QueueBackpressurePayload |
| queue.drained | { itemsProcessed: number; durationMs: number } |
| queue.overflow | { droppedTransactionId?: string } |
| worker.* | Various worker payloads |
| plugin.* | PluginEventPayload |
| enrichment.* | EnrichmentEventPayload |
| storage.* | StorageEventPayload |
| ai.* | AIEventPayload |
| notification.* | NotificationEventPayload |
| hook.* | Hook payloads |
| security.* | SecurityThreatPayload, masked payload |
| configuration.* | config change payloads |
Hook System
Hook Points
15 hook points available: beforeTransaction, afterTransaction, beforeQueue, afterQueue, beforeEnrichment, afterEnrichment, beforeStorage, afterStorage, beforePlugin, afterPlugin, beforeAI, afterAI, beforeNotification, afterNotification, beforeShutdown, afterShutdown, onError.
Implementing Hooks
import { HookEngine } from "@agstack/plugin-sdk";
const engine = new HookEngine();
engine.register("afterTransaction", "my-plugin", (input) => {
console.log("Transaction completed:", input.transactionId);
return { cancelled: false as const };
});Transaction Data Model
RawTransaction
interface RawTransaction {
id: string;
correlationId: string;
traceId: string;
status: TransactionStatus;
request: RawRequestInfo;
response?: RawResponseInfo;
errors: ErrorEventData[];
events: TimelineEventData[];
metadata: Record<string, unknown>;
startedAt: number;
completedAt?: number;
duration?: number;
}EnrichedTransaction
Extends RawTransaction with:
request: EnrichedRequestInfo— addsbodySize,bodyText,bodyHash,bodyTruncated,bodyPreviewclient?: ClientInfo— browser, OS, device, IPgeo?: GeoInfo— city, region, country, ISP, ASN, coordinatesperformance?: PerformanceInfo— memory, CPU, event loop, GCsecurity?: SecurityInfo— masked fields, detected threatspayload?: PayloadInfo— raw, text, hash, preview, classification, threat scoredatabaseOps,externalCalls,pluginResults,aiMetadata,storageMetadata,notificationMetadata
Error Reference
| Error Class | Code | When Thrown |
|-------------|------|-------------|
| ConfigurationError | CONFIGURATION_ERROR | Invalid runtime or plugin configuration |
| PluginError | PLUGIN_ERROR | Plugin-level failures (initialization, runtime, shutdown) |
| ValidationError | VALIDATION_ERROR | Metadata or configuration validation failures |
| InitializationError | INITIALIZATION_ERROR | Component initialization failures |
| ShutdownError | SHUTDOWN_ERROR | Component shutdown failures |
| DependencyError | DEPENDENCY_ERROR | Missing plugin dependencies |
| CompatibilityError | COMPATIBILITY_ERROR | Version incompatibility between plugin and runtime |
| TimeoutError | TIMEOUT_ERROR | Operation exceeded time limit |
| QueueError | QUEUE_ERROR | Queue overflow or backpressure |
Every error class provides a suggestion property with remediation guidance.
Utility Reference
| Function | Description |
|----------|-------------|
| delay(ms) | Promise-based setTimeout |
| withTimeout(promise, ms, name) | Race promise against timeout |
| createRetryPolicy(options) | Exponential backoff with jitter |
| DisposableGroup | Dispose resources in reverse order |
| generateId(prefix?) | UUID v4 with optional prefix |
| circularSafeStringify(value) | JSON.stringify safe for circular refs |
| truncate(str, maxLength) | Truncate with ellipsis |
| maskField(value) | Mask sensitive data (passwords, tokens) |
Retry Policy
const policy = createRetryPolicy({
maxRetries: 3,
baseDelayMs: 100,
maxDelayMs: 10_000,
jitter: true,
});
await policy.execute(async () => {
return await fetch("https://api.example.com/data");
});Configuration Defaults
DEFAULT_RUNTIME_CONFIG
{
queueMaxSize: 10_000,
workerConcurrency: 4,
workerPollIntervalMs: 100,
shutdownTimeoutMs: 30_000,
debug: false,
}DEFAULT_PAYLOAD_CONFIG
{
maxBodySize: 102_400, // 100 KB
previewSize: 1024, // 1 KB preview
captureContentTypes: ["application/json", "application/xml", "text/plain", "application/x-www-form-urlencoded"],
maskFields: ["password", "secret", "token", "jwt", "authorization", "api_key",
"credit_card", "ssn", "otp", "pin", "cvv", "access_token",
"refresh_token", "private_key"],
enableHashing: true,
enableThreatDetection: false,
}DEFAULT_STORAGE_CONFIG
{
retryCount: 3,
batchSize: 100,
flushIntervalMs: 5000,
}Best Practices
Plugin Naming
Use scoped names following the pattern @org/plugin-name:
@agstack/storage-file— File-based storage plugin@agstack/enricher-geo— Geo-enrichment plugin@mycompany/security-scanner— Custom security plugin
Versioning
- Follow SemVer for your plugins
- Use
AGSTACK_SDK_VERSIONto check compatibility with the SDK - Set realistic
supportedRuntimeVersionsconstraints - Use
checkPluginCompatibility()during registration
Error Handling
- Always call
super.initialize()when extendingBasePlugin - Use
recordError()helper for recoverable errors - Provide meaningful
suggestionmessages in custom errors - Clean up resources in
shutdown()— don't rely ondispose()
Performance
- Avoid blocking operations in plugin methods
- Use batches where possible (e.g.,
saveBatch,analyzeBatch) - Respect the configuration's
maxBodySizeandpreviewSize - Register for relevant events only — don't subscribe to everything
API Reference
Exported Types
Interfaces: IPlugin, IStoragePlugin, IAIPlugin, INotificationPlugin, IFrameworkAdapter, ISecurityPlugin, IPayloadPlugin, IDatabaseObserver, IMetricsPlugin, ICloudPlugin, ITracingPlugin, IAnalyticsPlugin, IEnricherPlugin
Configuration: PluginConfiguration, RuntimeConfiguration, StorageConfiguration, AIConfiguration, NotificationConfiguration, NotificationFilter, PayloadConfiguration, SecurityConfiguration, PluginConfigSchema, SchemaProperty, ConfigurationSchema, ConfigurationValidationResult, ConfigurationValidationError
Context: IRuntimeContext, IEventBus, ILogger, ILifecycle, EventHandler
Events: RuntimeEventType, EventPriority, EventMetadata, EventPayloadMap, BusEvent, plus all XxxPayload types
Hooks: HookPoint, HookPriority, HookContext, HookCancellation, HookResult, HookPayloadMap, plus all XxxInput types
Contracts: TransactionStatus, HttpMethod, RawRequestInfo, RawResponseInfo, RawTransaction, EnrichedRequestInfo, EnrichedResponseInfo, ClientInfo, GeoInfo, PerformanceInfo, MemoryUsage, HeapUsage, CPUUsage, GCEventData, DatabaseOperationData, ExternalCallData, SecurityInfo, SecurityThreat, ErrorEventData, TimelineEventData, PayloadInfo, PluginResult, EnrichedTransaction
Metadata: PluginType, PluginMetadata
Version: SemVer, VersionRange, VersionCompatibility, CompatibilityResult, VersionConstraint
Health: HealthStatus, HealthCheckResult, HealthMetrics, HealthError, HealthWarning, HealthReporter
Errors: ConfigurationError, PluginError, ValidationError, InitializationError, ShutdownError, DependencyError, CompatibilityError, TimeoutError, QueueError, SDKError
Exported Values
Classes: BasePlugin, BaseStoragePlugin, BaseAIPlugin, BaseNotificationPlugin, BaseSecurityPlugin, BasePayloadPlugin, BaseDatabaseObserver, BaseMetricsPlugin, BaseEnricherPlugin, ConfigurationError, PluginError, ValidationError, InitializationError, ShutdownError, DependencyError, CompatibilityError, TimeoutError, QueueError
Functions: compareVersions, satisfiesVersion, checkPluginCompatibility, validatePluginMetadata, validatePluginConfiguration, validateVersionCompatibility, validatePluginDependencies, isValidSemVer, isValidPluginName, validateAgainstSchema, delay, withTimeout, createRetryPolicy, DisposableGroup, generateId, circularSafeStringify, truncate, maskField
Constants: AGSTACK_SDK_VERSION, AGSTACK_MIN_RUNTIME_VERSION, PLUGIN_PRIORITY_HIGHEST, PLUGIN_PRIORITY_HIGH, PLUGIN_PRIORITY_NORMAL, PLUGIN_PRIORITY_LOW, PLUGIN_PRIORITY_LOWEST, DEFAULT_RETRY_COUNT, DEFAULT_TIMEOUT_MS, DEFAULT_QUEUE_MAX_SIZE, DEFAULT_HEALTH_INTERVAL_MS, EVENT_PRIORITY_CRITICAL, EVENT_PRIORITY_HIGH, EVENT_PRIORITY_NORMAL, EVENT_PRIORITY_LOW, HOOK_PRIORITY_FIRST, HOOK_PRIORITY_EARLY, HOOK_PRIORITY_NORMAL, HOOK_PRIORITY_LATE, HOOK_PRIORITY_LAST, DEFAULT_PAYLOAD_CONFIG, DEFAULT_STORAGE_CONFIG, DEFAULT_RUNTIME_CONFIG
Migrating from v1.x
If you were using the old plugin system integrated into the logger:
- Install
@agstack/plugin-sdkas a dependency - Change
import { IPlugin } from "@agstack/logger"toimport type { IPlugin } from "@agstack/plugin-sdk" - Change
extends Plugintoextends BasePlugin(or implementIPlugindirectly) - Update metadata to include
supportedRuntimeVersions - Use
validatePluginMetadata()instead of inline validation - Import error classes from
@agstack/plugin-sdkinstead of@agstack/logger
License
MIT
