@almadar/integrations 2.39.0 → 2.41.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/types.ts","../../src/core/logger.ts","../../src/core/validation.ts","../../src/core/retry.ts","../../src/core/BaseIntegration.ts","../../src/mocks/MockIntegration.ts","../../src/registry.ts","../../src/contracts.ts","../../src/factory.ts","../../src/mocks/MockIntegrationFactory.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Core types for Almadar integrations\n */\n\n/**\n * Configuration for an integration instance\n */\nexport interface IntegrationConfig {\n /** Integration name (matches registry) */\n name: string;\n\n /** Environment variables (API keys, secrets) */\n env: Record<string, string>;\n\n /** Optional logger */\n logger?: IntegrationLogger;\n\n /** Optional rate limiting config */\n rateLimit?: {\n requestsPerSecond: number;\n burstSize: number;\n };\n\n /** Optional timeout (ms) */\n timeout?: number;\n\n /** Optional retry config */\n retry?: {\n maxAttempts: number;\n backoffMs: number;\n maxBackoffMs?: number;\n };\n}\n\n/**\n * Result of an integration action call\n */\nexport interface IntegrationResult<T = unknown> {\n /** Success flag */\n success: boolean;\n\n /** Response data (on success) */\n data?: T;\n\n /** Error (on failure) */\n error?: IntegrationError;\n\n /** Metadata (timing, retries, etc.) */\n metadata: {\n integration: string;\n action: string;\n duration: number;\n retries: number;\n timestamp: number;\n };\n}\n\n/**\n * Integration error codes\n */\nexport type IntegrationErrorCode =\n | 'VALIDATION_ERROR'\n | 'AUTH_ERROR'\n | 'RATE_LIMIT_ERROR'\n | 'TIMEOUT_ERROR'\n | 'NETWORK_ERROR'\n | 'SERVICE_ERROR'\n /** The service is known but its credentials/config are absent. */\n | 'NOT_CONFIGURED'\n /** No integration is registered under this name. */\n | 'UNKNOWN_INTEGRATION'\n | 'UNKNOWN_ERROR';\n\n/**\n * Integration error\n */\nexport class IntegrationError extends Error {\n code: IntegrationErrorCode;\n integration?: string;\n action?: string;\n details?: unknown;\n\n constructor(\n message: string,\n code: IntegrationErrorCode = 'UNKNOWN_ERROR',\n details?: unknown,\n ) {\n super(message);\n this.name = 'IntegrationError';\n this.code = code;\n this.details = details;\n }\n\n toJSON() {\n return {\n name: this.name,\n message: this.message,\n code: this.code,\n integration: this.integration,\n action: this.action,\n details: this.details,\n };\n }\n}\n\nimport type { LogMeta } from '@almadar/core';\n\n/** Re-export LogMeta from @almadar/core as the canonical log metadata type. */\nexport type { LogMeta };\n\n/**\n * Logger interface\n */\nexport interface IntegrationLogger {\n debug(message: string, meta?: LogMeta): void;\n info(message: string, meta?: LogMeta): void;\n warn(message: string, meta?: LogMeta): void;\n error(message: string, meta?: LogMeta): void;\n}\n\n/**\n * Integration action parameters.\n * Each integration's execute() method receives params as this type.\n * Individual methods cast to specific param shapes from their contracts.\n *\n * Per-key value union is widened to admit `@almadar/core`'s `FieldValue`\n * shape (entity / payload values flowing into call-service args): `Date`\n * for date fields, `IntegrationParams[]` for nested object arrays, and a\n * raw-value array catch-all so `string[]` / `number[]` / mixed payload\n * arrays satisfy the index signature without requiring `as unknown as`\n * casts at the call site. Adapters narrow these to their canonical input\n * types (typically JSON-string serialisation for `Date`).\n */\nexport type IntegrationParamValue =\n | string\n | number\n | boolean\n | Date\n | null\n | undefined\n | ReadonlyArray<IntegrationParamValue>\n | { readonly [key: string]: IntegrationParamValue };\n\nexport interface IntegrationParams {\n [key: string]: IntegrationParamValue;\n}\n\n/**\n * Validation result\n */\nexport interface ValidationResult {\n valid: boolean;\n errors: ValidationError[];\n}\n\n/**\n * Validation error\n */\nexport interface ValidationError {\n param: string;\n message: string;\n}\n","import { createLogger, type Logger } from '@almadar/logger';\nimport type { IntegrationLogger, LogMeta } from '../types';\n\n/**\n * Console-based logger implementation.\n *\n * Routes through `@almadar/logger`'s shared gate so namespace filtering\n * (`ALMADAR_DEBUG`, `globalThis.__ALMADAR_DEBUG__`) and the production\n * level default (WARN+) apply uniformly with the rest of `@almadar/*`.\n *\n * The constructor's `level` argument is retained for backwards\n * compatibility but is now a no-op — the active level is owned by the\n * shared logger (compile-time + env). To filter integration logs at\n * runtime, set `globalThis.__ALMADAR_DEBUG__ = 'almadar:integrations:*'`.\n */\nexport class ConsoleLogger implements IntegrationLogger {\n private readonly log: Logger;\n\n constructor(_level: 'debug' | 'info' | 'warn' | 'error' = 'info') {\n void _level;\n this.log = createLogger('almadar:integrations');\n }\n\n debug(message: string, meta?: LogMeta): void {\n this.log.debug(message, meta);\n }\n info(message: string, meta?: LogMeta): void {\n this.log.info(message, meta);\n }\n warn(message: string, meta?: LogMeta): void {\n this.log.warn(message, meta);\n }\n error(message: string, meta?: LogMeta): void {\n this.log.error(message, meta);\n }\n}\n","import type { ValidationResult, ValidationError, IntegrationParams } from '../types';\n\n// Import integrators registry from the package main export (JSON is inlined in the bundle)\nimport { integratorsRegistry } from '@almadar/core/patterns';\n\ninterface ActionParam {\n name: string;\n type: string;\n required?: boolean;\n description?: string;\n}\n\ninterface ActionDef {\n name: string;\n description?: string;\n params: ActionParam[];\n}\n\ninterface IntegratorEntry {\n name: string;\n description?: string;\n category?: string;\n actions: ActionDef[];\n}\n\ntype IntegratorsRegistry = Record<string, {\n version?: string;\n exportedAt?: string;\n integrators: Record<string, IntegratorEntry>;\n}>;\n\n/** Envelope keys owned by the call-service form, never action params. */\nconst RESERVED_ENVELOPE_KEYS = new Set(['emit', 'onSuccess', 'onError', 'timeout']);\n\n/** Thrown by an integration's dispatch default/miss arm — contract↔dispatch parity marker. */\nexport class UnknownActionError extends Error {\n integration: string;\n action: string;\n\n constructor(integration: string, action: string) {\n super(`Unknown ${integration} action: ${action}`);\n this.name = 'UnknownActionError';\n this.integration = integration;\n this.action = action;\n }\n}\n\n/**\n * Validate action params against registry schema\n */\nexport function validateParams(\n integration: string,\n action: string,\n params: IntegrationParams,\n): ValidationResult {\n const typedRegistry = integratorsRegistry as IntegratorsRegistry[string];\n const registry = typedRegistry.integrators[integration];\n\n if (!registry) {\n return {\n valid: false,\n errors: [\n {\n param: 'integration',\n message: `Unknown integration: ${integration}`,\n },\n ],\n };\n }\n\n const actionDef = registry.actions.find((a: ActionDef) => a.name === action);\n\n if (!actionDef) {\n return {\n valid: false,\n errors: [{ param: 'action', message: `Unknown action: ${action}` }],\n };\n }\n\n const errors: ValidationError[] = [];\n\n // Unknown-key check (I-13 runtime parity with orb validate): a param the\n // contract does not declare is an error, not a silent drop. Envelope keys\n // belong to the call-service form itself, never to the action contract.\n const declaredNames = new Set(actionDef.params.map((p: ActionParam) => p.name));\n for (const key of Object.keys(params)) {\n if (RESERVED_ENVELOPE_KEYS.has(key)) continue;\n if (!declaredNames.has(key)) {\n errors.push({\n param: key,\n message: `Unknown parameter: ${key} (declared: ${[...declaredNames].sort().join(', ')})`,\n });\n }\n }\n\n // Check required params. A key holding `undefined` is an absent param: a\n // service atom sends every declared param and an unset one arrives undefined.\n for (const paramDef of actionDef.params) {\n const provided = params[paramDef.name] !== undefined;\n if (paramDef.required && !provided) {\n errors.push({\n param: paramDef.name,\n message: `Missing required parameter: ${paramDef.name}`,\n });\n }\n\n // Type validation\n if (provided) {\n const value = params[paramDef.name];\n const expectedType = paramDef.type;\n const actualType = typeof value;\n\n if (expectedType === 'number' && actualType !== 'number') {\n errors.push({\n param: paramDef.name,\n message: `Expected ${expectedType}, got ${actualType}`,\n });\n }\n\n if (expectedType === 'string' && actualType !== 'string') {\n errors.push({\n param: paramDef.name,\n message: `Expected ${expectedType}, got ${actualType}`,\n });\n }\n\n if (expectedType === 'array' && !Array.isArray(value)) {\n errors.push({\n param: paramDef.name,\n message: `Expected array, got ${actualType}`,\n });\n }\n\n if (\n expectedType === 'object' &&\n (actualType !== 'object' || Array.isArray(value) || value === null)\n ) {\n errors.push({\n param: paramDef.name,\n message: `Expected object, got ${actualType}`,\n });\n }\n }\n }\n\n return {\n valid: errors.length === 0,\n errors,\n };\n}\n","import type { IntegrationError, IntegrationErrorCode } from '../types';\n\n/**\n * Retry configuration\n */\nexport interface RetryConfig {\n maxAttempts: number;\n backoffMs: number;\n maxBackoffMs?: number;\n retryableErrors?: IntegrationErrorCode[];\n}\n\n/**\n * Execute a function with retry logic\n */\nexport async function withRetry<T>(\n fn: () => Promise<T>,\n config: RetryConfig,\n): Promise<T> {\n const {\n maxAttempts,\n backoffMs,\n maxBackoffMs = 30000,\n retryableErrors,\n } = config;\n\n let lastError: unknown;\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n return await fn();\n } catch (error) {\n lastError = error;\n\n // Check if error is retryable\n if (\n error &&\n typeof error === 'object' &&\n 'code' in error &&\n retryableErrors\n ) {\n const integrationError = error as IntegrationError;\n if (!retryableErrors.includes(integrationError.code)) {\n throw error;\n }\n }\n\n // Last attempt, throw\n if (attempt === maxAttempts) {\n throw error;\n }\n\n // Wait before retry (exponential backoff)\n const delay = Math.min(\n backoffMs * Math.pow(2, attempt - 1),\n maxBackoffMs,\n );\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n\n throw lastError;\n}\n","import type {\n IntegrationConfig,\n IntegrationResult,\n IntegrationLogger,\n IntegrationParams,\n ValidationError,\n} from '../types';\nimport { IntegrationError } from '../types';\nimport { ConsoleLogger } from './logger';\nimport { validateParams } from './validation';\nimport { withRetry } from './retry';\nimport type { IntegrationCallContext } from '../factory';\n\n/**\n * Base class for all integrations\n */\nexport abstract class BaseIntegration {\n protected config: IntegrationConfig;\n protected logger: IntegrationLogger;\n\n constructor(config: IntegrationConfig) {\n this.config = config;\n this.logger = config.logger || new ConsoleLogger();\n }\n\n /**\n * Execute an action. `context` carries the caller's identity when the host\n * supplies one; integrations that gate actions by role read it and fail\n * closed when it is absent.\n */\n abstract execute(\n action: string,\n params: IntegrationParams,\n context?: IntegrationCallContext,\n ): Promise<IntegrationResult>;\n\n /**\n * Validate action params against registry\n */\n protected validateParams(\n action: string,\n params: IntegrationParams,\n ): ReturnType<typeof validateParams> {\n return validateParams(this.config.name, action, params);\n }\n\n /** A rejected call: a real IntegrationError naming every failing param (kept in `details`). */\n protected validationFailure(action: string, errors: ValidationError[]): IntegrationResult {\n const reasons = errors.map((e) => `${e.param}: ${e.message}`).join('; ');\n const error = new IntegrationError(`Validation failed for ${this.config.name}.${action}: ${reasons}`, 'VALIDATION_ERROR', errors);\n error.integration = this.config.name;\n error.action = action;\n return { success: false, error, metadata: this.createMetadata(action, 0) };\n }\n\n /**\n * Handle errors uniformly\n */\n protected handleError(action: string, error: unknown): IntegrationResult {\n this.logger.error(`Integration error in ${this.config.name}.${action}`, {\n error: error instanceof Error ? error : new Error(String(error)),\n });\n\n const integrationError =\n error instanceof Error\n ? error\n : new Error(String(error));\n\n return {\n success: false,\n error: integrationError as IntegrationError,\n metadata: this.createMetadata(action, 0, 0),\n };\n }\n\n /**\n * Create metadata for result\n */\n protected createMetadata(\n action: string,\n duration: number,\n retries: number = 0,\n ): IntegrationResult['metadata'] {\n return {\n integration: this.config.name,\n action,\n duration,\n retries,\n timestamp: Date.now(),\n };\n }\n\n /**\n * Execute with retry logic\n */\n protected async executeWithRetry<T>(\n fn: () => Promise<T>,\n ): Promise<T> {\n if (!this.config.retry) {\n return fn();\n }\n\n return withRetry(fn, {\n maxAttempts: this.config.retry.maxAttempts,\n backoffMs: this.config.retry.backoffMs,\n maxBackoffMs: this.config.retry.maxBackoffMs,\n retryableErrors: [\n 'TIMEOUT_ERROR',\n 'NETWORK_ERROR',\n 'RATE_LIMIT_ERROR',\n ],\n });\n }\n}\n","import { BaseIntegration } from '../core/BaseIntegration';\nimport type { IntegrationConfig, IntegrationResult, IntegrationParams } from '../types';\nimport { IntegrationError } from '../types';\n\n/**\n * Mock integration for testing\n */\nexport class MockIntegration extends BaseIntegration {\n private responses: Map<string, unknown> = new Map();\n private calls: Array<{ action: string; params: IntegrationParams }> =\n [];\n\n constructor(config: IntegrationConfig) {\n super(config);\n }\n\n /**\n * Set mock response for an action\n */\n setResponse(action: string, data: unknown): void {\n this.responses.set(action, data);\n }\n\n /**\n * Get all calls made to this integration\n */\n getCalls(): Array<{ action: string; params: IntegrationParams }> {\n return this.calls;\n }\n\n /**\n * Clear all calls\n */\n clearCalls(): void {\n this.calls = [];\n }\n\n async execute(\n action: string,\n params: IntegrationParams,\n ): Promise<IntegrationResult> {\n // Record the call\n this.calls.push({ action, params });\n\n const data = this.responses.get(action);\n\n if (!data) {\n return {\n success: false,\n error: new IntegrationError(\n `No mock response for action: ${action}`,\n 'UNKNOWN_ERROR',\n ),\n metadata: this.createMetadata(action, 0),\n };\n }\n\n return {\n success: true,\n data,\n metadata: this.createMetadata(action, 0),\n };\n }\n}\n","import type { IntegrationConfig } from './types';\nimport { BaseIntegration } from './core/BaseIntegration';\n\n/**\n * Integration constructor type\n */\nexport type IntegrationConstructor = new (\n config: IntegrationConfig,\n) => BaseIntegration;\n\n/**\n * Integration registry (populated as integrations are imported)\n */\nexport const INTEGRATION_REGISTRY: Record<string, IntegrationConstructor> = {};\n\n/**\n * Register an integration\n */\nexport function registerIntegration(\n name: string,\n constructor: IntegrationConstructor,\n): void {\n INTEGRATION_REGISTRY[name] = constructor;\n}\n\n/**\n * Get integration constructor by name\n */\nexport function getIntegration(\n name: string,\n): IntegrationConstructor | undefined {\n return INTEGRATION_REGISTRY[name];\n}\n\n/**\n * Check if integration is known\n */\nexport function isKnownIntegration(name: string): boolean {\n return name in INTEGRATION_REGISTRY;\n}\n\n/**\n * Get all registered integration names\n */\nexport function getRegisteredIntegrations(): string[] {\n return Object.keys(INTEGRATION_REGISTRY);\n}\n","/**\n * ServiceContract type definitions for all @almadar/integrations.\n *\n * Each integration defines its own action map as a Record<string, ServiceAction>,\n * suitable for use with ServiceContract<T> from @almadar/core.\n *\n * Types are simplified representations of the actual params/results. They capture\n * the contract shape without pulling in third-party SDK types (Stripe, Twilio, etc.).\n *\n * @packageDocumentation\n */\n\nimport type { EventPayload, ServiceAction, ServiceEvents, ServiceParams, ServiceParamsValue } from '@almadar/core';\nimport type { IntegrationParams } from './types';\nimport type { DatabaseQueryParamValue, DatabaseRow } from './integrations/database/types';\n\n// ============================================================================\n// GitHub\n// ============================================================================\n\nexport type GitHubActions = {\n /** Clone a repository via the git CLI into a local target directory, using the configured token for auth. */\n cloneRepo: {\n params: {\n /** HTTPS clone URL, e.g. https://github.com/owner/repo. Owner/repo are parsed from it when not preconfigured. */\n repoUrl: string;\n /** Local directory to clone into. */\n targetDir: string;\n /** Branch to check out after cloning; defaults to the repo's default branch. */\n branch?: string;\n };\n result: { message: string };\n };\n /** Create a new local branch via the git CLI in the configured working directory. */\n createBranch: {\n params: {\n /** Name of the branch to create. */\n branchName: string;\n /** Branch to branch from; defaults to the current branch. */\n baseBranch?: string;\n };\n result: { message: string };\n };\n /** Commit staged (or given) changes via the git CLI in the configured working directory. */\n commit: {\n params: {\n /** Commit message. */\n message: string;\n /** Specific files to commit; when omitted, all changes are committed. */\n files?: string[];\n };\n result: { message: string };\n };\n /** Push a local branch to the remote via the git CLI, using the configured token for auth. */\n push: {\n params: {\n /** Branch name to push. */\n branchName: string;\n /** Force-push (overwrites remote history); never set true in an agent context. */\n force?: boolean;\n };\n result: { message: string };\n };\n /** Open a pull request via the GitHub REST API. */\n createPR: {\n params: {\n /** Pull request title. */\n title: string;\n /** Pull request description body. */\n body: string;\n /** Branch the PR merges into, e.g. main. */\n baseBranch: string;\n /** Branch the PR merges from (the feature branch). */\n headBranch: string;\n /** Open as a draft PR instead of ready-for-review. */\n draft?: boolean;\n };\n result: { number: number; url: string; title: string };\n };\n /** List review comments on a pull request via the GitHub REST API. */\n getPRComments: {\n params: {\n /** Pull request number. */\n prNumber: number;\n };\n result: { comments: Array<{ id: number; body: string; user: string }> };\n };\n /** List issues on the configured repository via the GitHub REST API. */\n listIssues: {\n params: {\n /** Issue state filter: 'open', 'closed', or 'all'; defaults to open. */\n state?: string;\n /** Only issues carrying all of these labels. */\n labels?: string[];\n /** Page size for the GitHub API request. */\n per_page?: number;\n };\n result: { issues: Array<{ number: number; title: string; state: string }> };\n };\n /** Fetch a single issue's details via the GitHub REST API. */\n getIssue: {\n params: {\n /** Issue number. */\n issueNumber: number;\n };\n result: { number: number; title: string; body: string; state: string };\n };\n /** Read a file's content from a repository at a given ref (declared intent — dispatch is being wired). */\n getFile: {\n params: {\n /** Repository owner (user or org). */\n owner: string;\n /** Repository name. */\n repo: string;\n /** File path within the repository. */\n path: string;\n /** Branch, tag, or commit SHA to read from; defaults to the default branch. */\n ref?: string;\n };\n result: { path: string; content: string; sha: string; size: number };\n };\n /** List commits on a repository, optionally filtered by file path (declared intent — dispatch is being wired). */\n listCommits: {\n params: {\n /** Repository owner (user or org). */\n owner: string;\n /** Repository name. */\n repo: string;\n /** Only commits touching this file path. */\n path?: string;\n /** Branch, tag, or commit SHA to start from; defaults to the default branch. */\n ref?: string;\n /** Page size for the GitHub API request. */\n per_page?: number;\n };\n result: { commits: Array<{ sha: string; message: string; author: string; date: string }> };\n };\n /** Open a new issue on a repository (declared intent — dispatch is being wired). */\n createIssue: {\n params: {\n /** Repository owner (user or org). */\n owner: string;\n /** Repository name. */\n repo: string;\n /** Issue title. */\n title: string;\n /** Issue body/description. */\n body?: string;\n /** Labels to apply to the new issue. */\n labels?: string[];\n };\n result: { number: number; title: string; url: string };\n };\n};\n\n// ============================================================================\n// Stripe\n// ============================================================================\n\nexport type StripeActions = {\n /**\n * Create a Stripe PaymentIntent for a one-off charge.\n * @synonyms charge, pay\n */\n createPaymentIntent: {\n params: {\n /** Charge amount in the currency's smallest unit (e.g. cents for USD). */\n amount: number;\n /** ISO 4217 currency code, e.g. usd. */\n currency: string;\n /** Arbitrary key-value tags attached to the PaymentIntent (string values only). */\n metadata?: Record<string, string>;\n };\n result: { id: string; clientSecret: string; status: string; amount: number; currency: string };\n };\n /** Confirm a previously created PaymentIntent, attempting to complete the charge. */\n confirmPayment: {\n params: {\n /** The PaymentIntent id returned by createPaymentIntent. */\n paymentIntentId: string;\n };\n result: { id: string; status: string };\n };\n /** Refund a charged PaymentIntent, in full or in part. */\n refund: {\n params: {\n /** The PaymentIntent id to refund. */\n paymentIntentId: string;\n /** Amount to refund in the currency's smallest unit; omit for a full refund. */\n amount?: number;\n };\n result: { id: string; status: string; amount: number };\n };\n};\n\n// ============================================================================\n// LLM\n// ============================================================================\n\nexport type LLMIntegrationActions = {\n /** Generate free-form text from a prompt via the configured LLM provider (default 'You are a helpful assistant.' system prompt). */\n generate: {\n params: {\n /** The user/task prompt sent to the model. */\n userPrompt: string;\n /** System prompt steering the model's behavior; defaults to a generic assistant prompt. */\n systemPrompt?: string;\n /** Model id override; when set, a fresh client is built for this call instead of the shared one. */\n model?: string;\n /** Sampling temperature (0-1ish, provider-dependent); higher is more random. */\n temperature?: number;\n /** Maximum tokens to generate; defaults to 1024. */\n maxTokens?: number;\n };\n result: {\n content: string;\n usage: { promptTokens?: number; completionTokens?: number; totalTokens?: number; tokens?: number };\n };\n };\n /** Classify text into exactly one of a caller-supplied category list, with a confidence score and reasoning. */\n classify: {\n params: {\n /** Text to classify. */\n text: string;\n /** Closed set of category labels the model must choose from. */\n categories: string[];\n /** Model id override. */\n model?: string;\n };\n result: { category: string; confidence: number; reasoning: string };\n };\n /** Extract structured data from text according to a JSON-schema-shaped description; returned data is not validated against the schema. */\n extract: {\n params: {\n /** Source text to extract data from. */\n text: string;\n /** JSON-schema-like description of the fields to extract; passed to the model as a prompt, not enforced structurally. */\n schema: IntegrationParams;\n /** Model id override. */\n model?: string;\n };\n result: IntegrationParams;\n };\n /** Summarize text, optionally constrained by length and style, returning a summary plus key points. */\n summarize: {\n params: {\n /** Text to summarize. */\n text: string;\n /** Target maximum summary length in words. */\n maxLength?: number;\n /** Summary style: 'bullet' for bullet points, 'detailed' for thorough, otherwise concise. */\n style?: string;\n /** Model id override. */\n model?: string;\n };\n result: { summary: string; keyPoints: string[] };\n };\n /** Generate embedding vectors for an array of texts via the configured embedding model (default baai/bge-base-en-v1.5 on OpenRouter, independent of the chat provider). */\n embed: {\n params: {\n /** Texts to embed, one vector per entry, in order. */\n texts: string[];\n /** Embedding model id override. */\n model?: string;\n };\n result: { embeddings: number[][] };\n };\n};\n\n// ============================================================================\n// ML (model inference)\n// ============================================================================\n\nexport type MLActions = {\n /** Run inference against a deployed model-serving orbital by posting an INFER event and reading the prediction back out of the emitted INFERRED result; may cold-start (up to 60s default timeout). */\n infer: {\n params: {\n /** Checkpoint or model identifier the serving orbital resolves. */\n model: string;\n /** JSON-safe model input payload. */\n input: ServiceParamsValue;\n };\n result: { output: ServiceParamsValue; confidence: number; violations: ServiceParamsValue[] };\n };\n};\n\n// ============================================================================\n// YouTube\n// ============================================================================\n\nexport type YouTubeActions = {\n /** Search YouTube via the Data API and return matching video snippets. */\n search: {\n params: {\n /** Search query text. */\n query: string;\n /** Maximum number of results; defaults to 10. */\n maxResults?: number;\n /** Resource type filter passed to the Data API (e.g. 'video', 'channel', 'playlist'). */\n type?: string;\n };\n result: Array<{ videoId: string; title: string; thumbnail: string; description: string }>;\n };\n /** Fetch a video's snippet and statistics via the YouTube Data API. */\n getVideo: {\n params: {\n /** YouTube video id. */\n videoId: string;\n };\n result: { title: string; description: string; viewCount: string; likeCount: string };\n };\n /** Fetch a channel's snippet and statistics via the YouTube Data API. */\n getChannel: {\n params: {\n /** YouTube channel id. */\n channelId: string;\n };\n result: { name: string; description: string; subscriberCount: string };\n };\n};\n\n// ============================================================================\n// Twilio\n// ============================================================================\n\nexport type TwilioActions = {\n /**\n * Send an SMS message via Twilio.\n * @synonyms text message, text\n */\n sendSMS: {\n params: {\n /** Destination phone number, E.164 format. */\n to: string;\n /** Message text. */\n body: string;\n /** Sender phone number; defaults to TWILIO_PHONE_NUMBER. */\n from?: string;\n };\n result: { sid: string; status: string };\n };\n /** Send a WhatsApp message via Twilio, using the configured phone number as sender on both ends. */\n sendWhatsApp: {\n params: {\n /** Destination phone number, E.164 format (sent as whatsapp:number). */\n to: string;\n /** Message text. */\n body: string;\n };\n result: { sid: string; status: string };\n };\n};\n\n// ============================================================================\n// Email\n// ============================================================================\n\nexport type EmailActions = {\n /** Send a transactional email via the configured provider (SendGrid or Resend). */\n send: {\n params: {\n /** Recipient email address. */\n to: string;\n /** Email subject line. */\n subject: string;\n /** Plain-text (or HTML, if `htmlBody` is absent) body content. */\n body: string;\n /** Sender address; defaults to FROM_EMAIL. */\n from?: string;\n /** HTML content; when present `body` becomes the plain-text alternative. */\n htmlBody?: string;\n /** Reply-To address. */\n replyTo?: string;\n /** Provider template reference (SendGrid dynamic templates; Resend rejects it loudly). */\n templateId?: string;\n };\n result: { id: string; status: string };\n };\n};\n\n// ============================================================================\n// Webhook\n// ============================================================================\n\nexport type WebhookActions = {\n /** POST a JSON event notification to an external URL, HMAC-SHA256-signed when a secret is configured; 5xx responses retry, 4xx do not. */\n send: {\n params: {\n /** Destination URL to POST to. */\n url: string;\n /** Event name, carried in the body and the X-Almadar-Event header. */\n event: string;\n /** Event payload; defaults to an empty object. */\n payload?: Record<string, string>;\n /** Per-call HMAC signing secret, overriding WEBHOOK_SIGNING_SECRET. */\n secret?: string;\n };\n result: { status: number; ok: boolean; durationMs: number };\n };\n};\n\n// ============================================================================\n// Push (Web Push / VAPID)\n// ============================================================================\n\nexport type PushActions = {\n /** Send a VAPID-signed Web Push notification to a browser PushSubscription endpoint. */\n send: {\n params: {\n /** Browser PushSubscription: endpoint URL plus the p256dh/auth encryption keys. */\n subscription: { endpoint: string; keys: { p256dh: string; auth: string } };\n /** Notification title. */\n title: string;\n /** Notification body text. */\n body: string;\n /** URL to open when the notification is clicked. */\n url?: string;\n /** Notification icon URL. */\n icon?: string;\n };\n result: {\n statusCode: number;\n ok: boolean;\n /** True on a 404/410 push-service response — the subscription is gone and must be pruned, never retried. */\n expired: boolean;\n };\n };\n};\n\n// ============================================================================\n// Calendar (Google Calendar, service account w/ domain-wide delegation)\n// ============================================================================\n\nexport type CalendarActions = {\n /** List events on a Google Calendar; pass `syncToken` for incremental sync instead of a time window (the API rejects combining them). */\n listEvents: {\n params: {\n /** Calendar id; defaults to GOOGLE_CALENDAR_ID (or 'primary'). */\n calendarId?: string;\n /** ISO start of the time window (ignored when `syncToken` is set). */\n timeMin?: string;\n /** ISO end of the time window (ignored when `syncToken` is set). */\n timeMax?: string;\n /** Incremental-sync token from a previous call's `nextSyncToken`; supersedes timeMin/timeMax. */\n syncToken?: string;\n /** Maximum events to return; defaults to 250. */\n maxResults?: number;\n };\n result: {\n events: Array<{ id: string; summary: string; description: string; location: string; start: string; end: string; status: string; updated: string }>;\n /** Token to pass as `syncToken` on the next call; null when incremental sync isn't available for this response. */\n nextSyncToken: string | null;\n };\n };\n /** Create a calendar event; an all-day event uses a 10-char (YYYY-MM-DD) start with no `end`/`durationMinutes`, defaulting to the next day. */\n createEvent: {\n params: {\n /** Calendar id; defaults to GOOGLE_CALENDAR_ID. */\n calendarId?: string;\n /** Event title. */\n summary: string;\n /** Event description. */\n description?: string;\n /** Event location text. */\n location?: string;\n /** ISO start (10 chars = all-day date, longer = dateTime). */\n start: string;\n /** ISO end; if omitted, derived from `durationMinutes` or the all-day next-day convention. */\n end?: string;\n /** Duration in minutes used to derive `end` when `end` is omitted (non-all-day events only). */\n durationMinutes?: number;\n };\n result: { id: string; status: string; htmlLink: string };\n };\n /** Patch an existing event's fields; only fields present in params are changed. */\n updateEvent: {\n params: {\n /** Calendar id; defaults to GOOGLE_CALENDAR_ID. */\n calendarId?: string;\n /** Id of the event to update. */\n eventId: string;\n /** New event title. */\n summary?: string;\n /** New event description. */\n description?: string;\n /** New event location text. */\n location?: string;\n /** New ISO start. */\n start?: string;\n /** New ISO end. */\n end?: string;\n };\n result: { id: string; status: string };\n };\n /** Delete a calendar event. */\n deleteEvent: {\n params: {\n /** Calendar id; defaults to GOOGLE_CALENDAR_ID. */\n calendarId?: string;\n /** Id of the event to delete. */\n eventId: string;\n };\n result: { id: string; deleted: boolean };\n };\n /** Query busy time blocks on a calendar within a window. */\n freeBusy: {\n params: {\n /** Calendar id; defaults to GOOGLE_CALENDAR_ID. */\n calendarId?: string;\n /** ISO start of the query window. */\n timeMin: string;\n /** ISO end of the query window. */\n timeMax: string;\n };\n result: { busy: Array<{ start: string; end: string }> };\n };\n /** Register a push-notification channel for calendar change events (Google Calendar API watch). */\n watch: {\n params: {\n /** Calendar id; defaults to GOOGLE_CALENDAR_ID. */\n calendarId?: string;\n /** Caller-chosen id identifying this notification channel. */\n channelId: string;\n /** HTTPS callback URL Google POSTs change notifications to. */\n address: string;\n /** Requested channel time-to-live in seconds. */\n ttlSeconds?: number;\n };\n result: { channelId: string; resourceId: string; expiration: string };\n };\n /** Stop an active watch channel, ending calendar change notifications. */\n stopWatch: {\n params: {\n /** The channel id from the corresponding `watch` call. */\n channelId: string;\n /** The resourceId returned by the corresponding `watch` call. */\n resourceId: string;\n };\n result: { stopped: boolean };\n };\n};\n\n// ============================================================================\n// Drive (Google Drive, service account w/ domain-wide delegation)\n// ============================================================================\n\nexport type DriveActions = {\n /** List non-trashed files in a Google Drive folder, optionally filtered by a Drive API query clause. */\n listFiles: {\n params: {\n /** Restrict to files whose parents include this folder id. */\n folderId?: string;\n /** Extra Drive API query clause, ANDed with the folder/trashed filters. */\n query?: string;\n /** Maximum files to return; defaults to 100. */\n maxResults?: number;\n };\n result: {\n files: Array<{ id: string; name: string; mimeType: string; size: number; modifiedTime: string; webViewLink: string }>;\n };\n };\n /** Download a file's content, base64-encoded. */\n getFile: {\n params: {\n /** Drive file id. */\n fileId: string;\n };\n result: { id: string; name: string; mimeType: string; content: string; size: number };\n };\n /**\n * Upload a file to Drive; writes go through the user OAuth client when configured (service-account uploads have no storage quota on personal accounts).\n * @synonyms upload file\n */\n uploadFile: {\n params: {\n /** File name to create. */\n name: string;\n /** File content as a base64 data URL (data:mime;base64,...) or raw base64/plain text. */\n content: string;\n /** MIME type; inferred from a data URL when omitted. */\n mimeType?: string;\n /** Parent folder id; defaults to GOOGLE_DRIVE_FOLDER_ID. */\n folderId?: string;\n };\n result: { id: string; name: string; webViewLink: string };\n };\n /** Create a new Drive folder. */\n createFolder: {\n params: {\n /** Folder name to create. */\n name: string;\n /** Parent folder id; defaults to GOOGLE_DRIVE_FOLDER_ID. */\n parentId?: string;\n };\n result: { id: string; name: string };\n };\n /** Grant a user permission on a Drive file. */\n shareFile: {\n params: {\n /** Drive file id. */\n fileId: string;\n /** Email address of the grantee. */\n email: string;\n /** Permission level; defaults to 'reader'. */\n role?: 'reader' | 'writer' | 'commenter';\n };\n result: { shared: boolean; permissionId: string };\n };\n};\n\n// ============================================================================\n// Meta Ads (Graph API Marketing Insights, read-only)\n// ============================================================================\n\nexport type MetaAdsActions = {\n /** Fetch total ad spend, impressions, and clicks for an account over a date range via the Meta Graph API Marketing Insights endpoint (read-only). */\n getSpend: {\n params: {\n /** Ad account id (with or without the `act_` prefix); defaults to META_AD_ACCOUNT_ID. */\n accountId?: string;\n /** Range start date (YYYY-MM-DD). */\n since: string;\n /** Range end date (YYYY-MM-DD). */\n until: string;\n };\n result: { spend: number; currency: string; impressions: number; clicks: number };\n };\n /** List campaigns on an ad account via the Meta Graph API (read-only); dailyBudget is converted from Meta's minor-unit reporting. */\n listCampaigns: {\n params: {\n /** Ad account id (with or without the `act_` prefix); defaults to META_AD_ACCOUNT_ID. */\n accountId?: string;\n /** Filter by campaign effective_status (e.g. 'ACTIVE', 'PAUSED'). */\n status?: string;\n };\n result: { campaigns: Array<{ id: string; name: string; status: string; dailyBudget: number }> };\n };\n};\n\n// ============================================================================\n// Accounting (generic export — provider-pluggable, CSV first)\n// ============================================================================\n\nexport type AccountingActions = {\n /** Shape invoice rows into an import-ready CSV export (no accounting-vendor connection — generic column set). */\n exportInvoices: {\n params: {\n /** Invoice rows to export, in column order id/number/customer/issuedAt/dueAt/currency/net/tax/gross/status. */\n invoices: Array<{ id: string; number: string; customer: string; issuedAt: string; dueAt: string; currency: string; net: number; tax: number; gross: number; status: string }>;\n /** Export format; only 'csv' is currently supported. */\n format?: 'csv';\n };\n result: { content: string; filename: string; count: number };\n };\n /** Shape journal entry rows into an import-ready CSV export. */\n exportJournal: {\n params: {\n /** Journal entry rows to export, in column order date/account/description/debit/credit/reference. */\n entries: Array<{ date: string; account: string; description: string; debit: number; credit: number; reference: string }>;\n /** Export format; only 'csv' is currently supported. */\n format?: 'csv';\n };\n result: { content: string; filename: string; count: number };\n };\n};\n\n// ============================================================================\n// Banking (GoCardless Bank Account Data)\n// ============================================================================\n\nexport type BankingActions = {\n /** Start a GoCardless Bank Account Data requisition (bank-connection consent flow); the returned `link` is where the end user authorizes access. */\n createRequisition: {\n params: {\n /** GoCardless institution id (the bank to connect). */\n institutionId: string;\n /** URL GoCardless redirects the user to after consent. */\n redirectUrl: string;\n /** Caller-chosen reference to correlate the requisition. */\n reference?: string;\n };\n result: { requisitionId: string; link: string };\n };\n /** List account ids linked under a completed requisition. */\n listAccounts: {\n params: {\n /** Requisition id from createRequisition. */\n requisitionId: string;\n };\n result: { accounts: string[] };\n };\n /** List booked transactions on a linked bank account, optionally filtered by date range. */\n listTransactions: {\n params: {\n /** GoCardless account id. */\n accountId: string;\n /** Only transactions booked on/after this date (YYYY-MM-DD). */\n dateFrom?: string;\n /** Only transactions booked on/before this date (YYYY-MM-DD). */\n dateTo?: string;\n };\n result: {\n transactions: Array<{ id: string; amount: number; currency: string; date: string; description: string; counterparty: string }>;\n };\n };\n};\n\n// ============================================================================\n// E-sign (pluggable, DocuSign first)\n// ============================================================================\n\nexport type EsignActions = {\n /** Send a document for e-signature via DocuSign; accepts the document content as a data URL or raw base64. */\n sendEnvelope: {\n params: {\n /** Signer's email address. */\n recipientEmail: string;\n /** Signer's display name. */\n recipientName: string;\n /** Document file name (its extension also picks the DocuSign fileExtension). */\n documentName: string;\n /** Document content as a base64 data URL or raw base64. */\n documentContent: string;\n /** Envelope email subject; defaults to \"Please sign: \" + documentName. */\n emailSubject?: string;\n };\n result: { envelopeId: string; status: string };\n };\n /** Poll a DocuSign envelope's signing status. */\n getEnvelopeStatus: {\n params: {\n /** DocuSign envelope id from sendEnvelope. */\n envelopeId: string;\n };\n result: { status: string; completedAt: string };\n };\n /** Download the combined signed document(s) for a completed envelope, base64-encoded PDF. */\n downloadDocument: {\n params: {\n /** DocuSign envelope id. */\n envelopeId: string;\n };\n result: { content: string; documentName: string };\n };\n};\n\n// ============================================================================\n// Docker\n// ============================================================================\n\nexport type DockerActions = {\n /** Simulate building a container image (in-memory backend — no real Docker daemon/SDK; a real build is not yet implemented). */\n build: {\n params: {\n /** Image tag to build, e.g. myapp:latest. */\n tag: string;\n /** Dockerfile path; defaults to 'Dockerfile'. */\n dockerfile?: string;\n /** Build context directory; defaults to '.'. */\n context?: string;\n /** Build-time --build-arg values. */\n buildArgs?: Record<string, string>;\n };\n result: { imageId: string; tag: string; size: number; buildTime: number };\n };\n /** Simulate starting a container from an image (in-memory backend). */\n run: {\n params: {\n /** Image to run (as built/tagged by the `build` action, or an arbitrary name). */\n image: string;\n /** Container name; auto-generated when omitted. */\n name?: string;\n /** Host/container port mappings. */\n ports?: Array<{ host: number; container: number; protocol?: string }>;\n /** Environment variables set inside the container. */\n env?: Record<string, string>;\n /** Host/container path mounts. */\n volumes?: Array<{ host: string; container: string }>;\n /** Override command run inside the container. */\n command?: string;\n };\n result: {\n containerId: string;\n name: string;\n status: string;\n ports: Array<{ host: number; container: number; protocol: string }>;\n };\n };\n /** Stop a running (or paused) simulated container. */\n stop: {\n params: {\n /** Container id, or a unique id prefix. */\n containerId: string;\n };\n result: { containerId: string; status: string; stoppedAt: number };\n };\n /** Remove a simulated container; fails if it is running unless `force` is set. */\n remove: {\n params: {\n /** Container id, or a unique id prefix. */\n containerId: string;\n /** Remove even if the container is currently running. */\n force?: boolean;\n };\n result: { containerId: string; removed: boolean };\n };\n /** Fetch generated log lines for a simulated container. */\n logs: {\n params: {\n /** Container id, or a unique id prefix. */\n containerId: string;\n /** Number of most-recent lines to return; defaults to 100. */\n tail?: number;\n /** Only lines timestamped at/after this ISO time. */\n since?: string;\n };\n result: { containerId: string; logs: string[]; lineCount: number };\n };\n /** Fetch a simulated container's current status and metadata. */\n status: {\n params: {\n /** Container id, or a unique id prefix. */\n containerId: string;\n };\n result: {\n containerId: string;\n name: string;\n image: string;\n status: string;\n ports: Array<{ host: number; container: number; protocol: string }>;\n createdAt: number;\n startedAt: number | null;\n stoppedAt: number | null;\n };\n };\n /** List simulated containers; by default only running ones (like `docker ps`). */\n list: {\n params: {\n /** Include stopped/exited containers, not just running ones. */\n all?: boolean;\n /** Filter by a `key` or `key=value` label match. */\n filterByLabel?: string;\n };\n result: {\n containers: Array<{\n id: string;\n name: string;\n image: string;\n status: string;\n ports: Array<{ host: number; container: number; protocol: string }>;\n createdAt: number;\n }>;\n total: number;\n };\n };\n};\n\n// ============================================================================\n// Storage\n// ============================================================================\n\nexport type StorageActions = {\n /**\n * Upload an object to an S3-compatible bucket (real S3/R2/MinIO backend when storage credentials are configured, in-memory dev fallback otherwise — refused in production). Admits either the canonical `key`+`content` pair or the file-form `file` payload (UploadDropZone's `{ name, size, type, content }` shape); when `file` is present its `key`/`content` are derived from it.\n * @synonyms upload file, upload object\n */\n upload: {\n params: {\n /** Target bucket; defaults to STORAGE_BUCKET. */\n bucket: string;\n /** Object key to store under (canonical shape); ignored when `file` is present. */\n key?: string;\n /** Object content as a base64 data URL, raw base64, or plain text (canonical shape); ignored when `file` is present. */\n content?: string;\n /** MIME type override for the canonical shape; inferred from a data URL when omitted. */\n contentType?: string;\n /** File-form upload payload (as emitted by UploadDropZone); `content` is a base64 data URL. */\n file?: { name: string; size: number; type: string; content?: string };\n /** Object ACL — 'public' produces a public URL, otherwise the URL is signed/private. */\n acl?: 'public' | 'private';\n /** Reject the upload if the file-form payload's declared size exceeds this many bytes. */\n maxSize?: number;\n /** Object metadata stored alongside the content (S3-compatible object stores carry metadata as string headers — never structured values). */\n metadata?: Record<string, string>;\n };\n result: { key: string; bucket: string; size: number; etag: string; id: string; url: string };\n };\n /** Upload a whole UploadDropZone batch in one call, one item per file — the FSM-friendly shape for bulk-persisting uploaded-file rows. */\n uploadMany: {\n params: {\n /** Target bucket; defaults to STORAGE_BUCKET. */\n bucket: string;\n /** Files to upload, in UploadDropZone's `{ name, size, type, content }` shape. */\n files: Array<{ name: string; size: number; type: string; content?: string }>;\n /** Object ACL applied to every file in the batch. */\n acl?: 'public' | 'private';\n /** Reject any file whose declared size exceeds this many bytes. */\n maxSize?: number;\n };\n result: {\n items: Array<{ id: string; key: string; url: string; name: string; sizeBytes: number; mimeType: string }>;\n count: number;\n };\n };\n /** Download an object's content, base64-encoded. */\n download: {\n params: {\n /** Bucket the object lives in; defaults to STORAGE_BUCKET. */\n bucket: string;\n /** Object key. */\n key: string;\n };\n result: { content: string; contentType: string; size: number; metadata: Record<string, string> };\n };\n /** List object keys in a bucket, optionally prefix-filtered, one page at a time. */\n list: {\n params: {\n /** Bucket to list; defaults to STORAGE_BUCKET. */\n bucket: string;\n /** Only keys starting with this prefix. */\n prefix?: string;\n /** Maximum keys per page; defaults to 1000. */\n maxKeys?: number;\n /** Pagination token from a previous call's `nextToken`. */\n continuationToken?: string;\n };\n result: {\n keys: Array<{ key: string; size: number; lastModified: number }>;\n truncated: boolean;\n /** Present iff `truncated` — pass back as `continuationToken` for the next page. */\n nextToken?: string;\n };\n };\n /** Delete an object from a bucket. */\n delete: {\n params: {\n /** Bucket the object lives in; defaults to STORAGE_BUCKET. */\n bucket: string;\n /** Object key to delete. */\n key: string;\n };\n result: { deleted: boolean };\n };\n /** Generate a time-limited pre-signed URL for direct GET or PUT access to an object. */\n getSignedUrl: {\n params: {\n /** Bucket the object lives in; defaults to STORAGE_BUCKET. */\n bucket: string;\n /** Object key. */\n key: string;\n /** URL validity window in seconds; defaults to 3600. */\n expiresIn?: number;\n /** Whether the URL authorizes a read or a write; defaults to 'get'. */\n operation?: 'get' | 'put';\n };\n result: { url: string; expiresAt: number };\n };\n};\n\n// ============================================================================\n// Queue\n// ============================================================================\n\n/** A queued job as stored/returned by the in-memory queue. Extends\n * `EventPayload` so it flows through a `ServiceAction.result` (bus-payload-\n * shaped) without a boundary cast, the same reason `JsonSchemaDefinition`\n * extends `ServiceParams` above. */\ninterface QueueJob extends EventPayload {\n id: string;\n /** Whatever `enqueue` was given — the caller declares its own shape.\n * @shapedBy payload */\n payload: ServiceParams;\n enqueuedAt: number;\n priority: number;\n}\n\nexport type QueueActions = {\n /** Add a job to an in-memory job queue, ordered by priority (higher first) among ready jobs. */\n enqueue: {\n params: {\n /** Queue name. */\n queue: string;\n /** Job payload — the caller declares its own shape.\n * @shapedBy payload */\n payload: ServiceParams;\n /** Delay before the job becomes eligible for dequeue, in milliseconds. */\n delay?: number;\n /** Priority — higher values are dequeued before lower ones. */\n priority?: number;\n };\n result: { jobId: string; position: number };\n };\n /** Pop the highest-priority pending job whose delay has elapsed, marking it 'processing'; returns null if none is ready. */\n dequeue: {\n params: {\n /** Queue name. */\n queue: string;\n };\n result: { job: QueueJob | null };\n };\n /** Look up a job's current status by id. */\n status: {\n params: {\n /** Job id from enqueue. */\n jobId: string;\n };\n result: {\n status: 'pending' | 'processing' | 'completed' | 'failed';\n job: QueueJob | null;\n };\n };\n /** Mark a 'processing' job completed and store its result; no-op (returns false) if the job isn't in 'processing' state. */\n complete: {\n params: {\n /** Job id. */\n jobId: string;\n /** Result payload to attach to the job — the caller declares its own shape (not necessarily the same shape as the job's `payload`).\n * @shapedBy result */\n result?: ServiceParams;\n };\n result: { completed: boolean };\n };\n /** Mark a 'processing' job failed; no-op (returns false) if the job isn't in 'processing' state. */\n fail: {\n params: {\n /** Job id. */\n jobId: string;\n /** Failure reason to attach to the job. */\n error?: string;\n };\n result: { failed: boolean };\n };\n /** Remove a 'pending' job from its queue; no-op (returns false) once it has been dequeued. */\n cancel: {\n params: {\n /** Job id. */\n jobId: string;\n };\n result: { cancelled: boolean };\n };\n /** Count pending and processing jobs in a queue. */\n size: {\n params: {\n /** Queue name. */\n queue: string;\n };\n result: { size: number; pending: number; processing: number };\n };\n};\n\n// ============================================================================\n// Redis\n// ============================================================================\n\nexport type RedisActions = {\n /** Read a key's value from the in-memory cache; returns null (as `value`) if absent or expired. */\n get: {\n params: {\n /** Cache key. */\n key: string;\n };\n result: {\n /** Whatever `set` stored under this key — the caller declares its shape.\n * @shapedBy value */\n value: ServiceParams;\n };\n };\n /** Write a key's value, optionally with an expiry. */\n set: {\n params: {\n /** Cache key. */\n key: string;\n /** Value to store — the caller declares its own shape.\n * @shapedBy value */\n value: ServiceParams;\n /** Time-to-live in seconds; omit for no expiry. */\n ttl?: number;\n };\n result: { ok: boolean };\n };\n /** Delete a key. */\n delete: {\n params: {\n /** Cache key. */\n key: string;\n };\n result: { deleted: boolean };\n };\n /** Acquire a mutex on a key; fails (returns acquired:false) if another lock on the key is still alive. */\n lock: {\n params: {\n /** Lock key. */\n key: string;\n /** Lock time-to-live in milliseconds; defaults to 30000 (30s). */\n ttl?: number;\n };\n result: { acquired: boolean; lockId: string };\n };\n /** Release a lock; only succeeds when `lockId` matches the current holder. */\n unlock: {\n params: {\n /** Lock key. */\n key: string;\n /** Lock id returned by the corresponding `lock` call. */\n lockId: string;\n };\n result: { released: boolean };\n };\n /** Atomically add to a numeric key's value (creating it at 0 if absent), preserving any existing TTL. */\n increment: {\n params: {\n /** Cache key. */\n key: string;\n /** Amount to add; defaults to 1 (can be negative to decrement). */\n by?: number;\n };\n result: { value: number };\n };\n /** Set (or reset) a key's expiry; no-op (returns set:false) if the key doesn't exist or is already expired. */\n expire: {\n params: {\n /** Cache key. */\n key: string;\n /** New time-to-live in seconds. */\n ttl: number;\n };\n result: { set: boolean };\n };\n /** Publish a message to a channel; delivered synchronously to in-process subscriber callbacks only. */\n publish: {\n params: {\n /** Channel name. */\n channel: string;\n /** Message payload — the caller declares its own shape.\n * @shapedBy message */\n message: ServiceParams;\n };\n result: { receivers: number };\n };\n /** Register interest in a channel; does not itself wire a callback (that happens at a higher-level API). */\n subscribe: {\n params: {\n /** Channel name. */\n channel: string;\n };\n result: { subscribed: boolean };\n };\n};\n\n// ============================================================================\n// OAuth\n// ============================================================================\n\nexport type OAuthActions = {\n /** Build the provider authorization URL (PKCE + state) for an OAuth/OIDC login redirect; OIDC discovery (Google) or static OAuth 2 metadata (GitHub) when OAUTH_CLIENT_ID/SECRET are configured, an in-memory mock otherwise. */\n authorize: {\n params: {\n /** Identity provider; OIDC issuer defaults to Google unless OIDC_ISSUER_URL overrides it; GitHub uses its OAuth 2 endpoints. */\n provider: 'google' | 'github' | 'auth0';\n /** OAuth scopes to request. */\n scopes: string[];\n /** Callback URL the provider redirects back to after consent. */\n redirectUri: string;\n /** Opaque id of whoever started the flow (the verified user); `token` returns it so the host can refuse anyone else completing. */\n subject?: string;\n };\n result: { authUrl: string; state: string };\n };\n /** Exchange an authorization code for tokens, consuming the one-time-use `state` from the matching `authorize` call. */\n token: {\n params: {\n /** Authorization code from the provider's redirect callback. */\n code: string;\n /** The `state` value returned by `authorize`. */\n state: string;\n /** Who is completing; must equal the `subject` bound at `authorize`, checked before the code is exchanged. */\n subject?: string;\n };\n result: { accessToken: string; refreshToken: string; expiresIn: number; tokenType: 'bearer'; scope: string[]; subject?: string };\n };\n /** Exchange a refresh token for a new access token. */\n refresh: {\n params: {\n /** Refresh token from a prior `token` call. */\n refreshToken: string;\n };\n result: { accessToken: string; expiresIn: number };\n };\n /** Revoke an access or refresh token. */\n revoke: {\n params: {\n /** The access or refresh token to revoke. */\n token: string;\n };\n result: { revoked: boolean };\n };\n /** Fetch the authenticated user's profile claims for an access token. */\n userinfo: {\n params: {\n /** Access token from a prior `token`/`refresh` call. */\n accessToken: string;\n };\n result: { sub: string; email: string; name: string; picture: string };\n };\n};\n\n// ============================================================================\n// Credentials (hosted tenant credential store — the settings-page backend)\n// ============================================================================\n\nexport type CredentialsActions = {\n /** List declared credentials and their masked (last-4) configuration status; open to any caller. */\n list: {\n params: {\n /** Restrict the listing to one service's declared credentials; omit for all services. */\n service?: string;\n };\n result: {\n enabled: boolean;\n entries: Array<{\n service: string;\n envVar: string;\n required: boolean;\n description: string;\n configured: boolean;\n source: 'store' | 'env' | 'none';\n last4: string;\n }>;\n };\n };\n /** Save a credential value into the hosted store; the env var must be one declared for the service (or a well-formed connection-ref name for `database`). Admin/owner role required. */\n set: {\n params: {\n /** Integration name the credential belongs to. */\n service: string;\n /** Declared env-var name being set. */\n envVar: string;\n /** Secret value to store; never logged. */\n value: string;\n };\n result: { saved: boolean; service: string; envVar: string; last4: string };\n };\n /** Delete a stored credential from the hosted store. Admin/owner role required. */\n remove: {\n params: {\n /** Integration name the credential belongs to. */\n service: string;\n /** Env-var name to remove. */\n envVar: string;\n };\n result: { removed: boolean };\n };\n /** Check whether a service's required credentials are present and, if a live probe is declared for it, exercise the service to confirm they actually work. Admin/owner role required. */\n test: {\n params: {\n /** Integration name to test. */\n service: string;\n };\n result: { service: string; configured: boolean; missing: string[]; probed: boolean; ok: boolean; message: string };\n };\n /** Re-encrypt all stored credentials under the current master key (ALMADAR_CREDENTIAL_MASTER_KEY), retiring ALMADAR_CREDENTIAL_MASTER_KEY_PREVIOUS. Admin/owner role required. */\n rotate: {\n params: Record<string, never>;\n result: {\n /** Entries successfully re-encrypted under the current key. */\n rotated: number;\n /** Entries already under the current key (no-op). */\n alreadyCurrent: number;\n /** Entries that could not be decrypted with either key (data loss risk). */\n unreadable: number;\n };\n };\n};\n\n// ============================================================================\n// OpenTelemetry\n// ============================================================================\n\nexport type OtelActions = {\n /** Start a trace span (in-memory backend — no OpenTelemetry SDK/exporter). */\n startSpan: {\n params: {\n /** Span name. */\n name: string;\n /** Key-value attributes attached to the span (OTel attribute values are scalar). */\n attributes?: Record<string, string | number | boolean>;\n /** Existing trace id to attach this span to; a new one is generated when omitted. */\n traceId?: string;\n };\n result: { spanId: string; traceId: string };\n };\n /** Mark a span ended, optionally recording its final status; no-op (returns ended:false) for an unknown span id. */\n endSpan: {\n params: {\n /** Span id from startSpan. */\n spanId: string;\n /** Final span status. */\n status?: 'ok' | 'error';\n };\n result: { ended: boolean };\n };\n /** Attach a timestamped event to an open span; no-op (returns added:false) for an unknown span id. */\n addEvent: {\n params: {\n /** Span id from startSpan. */\n spanId: string;\n /** Event name. */\n name: string;\n /** Key-value attributes attached to the event (OTel attribute values are scalar). */\n attributes?: Record<string, string | number | boolean>;\n };\n result: { added: boolean };\n };\n /** Record a metric sample: counters accumulate, gauges overwrite, histograms append to the sample list. */\n recordMetric: {\n params: {\n /** Metric name. */\n name: string;\n /** Sample value. */\n value: number;\n /** Aggregation kind; defaults to 'counter'. */\n type?: 'counter' | 'gauge' | 'histogram';\n /** Key-value labels attached to the metric (replaces prior labels on the same name; OTel label values are scalar). */\n labels?: Record<string, string | number | boolean>;\n };\n result: { recorded: boolean };\n };\n /** Fetch a stored span's full record by id; null if unknown. */\n getSpan: {\n params: {\n /** Span id from startSpan. */\n spanId: string;\n };\n result: {\n spanId: string;\n traceId: string;\n name: string;\n startTime: number;\n endTime?: number;\n status?: 'ok' | 'error';\n attributes: IntegrationParams;\n } | null;\n };\n /** Fetch all recorded metrics, keyed by metric name. */\n getMetrics: {\n params: Record<string, never>;\n result: Record<string, { name: string; type: string; value: number; values: number[]; labels: IntegrationParams }>;\n };\n};\n\n// ============================================================================\n// CLI\n// ============================================================================\n\nexport type CLIActions = {\n /** Validate an orbital schema by writing it to a temp file and shelling out to `npx @almadar/cli validate --format=json`. */\n validate: {\n params: {\n /** Raw .orb schema content to validate. */\n schema: string;\n };\n result: { valid: boolean; errors: string[]; warnings: string[]; summary: string };\n };\n};\n\n// ============================================================================\n// DeepAgent\n// ============================================================================\n\nexport type DeepAgentActions = {\n /** Send a chat message to the DeepAgent server (POST /api/agent/message), continuing an existing thread or starting one. */\n sendMessage: {\n params: {\n /** Message text. */\n message: string;\n /** Existing thread id to continue; a new thread starts when omitted. */\n threadId?: string;\n /** Named skill to invoke for this message. */\n skill?: string;\n /** Extra context passed through to the agent — the caller declares its own shape.\n * @shapedBy context */\n context?: IntegrationParams;\n };\n result: { response: string; threadId: string };\n };\n /** Cancel an in-flight generation on a DeepAgent thread (POST /api/agent/cancel). */\n cancelGeneration: {\n params: {\n /** Thread whose generation to cancel. */\n threadId: string;\n };\n result: { cancelled: boolean };\n };\n /** Validate a schema via the DeepAgent server (POST /api/schema/validate). */\n validateSchema: {\n params: {\n /** Schema content to validate. */\n schema: string;\n };\n result: { valid: boolean; errors: string[] };\n };\n /** Compile a schema to a target shell via the DeepAgent server (POST /api/schema/compile). */\n compileSchema: {\n params: {\n /** Schema content to compile. */\n schema: string;\n /** Target codegen shell (e.g. 'typescript'). */\n shell?: string;\n };\n result: { output: string; files: string[] };\n };\n /** Fetch a thread's message history via the DeepAgent server (POST /api/agent/history). */\n getThreadHistory: {\n params: {\n /** Thread id. */\n threadId: string;\n };\n result: { messages: Array<{ role: string; content: string; timestamp: number }> };\n };\n};\n\n// ============================================================================\n// Database\n// ============================================================================\n\nexport type DatabaseActions = {\n /** Run a single read-only SELECT against a Postgres connection (write statements and multi-statement SQL are rejected before execution). */\n query: {\n params: {\n /** Env-var NAME whose value is the Postgres connection string — never the secret itself; resolved from the credential store/environment at run time. */\n connectionRef: string;\n /** SQL text; must be exactly one SELECT statement. */\n sql: string;\n /** Positional bind parameters for the query. */\n params?: DatabaseQueryParamValue[];\n };\n result: {\n /** One row per matched record, keyed by column name — the shape follows\n * the caller's own `sql` SELECT list, which no param's TYPE can express.\n * @shapedBy rows */\n rows: DatabaseRow[];\n rowCount: number;\n };\n };\n};\n\n// ============================================================================\n// Wikimedia\n// ============================================================================\n\nexport type WikimediaActions = {\n /** Look up a Wikipedia page (redirect-resolved): a one-line description, a Commons thumbnail, and the lead-section extract; empty fields on a miss. */\n getPage: {\n params: {\n /** Page title to look up. */\n title: string;\n };\n result: { title?: string; description?: string; portraitUrl?: string; extract?: string };\n };\n};\n\n// ============================================================================\n// Iconify\n// ============================================================================\n\nexport type IconifyActions = {\n /** Check whether an icon exists at the given Iconify API path (a GET, not a HEAD, despite the name). */\n svgExists: {\n params: {\n /** Iconify API path segment to probe, e.g. 'prefix/name.svg'. */\n path: string;\n };\n result: { exists: boolean };\n };\n /** Search Iconify's icon sets by relevance. */\n search: {\n params: {\n /** Search query text. */\n query: string;\n /** Maximum icon ids to return; defaults to 1. */\n limit?: number;\n };\n result: { icons: string[] };\n };\n /** Fetch an icon's raw SVG body (for color-tone classification); null if the icon or its collection isn't found. */\n getIconBody: {\n params: {\n /** Icon id in \"prefix:name\" form. */\n iconId: string;\n };\n result: { body: string | null };\n };\n};\n\n// ============================================================================\n// arXiv\n// ============================================================================\n\nexport type ArxivActions = {\n /** Search arXiv's public Atom API by relevance. */\n search: {\n params: {\n /** Search query text (matched across all fields). */\n query: string;\n /** Maximum results to return; defaults to 8. */\n maxResults?: number;\n };\n result: { results: Array<{ id: string; title: string; summary: string; authors: string[]; published: string; url: string }> };\n };\n};\n\n// ============================================================================\n// Rigger\n// ============================================================================\n\nexport type RiggerActions = {\n /** Liveness probe of the local rigger service. */\n health: {\n params: Record<string, never>;\n result: { ok: boolean };\n };\n /** Build a rigged mesh bundle from a source image via the local rigger service. */\n buildMesh: {\n params: {\n /** Path to the source image. */\n imagePath: string;\n /** The rig definition (parts, boxes, pivots, …) as JSON. */\n rig: object;\n };\n result: { bundlePath: string; boneCount: number; vertexCount: number; triangleCount: number; clips: string[] };\n };\n /** Render a rigged mesh bundle to output files via the local rigger service. */\n render: {\n params: {\n /** Path to the source image. */\n imagePath: string;\n /** The rig definition (parts, boxes, pivots, …) as JSON. */\n rig: object;\n /** Render preset name. */\n preset: string;\n /** Output directory for rendered files. */\n outDir: string;\n /** Render path: 'mesh' (skinned, deforming) or 'cutout' (rigid per-part layers). Defaults to 'mesh'. */\n mode?: string;\n /** Supersampling factor for the cutout path. Defaults to 1. */\n supersample?: number;\n };\n result: { outdir: string; frames: number; framePaths: string[]; sheet: string; gif: string; atlas: string };\n };\n /** List the available animation presets (name, fps, frame count, bone roles) from the local rigger service. */\n listPresets: {\n params: Record<string, never>;\n result: {\n presets: Array<{ name: string; path: string; fps: number; frames: number; bones: object[] }>;\n };\n };\n /** Load a rig file, resolving `cloneOf` parts to concrete box/pivot geometry. */\n loadRig: {\n params: {\n /** Path to the rig JSON file. */\n rigPath: string;\n };\n result: {\n /** The rig file's JSON, verbatim. */\n rig: object;\n /** The rig with every part's box/pivot resolved (clones included) plus rig-level knobs. */\n resolved: object;\n };\n };\n /** Save a rig file; the server validates it and rejects a malformed part. */\n saveRig: {\n params: {\n /** Path to write the rig JSON file. */\n rigPath: string;\n /** The rig definition (parts, boxes, pivots, …) as JSON. */\n rig: object;\n };\n result: { rigPath: string; parts: number };\n };\n /** Render the per-part pixel ownership map for a rig over a source image. */\n ownership: {\n params: {\n /** Path to the source image. */\n imagePath: string;\n /** The rig definition (parts, boxes, pivots, …) as JSON. */\n rig: object;\n /** Path to write the ownership-map PNG. */\n outPath: string;\n };\n result: { outPath: string; legend: Array<{ name: string; color: string }> };\n };\n /** Build per-part cutout layer images (name, box, z-order, pivot) from a source image and rig. */\n buildLayers: {\n params: {\n /** Path to the source image. */\n imagePath: string;\n /** The rig definition (parts, boxes, pivots, …) as JSON. */\n rig: object;\n /** Output directory for the layer images. */\n outDir: string;\n };\n result: {\n layers: Array<{\n name: string;\n path: string;\n x: number;\n y: number;\n w: number;\n h: number;\n z: number;\n pivot: [number, number];\n isRest: boolean;\n }>;\n };\n };\n /** Sample a pose/animation preset into per-frame, per-part transforms for a rig. */\n pose: {\n params: {\n /** Path to the source image. */\n imagePath: string;\n /** The rig definition (parts, boxes, pivots, …) as JSON. */\n rig: object;\n /** Map of part name to preset bone role. */\n roles: Record<string, string>;\n /** Animation preset name. */\n preset?: string;\n /** Per-part motion overrides applied over the preset. */\n overrides?: object;\n };\n result: {\n fps: number;\n frames: Array<Record<string, { angle: number; bx: number; by: number }>>;\n };\n };\n /** Generate one image on the local image server (text-to-image, or a Kontext edit conditioned on `sourcePath`). */\n generate: {\n params: {\n /** Generation model: 'z-image-turbo' (text-to-image) or 'kontext' (edit; requires sourcePath). */\n model: 'z-image-turbo' | 'kontext';\n /** The generation/edit prompt. */\n prompt: string;\n /** Random seed; omit for a fresh one each call. */\n seed?: number;\n /** Output image size, e.g. '1024x1024'. Defaults to '1024x1024'. */\n size?: string;\n /** Diffusion step count. */\n steps?: number;\n /** Path to the source image to edit; REQUIRED for 'kontext'. */\n sourcePath?: string;\n /** Output directory the generated PNG is written under. */\n outDir: string;\n };\n result: {\n outPath: string;\n model: string;\n prompt: string;\n seed: number | null;\n size: string;\n sourcePath: string | null;\n elapsedMs: number;\n };\n };\n};\n\n// ============================================================================\n// Barrel: IntegrationContracts\n// ============================================================================\n\n/**\n * Maps each integration name (as used in `registerIntegration`) to its action\n * map type. Use with `ServiceContract<IntegrationContracts[K]>` to get a fully\n * typed service interface for a specific integration.\n *\n * @example\n * ```typescript\n * import type { ServiceContract } from '@almadar/core';\n * import type { IntegrationContracts } from '@almadar/integrations';\n *\n * type TypedStripe = ServiceContract<IntegrationContracts['stripe']>;\n * ```\n */\nexport type IntegrationContracts = {\n github: GitHubActions;\n stripe: StripeActions;\n llm: LLMIntegrationActions;\n ml: MLActions;\n youtube: YouTubeActions;\n twilio: TwilioActions;\n email: EmailActions;\n webhook: WebhookActions;\n push: PushActions;\n calendar: CalendarActions;\n drive: DriveActions;\n metaAds: MetaAdsActions;\n accounting: AccountingActions;\n banking: BankingActions;\n esign: EsignActions;\n docker: DockerActions;\n storage: StorageActions;\n queue: QueueActions;\n redis: RedisActions;\n oauth: OAuthActions;\n credentials: CredentialsActions;\n otel: OtelActions;\n cli: CLIActions;\n deepagent: DeepAgentActions;\n database: DatabaseActions;\n wikimedia: WikimediaActions;\n iconify: IconifyActions;\n arxiv: ArxivActions;\n rigger: RiggerActions;\n};\n\n/** Integration name literal union. */\nexport type IntegrationName = keyof IntegrationContracts;\n\n/**\n * Helper: extract the action names for a given integration.\n *\n * @example\n * ```typescript\n * type StripeAction = IntegrationActionName<'stripe'>;\n * // => 'createPaymentIntent' | 'confirmPayment' | 'refund'\n * ```\n */\nexport type IntegrationActionName<K extends IntegrationName> =\n keyof IntegrationContracts[K] & string;\n\n// ============================================================================\n// Event Map\n// ============================================================================\n\n/** Events emitted by integrations during execution. */\nexport type IntegrationEventMap = {\n INTEGRATION_CALL_STARTED: { integration: string; action: string; timestamp: number };\n INTEGRATION_CALL_SUCCEEDED: { integration: string; action: string; durationMs: number };\n INTEGRATION_CALL_FAILED: { integration: string; action: string; error: string; retryCount: number };\n INTEGRATION_RETRY: { integration: string; action: string; attempt: number; maxRetries: number };\n INTEGRATION_REGISTERED: { integration: string; actions: string[] };\n};\n\n/** Typed event emitter for integration events. */\nexport type IntegrationServiceEvents = ServiceEvents<IntegrationEventMap>;\n\n// ============================================================================\n// Type Assertions\n// ============================================================================\n\n// Compile-time verification that all action maps satisfy Record<string, ServiceAction>.\n// This is a type-level assertion, not runtime code.\ntype _AssertActions<T extends Record<string, ServiceAction>> = T;\ntype _CheckGitHub = _AssertActions<GitHubActions>;\ntype _CheckStripe = _AssertActions<StripeActions>;\ntype _CheckLLM = _AssertActions<LLMIntegrationActions>;\ntype _CheckML = _AssertActions<MLActions>;\ntype _CheckYouTube = _AssertActions<YouTubeActions>;\ntype _CheckTwilio = _AssertActions<TwilioActions>;\ntype _CheckEmail = _AssertActions<EmailActions>;\ntype _CheckWebhook = _AssertActions<WebhookActions>;\ntype _CheckPush = _AssertActions<PushActions>;\ntype _CheckCalendar = _AssertActions<CalendarActions>;\ntype _CheckDrive = _AssertActions<DriveActions>;\ntype _CheckMetaAds = _AssertActions<MetaAdsActions>;\ntype _CheckAccounting = _AssertActions<AccountingActions>;\ntype _CheckBanking = _AssertActions<BankingActions>;\ntype _CheckEsign = _AssertActions<EsignActions>;\ntype _CheckDocker = _AssertActions<DockerActions>;\ntype _CheckStorage = _AssertActions<StorageActions>;\ntype _CheckQueue = _AssertActions<QueueActions>;\ntype _CheckRedis = _AssertActions<RedisActions>;\ntype _CheckOAuth = _AssertActions<OAuthActions>;\ntype _CheckCredentials = _AssertActions<CredentialsActions>;\ntype _CheckOtel = _AssertActions<OtelActions>;\ntype _CheckCLI = _AssertActions<CLIActions>;\ntype _CheckDeepAgent = _AssertActions<DeepAgentActions>;\ntype _CheckDatabase = _AssertActions<DatabaseActions>;\ntype _CheckWikimedia = _AssertActions<WikimediaActions>;\ntype _CheckIconify = _AssertActions<IconifyActions>;\ntype _CheckArxiv = _AssertActions<ArxivActions>;\n\n// ============================================================================\n// Credential Declarations\n// ============================================================================\n\n/** A credential (environment variable) required by an integration. */\nexport interface CredentialDeclaration {\n envVar: string;\n required: boolean;\n description: string;\n /** Alternatives: at least one declaration sharing this group must be set (e.g. SendGrid OR Resend). */\n oneOf?: string;\n}\n\n/**\n * Credential declarations for all integrations.\n *\n * This is the single source of truth for what env vars each service needs.\n * Consumed by:\n * - almadar-service-sync (extracts into services-registry.json)\n * - @almadar-io/terraform (provisions Secret Manager resources)\n * - RuntimeIntegrationManager (validates env configuration)\n */\nexport const serviceCredentials: Record<string, CredentialDeclaration[]> = {\n stripe: [\n { envVar: 'STRIPE_SECRET_KEY', required: true, description: 'Stripe secret API key' },\n { envVar: 'STRIPE_WEBHOOK_SECRET', required: false, description: 'Webhook endpoint signing secret' },\n ],\n youtube: [\n { envVar: 'YOUTUBE_API_KEY', required: true, description: 'YouTube Data API key' },\n ],\n twilio: [\n { envVar: 'TWILIO_ACCOUNT_SID', required: true, description: 'Twilio account SID' },\n { envVar: 'TWILIO_AUTH_TOKEN', required: true, description: 'Twilio auth token' },\n { envVar: 'TWILIO_PHONE_NUMBER', required: false, description: 'Default sender phone number' },\n ],\n email: [\n { envVar: 'SENDGRID_API_KEY', required: false, oneOf: 'provider', description: 'SendGrid API key (use this OR RESEND_API_KEY)' },\n { envVar: 'RESEND_API_KEY', required: false, oneOf: 'provider', description: 'Resend API key (use this OR SENDGRID_API_KEY)' },\n { envVar: 'FROM_EMAIL', required: false, description: 'Default sender email address' },\n ],\n webhook: [\n { envVar: 'WEBHOOK_SIGNING_SECRET', required: false, description: 'Default HMAC-SHA256 signing secret (per-call secret overrides)' },\n { envVar: 'WEBHOOK_TIMEOUT_MS', required: false, description: 'Per-request timeout (ms, default 10000)' },\n ],\n push: [\n { envVar: 'VAPID_PUBLIC_KEY', required: true, description: 'VAPID public key (web-push generate-vapid-keys)' },\n { envVar: 'VAPID_PRIVATE_KEY', required: true, description: 'VAPID private key' },\n { envVar: 'VAPID_SUBJECT', required: true, description: 'VAPID subject (mailto: or https: contact URI)' },\n ],\n calendar: [\n { envVar: 'GOOGLE_CALENDAR_SA_KEY', required: true, description: 'Google service-account key JSON (raw or base64) with calendar scope; store in Secret Manager, bind as env' },\n { envVar: 'GOOGLE_CALENDAR_SUBJECT', required: false, description: 'Workspace user to impersonate (domain-wide delegation); empty = act as the service account' },\n { envVar: 'GOOGLE_CALENDAR_ID', required: false, description: 'Default calendar id (default: primary)' },\n { envVar: 'GOOGLE_CALENDAR_CHANNEL_TOKEN', required: false, description: 'Watch-channel verification token — required to receive inbound calendar hooks (two-way sync)' },\n ],\n drive: [\n { envVar: 'GOOGLE_DRIVE_SA_KEY', required: false, oneOf: 'auth', description: 'Google service-account key JSON (raw or base64) with drive scope — serves reads; store in Secret Manager, bind as env' },\n { envVar: 'GOOGLE_DRIVE_SUBJECT', required: false, description: 'Workspace user to impersonate (domain-wide delegation); empty = act as the service account' },\n { envVar: 'GOOGLE_DRIVE_REFRESH_TOKEN', required: false, oneOf: 'auth', description: 'User OAuth refresh token (drive-consent.mjs) — serves writes; SA uploads are impossible on personal accounts (no SA storage quota)' },\n { envVar: 'GOOGLE_DRIVE_FOLDER_ID', required: false, description: 'Default parent folder for uploads/new folders when the call names none' },\n ],\n metaAds: [\n { envVar: 'META_ACCESS_TOKEN', required: true, description: 'Meta Graph API access token (Marketing API, ads_read)' },\n { envVar: 'META_AD_ACCOUNT_ID', required: false, description: 'Default ad account id (act_…); per-call accountId overrides' },\n ],\n accounting: [],\n banking: [\n { envVar: 'GOCARDLESS_SECRET_ID', required: true, description: 'GoCardless Bank Account Data secret id' },\n { envVar: 'GOCARDLESS_SECRET_KEY', required: true, description: 'GoCardless Bank Account Data secret key' },\n ],\n esign: [\n { envVar: 'DOCUSIGN_BASE_URL', required: true, description: 'DocuSign REST base (e.g. https://demo.docusign.net/restapi/v2.1/accounts/<accountId>)' },\n { envVar: 'DOCUSIGN_ACCESS_TOKEN', required: true, description: 'DocuSign OAuth access token (JWT grant rotation is the deployment concern)' },\n ],\n llm: [\n { envVar: 'ANTHROPIC_API_KEY', required: false, oneOf: 'provider', description: 'Anthropic API key (use this OR OPENAI_API_KEY)' },\n { envVar: 'OPENAI_API_KEY', required: false, oneOf: 'provider', description: 'OpenAI API key (use this OR ANTHROPIC_API_KEY)' },\n { envVar: 'LLM_PROVIDER', required: false, description: 'Chat provider when several keys are set: anthropic | openai | deepseek | kimi | openrouter (default: the first key found)' },\n ],\n 'llm-integration': [\n { envVar: 'ANTHROPIC_API_KEY', required: false, oneOf: 'provider', description: 'Anthropic API key' },\n { envVar: 'OPENAI_API_KEY', required: false, oneOf: 'provider', description: 'OpenAI API key' },\n ],\n ml: [\n { envVar: 'MASAR_URL', required: true, description: 'Base URL of the deployed model-serving orbital' },\n { envVar: 'MASAR_ML_TRAIT', required: true, description: 'Kebab-case trait name the serving orbital exposes its /events route under' },\n ],\n deepagent: [\n { envVar: 'DEEPAGENT_API_URL', required: true, description: 'DeepAgent server URL' },\n { envVar: 'DEEPAGENT_API_KEY', required: false, description: 'DeepAgent API key' },\n ],\n github: [\n { envVar: 'GITHUB_TOKEN', required: true, description: 'GitHub personal access token' },\n { envVar: 'GITHUB_OWNER', required: false, description: 'Default repository owner' },\n { envVar: 'GITHUB_REPO', required: false, description: 'Default repository name' },\n ],\n docker: [\n { envVar: 'DOCKER_HOST', required: false, description: 'Docker daemon host URL' },\n ],\n storage: [\n { envVar: 'STORAGE_ACCESS_KEY_ID', required: true, description: 'S3-compatible access key id' },\n { envVar: 'STORAGE_SECRET_ACCESS_KEY', required: true, description: 'S3-compatible secret access key' },\n { envVar: 'STORAGE_BUCKET', required: true, description: 'Default bucket name' },\n { envVar: 'STORAGE_REGION', required: false, description: 'Region (default us-east-1)' },\n { envVar: 'STORAGE_ENDPOINT', required: false, description: 'Custom S3-compatible endpoint (R2/MinIO/…); empty = AWS S3' },\n { envVar: 'STORAGE_PUBLIC_URL_BASE', required: false, description: 'Base URL for public-acl object links; empty = endpoint-derived' },\n ],\n queue: [\n { envVar: 'QUEUE_URL', required: true, description: 'Message queue connection URL' },\n ],\n redis: [\n { envVar: 'REDIS_URL', required: true, description: 'Redis connection URL' },\n ],\n oauth: [\n { envVar: 'OAUTH_CLIENT_ID', required: true, description: 'OAuth/OIDC client ID' },\n { envVar: 'OAUTH_CLIENT_SECRET', required: true, description: 'OAuth/OIDC client secret' },\n { envVar: 'OAUTH_REDIRECT_URI', required: false, description: 'OIDC redirect URI (default per-call param)' },\n { envVar: 'OIDC_ISSUER_URL', required: false, description: 'OIDC issuer URL (default https://accounts.google.com)' },\n ],\n credentials: [\n { envVar: 'ALMADAR_CREDENTIAL_MASTER_KEY', required: false, description: 'AES-256 master key (64-char hex) enabling the hosted credential store — hold it alone in the platform secret store' },\n { envVar: 'ALMADAR_CREDENTIAL_MASTER_KEY_PREVIOUS', required: false, description: 'Previous master key, present only during a rotation window — decrypts old rows until `credentials.rotate` re-encrypts them' },\n ],\n otel: [\n { envVar: 'OTEL_EXPORTER_OTLP_ENDPOINT', required: true, description: 'OpenTelemetry collector endpoint' },\n { envVar: 'OTEL_SERVICE_NAME', required: false, description: 'Service name for traces' },\n ],\n cli: [],\n // No fixed env vars — connection strings are resolved per-query from the\n // caller-supplied connectionRef, so credentials cannot be declared statically.\n database: [],\n wikimedia: [\n { envVar: 'WIKIMEDIA_USER_AGENT', required: false, description: 'Descriptive User-Agent for the Wikipedia API' },\n { envVar: 'WIKIMEDIA_TIMEOUT_MS', required: false, description: 'Per-request timeout (ms)' },\n ],\n iconify: [\n { envVar: 'ICONIFY_USER_AGENT', required: false, description: 'User-Agent for the Iconify API' },\n { envVar: 'ICONIFY_TIMEOUT_MS', required: false, description: 'Per-request timeout (ms)' },\n ],\n arxiv: [\n { envVar: 'ARXIV_TIMEOUT_MS', required: false, description: 'Per-request timeout (ms)' },\n ],\n rigger: [\n { envVar: 'RIGGER_BASE_URL', required: false, description: 'Local rigger service base URL (default http://127.0.0.1:8181)' },\n { envVar: 'RIGGER_TIMEOUT_MS', required: false, description: 'Per-request timeout (ms)' },\n { envVar: 'RIGGER_GENERATE_TIMEOUT_MS', required: false, description: \"Timeout (ms) for the 'generate' action; a local z-image-turbo run takes ~60-90 s and a kontext edit at 1024px several minutes (default 600000)\" },\n ],\n};\n\n/**\n * Every env var name declared anywhere in `serviceCredentials` — provider\n * secrets and the credential-store's own bootstrap keys alike. The single\n * source a forked verify/harness process scrubs from its env so it can never\n * see, and therefore never spend, a real credential (regardless of which\n * integration mode the fork ends up in).\n */\nexport function allCredentialEnvVars(): Set<string> {\n const vars = new Set<string>();\n for (const decls of Object.values(serviceCredentials)) {\n for (const { envVar } of decls) vars.add(envVar);\n }\n return vars;\n}\n\n/**\n * Bootstrap secrets that must NEVER enter store custody: the master key(s)\n * decrypt the store itself, so seeding them as rows (encrypted under\n * themselves) or stripping them from the env would be circular.\n */\nexport const STORE_BOOTSTRAP_ENV_VARS = new Set([\n 'ALMADAR_CREDENTIAL_MASTER_KEY',\n 'ALMADAR_CREDENTIAL_MASTER_KEY_PREVIOUS',\n]);\n\n/**\n * Every declared PROVIDER credential env var — `allCredentialEnvVars()` minus\n * the store's own bootstrap keys. The store-first custody set (I-31):\n * `RuntimeIntegrationManager` strips these from the base env before merging\n * the store's decrypted values, so every resolution flows the exact store →\n * env → unconfigured path a deployed client app uses.\n */\nexport function declaredCredentialEnvVars(): Set<string> {\n const vars = allCredentialEnvVars();\n for (const bootstrapVar of STORE_BOOTSTRAP_ENV_VARS) vars.delete(bootstrapVar);\n return vars;\n}\n\n/**\n * Declared side-effect-free \"Test connection\" probes, executed by the\n * `credentials.test` action through the normal factory path. Only services\n * with a genuinely cheap, read-only, env-defaulted action carry one; the\n * rest report credential presence + constructor validation.\n */\n/**\n * One inbound-hook mount a service's provider needs in a deployed app:\n * `POST /api/hooks/:provider` verified by the provider's own token/signature,\n * dispatching `event` onto the app bus. A hook is a PROVIDER property — the\n * service invocation is the `.lolo` surface; codegen derives the mount from\n * the app's invoked services × these declarations (I-26).\n */\nexport interface HookDeclaration {\n /** Provider key mounted at POST /api/hooks/:provider */\n provider: string;\n /** Bus event dispatched into the app on a verified callback */\n event: string;\n /** Env var carrying the provider's verification secret/channel token */\n credentialEnv: string;\n /** Factory export in @almadar/integrations: (secret?) => HookProvider */\n providerExport: string;\n}\n\n/**\n * Inbound-hook declarations per service — the single source the registry,\n * codegen, and the playground mount derive from.\n */\nexport const serviceHooks: Record<string, HookDeclaration[]> = {\n calendar: [\n {\n provider: 'google-calendar',\n event: 'CAL_REMOTE_CHANGED',\n credentialEnv: 'GOOGLE_CALENDAR_CHANNEL_TOKEN',\n providerExport: 'googleCalendarHookProvider',\n },\n ],\n};\n\nexport const serviceProbes: Partial<Record<IntegrationName, { action: string; params: ServiceParams }>> = {\n calendar: { action: 'listEvents', params: { maxResults: 1 } },\n drive: { action: 'listFiles', params: { maxResults: 1 } },\n metaAds: { action: 'listCampaigns', params: {} },\n};\n\n/** Backend maturity grade of an integration implementation. */\nexport type IntegrationBackendGrade = 'production' | 'simulated' | 'stub';\n\n/**\n * Honest backend grade per integration — 'simulated' marks an in-memory\n * implementation that must never be mistaken for a real backend. Synced into\n * services-registry.json (`backend`) by `almadar-sync services` and surfaced\n * by the verify service manifest and /health.\n */\nexport const serviceBackends: Record<IntegrationName, IntegrationBackendGrade> = {\n github: 'production',\n stripe: 'production',\n llm: 'production',\n ml: 'production',\n youtube: 'production',\n twilio: 'production',\n email: 'production',\n webhook: 'production',\n push: 'production',\n calendar: 'production',\n drive: 'production',\n metaAds: 'production',\n accounting: 'production',\n banking: 'production',\n esign: 'production',\n docker: 'simulated',\n storage: 'production',\n queue: 'simulated',\n redis: 'simulated',\n oauth: 'production',\n credentials: 'production',\n otel: 'simulated',\n cli: 'production',\n deepagent: 'production',\n database: 'production',\n wikimedia: 'production',\n iconify: 'production',\n arxiv: 'production',\n rigger: 'production',\n};\n","import type { IntegrationConfig, IntegrationResult, IntegrationParams } from './types';\nimport { BaseIntegration } from './core/BaseIntegration';\nimport { getIntegration } from './registry';\nimport { IntegrationError } from './types';\nimport { serviceCredentials } from './contracts';\n\n/**\n * Optional per-call context for principal-scoped resolution (W4) and\n * caller-identity enforcement (I-25). The default — absent — means the\n * tenant/app-wide credential set with an anonymous caller; role-gated\n * integrations (credentials) fail closed without it. Additive: every\n * existing call site is untouched.\n */\nexport interface IntegrationCallContext {\n principal?: string;\n /** The caller's roster role, from the same identity entity-ACL enforces against. */\n role?: string;\n}\n\n/** Cache key for (name, principal?) — NUL never appears in either part. */\nfunction instanceKey(name: string, principal?: string): string {\n return principal ? `${name}\\u0000${principal}` : name;\n}\n\n/**\n * Factory for creating and managing integration instances\n */\nexport class IntegrationFactory {\n private instances: Map<string, BaseIntegration> = new Map();\n private configs: Map<string, IntegrationConfig> = new Map();\n\n /**\n * Configure an integration (doesn't instantiate yet). A `principal` scopes\n * the config to that principal; the app-wide config (no principal) is the\n * fallback for every principal.\n */\n configure(name: string, config: Omit<IntegrationConfig, 'name'>, principal?: string): void {\n this.configs.set(instanceKey(name, principal), { name, ...config });\n }\n\n /**\n * Get or create an integration instance. Principal-scoped lookups fall\n * back to the app-wide config when no per-principal config exists.\n */\n get(name: string, principal?: string): BaseIntegration {\n const key = instanceKey(name, principal);\n const cached = this.instances.get(key);\n if (cached) {\n return cached;\n }\n\n // Get constructor\n const Constructor = getIntegration(name);\n if (!Constructor) {\n throw new IntegrationError(`Unknown integration: ${name}. Make sure it's imported.`, 'UNKNOWN_INTEGRATION');\n }\n\n // Get config — per-principal first, app-wide fallback\n const config = this.configs.get(key) ?? this.configs.get(name);\n if (!config) {\n const envVars = (serviceCredentials[name] ?? []).map((c) => c.envVar);\n const needs = envVars.length > 0 ? ` Set ${envVars.join(' / ')}.` : '';\n throw new IntegrationError(`Integration not configured: ${name}.${needs}`, 'NOT_CONFIGURED');\n }\n\n // Create instance\n const instance = new Constructor(config);\n this.instances.set(key, instance);\n\n return instance;\n }\n\n /**\n * Execute an action on an integration\n */\n async execute(\n integration: string,\n action: string,\n params: IntegrationParams,\n context?: IntegrationCallContext,\n ): Promise<IntegrationResult> {\n const instance = this.get(integration, context?.principal);\n return await instance.execute(action, params, context);\n }\n\n /**\n * Check if integration is configured\n */\n isConfigured(name: string, principal?: string): boolean {\n return this.configs.has(instanceKey(name, principal)) || this.configs.has(name);\n }\n\n /**\n * Register an integration instance directly (used by mock infrastructure)\n */\n registerInstance(name: string, instance: BaseIntegration, principal?: string): void {\n this.instances.set(instanceKey(name, principal), instance);\n }\n\n /**\n * Drop the cached instance(s) for a name so the next `get` rebuilds from\n * the current config — how a credential change goes live without restart.\n * Configs are kept; without a name, every instance is dropped.\n */\n invalidate(name?: string): void {\n if (name === undefined) {\n this.instances.clear();\n return;\n }\n for (const key of this.instances.keys()) {\n if (key === name || key.startsWith(`${name}\\u0000`)) {\n this.instances.delete(key);\n }\n }\n }\n\n /**\n * Clear all instances (useful for testing)\n */\n clear(): void {\n this.instances.clear();\n }\n\n /**\n * Clear all instances and configs\n */\n reset(): void {\n this.instances.clear();\n this.configs.clear();\n }\n}\n\nlet _factory: IntegrationFactory | null = null;\n\nexport function getIntegrationFactory(): IntegrationFactory {\n if (!_factory) {\n _factory = new IntegrationFactory();\n }\n return _factory;\n}\n\nexport function resetIntegrationFactory(): void {\n _factory?.reset();\n _factory = null;\n}\n","import { IntegrationFactory } from '../factory';\nimport { MockIntegration } from './MockIntegration';\nimport { registerIntegration } from '../registry';\nimport type { IntegrationParams } from '../types';\n\n/**\n * Mock integration factory for testing\n */\nexport class MockIntegrationFactory extends IntegrationFactory {\n constructor() {\n super();\n \n // Register mock integration for all known services\n const services = ['stripe', 'youtube', 'twilio', 'email', 'llm', 'deepagent'];\n services.forEach((service) => {\n registerIntegration(`mock-${service}`, MockIntegration);\n \n // Configure with mock config\n this.configure(service, {\n env: {},\n });\n });\n }\n\n /**\n * Set mock response for an integration action\n */\n setMockResponse(integration: string, action: string, data: unknown): void {\n const instance = this.get(integration) as MockIntegration;\n instance.setResponse(action, data);\n }\n\n /**\n * Get calls made to an integration\n */\n getMockCalls(\n integration: string,\n ): Array<{ action: string; params: IntegrationParams }> {\n const instance = this.get(integration) as MockIntegration;\n return instance.getCalls();\n }\n\n /**\n * Clear calls for an integration\n */\n clearMockCalls(integration: string): void {\n const instance = this.get(integration) as MockIntegration;\n instance.clearCalls();\n }\n}\n"]}
1
+ {"version":3,"sources":["../../src/types.ts","../../src/core/logger.ts","../../src/core/validation.ts","../../src/core/retry.ts","../../src/core/BaseIntegration.ts","../../src/mocks/MockIntegration.ts","../../src/registry.ts","../../src/contracts.ts","../../src/factory.ts","../../src/mocks/MockIntegrationFactory.ts"],"names":["createLogger"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Core types for Almadar integrations\n */\n\n/**\n * Configuration for an integration instance\n */\nexport interface IntegrationConfig {\n /** Integration name (matches registry) */\n name: string;\n\n /** Environment variables (API keys, secrets) */\n env: Record<string, string>;\n\n /** Optional logger */\n logger?: IntegrationLogger;\n\n /** Optional rate limiting config */\n rateLimit?: {\n requestsPerSecond: number;\n burstSize: number;\n };\n\n /** Optional timeout (ms) */\n timeout?: number;\n\n /** Optional retry config */\n retry?: {\n maxAttempts: number;\n backoffMs: number;\n maxBackoffMs?: number;\n };\n}\n\n/**\n * Result of an integration action call\n */\nexport interface IntegrationResult<T = unknown> {\n /** Success flag */\n success: boolean;\n\n /** Response data (on success) */\n data?: T;\n\n /** Error (on failure) */\n error?: IntegrationError;\n\n /** Metadata (timing, retries, etc.) */\n metadata: {\n integration: string;\n action: string;\n duration: number;\n retries: number;\n timestamp: number;\n };\n}\n\n/**\n * Integration error codes\n */\nexport type IntegrationErrorCode =\n | 'VALIDATION_ERROR'\n | 'AUTH_ERROR'\n | 'RATE_LIMIT_ERROR'\n | 'TIMEOUT_ERROR'\n | 'NETWORK_ERROR'\n | 'SERVICE_ERROR'\n /** The service is known but its credentials/config are absent. */\n | 'NOT_CONFIGURED'\n /** No integration is registered under this name. */\n | 'UNKNOWN_INTEGRATION'\n | 'UNKNOWN_ERROR';\n\n/**\n * Integration error\n */\nexport class IntegrationError extends Error {\n code: IntegrationErrorCode;\n integration?: string;\n action?: string;\n details?: unknown;\n\n constructor(\n message: string,\n code: IntegrationErrorCode = 'UNKNOWN_ERROR',\n details?: unknown,\n ) {\n super(message);\n this.name = 'IntegrationError';\n this.code = code;\n this.details = details;\n }\n\n toJSON() {\n return {\n name: this.name,\n message: this.message,\n code: this.code,\n integration: this.integration,\n action: this.action,\n details: this.details,\n };\n }\n}\n\nimport type { LogMeta } from '@almadar/core';\n\n/** Re-export LogMeta from @almadar/core as the canonical log metadata type. */\nexport type { LogMeta };\n\n/**\n * Logger interface\n */\nexport interface IntegrationLogger {\n debug(message: string, meta?: LogMeta): void;\n info(message: string, meta?: LogMeta): void;\n warn(message: string, meta?: LogMeta): void;\n error(message: string, meta?: LogMeta): void;\n}\n\n/**\n * Integration action parameters.\n * Each integration's execute() method receives params as this type.\n * Individual methods cast to specific param shapes from their contracts.\n *\n * Per-key value union is widened to admit `@almadar/core`'s `FieldValue`\n * shape (entity / payload values flowing into call-service args): `Date`\n * for date fields, `IntegrationParams[]` for nested object arrays, and a\n * raw-value array catch-all so `string[]` / `number[]` / mixed payload\n * arrays satisfy the index signature without requiring `as unknown as`\n * casts at the call site. Adapters narrow these to their canonical input\n * types (typically JSON-string serialisation for `Date`).\n */\nexport type IntegrationParamValue =\n | string\n | number\n | boolean\n | Date\n | null\n | undefined\n | ReadonlyArray<IntegrationParamValue>\n | { readonly [key: string]: IntegrationParamValue };\n\nexport interface IntegrationParams {\n [key: string]: IntegrationParamValue;\n}\n\n/**\n * Validation result\n */\nexport interface ValidationResult {\n valid: boolean;\n errors: ValidationError[];\n}\n\n/**\n * Validation error\n */\nexport interface ValidationError {\n param: string;\n message: string;\n}\n","import { createLogger, type Logger } from '@almadar/logger';\nimport type { IntegrationLogger, LogMeta } from '../types';\n\n/**\n * Console-based logger implementation.\n *\n * Routes through `@almadar/logger`'s shared gate so namespace filtering\n * (`ALMADAR_DEBUG`, `globalThis.__ALMADAR_DEBUG__`) and the production\n * level default (WARN+) apply uniformly with the rest of `@almadar/*`.\n *\n * The constructor's `level` argument is retained for backwards\n * compatibility but is now a no-op — the active level is owned by the\n * shared logger (compile-time + env). To filter integration logs at\n * runtime, set `globalThis.__ALMADAR_DEBUG__ = 'almadar:integrations:*'`.\n */\nexport class ConsoleLogger implements IntegrationLogger {\n private readonly log: Logger;\n\n constructor(_level: 'debug' | 'info' | 'warn' | 'error' = 'info') {\n void _level;\n this.log = createLogger('almadar:integrations');\n }\n\n debug(message: string, meta?: LogMeta): void {\n this.log.debug(message, meta);\n }\n info(message: string, meta?: LogMeta): void {\n this.log.info(message, meta);\n }\n warn(message: string, meta?: LogMeta): void {\n this.log.warn(message, meta);\n }\n error(message: string, meta?: LogMeta): void {\n this.log.error(message, meta);\n }\n}\n","import type { ValidationResult, ValidationError, IntegrationParams } from '../types';\n\n// Import integrators registry from the package main export (JSON is inlined in the bundle)\nimport { integratorsRegistry } from '@almadar/core/patterns';\n\ninterface ActionParam {\n name: string;\n type: string;\n required?: boolean;\n description?: string;\n}\n\ninterface ActionDef {\n name: string;\n description?: string;\n params: ActionParam[];\n}\n\ninterface IntegratorEntry {\n name: string;\n description?: string;\n category?: string;\n actions: ActionDef[];\n}\n\ntype IntegratorsRegistry = Record<string, {\n version?: string;\n exportedAt?: string;\n integrators: Record<string, IntegratorEntry>;\n}>;\n\n/** Envelope keys owned by the call-service form, never action params. */\nconst RESERVED_ENVELOPE_KEYS = new Set(['emit', 'onSuccess', 'onError', 'timeout']);\n\n/** Thrown by an integration's dispatch default/miss arm — contract↔dispatch parity marker. */\nexport class UnknownActionError extends Error {\n integration: string;\n action: string;\n\n constructor(integration: string, action: string) {\n super(`Unknown ${integration} action: ${action}`);\n this.name = 'UnknownActionError';\n this.integration = integration;\n this.action = action;\n }\n}\n\n/**\n * Validate action params against registry schema\n */\nexport function validateParams(\n integration: string,\n action: string,\n params: IntegrationParams,\n): ValidationResult {\n const typedRegistry = integratorsRegistry as IntegratorsRegistry[string];\n const registry = typedRegistry.integrators[integration];\n\n if (!registry) {\n return {\n valid: false,\n errors: [\n {\n param: 'integration',\n message: `Unknown integration: ${integration}`,\n },\n ],\n };\n }\n\n const actionDef = registry.actions.find((a: ActionDef) => a.name === action);\n\n if (!actionDef) {\n return {\n valid: false,\n errors: [{ param: 'action', message: `Unknown action: ${action}` }],\n };\n }\n\n const errors: ValidationError[] = [];\n\n // Unknown-key check (I-13 runtime parity with orb validate): a param the\n // contract does not declare is an error, not a silent drop. Envelope keys\n // belong to the call-service form itself, never to the action contract.\n const declaredNames = new Set(actionDef.params.map((p: ActionParam) => p.name));\n for (const key of Object.keys(params)) {\n if (RESERVED_ENVELOPE_KEYS.has(key)) continue;\n if (!declaredNames.has(key)) {\n errors.push({\n param: key,\n message: `Unknown parameter: ${key} (declared: ${[...declaredNames].sort().join(', ')})`,\n });\n }\n }\n\n // Check required params. A key holding `undefined` is an absent param: a\n // service atom sends every declared param and an unset one arrives undefined.\n for (const paramDef of actionDef.params) {\n const provided = params[paramDef.name] !== undefined;\n if (paramDef.required && !provided) {\n errors.push({\n param: paramDef.name,\n message: `Missing required parameter: ${paramDef.name}`,\n });\n }\n\n // Type validation\n if (provided) {\n const value = params[paramDef.name];\n const expectedType = paramDef.type;\n const actualType = typeof value;\n\n if (expectedType === 'number' && actualType !== 'number') {\n errors.push({\n param: paramDef.name,\n message: `Expected ${expectedType}, got ${actualType}`,\n });\n }\n\n if (expectedType === 'string' && actualType !== 'string') {\n errors.push({\n param: paramDef.name,\n message: `Expected ${expectedType}, got ${actualType}`,\n });\n }\n\n if (expectedType === 'array' && !Array.isArray(value)) {\n errors.push({\n param: paramDef.name,\n message: `Expected array, got ${actualType}`,\n });\n }\n\n if (\n expectedType === 'object' &&\n (actualType !== 'object' || Array.isArray(value) || value === null)\n ) {\n errors.push({\n param: paramDef.name,\n message: `Expected object, got ${actualType}`,\n });\n }\n }\n }\n\n return {\n valid: errors.length === 0,\n errors,\n };\n}\n","import type { IntegrationError, IntegrationErrorCode } from '../types';\n\n/**\n * Retry configuration\n */\nexport interface RetryConfig {\n maxAttempts: number;\n backoffMs: number;\n maxBackoffMs?: number;\n retryableErrors?: IntegrationErrorCode[];\n}\n\n/**\n * Execute a function with retry logic\n */\nexport async function withRetry<T>(\n fn: () => Promise<T>,\n config: RetryConfig,\n): Promise<T> {\n const {\n maxAttempts,\n backoffMs,\n maxBackoffMs = 30000,\n retryableErrors,\n } = config;\n\n let lastError: unknown;\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n return await fn();\n } catch (error) {\n lastError = error;\n\n // Check if error is retryable\n if (\n error &&\n typeof error === 'object' &&\n 'code' in error &&\n retryableErrors\n ) {\n const integrationError = error as IntegrationError;\n if (!retryableErrors.includes(integrationError.code)) {\n throw error;\n }\n }\n\n // Last attempt, throw\n if (attempt === maxAttempts) {\n throw error;\n }\n\n // Wait before retry (exponential backoff)\n const delay = Math.min(\n backoffMs * Math.pow(2, attempt - 1),\n maxBackoffMs,\n );\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n\n throw lastError;\n}\n","import type {\n IntegrationConfig,\n IntegrationResult,\n IntegrationLogger,\n IntegrationParams,\n ValidationError,\n} from '../types';\nimport { IntegrationError } from '../types';\nimport { ConsoleLogger } from './logger';\nimport { validateParams } from './validation';\nimport { withRetry } from './retry';\nimport type { IntegrationCallContext } from '../factory';\n\n/**\n * Base class for all integrations\n */\nexport abstract class BaseIntegration {\n protected config: IntegrationConfig;\n protected logger: IntegrationLogger;\n\n constructor(config: IntegrationConfig) {\n this.config = config;\n this.logger = config.logger || new ConsoleLogger();\n }\n\n /**\n * Execute an action. `context` carries the caller's identity when the host\n * supplies one; integrations that gate actions by role read it and fail\n * closed when it is absent.\n */\n abstract execute(\n action: string,\n params: IntegrationParams,\n context?: IntegrationCallContext,\n ): Promise<IntegrationResult>;\n\n /**\n * Validate action params against registry\n */\n protected validateParams(\n action: string,\n params: IntegrationParams,\n ): ReturnType<typeof validateParams> {\n return validateParams(this.config.name, action, params);\n }\n\n /** A rejected call: a real IntegrationError naming every failing param (kept in `details`). */\n protected validationFailure(action: string, errors: ValidationError[]): IntegrationResult {\n const reasons = errors.map((e) => `${e.param}: ${e.message}`).join('; ');\n const error = new IntegrationError(`Validation failed for ${this.config.name}.${action}: ${reasons}`, 'VALIDATION_ERROR', errors);\n error.integration = this.config.name;\n error.action = action;\n return { success: false, error, metadata: this.createMetadata(action, 0) };\n }\n\n /**\n * Handle errors uniformly\n */\n protected handleError(action: string, error: unknown): IntegrationResult {\n this.logger.error(`Integration error in ${this.config.name}.${action}`, {\n error: error instanceof Error ? error : new Error(String(error)),\n });\n\n const integrationError =\n error instanceof Error\n ? error\n : new Error(String(error));\n\n return {\n success: false,\n error: integrationError as IntegrationError,\n metadata: this.createMetadata(action, 0, 0),\n };\n }\n\n /**\n * Create metadata for result\n */\n protected createMetadata(\n action: string,\n duration: number,\n retries: number = 0,\n ): IntegrationResult['metadata'] {\n return {\n integration: this.config.name,\n action,\n duration,\n retries,\n timestamp: Date.now(),\n };\n }\n\n /**\n * Execute with retry logic\n */\n protected async executeWithRetry<T>(\n fn: () => Promise<T>,\n ): Promise<T> {\n if (!this.config.retry) {\n return fn();\n }\n\n return withRetry(fn, {\n maxAttempts: this.config.retry.maxAttempts,\n backoffMs: this.config.retry.backoffMs,\n maxBackoffMs: this.config.retry.maxBackoffMs,\n retryableErrors: [\n 'TIMEOUT_ERROR',\n 'NETWORK_ERROR',\n 'RATE_LIMIT_ERROR',\n ],\n });\n }\n}\n","import { BaseIntegration } from '../core/BaseIntegration';\nimport type { IntegrationConfig, IntegrationResult, IntegrationParams } from '../types';\nimport { IntegrationError } from '../types';\n\n/**\n * Mock integration for testing\n */\nexport class MockIntegration extends BaseIntegration {\n private responses: Map<string, unknown> = new Map();\n private calls: Array<{ action: string; params: IntegrationParams }> =\n [];\n\n constructor(config: IntegrationConfig) {\n super(config);\n }\n\n /**\n * Set mock response for an action\n */\n setResponse(action: string, data: unknown): void {\n this.responses.set(action, data);\n }\n\n /**\n * Get all calls made to this integration\n */\n getCalls(): Array<{ action: string; params: IntegrationParams }> {\n return this.calls;\n }\n\n /**\n * Clear all calls\n */\n clearCalls(): void {\n this.calls = [];\n }\n\n async execute(\n action: string,\n params: IntegrationParams,\n ): Promise<IntegrationResult> {\n // Record the call\n this.calls.push({ action, params });\n\n const data = this.responses.get(action);\n\n if (!data) {\n return {\n success: false,\n error: new IntegrationError(\n `No mock response for action: ${action}`,\n 'UNKNOWN_ERROR',\n ),\n metadata: this.createMetadata(action, 0),\n };\n }\n\n return {\n success: true,\n data,\n metadata: this.createMetadata(action, 0),\n };\n }\n}\n","import type { IntegrationConfig } from './types';\nimport { BaseIntegration } from './core/BaseIntegration';\n\n/**\n * Integration constructor type\n */\nexport type IntegrationConstructor = new (\n config: IntegrationConfig,\n) => BaseIntegration;\n\n/**\n * Integration registry (populated as integrations are imported)\n */\nexport const INTEGRATION_REGISTRY: Record<string, IntegrationConstructor> = {};\n\n/**\n * Register an integration\n */\nexport function registerIntegration(\n name: string,\n constructor: IntegrationConstructor,\n): void {\n INTEGRATION_REGISTRY[name] = constructor;\n}\n\n/**\n * Get integration constructor by name\n */\nexport function getIntegration(\n name: string,\n): IntegrationConstructor | undefined {\n return INTEGRATION_REGISTRY[name];\n}\n\n/**\n * Check if integration is known\n */\nexport function isKnownIntegration(name: string): boolean {\n return name in INTEGRATION_REGISTRY;\n}\n\n/**\n * Get all registered integration names\n */\nexport function getRegisteredIntegrations(): string[] {\n return Object.keys(INTEGRATION_REGISTRY);\n}\n","/**\n * ServiceContract type definitions for all @almadar/integrations.\n *\n * Each integration defines its own action map as a Record<string, ServiceAction>,\n * suitable for use with ServiceContract<T> from @almadar/core.\n *\n * Types are simplified representations of the actual params/results. They capture\n * the contract shape without pulling in third-party SDK types (Stripe, Twilio, etc.).\n *\n * @packageDocumentation\n */\n\nimport type { EventPayload, ServiceAction, ServiceEvents, ServiceParams, ServiceParamsValue } from '@almadar/core';\nimport type { IntegrationParams } from './types';\nimport type { DatabaseQueryParamValue, DatabaseRow } from './integrations/database/types';\n\n// ============================================================================\n// GitHub\n// ============================================================================\n\nexport type GitHubActions = {\n /** Clone a repository via the git CLI into a local target directory, using the configured token for auth. */\n cloneRepo: {\n params: {\n /** HTTPS clone URL, e.g. https://github.com/owner/repo. Owner/repo are parsed from it when not preconfigured. */\n repoUrl: string;\n /** Local directory to clone into. */\n targetDir: string;\n /** Branch to check out after cloning; defaults to the repo's default branch. */\n branch?: string;\n };\n result: { message: string };\n };\n /** Create a new local branch via the git CLI in the configured working directory. */\n createBranch: {\n params: {\n /** Name of the branch to create. */\n branchName: string;\n /** Branch to branch from; defaults to the current branch. */\n baseBranch?: string;\n };\n result: { message: string };\n };\n /** Commit staged (or given) changes via the git CLI in the configured working directory. */\n commit: {\n params: {\n /** Commit message. */\n message: string;\n /** Specific files to commit; when omitted, all changes are committed. */\n files?: string[];\n };\n result: { message: string };\n };\n /** Push a local branch to the remote via the git CLI, using the configured token for auth. */\n push: {\n params: {\n /** Branch name to push. */\n branchName: string;\n /** Force-push (overwrites remote history); never set true in an agent context. */\n force?: boolean;\n };\n result: { message: string };\n };\n /** Open a pull request via the GitHub REST API. */\n createPR: {\n params: {\n /** Pull request title. */\n title: string;\n /** Pull request description body. */\n body: string;\n /** Branch the PR merges into, e.g. main. */\n baseBranch: string;\n /** Branch the PR merges from (the feature branch). */\n headBranch: string;\n /** Open as a draft PR instead of ready-for-review. */\n draft?: boolean;\n };\n result: { number: number; url: string; title: string };\n };\n /** List review comments on a pull request via the GitHub REST API. */\n getPRComments: {\n params: {\n /** Pull request number. */\n prNumber: number;\n };\n result: { comments: Array<{ id: number; body: string; user: string }> };\n };\n /** List issues on the configured repository via the GitHub REST API. */\n listIssues: {\n params: {\n /** Issue state filter: 'open', 'closed', or 'all'; defaults to open. */\n state?: string;\n /** Only issues carrying all of these labels. */\n labels?: string[];\n /** Page size for the GitHub API request. */\n per_page?: number;\n };\n result: { issues: Array<{ number: number; title: string; state: string }> };\n };\n /** Fetch a single issue's details via the GitHub REST API. */\n getIssue: {\n params: {\n /** Issue number. */\n issueNumber: number;\n };\n result: { number: number; title: string; body: string; state: string };\n };\n /** Read a file's content from a repository at a given ref (declared intent — dispatch is being wired). */\n getFile: {\n params: {\n /** Repository owner (user or org). */\n owner: string;\n /** Repository name. */\n repo: string;\n /** File path within the repository. */\n path: string;\n /** Branch, tag, or commit SHA to read from; defaults to the default branch. */\n ref?: string;\n };\n result: { path: string; content: string; sha: string; size: number };\n };\n /** List commits on a repository, optionally filtered by file path (declared intent — dispatch is being wired). */\n listCommits: {\n params: {\n /** Repository owner (user or org). */\n owner: string;\n /** Repository name. */\n repo: string;\n /** Only commits touching this file path. */\n path?: string;\n /** Branch, tag, or commit SHA to start from; defaults to the default branch. */\n ref?: string;\n /** Page size for the GitHub API request. */\n per_page?: number;\n };\n result: { commits: Array<{ sha: string; message: string; author: string; date: string }> };\n };\n /** Open a new issue on a repository (declared intent — dispatch is being wired). */\n createIssue: {\n params: {\n /** Repository owner (user or org). */\n owner: string;\n /** Repository name. */\n repo: string;\n /** Issue title. */\n title: string;\n /** Issue body/description. */\n body?: string;\n /** Labels to apply to the new issue. */\n labels?: string[];\n };\n result: { number: number; title: string; url: string };\n };\n};\n\n// ============================================================================\n// Stripe\n// ============================================================================\n\nexport type StripeActions = {\n /**\n * Create a Stripe PaymentIntent for a one-off charge.\n * @synonyms charge, pay\n */\n createPaymentIntent: {\n params: {\n /** Charge amount in the currency's smallest unit (e.g. cents for USD). */\n amount: number;\n /** ISO 4217 currency code, e.g. usd. */\n currency: string;\n /** Arbitrary key-value tags attached to the PaymentIntent (string values only). */\n metadata?: Record<string, string>;\n };\n result: { id: string; clientSecret: string; status: string; amount: number; currency: string };\n };\n /** Confirm a previously created PaymentIntent, attempting to complete the charge. */\n confirmPayment: {\n params: {\n /** The PaymentIntent id returned by createPaymentIntent. */\n paymentIntentId: string;\n };\n result: { id: string; status: string };\n };\n /** Refund a charged PaymentIntent, in full or in part. */\n refund: {\n params: {\n /** The PaymentIntent id to refund. */\n paymentIntentId: string;\n /** Amount to refund in the currency's smallest unit; omit for a full refund. */\n amount?: number;\n };\n result: { id: string; status: string; amount: number };\n };\n};\n\n// ============================================================================\n// LLM\n// ============================================================================\n\nexport type LLMIntegrationActions = {\n /** Generate free-form text from a prompt via the configured LLM provider (default 'You are a helpful assistant.' system prompt). */\n generate: {\n params: {\n /** The user/task prompt sent to the model. */\n userPrompt: string;\n /** System prompt steering the model's behavior; defaults to a generic assistant prompt. */\n systemPrompt?: string;\n /** Model id override; when set, a fresh client is built for this call instead of the shared one. */\n model?: string;\n /** Sampling temperature (0-1ish, provider-dependent); higher is more random. */\n temperature?: number;\n /** Maximum tokens to generate; defaults to 1024. */\n maxTokens?: number;\n };\n result: {\n content: string;\n usage: { promptTokens?: number; completionTokens?: number; totalTokens?: number; tokens?: number };\n };\n };\n /** Classify text into exactly one of a caller-supplied category list, with a confidence score and reasoning. */\n classify: {\n params: {\n /** Text to classify. */\n text: string;\n /** Closed set of category labels the model must choose from. */\n categories: string[];\n /** Model id override. */\n model?: string;\n };\n result: { category: string; confidence: number; reasoning: string };\n };\n /** Extract structured data from text according to a JSON-schema-shaped description; returned data is not validated against the schema. */\n extract: {\n params: {\n /** Source text to extract data from. */\n text: string;\n /** JSON-schema-like description of the fields to extract; passed to the model as a prompt, not enforced structurally. */\n schema: IntegrationParams;\n /** Model id override. */\n model?: string;\n };\n result: IntegrationParams;\n };\n /** Summarize text, optionally constrained by length and style, returning a summary plus key points. */\n summarize: {\n params: {\n /** Text to summarize. */\n text: string;\n /** Target maximum summary length in words. */\n maxLength?: number;\n /** Summary style: 'bullet' for bullet points, 'detailed' for thorough, otherwise concise. */\n style?: string;\n /** Model id override. */\n model?: string;\n };\n result: { summary: string; keyPoints: string[] };\n };\n /** Generate embedding vectors for an array of texts via the configured embedding model (default baai/bge-base-en-v1.5 on OpenRouter, independent of the chat provider). */\n embed: {\n params: {\n /** Texts to embed, one vector per entry, in order. */\n texts: string[];\n /** Embedding model id override. */\n model?: string;\n };\n result: { embeddings: number[][] };\n };\n};\n\n// ============================================================================\n// ML (model inference)\n// ============================================================================\n\nexport type MLActions = {\n /** Run inference against a deployed model-serving orbital by posting an INFER event and reading the prediction back out of the emitted INFERRED result; may cold-start (up to 60s default timeout). */\n infer: {\n params: {\n /** Checkpoint or model identifier the serving orbital resolves. */\n model: string;\n /** JSON-safe model input payload. */\n input: ServiceParamsValue;\n };\n result: { output: ServiceParamsValue; confidence: number; violations: ServiceParamsValue[] };\n };\n};\n\n// ============================================================================\n// YouTube\n// ============================================================================\n\nexport type YouTubeActions = {\n /** Search YouTube via the Data API and return matching video snippets. */\n search: {\n params: {\n /** Search query text. */\n query: string;\n /** Maximum number of results; defaults to 10. */\n maxResults?: number;\n /** Resource type filter passed to the Data API (e.g. 'video', 'channel', 'playlist'). */\n type?: string;\n };\n result: Array<{ videoId: string; title: string; thumbnail: string; description: string }>;\n };\n /** Fetch a video's snippet and statistics via the YouTube Data API. */\n getVideo: {\n params: {\n /** YouTube video id. */\n videoId: string;\n };\n result: { title: string; description: string; viewCount: string; likeCount: string };\n };\n /** Fetch a channel's snippet and statistics via the YouTube Data API. */\n getChannel: {\n params: {\n /** YouTube channel id. */\n channelId: string;\n };\n result: { name: string; description: string; subscriberCount: string };\n };\n};\n\n// ============================================================================\n// Twilio\n// ============================================================================\n\nexport type TwilioActions = {\n /**\n * Send an SMS message via Twilio.\n * @synonyms text message, text\n */\n sendSMS: {\n params: {\n /** Destination phone number, E.164 format. */\n to: string;\n /** Message text. */\n body: string;\n /** Sender phone number; defaults to TWILIO_PHONE_NUMBER. */\n from?: string;\n };\n result: { sid: string; status: string };\n };\n /** Send a WhatsApp message via Twilio, using the configured phone number as sender on both ends. */\n sendWhatsApp: {\n params: {\n /** Destination phone number, E.164 format (sent as whatsapp:number). */\n to: string;\n /** Message text. */\n body: string;\n };\n result: { sid: string; status: string };\n };\n};\n\n// ============================================================================\n// Email\n// ============================================================================\n\nexport type EmailActions = {\n /** Send a transactional email via the configured provider (SendGrid or Resend). */\n send: {\n params: {\n /** Recipient email address. */\n to: string;\n /** Email subject line. */\n subject: string;\n /** Plain-text (or HTML, if `htmlBody` is absent) body content. */\n body: string;\n /** Sender address; defaults to FROM_EMAIL. */\n from?: string;\n /** HTML content; when present `body` becomes the plain-text alternative. */\n htmlBody?: string;\n /** Reply-To address. */\n replyTo?: string;\n /** Provider template reference (SendGrid dynamic templates; Resend rejects it loudly). */\n templateId?: string;\n };\n result: { id: string; status: string };\n };\n};\n\n// ============================================================================\n// Webhook\n// ============================================================================\n\nexport type WebhookActions = {\n /** POST a JSON event notification to an external URL, HMAC-SHA256-signed when a secret is configured; 5xx responses retry, 4xx do not. */\n send: {\n params: {\n /** Destination URL to POST to. */\n url: string;\n /** Event name, carried in the body and the X-Almadar-Event header. */\n event: string;\n /** Event payload; defaults to an empty object. */\n payload?: Record<string, string>;\n /** Per-call HMAC signing secret, overriding WEBHOOK_SIGNING_SECRET. */\n secret?: string;\n };\n result: { status: number; ok: boolean; durationMs: number };\n };\n};\n\n// ============================================================================\n// Push (Web Push / VAPID)\n// ============================================================================\n\nexport type PushActions = {\n /** Send a VAPID-signed Web Push notification to a browser PushSubscription endpoint. */\n send: {\n params: {\n /** Browser PushSubscription: endpoint URL plus the p256dh/auth encryption keys. */\n subscription: { endpoint: string; keys: { p256dh: string; auth: string } };\n /** Notification title. */\n title: string;\n /** Notification body text. */\n body: string;\n /** URL to open when the notification is clicked. */\n url?: string;\n /** Notification icon URL. */\n icon?: string;\n };\n result: {\n statusCode: number;\n ok: boolean;\n /** True on a 404/410 push-service response — the subscription is gone and must be pruned, never retried. */\n expired: boolean;\n };\n };\n};\n\n// ============================================================================\n// Calendar (Google Calendar, service account w/ domain-wide delegation)\n// ============================================================================\n\nexport type CalendarActions = {\n /** List events on a Google Calendar; pass `syncToken` for incremental sync instead of a time window (the API rejects combining them). */\n listEvents: {\n params: {\n /** Calendar id; defaults to GOOGLE_CALENDAR_ID (or 'primary'). */\n calendarId?: string;\n /** ISO start of the time window (ignored when `syncToken` is set). */\n timeMin?: string;\n /** ISO end of the time window (ignored when `syncToken` is set). */\n timeMax?: string;\n /** Incremental-sync token from a previous call's `nextSyncToken`; supersedes timeMin/timeMax. */\n syncToken?: string;\n /** Maximum events to return; defaults to 250. */\n maxResults?: number;\n };\n result: {\n events: Array<{ id: string; summary: string; description: string; location: string; start: string; end: string; status: string; updated: string }>;\n /** Token to pass as `syncToken` on the next call; null when incremental sync isn't available for this response. */\n nextSyncToken: string | null;\n };\n };\n /** Create a calendar event; an all-day event uses a 10-char (YYYY-MM-DD) start with no `end`/`durationMinutes`, defaulting to the next day. */\n createEvent: {\n params: {\n /** Calendar id; defaults to GOOGLE_CALENDAR_ID. */\n calendarId?: string;\n /** Event title. */\n summary: string;\n /** Event description. */\n description?: string;\n /** Event location text. */\n location?: string;\n /** ISO start (10 chars = all-day date, longer = dateTime). */\n start: string;\n /** ISO end; if omitted, derived from `durationMinutes` or the all-day next-day convention. */\n end?: string;\n /** Duration in minutes used to derive `end` when `end` is omitted (non-all-day events only). */\n durationMinutes?: number;\n };\n result: { id: string; status: string; htmlLink: string };\n };\n /** Patch an existing event's fields; only fields present in params are changed. */\n updateEvent: {\n params: {\n /** Calendar id; defaults to GOOGLE_CALENDAR_ID. */\n calendarId?: string;\n /** Id of the event to update. */\n eventId: string;\n /** New event title. */\n summary?: string;\n /** New event description. */\n description?: string;\n /** New event location text. */\n location?: string;\n /** New ISO start. */\n start?: string;\n /** New ISO end. */\n end?: string;\n };\n result: { id: string; status: string };\n };\n /** Delete a calendar event. */\n deleteEvent: {\n params: {\n /** Calendar id; defaults to GOOGLE_CALENDAR_ID. */\n calendarId?: string;\n /** Id of the event to delete. */\n eventId: string;\n };\n result: { id: string; deleted: boolean };\n };\n /** Query busy time blocks on a calendar within a window. */\n freeBusy: {\n params: {\n /** Calendar id; defaults to GOOGLE_CALENDAR_ID. */\n calendarId?: string;\n /** ISO start of the query window. */\n timeMin: string;\n /** ISO end of the query window. */\n timeMax: string;\n };\n result: { busy: Array<{ start: string; end: string }> };\n };\n /** Register a push-notification channel for calendar change events (Google Calendar API watch). */\n watch: {\n params: {\n /** Calendar id; defaults to GOOGLE_CALENDAR_ID. */\n calendarId?: string;\n /** Caller-chosen id identifying this notification channel. */\n channelId: string;\n /** HTTPS callback URL Google POSTs change notifications to. */\n address: string;\n /** Requested channel time-to-live in seconds. */\n ttlSeconds?: number;\n };\n result: { channelId: string; resourceId: string; expiration: string };\n };\n /** Stop an active watch channel, ending calendar change notifications. */\n stopWatch: {\n params: {\n /** The channel id from the corresponding `watch` call. */\n channelId: string;\n /** The resourceId returned by the corresponding `watch` call. */\n resourceId: string;\n };\n result: { stopped: boolean };\n };\n};\n\n// ============================================================================\n// Drive (Google Drive, service account w/ domain-wide delegation)\n// ============================================================================\n\nexport type DriveActions = {\n /** List non-trashed files in a Google Drive folder, optionally filtered by a Drive API query clause. */\n listFiles: {\n params: {\n /** Restrict to files whose parents include this folder id. */\n folderId?: string;\n /** Extra Drive API query clause, ANDed with the folder/trashed filters. */\n query?: string;\n /** Maximum files to return; defaults to 100. */\n maxResults?: number;\n };\n result: {\n files: Array<{ id: string; name: string; mimeType: string; size: number; modifiedTime: string; webViewLink: string }>;\n };\n };\n /** Download a file's content, base64-encoded. */\n getFile: {\n params: {\n /** Drive file id. */\n fileId: string;\n };\n result: { id: string; name: string; mimeType: string; content: string; size: number };\n };\n /**\n * Upload a file to Drive; writes go through the user OAuth client when configured (service-account uploads have no storage quota on personal accounts).\n * @synonyms upload file\n */\n uploadFile: {\n params: {\n /** File name to create. */\n name: string;\n /** File content as a base64 data URL (data:mime;base64,...) or raw base64/plain text. */\n content: string;\n /** MIME type; inferred from a data URL when omitted. */\n mimeType?: string;\n /** Parent folder id; defaults to GOOGLE_DRIVE_FOLDER_ID. */\n folderId?: string;\n };\n result: { id: string; name: string; webViewLink: string };\n };\n /** Create a new Drive folder. */\n createFolder: {\n params: {\n /** Folder name to create. */\n name: string;\n /** Parent folder id; defaults to GOOGLE_DRIVE_FOLDER_ID. */\n parentId?: string;\n };\n result: { id: string; name: string };\n };\n /** Grant a user permission on a Drive file. */\n shareFile: {\n params: {\n /** Drive file id. */\n fileId: string;\n /** Email address of the grantee. */\n email: string;\n /** Permission level; defaults to 'reader'. */\n role?: 'reader' | 'writer' | 'commenter';\n };\n result: { shared: boolean; permissionId: string };\n };\n};\n\n// ============================================================================\n// Meta Ads (Graph API Marketing Insights, read-only)\n// ============================================================================\n\nexport type MetaAdsActions = {\n /** Fetch total ad spend, impressions, and clicks for an account over a date range via the Meta Graph API Marketing Insights endpoint (read-only). */\n getSpend: {\n params: {\n /** Ad account id (with or without the `act_` prefix); defaults to META_AD_ACCOUNT_ID. */\n accountId?: string;\n /** Range start date (YYYY-MM-DD). */\n since: string;\n /** Range end date (YYYY-MM-DD). */\n until: string;\n };\n result: { spend: number; currency: string; impressions: number; clicks: number };\n };\n /** List campaigns on an ad account via the Meta Graph API (read-only); dailyBudget is converted from Meta's minor-unit reporting. */\n listCampaigns: {\n params: {\n /** Ad account id (with or without the `act_` prefix); defaults to META_AD_ACCOUNT_ID. */\n accountId?: string;\n /** Filter by campaign effective_status (e.g. 'ACTIVE', 'PAUSED'). */\n status?: string;\n };\n result: { campaigns: Array<{ id: string; name: string; status: string; dailyBudget: number }> };\n };\n};\n\n// ============================================================================\n// Accounting (generic export — provider-pluggable, CSV first)\n// ============================================================================\n\nexport type AccountingActions = {\n /** Shape invoice rows into an import-ready CSV export (no accounting-vendor connection — generic column set). */\n exportInvoices: {\n params: {\n /** Invoice rows to export, in column order id/number/customer/issuedAt/dueAt/currency/net/tax/gross/status. */\n invoices: Array<{ id: string; number: string; customer: string; issuedAt: string; dueAt: string; currency: string; net: number; tax: number; gross: number; status: string }>;\n /** Export format; only 'csv' is currently supported. */\n format?: 'csv';\n };\n result: { content: string; filename: string; count: number };\n };\n /** Shape journal entry rows into an import-ready CSV export. */\n exportJournal: {\n params: {\n /** Journal entry rows to export, in column order date/account/description/debit/credit/reference. */\n entries: Array<{ date: string; account: string; description: string; debit: number; credit: number; reference: string }>;\n /** Export format; only 'csv' is currently supported. */\n format?: 'csv';\n };\n result: { content: string; filename: string; count: number };\n };\n};\n\n// ============================================================================\n// Banking (GoCardless Bank Account Data)\n// ============================================================================\n\nexport type BankingActions = {\n /** Start a GoCardless Bank Account Data requisition (bank-connection consent flow); the returned `link` is where the end user authorizes access. */\n createRequisition: {\n params: {\n /** GoCardless institution id (the bank to connect). */\n institutionId: string;\n /** URL GoCardless redirects the user to after consent. */\n redirectUrl: string;\n /** Caller-chosen reference to correlate the requisition. */\n reference?: string;\n };\n result: { requisitionId: string; link: string };\n };\n /** List account ids linked under a completed requisition. */\n listAccounts: {\n params: {\n /** Requisition id from createRequisition. */\n requisitionId: string;\n };\n result: { accounts: string[] };\n };\n /** List booked transactions on a linked bank account, optionally filtered by date range. */\n listTransactions: {\n params: {\n /** GoCardless account id. */\n accountId: string;\n /** Only transactions booked on/after this date (YYYY-MM-DD). */\n dateFrom?: string;\n /** Only transactions booked on/before this date (YYYY-MM-DD). */\n dateTo?: string;\n };\n result: {\n transactions: Array<{ id: string; amount: number; currency: string; date: string; description: string; counterparty: string }>;\n };\n };\n};\n\n// ============================================================================\n// E-sign (pluggable, DocuSign first)\n// ============================================================================\n\nexport type EsignActions = {\n /** Send a document for e-signature via DocuSign; accepts the document content as a data URL or raw base64. */\n sendEnvelope: {\n params: {\n /** Signer's email address. */\n recipientEmail: string;\n /** Signer's display name. */\n recipientName: string;\n /** Document file name (its extension also picks the DocuSign fileExtension). */\n documentName: string;\n /** Document content as a base64 data URL or raw base64. */\n documentContent: string;\n /** Envelope email subject; defaults to \"Please sign: \" + documentName. */\n emailSubject?: string;\n };\n result: { envelopeId: string; status: string };\n };\n /** Poll a DocuSign envelope's signing status. */\n getEnvelopeStatus: {\n params: {\n /** DocuSign envelope id from sendEnvelope. */\n envelopeId: string;\n };\n result: { status: string; completedAt: string };\n };\n /** Download the combined signed document(s) for a completed envelope, base64-encoded PDF. */\n downloadDocument: {\n params: {\n /** DocuSign envelope id. */\n envelopeId: string;\n };\n result: { content: string; documentName: string };\n };\n};\n\n// ============================================================================\n// Docker\n// ============================================================================\n\nexport type DockerActions = {\n /** Simulate building a container image (in-memory backend — no real Docker daemon/SDK; a real build is not yet implemented). */\n build: {\n params: {\n /** Image tag to build, e.g. myapp:latest. */\n tag: string;\n /** Dockerfile path; defaults to 'Dockerfile'. */\n dockerfile?: string;\n /** Build context directory; defaults to '.'. */\n context?: string;\n /** Build-time --build-arg values. */\n buildArgs?: Record<string, string>;\n };\n result: { imageId: string; tag: string; size: number; buildTime: number };\n };\n /** Simulate starting a container from an image (in-memory backend). */\n run: {\n params: {\n /** Image to run (as built/tagged by the `build` action, or an arbitrary name). */\n image: string;\n /** Container name; auto-generated when omitted. */\n name?: string;\n /** Host/container port mappings. */\n ports?: Array<{ host: number; container: number; protocol?: string }>;\n /** Environment variables set inside the container. */\n env?: Record<string, string>;\n /** Host/container path mounts. */\n volumes?: Array<{ host: string; container: string }>;\n /** Override command run inside the container. */\n command?: string;\n };\n result: {\n containerId: string;\n name: string;\n status: string;\n ports: Array<{ host: number; container: number; protocol: string }>;\n };\n };\n /** Stop a running (or paused) simulated container. */\n stop: {\n params: {\n /** Container id, or a unique id prefix. */\n containerId: string;\n };\n result: { containerId: string; status: string; stoppedAt: number };\n };\n /** Remove a simulated container; fails if it is running unless `force` is set. */\n remove: {\n params: {\n /** Container id, or a unique id prefix. */\n containerId: string;\n /** Remove even if the container is currently running. */\n force?: boolean;\n };\n result: { containerId: string; removed: boolean };\n };\n /** Fetch generated log lines for a simulated container. */\n logs: {\n params: {\n /** Container id, or a unique id prefix. */\n containerId: string;\n /** Number of most-recent lines to return; defaults to 100. */\n tail?: number;\n /** Only lines timestamped at/after this ISO time. */\n since?: string;\n };\n result: { containerId: string; logs: string[]; lineCount: number };\n };\n /** Fetch a simulated container's current status and metadata. */\n status: {\n params: {\n /** Container id, or a unique id prefix. */\n containerId: string;\n };\n result: {\n containerId: string;\n name: string;\n image: string;\n status: string;\n ports: Array<{ host: number; container: number; protocol: string }>;\n createdAt: number;\n startedAt: number | null;\n stoppedAt: number | null;\n };\n };\n /** List simulated containers; by default only running ones (like `docker ps`). */\n list: {\n params: {\n /** Include stopped/exited containers, not just running ones. */\n all?: boolean;\n /** Filter by a `key` or `key=value` label match. */\n filterByLabel?: string;\n };\n result: {\n containers: Array<{\n id: string;\n name: string;\n image: string;\n status: string;\n ports: Array<{ host: number; container: number; protocol: string }>;\n createdAt: number;\n }>;\n total: number;\n };\n };\n};\n\n// ============================================================================\n// Storage\n// ============================================================================\n\nexport type StorageActions = {\n /**\n * Upload an object to an S3-compatible bucket (real S3/R2/MinIO backend when storage credentials are configured, in-memory dev fallback otherwise — refused in production). Admits either the canonical `key`+`content` pair or the file-form `file` payload (UploadDropZone's `{ name, size, type, content }` shape); when `file` is present its `key`/`content` are derived from it.\n * @synonyms upload file, upload object\n */\n upload: {\n params: {\n /** Target bucket; defaults to STORAGE_BUCKET. */\n bucket: string;\n /** Object key to store under (canonical shape); ignored when `file` is present. */\n key?: string;\n /** Object content as a base64 data URL, raw base64, or plain text (canonical shape); ignored when `file` is present. */\n content?: string;\n /** MIME type override for the canonical shape; inferred from a data URL when omitted. */\n contentType?: string;\n /** File-form upload payload (as emitted by UploadDropZone); `content` is a base64 data URL. */\n file?: { name: string; size: number; type: string; content?: string };\n /** Object ACL — 'public' produces a public URL, otherwise the URL is signed/private. */\n acl?: 'public' | 'private';\n /** Reject the upload if the file-form payload's declared size exceeds this many bytes. */\n maxSize?: number;\n /** Object metadata stored alongside the content (S3-compatible object stores carry metadata as string headers — never structured values). */\n metadata?: Record<string, string>;\n };\n result: { key: string; bucket: string; size: number; etag: string; id: string; url: string };\n };\n /** Upload a whole UploadDropZone batch in one call, one item per file — the FSM-friendly shape for bulk-persisting uploaded-file rows. */\n uploadMany: {\n params: {\n /** Target bucket; defaults to STORAGE_BUCKET. */\n bucket: string;\n /** Files to upload, in UploadDropZone's `{ name, size, type, content }` shape. */\n files: Array<{ name: string; size: number; type: string; content?: string }>;\n /** Object ACL applied to every file in the batch. */\n acl?: 'public' | 'private';\n /** Reject any file whose declared size exceeds this many bytes. */\n maxSize?: number;\n };\n result: {\n items: Array<{ id: string; key: string; url: string; name: string; sizeBytes: number; mimeType: string }>;\n count: number;\n };\n };\n /** Download an object's content, base64-encoded. */\n download: {\n params: {\n /** Bucket the object lives in; defaults to STORAGE_BUCKET. */\n bucket: string;\n /** Object key. */\n key: string;\n };\n result: { content: string; contentType: string; size: number; metadata: Record<string, string> };\n };\n /** List object keys in a bucket, optionally prefix-filtered, one page at a time. */\n list: {\n params: {\n /** Bucket to list; defaults to STORAGE_BUCKET. */\n bucket: string;\n /** Only keys starting with this prefix. */\n prefix?: string;\n /** Maximum keys per page; defaults to 1000. */\n maxKeys?: number;\n /** Pagination token from a previous call's `nextToken`. */\n continuationToken?: string;\n };\n result: {\n keys: Array<{ key: string; size: number; lastModified: number }>;\n truncated: boolean;\n /** Present iff `truncated` — pass back as `continuationToken` for the next page. */\n nextToken?: string;\n };\n };\n /** Delete an object from a bucket. */\n delete: {\n params: {\n /** Bucket the object lives in; defaults to STORAGE_BUCKET. */\n bucket: string;\n /** Object key to delete. */\n key: string;\n };\n result: { deleted: boolean };\n };\n /** Generate a time-limited pre-signed URL for direct GET or PUT access to an object. */\n getSignedUrl: {\n params: {\n /** Bucket the object lives in; defaults to STORAGE_BUCKET. */\n bucket: string;\n /** Object key. */\n key: string;\n /** URL validity window in seconds; defaults to 3600. */\n expiresIn?: number;\n /** Whether the URL authorizes a read or a write; defaults to 'get'. */\n operation?: 'get' | 'put';\n };\n result: { url: string; expiresAt: number };\n };\n};\n\n// ============================================================================\n// Queue\n// ============================================================================\n\n/** A queued job as stored/returned by the in-memory queue. Extends\n * `EventPayload` so it flows through a `ServiceAction.result` (bus-payload-\n * shaped) without a boundary cast, the same reason `JsonSchemaDefinition`\n * extends `ServiceParams` above. */\ninterface QueueJob extends EventPayload {\n id: string;\n /** Whatever `enqueue` was given — the caller declares its own shape.\n * @shapedBy payload */\n payload: ServiceParams;\n enqueuedAt: number;\n priority: number;\n}\n\nexport type QueueActions = {\n /** Add a job to an in-memory job queue, ordered by priority (higher first) among ready jobs. */\n enqueue: {\n params: {\n /** Queue name. */\n queue: string;\n /** Job payload — the caller declares its own shape.\n * @shapedBy payload */\n payload: ServiceParams;\n /** Delay before the job becomes eligible for dequeue, in milliseconds. */\n delay?: number;\n /** Priority — higher values are dequeued before lower ones. */\n priority?: number;\n };\n result: { jobId: string; position: number };\n };\n /** Pop the highest-priority pending job whose delay has elapsed, marking it 'processing'; returns null if none is ready. */\n dequeue: {\n params: {\n /** Queue name. */\n queue: string;\n };\n result: { job: QueueJob | null };\n };\n /** Look up a job's current status by id. */\n status: {\n params: {\n /** Job id from enqueue. */\n jobId: string;\n };\n result: {\n status: 'pending' | 'processing' | 'completed' | 'failed';\n job: QueueJob | null;\n };\n };\n /** Mark a 'processing' job completed and store its result; no-op (returns false) if the job isn't in 'processing' state. */\n complete: {\n params: {\n /** Job id. */\n jobId: string;\n /** Result payload to attach to the job — the caller declares its own shape (not necessarily the same shape as the job's `payload`).\n * @shapedBy result */\n result?: ServiceParams;\n };\n result: { completed: boolean };\n };\n /** Mark a 'processing' job failed; no-op (returns false) if the job isn't in 'processing' state. */\n fail: {\n params: {\n /** Job id. */\n jobId: string;\n /** Failure reason to attach to the job. */\n error?: string;\n };\n result: { failed: boolean };\n };\n /** Remove a 'pending' job from its queue; no-op (returns false) once it has been dequeued. */\n cancel: {\n params: {\n /** Job id. */\n jobId: string;\n };\n result: { cancelled: boolean };\n };\n /** Count pending and processing jobs in a queue. */\n size: {\n params: {\n /** Queue name. */\n queue: string;\n };\n result: { size: number; pending: number; processing: number };\n };\n};\n\n// ============================================================================\n// Redis\n// ============================================================================\n\nexport type RedisActions = {\n /** Read a key's value from the in-memory cache; returns null (as `value`) if absent or expired. */\n get: {\n params: {\n /** Cache key. */\n key: string;\n };\n result: {\n /** Whatever `set` stored under this key — the caller declares its shape.\n * @shapedBy value */\n value: ServiceParams;\n };\n };\n /** Write a key's value, optionally with an expiry. */\n set: {\n params: {\n /** Cache key. */\n key: string;\n /** Value to store — the caller declares its own shape.\n * @shapedBy value */\n value: ServiceParams;\n /** Time-to-live in seconds; omit for no expiry. */\n ttl?: number;\n };\n result: { ok: boolean };\n };\n /** Delete a key. */\n delete: {\n params: {\n /** Cache key. */\n key: string;\n };\n result: { deleted: boolean };\n };\n /** Acquire a mutex on a key; fails (returns acquired:false) if another lock on the key is still alive. */\n lock: {\n params: {\n /** Lock key. */\n key: string;\n /** Lock time-to-live in milliseconds; defaults to 30000 (30s). */\n ttl?: number;\n };\n result: { acquired: boolean; lockId: string };\n };\n /** Release a lock; only succeeds when `lockId` matches the current holder. */\n unlock: {\n params: {\n /** Lock key. */\n key: string;\n /** Lock id returned by the corresponding `lock` call. */\n lockId: string;\n };\n result: { released: boolean };\n };\n /** Atomically add to a numeric key's value (creating it at 0 if absent), preserving any existing TTL. */\n increment: {\n params: {\n /** Cache key. */\n key: string;\n /** Amount to add; defaults to 1 (can be negative to decrement). */\n by?: number;\n };\n result: { value: number };\n };\n /** Set (or reset) a key's expiry; no-op (returns set:false) if the key doesn't exist or is already expired. */\n expire: {\n params: {\n /** Cache key. */\n key: string;\n /** New time-to-live in seconds. */\n ttl: number;\n };\n result: { set: boolean };\n };\n /** Publish a message to a channel; delivered synchronously to in-process subscriber callbacks only. */\n publish: {\n params: {\n /** Channel name. */\n channel: string;\n /** Message payload — the caller declares its own shape.\n * @shapedBy message */\n message: ServiceParams;\n };\n result: { receivers: number };\n };\n /** Register interest in a channel; does not itself wire a callback (that happens at a higher-level API). */\n subscribe: {\n params: {\n /** Channel name. */\n channel: string;\n };\n result: { subscribed: boolean };\n };\n};\n\n// ============================================================================\n// OAuth\n// ============================================================================\n\nexport type OAuthActions = {\n /** Build the provider authorization URL (PKCE + state) for an OAuth/OIDC login redirect; OIDC discovery (Google) or static OAuth 2 metadata (GitHub) when OAUTH_CLIENT_ID/SECRET are configured, an in-memory mock otherwise. */\n authorize: {\n params: {\n /** Identity provider; OIDC issuer defaults to Google unless OIDC_ISSUER_URL overrides it; GitHub uses its OAuth 2 endpoints. */\n provider: 'google' | 'github' | 'auth0';\n /** OAuth scopes to request. */\n scopes: string[];\n /** Callback URL the provider redirects back to after consent. */\n redirectUri: string;\n /** Opaque id of whoever started the flow (the verified user); `token` returns it so the host can refuse anyone else completing. */\n subject?: string;\n };\n result: { authUrl: string; state: string };\n };\n /** Exchange an authorization code for tokens, consuming the one-time-use `state` from the matching `authorize` call. */\n token: {\n params: {\n /** Authorization code from the provider's redirect callback. */\n code: string;\n /** The `state` value returned by `authorize`. */\n state: string;\n /** Who is completing; must equal the `subject` bound at `authorize`, checked before the code is exchanged. */\n subject?: string;\n };\n result: { accessToken: string; refreshToken: string; expiresIn: number; tokenType: 'bearer'; scope: string[]; subject?: string };\n };\n /** Exchange a refresh token for a new access token. */\n refresh: {\n params: {\n /** Refresh token from a prior `token` call. */\n refreshToken: string;\n };\n result: { accessToken: string; expiresIn: number };\n };\n /** Revoke an access or refresh token. */\n revoke: {\n params: {\n /** The access or refresh token to revoke. */\n token: string;\n };\n result: { revoked: boolean };\n };\n /** Fetch the authenticated user's profile claims for an access token. */\n userinfo: {\n params: {\n /** Access token from a prior `token`/`refresh` call. */\n accessToken: string;\n };\n result: { sub: string; email: string; name: string; picture: string };\n };\n};\n\n// ============================================================================\n// Credentials (hosted tenant credential store — the settings-page backend)\n// ============================================================================\n\nexport type CredentialsActions = {\n /** List declared credentials and their masked (last-4) configuration status; open to any caller. */\n list: {\n params: {\n /** Restrict the listing to one service's declared credentials; omit for all services. */\n service?: string;\n };\n result: {\n enabled: boolean;\n entries: Array<{\n service: string;\n envVar: string;\n required: boolean;\n description: string;\n configured: boolean;\n source: 'store' | 'env' | 'none';\n last4: string;\n }>;\n };\n };\n /** Save a credential value into the hosted store; the env var must be one declared for the service (or a well-formed connection-ref name for `database`). Admin/owner role required. */\n set: {\n params: {\n /** Integration name the credential belongs to. */\n service: string;\n /** Declared env-var name being set. */\n envVar: string;\n /** Secret value to store; never logged. */\n value: string;\n };\n result: { saved: boolean; service: string; envVar: string; last4: string };\n };\n /** Delete a stored credential from the hosted store. Admin/owner role required. */\n remove: {\n params: {\n /** Integration name the credential belongs to. */\n service: string;\n /** Env-var name to remove. */\n envVar: string;\n };\n result: { removed: boolean };\n };\n /** Check whether a service's required credentials are present and, if a live probe is declared for it, exercise the service to confirm they actually work. Admin/owner role required. */\n test: {\n params: {\n /** Integration name to test. */\n service: string;\n };\n result: { service: string; configured: boolean; missing: string[]; probed: boolean; ok: boolean; message: string };\n };\n /** Re-encrypt all stored credentials under the current master key (ALMADAR_CREDENTIAL_MASTER_KEY), retiring ALMADAR_CREDENTIAL_MASTER_KEY_PREVIOUS. Admin/owner role required. */\n rotate: {\n params: Record<string, never>;\n result: {\n /** Entries successfully re-encrypted under the current key. */\n rotated: number;\n /** Entries already under the current key (no-op). */\n alreadyCurrent: number;\n /** Entries that could not be decrypted with either key (data loss risk). */\n unreadable: number;\n };\n };\n};\n\n// ============================================================================\n// OpenTelemetry\n// ============================================================================\n\nexport type OtelActions = {\n /** Start a trace span (in-memory backend — no OpenTelemetry SDK/exporter). */\n startSpan: {\n params: {\n /** Span name. */\n name: string;\n /** Key-value attributes attached to the span (OTel attribute values are scalar). */\n attributes?: Record<string, string | number | boolean>;\n /** Existing trace id to attach this span to; a new one is generated when omitted. */\n traceId?: string;\n };\n result: { spanId: string; traceId: string };\n };\n /** Mark a span ended, optionally recording its final status; no-op (returns ended:false) for an unknown span id. */\n endSpan: {\n params: {\n /** Span id from startSpan. */\n spanId: string;\n /** Final span status. */\n status?: 'ok' | 'error';\n };\n result: { ended: boolean };\n };\n /** Attach a timestamped event to an open span; no-op (returns added:false) for an unknown span id. */\n addEvent: {\n params: {\n /** Span id from startSpan. */\n spanId: string;\n /** Event name. */\n name: string;\n /** Key-value attributes attached to the event (OTel attribute values are scalar). */\n attributes?: Record<string, string | number | boolean>;\n };\n result: { added: boolean };\n };\n /** Record a metric sample: counters accumulate, gauges overwrite, histograms append to the sample list. */\n recordMetric: {\n params: {\n /** Metric name. */\n name: string;\n /** Sample value. */\n value: number;\n /** Aggregation kind; defaults to 'counter'. */\n type?: 'counter' | 'gauge' | 'histogram';\n /** Key-value labels attached to the metric (replaces prior labels on the same name; OTel label values are scalar). */\n labels?: Record<string, string | number | boolean>;\n };\n result: { recorded: boolean };\n };\n /** Fetch a stored span's full record by id; null if unknown. */\n getSpan: {\n params: {\n /** Span id from startSpan. */\n spanId: string;\n };\n result: {\n spanId: string;\n traceId: string;\n name: string;\n startTime: number;\n endTime?: number;\n status?: 'ok' | 'error';\n attributes: IntegrationParams;\n } | null;\n };\n /** Fetch all recorded metrics, keyed by metric name. */\n getMetrics: {\n params: Record<string, never>;\n result: Record<string, { name: string; type: string; value: number; values: number[]; labels: IntegrationParams }>;\n };\n};\n\n// ============================================================================\n// CLI\n// ============================================================================\n\nexport type CLIActions = {\n /** Validate an orbital schema by writing it to a temp file and shelling out to `npx @almadar/cli validate --format=json`. */\n validate: {\n params: {\n /** Raw .orb schema content to validate. */\n schema: string;\n };\n result: { valid: boolean; errors: string[]; warnings: string[]; summary: string };\n };\n};\n\n// ============================================================================\n// DeepAgent\n// ============================================================================\n\nexport type DeepAgentActions = {\n /** Send a chat message to the DeepAgent server (POST /api/agent/message), continuing an existing thread or starting one. */\n sendMessage: {\n params: {\n /** Message text. */\n message: string;\n /** Existing thread id to continue; a new thread starts when omitted. */\n threadId?: string;\n /** Named skill to invoke for this message. */\n skill?: string;\n /** Extra context passed through to the agent — the caller declares its own shape.\n * @shapedBy context */\n context?: IntegrationParams;\n };\n result: { response: string; threadId: string };\n };\n /** Cancel an in-flight generation on a DeepAgent thread (POST /api/agent/cancel). */\n cancelGeneration: {\n params: {\n /** Thread whose generation to cancel. */\n threadId: string;\n };\n result: { cancelled: boolean };\n };\n /** Validate a schema via the DeepAgent server (POST /api/schema/validate). */\n validateSchema: {\n params: {\n /** Schema content to validate. */\n schema: string;\n };\n result: { valid: boolean; errors: string[] };\n };\n /** Compile a schema to a target shell via the DeepAgent server (POST /api/schema/compile). */\n compileSchema: {\n params: {\n /** Schema content to compile. */\n schema: string;\n /** Target codegen shell (e.g. 'typescript'). */\n shell?: string;\n };\n result: { output: string; files: string[] };\n };\n /** Fetch a thread's message history via the DeepAgent server (POST /api/agent/history). */\n getThreadHistory: {\n params: {\n /** Thread id. */\n threadId: string;\n };\n result: { messages: Array<{ role: string; content: string; timestamp: number }> };\n };\n};\n\n// ============================================================================\n// Database\n// ============================================================================\n\nexport type DatabaseActions = {\n /** Run a single read-only SELECT against a Postgres connection (write statements and multi-statement SQL are rejected before execution). */\n query: {\n params: {\n /** Env-var NAME whose value is the Postgres connection string — never the secret itself; resolved from the credential store/environment at run time. */\n connectionRef: string;\n /** SQL text; must be exactly one SELECT statement. */\n sql: string;\n /** Positional bind parameters for the query. */\n params?: DatabaseQueryParamValue[];\n };\n result: {\n /** One row per matched record, keyed by column name — the shape follows\n * the caller's own `sql` SELECT list, which no param's TYPE can express.\n * @shapedBy rows */\n rows: DatabaseRow[];\n rowCount: number;\n };\n };\n};\n\n// ============================================================================\n// Wikimedia\n// ============================================================================\n\nexport type WikimediaActions = {\n /** Look up a Wikipedia page (redirect-resolved): a one-line description, a Commons thumbnail, and the lead-section extract; empty fields on a miss. */\n getPage: {\n params: {\n /** Page title to look up. */\n title: string;\n };\n result: { title?: string; description?: string; portraitUrl?: string; extract?: string };\n };\n};\n\n// ============================================================================\n// Iconify\n// ============================================================================\n\nexport type IconifyActions = {\n /** Check whether an icon exists at the given Iconify API path (a GET, not a HEAD, despite the name). */\n svgExists: {\n params: {\n /** Iconify API path segment to probe, e.g. 'prefix/name.svg'. */\n path: string;\n };\n result: { exists: boolean };\n };\n /** Search Iconify's icon sets by relevance. */\n search: {\n params: {\n /** Search query text. */\n query: string;\n /** Maximum icon ids to return; defaults to 1. */\n limit?: number;\n };\n result: { icons: string[] };\n };\n /** Fetch an icon's raw SVG body (for color-tone classification); null if the icon or its collection isn't found. */\n getIconBody: {\n params: {\n /** Icon id in \"prefix:name\" form. */\n iconId: string;\n };\n result: { body: string | null };\n };\n};\n\n// ============================================================================\n// arXiv\n// ============================================================================\n\nexport type ArxivActions = {\n /** Search arXiv's public Atom API by relevance. */\n search: {\n params: {\n /** Search query text (matched across all fields). */\n query: string;\n /** Maximum results to return; defaults to 8. */\n maxResults?: number;\n };\n result: { results: Array<{ id: string; title: string; summary: string; authors: string[]; published: string; url: string }> };\n };\n};\n\n// ============================================================================\n// Rigger\n// ============================================================================\n\nexport type RiggerActions = {\n /** Liveness probe of the local rigger service. */\n health: {\n params: Record<string, never>;\n result: { ok: boolean };\n };\n /** Build a rigged mesh bundle from a source image via the local rigger service. */\n buildMesh: {\n params: {\n /** Path to the source image. */\n imagePath: string;\n /** The rig definition (parts, boxes, pivots, …) as JSON. */\n rig: object;\n };\n result: { bundlePath: string; boneCount: number; vertexCount: number; triangleCount: number; clips: string[] };\n };\n /** Render a rigged mesh bundle to output files via the local rigger service. */\n render: {\n params: {\n /** Path to the source image. */\n imagePath: string;\n /** The rig definition (parts, boxes, pivots, …) as JSON. */\n rig: object;\n /** Render preset name. */\n preset: string;\n /** Output directory for rendered files. */\n outDir: string;\n /** Render path: 'mesh' (skinned, deforming) or 'cutout' (rigid per-part layers). Defaults to 'mesh'. */\n mode?: string;\n /** Supersampling factor for the cutout path. Defaults to 1. */\n supersample?: number;\n };\n result: { outdir: string; frames: number; framePaths: string[]; sheet: string; gif: string; atlas: string };\n };\n /** List the available animation presets (name, fps, frame count, bone roles) from the local rigger service. */\n listPresets: {\n params: Record<string, never>;\n result: {\n presets: Array<{ name: string; path: string; fps: number; frames: number; bones: object[] }>;\n };\n };\n /** Load a rig file, resolving `cloneOf` parts to concrete box/pivot geometry. */\n loadRig: {\n params: {\n /** Path to the rig JSON file. */\n rigPath: string;\n };\n result: {\n /** The rig file's JSON, verbatim. */\n rig: object;\n /** The rig with every part's box/pivot resolved (clones included) plus rig-level knobs. */\n resolved: object;\n };\n };\n /** Save a rig file; the server validates it and rejects a malformed part. */\n saveRig: {\n params: {\n /** Path to write the rig JSON file. */\n rigPath: string;\n /** The rig definition (parts, boxes, pivots, …) as JSON. */\n rig: object;\n };\n result: { rigPath: string; parts: number };\n };\n /** Render the per-part pixel ownership map for a rig over a source image. */\n ownership: {\n params: {\n /** Path to the source image. */\n imagePath: string;\n /** The rig definition (parts, boxes, pivots, …) as JSON. */\n rig: object;\n /** Path to write the ownership-map PNG. */\n outPath: string;\n };\n result: { outPath: string; legend: Array<{ name: string; color: string }> };\n };\n /** Build per-part cutout layer images (name, box, z-order, pivot) from a source image and rig. */\n buildLayers: {\n params: {\n /** Path to the source image. */\n imagePath: string;\n /** The rig definition (parts, boxes, pivots, …) as JSON. */\n rig: object;\n /** Output directory for the layer images. */\n outDir: string;\n };\n result: {\n layers: Array<{\n name: string;\n path: string;\n x: number;\n y: number;\n w: number;\n h: number;\n z: number;\n pivot: [number, number];\n isRest: boolean;\n }>;\n };\n };\n /** Sample a pose/animation preset into per-frame, per-part transforms for a rig. */\n pose: {\n params: {\n /** Path to the source image. */\n imagePath: string;\n /** The rig definition (parts, boxes, pivots, …) as JSON. */\n rig: object;\n /** Map of part name to preset bone role. */\n roles: Record<string, string>;\n /** Animation preset name. */\n preset?: string;\n /** Per-part motion overrides applied over the preset. */\n overrides?: object;\n };\n result: {\n fps: number;\n frames: Array<Record<string, { angle: number; bx: number; by: number }>>;\n };\n };\n /** Generate one image on the local image server (text-to-image, or a Kontext edit conditioned on `sourcePath`). */\n generate: {\n params: {\n /** Generation model: 'z-image-turbo' (text-to-image) or 'kontext' (edit; requires sourcePath). */\n model: 'z-image-turbo' | 'kontext';\n /** The generation/edit prompt. */\n prompt: string;\n /** Random seed; omit for a fresh one each call. */\n seed?: number;\n /** Output image size, e.g. '1024x1024'. Defaults to '1024x1024'. */\n size?: string;\n /** Diffusion step count. */\n steps?: number;\n /** Path to the source image to edit; REQUIRED for 'kontext'. */\n sourcePath?: string;\n /** Output directory the generated PNG is written under. */\n outDir: string;\n };\n result: {\n outPath: string;\n model: string;\n prompt: string;\n seed: number | null;\n size: string;\n sourcePath: string | null;\n elapsedMs: number;\n };\n };\n};\n\n// ============================================================================\n// Barrel: IntegrationContracts\n// ============================================================================\n\n/**\n * Maps each integration name (as used in `registerIntegration`) to its action\n * map type. Use with `ServiceContract<IntegrationContracts[K]>` to get a fully\n * typed service interface for a specific integration.\n *\n * @example\n * ```typescript\n * import type { ServiceContract } from '@almadar/core';\n * import type { IntegrationContracts } from '@almadar/integrations';\n *\n * type TypedStripe = ServiceContract<IntegrationContracts['stripe']>;\n * ```\n */\nexport type IntegrationContracts = {\n github: GitHubActions;\n stripe: StripeActions;\n llm: LLMIntegrationActions;\n ml: MLActions;\n youtube: YouTubeActions;\n twilio: TwilioActions;\n email: EmailActions;\n webhook: WebhookActions;\n push: PushActions;\n calendar: CalendarActions;\n drive: DriveActions;\n metaAds: MetaAdsActions;\n accounting: AccountingActions;\n banking: BankingActions;\n esign: EsignActions;\n docker: DockerActions;\n storage: StorageActions;\n queue: QueueActions;\n redis: RedisActions;\n oauth: OAuthActions;\n credentials: CredentialsActions;\n otel: OtelActions;\n cli: CLIActions;\n deepagent: DeepAgentActions;\n database: DatabaseActions;\n wikimedia: WikimediaActions;\n iconify: IconifyActions;\n arxiv: ArxivActions;\n rigger: RiggerActions;\n};\n\n/** Integration name literal union. */\nexport type IntegrationName = keyof IntegrationContracts;\n\n/**\n * Helper: extract the action names for a given integration.\n *\n * @example\n * ```typescript\n * type StripeAction = IntegrationActionName<'stripe'>;\n * // => 'createPaymentIntent' | 'confirmPayment' | 'refund'\n * ```\n */\nexport type IntegrationActionName<K extends IntegrationName> =\n keyof IntegrationContracts[K] & string;\n\n// ============================================================================\n// Event Map\n// ============================================================================\n\n/** Events emitted by integrations during execution. */\nexport type IntegrationEventMap = {\n INTEGRATION_CALL_STARTED: { integration: string; action: string; timestamp: number };\n INTEGRATION_CALL_SUCCEEDED: { integration: string; action: string; durationMs: number };\n INTEGRATION_CALL_FAILED: { integration: string; action: string; error: string; retryCount: number };\n INTEGRATION_RETRY: { integration: string; action: string; attempt: number; maxRetries: number };\n INTEGRATION_REGISTERED: { integration: string; actions: string[] };\n};\n\n/** Typed event emitter for integration events. */\nexport type IntegrationServiceEvents = ServiceEvents<IntegrationEventMap>;\n\n// ============================================================================\n// Type Assertions\n// ============================================================================\n\n// Compile-time verification that all action maps satisfy Record<string, ServiceAction>.\n// This is a type-level assertion, not runtime code.\ntype _AssertActions<T extends Record<string, ServiceAction>> = T;\ntype _CheckGitHub = _AssertActions<GitHubActions>;\ntype _CheckStripe = _AssertActions<StripeActions>;\ntype _CheckLLM = _AssertActions<LLMIntegrationActions>;\ntype _CheckML = _AssertActions<MLActions>;\ntype _CheckYouTube = _AssertActions<YouTubeActions>;\ntype _CheckTwilio = _AssertActions<TwilioActions>;\ntype _CheckEmail = _AssertActions<EmailActions>;\ntype _CheckWebhook = _AssertActions<WebhookActions>;\ntype _CheckPush = _AssertActions<PushActions>;\ntype _CheckCalendar = _AssertActions<CalendarActions>;\ntype _CheckDrive = _AssertActions<DriveActions>;\ntype _CheckMetaAds = _AssertActions<MetaAdsActions>;\ntype _CheckAccounting = _AssertActions<AccountingActions>;\ntype _CheckBanking = _AssertActions<BankingActions>;\ntype _CheckEsign = _AssertActions<EsignActions>;\ntype _CheckDocker = _AssertActions<DockerActions>;\ntype _CheckStorage = _AssertActions<StorageActions>;\ntype _CheckQueue = _AssertActions<QueueActions>;\ntype _CheckRedis = _AssertActions<RedisActions>;\ntype _CheckOAuth = _AssertActions<OAuthActions>;\ntype _CheckCredentials = _AssertActions<CredentialsActions>;\ntype _CheckOtel = _AssertActions<OtelActions>;\ntype _CheckCLI = _AssertActions<CLIActions>;\ntype _CheckDeepAgent = _AssertActions<DeepAgentActions>;\ntype _CheckDatabase = _AssertActions<DatabaseActions>;\ntype _CheckWikimedia = _AssertActions<WikimediaActions>;\ntype _CheckIconify = _AssertActions<IconifyActions>;\ntype _CheckArxiv = _AssertActions<ArxivActions>;\n\n// ============================================================================\n// Credential Declarations\n// ============================================================================\n\n/** A credential (environment variable) required by an integration. */\nexport interface CredentialDeclaration {\n envVar: string;\n required: boolean;\n description: string;\n /** Alternatives: at least one declaration sharing this group must be set (e.g. SendGrid OR Resend). */\n oneOf?: string;\n}\n\n/**\n * Credential declarations for all integrations.\n *\n * This is the single source of truth for what env vars each service needs.\n * Consumed by:\n * - almadar-service-sync (extracts into services-registry.json)\n * - @almadar-io/terraform (provisions Secret Manager resources)\n * - RuntimeIntegrationManager (validates env configuration)\n */\nexport const serviceCredentials: Record<string, CredentialDeclaration[]> = {\n stripe: [\n { envVar: 'STRIPE_SECRET_KEY', required: true, description: 'Stripe secret API key' },\n { envVar: 'STRIPE_WEBHOOK_SECRET', required: false, description: 'Webhook endpoint signing secret' },\n ],\n youtube: [\n { envVar: 'YOUTUBE_API_KEY', required: true, description: 'YouTube Data API key' },\n ],\n twilio: [\n { envVar: 'TWILIO_ACCOUNT_SID', required: true, description: 'Twilio account SID' },\n { envVar: 'TWILIO_AUTH_TOKEN', required: true, description: 'Twilio auth token' },\n { envVar: 'TWILIO_PHONE_NUMBER', required: false, description: 'Default sender phone number' },\n ],\n email: [\n { envVar: 'SENDGRID_API_KEY', required: false, oneOf: 'provider', description: 'SendGrid API key (use this OR RESEND_API_KEY)' },\n { envVar: 'RESEND_API_KEY', required: false, oneOf: 'provider', description: 'Resend API key (use this OR SENDGRID_API_KEY)' },\n { envVar: 'FROM_EMAIL', required: false, description: 'Default sender email address' },\n ],\n webhook: [\n { envVar: 'WEBHOOK_SIGNING_SECRET', required: false, description: 'Default HMAC-SHA256 signing secret (per-call secret overrides)' },\n { envVar: 'WEBHOOK_TIMEOUT_MS', required: false, description: 'Per-request timeout (ms, default 10000)' },\n ],\n push: [\n { envVar: 'VAPID_PUBLIC_KEY', required: true, description: 'VAPID public key (web-push generate-vapid-keys)' },\n { envVar: 'VAPID_PRIVATE_KEY', required: true, description: 'VAPID private key' },\n { envVar: 'VAPID_SUBJECT', required: true, description: 'VAPID subject (mailto: or https: contact URI)' },\n ],\n calendar: [\n { envVar: 'GOOGLE_CALENDAR_SA_KEY', required: true, description: 'Google service-account key JSON (raw or base64) with calendar scope; store in Secret Manager, bind as env' },\n { envVar: 'GOOGLE_CALENDAR_SUBJECT', required: false, description: 'Workspace user to impersonate (domain-wide delegation); empty = act as the service account' },\n { envVar: 'GOOGLE_CALENDAR_ID', required: false, description: 'Default calendar id (default: primary)' },\n { envVar: 'GOOGLE_CALENDAR_CHANNEL_TOKEN', required: false, description: 'Watch-channel verification token — required to receive inbound calendar hooks (two-way sync)' },\n ],\n drive: [\n { envVar: 'GOOGLE_DRIVE_SA_KEY', required: false, oneOf: 'auth', description: 'Google service-account key JSON (raw or base64) with drive scope — serves reads; store in Secret Manager, bind as env' },\n { envVar: 'GOOGLE_DRIVE_SUBJECT', required: false, description: 'Workspace user to impersonate (domain-wide delegation); empty = act as the service account' },\n { envVar: 'GOOGLE_DRIVE_REFRESH_TOKEN', required: false, oneOf: 'auth', description: 'User OAuth refresh token (drive-consent.mjs) — serves writes; SA uploads are impossible on personal accounts (no SA storage quota)' },\n { envVar: 'GOOGLE_DRIVE_FOLDER_ID', required: false, description: 'Default parent folder for uploads/new folders when the call names none' },\n ],\n metaAds: [\n { envVar: 'META_ACCESS_TOKEN', required: true, description: 'Meta Graph API access token (Marketing API, ads_read)' },\n { envVar: 'META_AD_ACCOUNT_ID', required: false, description: 'Default ad account id (act_…); per-call accountId overrides' },\n ],\n accounting: [],\n banking: [\n { envVar: 'GOCARDLESS_SECRET_ID', required: true, description: 'GoCardless Bank Account Data secret id' },\n { envVar: 'GOCARDLESS_SECRET_KEY', required: true, description: 'GoCardless Bank Account Data secret key' },\n ],\n esign: [\n { envVar: 'DOCUSIGN_BASE_URL', required: true, description: 'DocuSign REST base (e.g. https://demo.docusign.net/restapi/v2.1/accounts/<accountId>)' },\n { envVar: 'DOCUSIGN_ACCESS_TOKEN', required: true, description: 'DocuSign OAuth access token (JWT grant rotation is the deployment concern)' },\n ],\n llm: [\n { envVar: 'ANTHROPIC_API_KEY', required: false, oneOf: 'provider', description: 'Anthropic API key (use this OR OPENAI_API_KEY)' },\n { envVar: 'OPENAI_API_KEY', required: false, oneOf: 'provider', description: 'OpenAI API key (use this OR ANTHROPIC_API_KEY)' },\n { envVar: 'LLM_PROVIDER', required: false, description: 'Chat provider when several keys are set: anthropic | openai | deepseek | kimi | openrouter (default: the first key found)' },\n ],\n 'llm-integration': [\n { envVar: 'ANTHROPIC_API_KEY', required: false, oneOf: 'provider', description: 'Anthropic API key' },\n { envVar: 'OPENAI_API_KEY', required: false, oneOf: 'provider', description: 'OpenAI API key' },\n ],\n ml: [\n { envVar: 'MASAR_URL', required: true, description: 'Base URL of the deployed model-serving orbital' },\n { envVar: 'MASAR_ML_TRAIT', required: true, description: 'Kebab-case trait name the serving orbital exposes its /events route under' },\n ],\n deepagent: [\n { envVar: 'DEEPAGENT_API_URL', required: true, description: 'DeepAgent server URL' },\n { envVar: 'DEEPAGENT_API_KEY', required: false, description: 'DeepAgent API key' },\n ],\n github: [\n { envVar: 'GITHUB_TOKEN', required: true, description: 'GitHub personal access token' },\n { envVar: 'GITHUB_OWNER', required: false, description: 'Default repository owner' },\n { envVar: 'GITHUB_REPO', required: false, description: 'Default repository name' },\n ],\n docker: [\n { envVar: 'DOCKER_HOST', required: false, description: 'Docker daemon host URL' },\n ],\n storage: [\n { envVar: 'STORAGE_ACCESS_KEY_ID', required: true, description: 'S3-compatible access key id' },\n { envVar: 'STORAGE_SECRET_ACCESS_KEY', required: true, description: 'S3-compatible secret access key' },\n { envVar: 'STORAGE_BUCKET', required: true, description: 'Default bucket name' },\n { envVar: 'STORAGE_REGION', required: false, description: 'Region (default us-east-1)' },\n { envVar: 'STORAGE_ENDPOINT', required: false, description: 'Custom S3-compatible endpoint (R2/MinIO/…); empty = AWS S3' },\n { envVar: 'STORAGE_PUBLIC_URL_BASE', required: false, description: 'Base URL for public-acl object links; empty = endpoint-derived' },\n ],\n queue: [\n { envVar: 'QUEUE_URL', required: true, description: 'Message queue connection URL' },\n ],\n redis: [\n { envVar: 'REDIS_URL', required: true, description: 'Redis connection URL' },\n ],\n oauth: [\n { envVar: 'OAUTH_CLIENT_ID', required: true, description: 'OAuth/OIDC client ID' },\n { envVar: 'OAUTH_CLIENT_SECRET', required: true, description: 'OAuth/OIDC client secret' },\n { envVar: 'OAUTH_REDIRECT_URI', required: false, description: 'OIDC redirect URI (default per-call param)' },\n { envVar: 'OIDC_ISSUER_URL', required: false, description: 'OIDC issuer URL (default https://accounts.google.com)' },\n ],\n credentials: [\n { envVar: 'ALMADAR_CREDENTIAL_MASTER_KEY', required: false, description: 'AES-256 master key (64-char hex) enabling the hosted credential store — hold it alone in the platform secret store' },\n { envVar: 'ALMADAR_CREDENTIAL_MASTER_KEY_PREVIOUS', required: false, description: 'Previous master key, present only during a rotation window — decrypts old rows until `credentials.rotate` re-encrypts them' },\n ],\n otel: [\n { envVar: 'OTEL_EXPORTER_OTLP_ENDPOINT', required: true, description: 'OpenTelemetry collector endpoint' },\n { envVar: 'OTEL_SERVICE_NAME', required: false, description: 'Service name for traces' },\n ],\n cli: [],\n // No fixed env vars — connection strings are resolved per-query from the\n // caller-supplied connectionRef, so credentials cannot be declared statically.\n database: [],\n wikimedia: [\n { envVar: 'WIKIMEDIA_USER_AGENT', required: false, description: 'Descriptive User-Agent for the Wikipedia API' },\n { envVar: 'WIKIMEDIA_TIMEOUT_MS', required: false, description: 'Per-request timeout (ms)' },\n ],\n iconify: [\n { envVar: 'ICONIFY_USER_AGENT', required: false, description: 'User-Agent for the Iconify API' },\n { envVar: 'ICONIFY_TIMEOUT_MS', required: false, description: 'Per-request timeout (ms)' },\n ],\n arxiv: [\n { envVar: 'ARXIV_TIMEOUT_MS', required: false, description: 'Per-request timeout (ms)' },\n ],\n rigger: [\n { envVar: 'RIGGER_BASE_URL', required: false, description: 'Local rigger service base URL (default http://127.0.0.1:8181)' },\n { envVar: 'RIGGER_TIMEOUT_MS', required: false, description: 'Per-request timeout (ms)' },\n { envVar: 'RIGGER_GENERATE_TIMEOUT_MS', required: false, description: \"Timeout (ms) for the 'generate' action; a local z-image-turbo run takes ~60-90 s and a kontext edit at 1024px several minutes (default 600000)\" },\n ],\n};\n\n/**\n * Every env var name declared anywhere in `serviceCredentials` — provider\n * secrets and the credential-store's own bootstrap keys alike. The single\n * source a forked verify/harness process scrubs from its env so it can never\n * see, and therefore never spend, a real credential (regardless of which\n * integration mode the fork ends up in).\n */\nexport function allCredentialEnvVars(): Set<string> {\n const vars = new Set<string>();\n for (const decls of Object.values(serviceCredentials)) {\n for (const { envVar } of decls) vars.add(envVar);\n }\n return vars;\n}\n\n/**\n * Bootstrap secrets that must NEVER enter store custody: the master key(s)\n * decrypt the store itself, so seeding them as rows (encrypted under\n * themselves) or stripping them from the env would be circular.\n */\nexport const STORE_BOOTSTRAP_ENV_VARS = new Set([\n 'ALMADAR_CREDENTIAL_MASTER_KEY',\n 'ALMADAR_CREDENTIAL_MASTER_KEY_PREVIOUS',\n]);\n\n/**\n * Every declared PROVIDER credential env var — `allCredentialEnvVars()` minus\n * the store's own bootstrap keys. The store-first custody set (I-31):\n * `RuntimeIntegrationManager` strips these from the base env before merging\n * the store's decrypted values, so every resolution flows the exact store →\n * env → unconfigured path a deployed client app uses.\n */\nexport function declaredCredentialEnvVars(): Set<string> {\n const vars = allCredentialEnvVars();\n for (const bootstrapVar of STORE_BOOTSTRAP_ENV_VARS) vars.delete(bootstrapVar);\n return vars;\n}\n\n/**\n * Declared side-effect-free \"Test connection\" probes, executed by the\n * `credentials.test` action through the normal factory path. Only services\n * with a genuinely cheap, read-only, env-defaulted action carry one; the\n * rest report credential presence + constructor validation.\n */\n/**\n * One inbound-hook mount a service's provider needs in a deployed app:\n * `POST /api/hooks/:provider` verified by the provider's own token/signature,\n * dispatching `event` onto the app bus. A hook is a PROVIDER property — the\n * service invocation is the `.lolo` surface; codegen derives the mount from\n * the app's invoked services × these declarations (I-26).\n */\nexport interface HookDeclaration {\n /** Provider key mounted at POST /api/hooks/:provider */\n provider: string;\n /** Bus event dispatched into the app on a verified callback */\n event: string;\n /** Env var carrying the provider's verification secret/channel token */\n credentialEnv: string;\n /** Factory export in @almadar/integrations: (secret?) => HookProvider */\n providerExport: string;\n}\n\n/**\n * Inbound-hook declarations per service — the single source the registry,\n * codegen, and the playground mount derive from.\n */\nexport const serviceHooks: Record<string, HookDeclaration[]> = {\n calendar: [\n {\n provider: 'google-calendar',\n event: 'CAL_REMOTE_CHANGED',\n credentialEnv: 'GOOGLE_CALENDAR_CHANNEL_TOKEN',\n providerExport: 'googleCalendarHookProvider',\n },\n ],\n};\n\nexport const serviceProbes: Partial<Record<IntegrationName, { action: string; params: ServiceParams }>> = {\n calendar: { action: 'listEvents', params: { maxResults: 1 } },\n drive: { action: 'listFiles', params: { maxResults: 1 } },\n metaAds: { action: 'listCampaigns', params: {} },\n};\n\n/** Backend maturity grade of an integration implementation. */\nexport type IntegrationBackendGrade = 'production' | 'simulated' | 'stub';\n\n/**\n * Honest backend grade per integration — 'simulated' marks an in-memory\n * implementation that must never be mistaken for a real backend. Synced into\n * services-registry.json (`backend`) by `almadar-sync services` and surfaced\n * by the verify service manifest and /health.\n */\nexport const serviceBackends: Record<IntegrationName, IntegrationBackendGrade> = {\n github: 'production',\n stripe: 'production',\n llm: 'production',\n ml: 'production',\n youtube: 'production',\n twilio: 'production',\n email: 'production',\n webhook: 'production',\n push: 'production',\n calendar: 'production',\n drive: 'production',\n metaAds: 'production',\n accounting: 'production',\n banking: 'production',\n esign: 'production',\n docker: 'simulated',\n storage: 'production',\n queue: 'simulated',\n redis: 'simulated',\n oauth: 'production',\n credentials: 'production',\n otel: 'simulated',\n cli: 'production',\n deepagent: 'production',\n database: 'production',\n wikimedia: 'production',\n iconify: 'production',\n arxiv: 'production',\n rigger: 'production',\n};\n","import type { IntegrationConfig, IntegrationResult, IntegrationParams } from './types';\nimport { BaseIntegration } from './core/BaseIntegration';\nimport { getIntegration } from './registry';\nimport { IntegrationError } from './types';\nimport { serviceCredentials } from './contracts';\nimport { createLogger } from '@almadar/logger';\n\nconst callLog = createLogger('almadar:integrations:calls');\n\n/**\n * Optional per-call context for principal-scoped resolution (W4) and\n * caller-identity enforcement (I-25). The default — absent — means the\n * tenant/app-wide credential set with an anonymous caller; role-gated\n * integrations (credentials) fail closed without it. Additive: every\n * existing call site is untouched.\n */\nexport interface IntegrationCallContext {\n principal?: string;\n /** The caller's roster role, from the same identity entity-ACL enforces against. */\n role?: string;\n}\n\n/** Cache key for (name, principal?) — NUL never appears in either part. */\nfunction instanceKey(name: string, principal?: string): string {\n return principal ? `${name}\\u0000${principal}` : name;\n}\n\n/**\n * Factory for creating and managing integration instances\n */\nexport class IntegrationFactory {\n private instances: Map<string, BaseIntegration> = new Map();\n private configs: Map<string, IntegrationConfig> = new Map();\n\n /**\n * Configure an integration (doesn't instantiate yet). A `principal` scopes\n * the config to that principal; the app-wide config (no principal) is the\n * fallback for every principal.\n */\n configure(name: string, config: Omit<IntegrationConfig, 'name'>, principal?: string): void {\n this.configs.set(instanceKey(name, principal), { name, ...config });\n }\n\n /**\n * Get or create an integration instance. Principal-scoped lookups fall\n * back to the app-wide config when no per-principal config exists.\n */\n get(name: string, principal?: string): BaseIntegration {\n const key = instanceKey(name, principal);\n const cached = this.instances.get(key);\n if (cached) {\n return cached;\n }\n\n // Get constructor\n const Constructor = getIntegration(name);\n if (!Constructor) {\n throw new IntegrationError(`Unknown integration: ${name}. Make sure it's imported.`, 'UNKNOWN_INTEGRATION');\n }\n\n // Get config — per-principal first, app-wide fallback\n const config = this.configs.get(key) ?? this.configs.get(name);\n if (!config) {\n const envVars = (serviceCredentials[name] ?? []).map((c) => c.envVar);\n const needs = envVars.length > 0 ? ` Set ${envVars.join(' / ')}.` : '';\n throw new IntegrationError(`Integration not configured: ${name}.${needs}`, 'NOT_CONFIGURED');\n }\n\n // Create instance\n const instance = new Constructor(config);\n this.instances.set(key, instance);\n\n return instance;\n }\n\n /**\n * Execute an action on an integration\n */\n async execute(\n integration: string,\n action: string,\n params: IntegrationParams,\n context?: IntegrationCallContext,\n ): Promise<IntegrationResult> {\n const start = performance.now();\n const record = (ok: boolean, error?: string): void =>\n callLog.call({ kind: 'external', service: integration, op: action, durationMs: performance.now() - start, ok, ...(error !== undefined ? { error } : {}) });\n try {\n const result = await this.get(integration, context?.principal).execute(action, params, context);\n record(result.success, result.success ? undefined : result.error?.message);\n return result;\n } catch (err) {\n record(false, err instanceof Error ? err.message : String(err));\n throw err;\n }\n }\n\n /**\n * Check if integration is configured\n */\n isConfigured(name: string, principal?: string): boolean {\n return this.configs.has(instanceKey(name, principal)) || this.configs.has(name);\n }\n\n /**\n * Register an integration instance directly (used by mock infrastructure)\n */\n registerInstance(name: string, instance: BaseIntegration, principal?: string): void {\n this.instances.set(instanceKey(name, principal), instance);\n }\n\n /**\n * Drop the cached instance(s) for a name so the next `get` rebuilds from\n * the current config — how a credential change goes live without restart.\n * Configs are kept; without a name, every instance is dropped.\n */\n invalidate(name?: string): void {\n if (name === undefined) {\n this.instances.clear();\n return;\n }\n for (const key of this.instances.keys()) {\n if (key === name || key.startsWith(`${name}\\u0000`)) {\n this.instances.delete(key);\n }\n }\n }\n\n /**\n * Clear all instances (useful for testing)\n */\n clear(): void {\n this.instances.clear();\n }\n\n /**\n * Clear all instances and configs\n */\n reset(): void {\n this.instances.clear();\n this.configs.clear();\n }\n}\n\nlet _factory: IntegrationFactory | null = null;\n\nexport function getIntegrationFactory(): IntegrationFactory {\n if (!_factory) {\n _factory = new IntegrationFactory();\n }\n return _factory;\n}\n\nexport function resetIntegrationFactory(): void {\n _factory?.reset();\n _factory = null;\n}\n","import { IntegrationFactory } from '../factory';\nimport { MockIntegration } from './MockIntegration';\nimport { registerIntegration } from '../registry';\nimport type { IntegrationParams } from '../types';\n\n/**\n * Mock integration factory for testing\n */\nexport class MockIntegrationFactory extends IntegrationFactory {\n constructor() {\n super();\n \n // Register mock integration for all known services\n const services = ['stripe', 'youtube', 'twilio', 'email', 'llm', 'deepagent'];\n services.forEach((service) => {\n registerIntegration(`mock-${service}`, MockIntegration);\n \n // Configure with mock config\n this.configure(service, {\n env: {},\n });\n });\n }\n\n /**\n * Set mock response for an integration action\n */\n setMockResponse(integration: string, action: string, data: unknown): void {\n const instance = this.get(integration) as MockIntegration;\n instance.setResponse(action, data);\n }\n\n /**\n * Get calls made to an integration\n */\n getMockCalls(\n integration: string,\n ): Array<{ action: string; params: IntegrationParams }> {\n const instance = this.get(integration) as MockIntegration;\n return instance.getCalls();\n }\n\n /**\n * Clear calls for an integration\n */\n clearMockCalls(integration: string): void {\n const instance = this.get(integration) as MockIntegration;\n instance.clearCalls();\n }\n}\n"]}