@stina/extension-api 0.18.0 → 0.19.1

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/index.ts","../src/types.ts","../src/messages.ts"],"sourcesContent":["/**\n * @stina/extension-api\n *\n * Types and utilities for building Stina extensions.\n *\n * Extensions should import from this package for type definitions.\n * The runtime (worker-side code) should import from '@stina/extension-api/runtime'.\n */\n\n// Localization\nexport type { LocalizedString } from './types.js'\nexport { resolveLocalizedString } from './types.js'\n\n// Types\nexport type {\n // Manifest\n ExtensionManifest,\n Platform,\n ExtensionContributions,\n SettingDefinition,\n SettingOptionsMapping,\n SettingCreateMapping,\n ToolSettingsViewDefinition,\n ToolSettingsView,\n ToolSettingsListView,\n ToolSettingsListMapping,\n ToolSettingsComponentView,\n ToolSettingsActionDataSource,\n PanelDefinition,\n PanelView,\n PanelComponentView,\n PanelActionDataSource,\n PanelUnknownView,\n ProviderDefinition,\n PromptContribution,\n PromptSection,\n ToolDefinition,\n CommandDefinition,\n\n // Provider Configuration Schema\n ProviderConfigSchema,\n ProviderConfigProperty,\n ProviderConfigPropertyType,\n ProviderConfigSelectOption,\n ProviderConfigValidation,\n\n // Permissions\n Permission,\n NetworkPermission,\n StoragePermission,\n UserDataPermission,\n CapabilityPermission,\n SystemPermission,\n\n // Context\n ExtensionContext,\n Disposable,\n NetworkAPI,\n SettingsAPI,\n ProvidersAPI,\n ToolsAPI,\n ActionsAPI,\n EventsAPI,\n SchedulerAPI,\n SchedulerJobRequest,\n SchedulerSchedule,\n SchedulerFirePayload,\n UserAPI,\n UserProfile,\n ChatAPI,\n ChatInstructionMessage,\n DatabaseAPI,\n StorageAPI,\n LogAPI,\n\n // AI Provider\n AIProvider,\n ModelInfo,\n ChatMessage,\n ChatOptions,\n GetModelsOptions,\n StreamEvent,\n ToolCall,\n\n // Tools\n Tool,\n ToolResult,\n\n // Actions\n Action,\n ActionResult,\n\n // Entry point\n ExtensionModule,\n} from './types.js'\n\n// Messages (for host implementation)\nexport type {\n HostToWorkerMessage,\n WorkerToHostMessage,\n ActivateMessage,\n DeactivateMessage,\n SettingsChangedMessage,\n ProviderChatRequestMessage,\n ProviderModelsRequestMessage,\n ToolExecuteRequestMessage,\n ToolExecuteResponseMessage,\n ActionExecuteRequestMessage,\n ActionExecuteResponseMessage,\n ResponseMessage,\n ReadyMessage,\n RequestMessage,\n RequestMethod,\n ProviderRegisteredMessage,\n ToolRegisteredMessage,\n ActionRegisteredMessage,\n StreamEventMessage,\n LogMessage,\n PendingRequest,\n} from './messages.js'\n\nexport { generateMessageId } from './messages.js'\n\n// Component types (for extension UI components)\nexport type {\n // Styling\n AllowedCSSProperty,\n ExtensionComponentStyle,\n // Base types\n ExtensionComponentData,\n // Iteration & Children\n ExtensionComponentIterator,\n ExtensionComponentChildren,\n // Actions\n ExtensionActionCall,\n ExtensionActionRef,\n // Data Sources & Panel Definition\n ExtensionDataSource,\n ExtensionPanelDefinition,\n // Component Props\n HeaderProps,\n LabelProps,\n ParagraphProps,\n ButtonProps,\n TextInputProps,\n DateTimeInputProps,\n SelectProps,\n VerticalStackProps,\n HorizontalStackProps,\n GridProps,\n DividerProps,\n IconProps,\n IconButtonType,\n IconButtonProps,\n PanelAction,\n PanelProps,\n ToggleProps,\n CollapsibleProps,\n PillVariant,\n PillProps,\n CheckboxProps,\n MarkdownProps,\n ModalProps,\n} from './types.components.js'\n","/**\n * A string that can be either a simple string or a map of language codes to localized strings.\n * When a simple string is provided, it's used as the default/fallback value.\n * When a map is provided, the appropriate language is selected at runtime.\n *\n * @example\n * // Simple string (backwards compatible)\n * name: \"Get Weather\"\n *\n * @example\n * // Localized strings\n * name: { en: \"Get Weather\", sv: \"Hämta väder\", de: \"Wetter abrufen\" }\n */\nexport type LocalizedString = string | Record<string, string>\n\n/**\n * Resolves a LocalizedString to an actual string value.\n * @param value The LocalizedString to resolve\n * @param lang The preferred language code (e.g., \"sv\", \"en\")\n * @param fallbackLang The fallback language code (defaults to \"en\")\n * @returns The resolved string value\n */\nexport function resolveLocalizedString(\n value: LocalizedString,\n lang: string,\n fallbackLang = 'en'\n): string {\n if (typeof value === 'string') {\n return value\n }\n // Try preferred language first, then fallback language, then first available, then empty string\n return value[lang] ?? value[fallbackLang] ?? Object.values(value)[0] ?? ''\n}\n\n/**\n * Extension manifest format (manifest.json)\n */\nexport interface ExtensionManifest {\n /** Unique identifier (e.g., \"ollama-provider\") */\n id: string\n /** Human-readable name */\n name: string\n /** Version string (semver) */\n version: string\n /** Short description */\n description: string\n /** Author information */\n author: {\n name: string\n url?: string\n }\n /** Repository URL */\n repository?: string\n /** License identifier */\n license?: string\n /** Minimum Stina version required */\n engines?: {\n stina: string\n }\n /** Supported platforms */\n platforms?: Platform[]\n /** Entry point file (relative to extension root) */\n main: string\n /** Required permissions */\n permissions: Permission[]\n /** What the extension contributes */\n contributes?: ExtensionContributions\n}\n\nexport type Platform = 'web' | 'electron' | 'tui'\n\n/**\n * What an extension can contribute to Stina\n */\nexport interface ExtensionContributions {\n /** User-configurable settings */\n settings?: SettingDefinition[]\n /** Tool settings views for UI */\n toolSettings?: ToolSettingsViewDefinition[]\n /** Right panel contributions */\n panels?: PanelDefinition[]\n /** AI providers */\n providers?: ProviderDefinition[]\n /** Tools for Stina to use */\n tools?: ToolDefinition[]\n /** Slash commands */\n commands?: CommandDefinition[]\n /** Prompt contributions for the system prompt */\n prompts?: PromptContribution[]\n}\n\n/**\n * Setting definition for the UI\n */\nexport interface SettingDefinition {\n /** Setting ID (namespaced automatically) */\n id: string\n /** Display title */\n title: string\n /** Help text */\n description?: string\n /** Setting type */\n type: 'string' | 'number' | 'boolean' | 'select'\n /** Default value */\n default?: unknown\n /** For select type: available options */\n options?: { value: string; label: string }[]\n /** For select type: load options from tool */\n optionsToolId?: string\n /** Params for options tool */\n optionsParams?: Record<string, unknown>\n /** Mapping for options tool response */\n optionsMapping?: SettingOptionsMapping\n /** Tool ID for creating a new option */\n createToolId?: string\n /** Label for create action */\n createLabel?: string\n /** Fields for create form */\n createFields?: SettingDefinition[]\n /** Static params always sent to create tool */\n createParams?: Record<string, unknown>\n /** Mapping for create tool response */\n createMapping?: SettingCreateMapping\n /** Validation rules */\n validation?: {\n required?: boolean\n min?: number\n max?: number\n pattern?: string\n }\n}\n\n/**\n * Mapping for select field options from tool response\n */\nexport interface SettingOptionsMapping {\n /** Key for items array in tool result data */\n itemsKey: string\n /** Key for option value */\n valueKey: string\n /** Key for option label */\n labelKey: string\n /** Optional key for description */\n descriptionKey?: string\n}\n\n/**\n * Mapping for create tool response\n */\nexport interface SettingCreateMapping {\n /** Key for result data object */\n resultKey?: string\n /** Key for option value (defaults to \"id\") */\n valueKey: string\n}\n\n/**\n * Tool settings view definition (UI schema)\n */\nexport interface ToolSettingsViewDefinition {\n /** Unique view ID within the extension */\n id: string\n /** Display title */\n title: string\n /** Help text */\n description?: string\n /** View configuration */\n view: ToolSettingsView\n /** Fields for create/edit forms (uses SettingDefinition) */\n fields?: SettingDefinition[]\n}\n\n/**\n * Tool settings view types\n */\nexport type ToolSettingsView = ToolSettingsListView | ToolSettingsComponentView\n\n/**\n * List view backed by tools\n */\nexport interface ToolSettingsListView {\n /** View kind */\n kind: 'list'\n /** Tool ID for listing items */\n listToolId: string\n /** Tool ID for fetching details (optional) */\n getToolId?: string\n /** Tool ID for creating/updating items (optional) */\n upsertToolId?: string\n /** Tool ID for deleting items (optional) */\n deleteToolId?: string\n /** Mapping from tool data to UI fields */\n mapping: ToolSettingsListMapping\n /** Param name for search query (default: \"query\") */\n searchParam?: string\n /** Param name for limit (default: \"limit\") */\n limitParam?: string\n /** Param name for get/delete ID (default: \"id\") */\n idParam?: string\n /** Static params always sent to list tool */\n listParams?: Record<string, unknown>\n}\n\n/**\n * Mapping from tool list data to UI fields\n */\nexport interface ToolSettingsListMapping {\n /** Key for items array in tool result data */\n itemsKey: string\n /** Key for total count in tool result data */\n countKey?: string\n /** Key for item ID */\n idKey: string\n /** Key for item label */\n labelKey: string\n /** Key for item description */\n descriptionKey?: string\n /** Key for secondary label */\n secondaryKey?: string\n}\n\n/**\n * Component-based tool settings view using the declarative DSL.\n * Reuses the same structure as PanelComponentView for consistency.\n */\nexport interface ToolSettingsComponentView {\n /** View kind */\n kind: 'component'\n /** Data sources. Keys become scope variables (e.g., \"$settings\"). */\n data?: Record<string, ToolSettingsActionDataSource>\n /** Root component to render */\n content: import('./types.components.js').ExtensionComponentData\n}\n\n/**\n * Action-based data source for tool settings.\n */\nexport interface ToolSettingsActionDataSource {\n /** Action ID to call for fetching data */\n action: string\n /** Parameters to pass to the action */\n params?: Record<string, unknown>\n /** Event names that trigger refresh */\n refreshOn?: string[]\n}\n\n/**\n * Panel definition for right panel views\n */\nexport interface PanelDefinition {\n /** Unique panel ID within the extension */\n id: string\n /** Display title */\n title: string\n /** Icon name (from huge-icons) */\n icon?: string\n /** Panel view schema */\n view: PanelView\n}\n\n/**\n * Panel view schema (declarative)\n */\nexport type PanelView = PanelComponentView | PanelUnknownView\n\nexport interface PanelUnknownView {\n /** View kind */\n kind: string\n /** Additional view configuration */\n [key: string]: unknown\n}\n\n/**\n * Action-based data source for declarative panels.\n * Uses actions (not tools) to fetch data.\n */\nexport interface PanelActionDataSource {\n /** Action ID to call for fetching data */\n action: string\n /** Parameters to pass to the action */\n params?: Record<string, unknown>\n /** Event names that should trigger a refresh of this data */\n refreshOn?: string[]\n}\n\n/**\n * Component-based panel view using the declarative DSL.\n * Data is fetched via actions, content is rendered via ExtensionComponent.\n */\nexport interface PanelComponentView {\n kind: 'component'\n /** Data sources available in the panel. Keys become variable names (e.g., \"$projects\"). */\n data?: Record<string, PanelActionDataSource>\n /** Root component to render */\n content: import('./types.components.js').ExtensionComponentData\n}\n\n/**\n * Provider definition (metadata only, implementation in code)\n */\nexport interface ProviderDefinition {\n /** Provider ID */\n id: string\n /** Display name */\n name: string\n /** Description */\n description?: string\n /** Suggested default model when creating a new model configuration */\n suggestedDefaultModel?: string\n /** Default settings for this provider (e.g., { url: \"http://localhost:11434\" }) */\n defaultSettings?: Record<string, unknown>\n /** Schema for provider-specific configuration UI */\n configSchema?: ProviderConfigSchema\n}\n\n// ============================================================================\n// Prompt Contributions\n// ============================================================================\n\nexport type PromptSection = 'system' | 'behavior' | 'tools'\n\nexport interface PromptContribution {\n /** Unique ID within the extension */\n id: string\n /** Optional title for the prompt chunk */\n title?: string\n /** Prompt section placement */\n section?: PromptSection\n /** Plain text prompt content */\n text?: string\n /** Optional localized prompt content (keyed by locale, e.g. \"en\", \"sv\") */\n i18n?: Record<string, string>\n /** Optional ordering hint (lower comes first) */\n order?: number\n}\n\n// ============================================================================\n// Provider Configuration Schema\n// ============================================================================\n\n/**\n * Schema for provider-specific configuration.\n * Used to generate UI forms for configuring provider settings.\n */\nexport interface ProviderConfigSchema {\n /** Property definitions */\n properties: Record<string, ProviderConfigProperty>\n /** Display order of properties in UI (optional, defaults to object key order) */\n order?: string[]\n}\n\n/**\n * Property types for provider configuration\n */\nexport type ProviderConfigPropertyType =\n | 'string'\n | 'number'\n | 'boolean'\n | 'select'\n | 'password'\n | 'url'\n\n/**\n * Single property in a provider configuration schema.\n * Defines how a setting should be rendered and validated in the UI.\n */\nexport interface ProviderConfigProperty {\n /** Property type - determines UI control */\n type: ProviderConfigPropertyType\n /** Display label */\n title: string\n /** Help text shown below the input */\n description?: string\n /** Default value */\n default?: unknown\n /** Whether the field is required */\n required?: boolean\n /** Placeholder text for input fields */\n placeholder?: string\n /** For 'select' type: static options */\n options?: ProviderConfigSelectOption[]\n /** Validation rules */\n validation?: ProviderConfigValidation\n}\n\n/**\n * Option for select-type properties\n */\nexport interface ProviderConfigSelectOption {\n /** Value stored in settings */\n value: string\n /** Display label */\n label: string\n}\n\n/**\n * Validation rules for a property\n */\nexport interface ProviderConfigValidation {\n /** Regex pattern the value must match */\n pattern?: string\n /** Minimum string length */\n minLength?: number\n /** Maximum string length */\n maxLength?: number\n /** Minimum number value */\n min?: number\n /** Maximum number value */\n max?: number\n}\n\n/**\n * Tool definition (metadata only, implementation in code)\n */\nexport interface ToolDefinition {\n /** Tool ID */\n id: string\n /**\n * Display name - can be a simple string or localized strings.\n * @example \"Get Weather\"\n * @example { en: \"Get Weather\", sv: \"Hämta väder\" }\n */\n name: LocalizedString\n /**\n * Description for Stina - can be a simple string or localized strings.\n * Note: The AI always receives the English description (or fallback) for consistency.\n * Localized descriptions are used for UI display only.\n * @example \"Fetches current weather for a location\"\n * @example { en: \"Fetches current weather\", sv: \"Hämtar aktuellt väder\" }\n */\n description: LocalizedString\n /** Parameter schema (JSON Schema) */\n parameters?: Record<string, unknown>\n}\n\n/**\n * Command definition\n */\nexport interface CommandDefinition {\n /** Command ID (e.g., \"weather\" for /weather) */\n id: string\n /** Display name */\n name: string\n /** Description */\n description: string\n}\n\n// ============================================================================\n// Permissions\n// ============================================================================\n\nexport type Permission =\n | NetworkPermission\n | StoragePermission\n | UserDataPermission\n | CapabilityPermission\n | SystemPermission\n\n/** Network access permissions */\nexport type NetworkPermission =\n | 'network:*'\n | `network:localhost`\n | `network:localhost:${number}`\n | `network:${string}`\n\n/** Storage permissions */\nexport type StoragePermission = 'database.own' | 'storage.local'\n\n/** User data permissions */\nexport type UserDataPermission =\n | 'user.profile.read'\n | 'user.location.read'\n | 'chat.history.read'\n | 'chat.current.read'\n\n/** Capability permissions */\nexport type CapabilityPermission =\n | 'provider.register'\n | 'tools.register'\n | 'actions.register'\n | 'settings.register'\n | 'commands.register'\n | 'panels.register'\n | 'events.emit'\n | 'scheduler.register'\n | 'chat.message.write'\n\n/** System permissions */\nexport type SystemPermission =\n | 'files.read'\n | 'files.write'\n | 'clipboard.read'\n | 'clipboard.write'\n\n// ============================================================================\n// Extension Context (API available to extensions)\n// ============================================================================\n\n/**\n * Disposable resource that can be cleaned up\n */\nexport interface Disposable {\n dispose(): void\n}\n\n/**\n * Context provided to extension's activate function.\n *\n * ## User ID Context\n *\n * The `userId` field provides the current user context for extensions. It is set when:\n * - A tool is executed by a user\n * - An action is executed by a user\n * - A scheduled job fires (if the job was created with a userId)\n *\n * For extension activation, `userId` is undefined since activation happens at system level.\n * Extensions should check for `userId` in their tool/action handlers to access user-specific data.\n *\n * @example\n * ```typescript\n * // In a tool execute handler:\n * execute: async (params, context) => {\n * if (context.userId) {\n * // User-specific logic\n * const userData = await storage.getForUser(context.userId, 'preferences')\n * }\n * }\n * ```\n */\nexport interface ExtensionContext {\n /** Extension metadata */\n readonly extension: {\n readonly id: string\n readonly version: string\n readonly storagePath: string\n }\n\n /**\n * Current user ID if in a user context, undefined for global/system operations.\n * Set when tools or actions are executed by a user, or when a user-scoped job fires.\n */\n readonly userId?: string\n\n /** Network access (if permitted) */\n readonly network?: NetworkAPI\n\n /** Settings access (if permitted) */\n readonly settings?: SettingsAPI\n\n /** Provider registration (if permitted) */\n readonly providers?: ProvidersAPI\n\n /** Tool registration (if permitted) */\n readonly tools?: ToolsAPI\n\n /** Action registration (if permitted) */\n readonly actions?: ActionsAPI\n\n /** Event emission (if permitted) */\n readonly events?: EventsAPI\n\n /** Scheduler access (if permitted) */\n readonly scheduler?: SchedulerAPI\n\n /** User data access (if permitted) */\n readonly user?: UserAPI\n\n /** Chat access (if permitted) */\n readonly chat?: ChatAPI\n\n /** Database access (if permitted) */\n readonly database?: DatabaseAPI\n\n /** Local storage (if permitted) */\n readonly storage?: StorageAPI\n\n /** Logging (always available) */\n readonly log: LogAPI\n}\n\n/**\n * Network API for making HTTP requests\n */\nexport interface NetworkAPI {\n /**\n * Fetch a URL (permissions are enforced by host)\n */\n fetch(url: string, options?: RequestInit): Promise<Response>\n\n /**\n * Streaming fetch for responses like NDJSON or SSE.\n * Yields text chunks as they arrive from the server.\n *\n * @throws {Error} If the request fails or encounters a network error.\n * The error message will contain details about the failure.\n *\n * @example\n * ```typescript\n * try {\n * for await (const chunk of context.network.fetchStream(url, options)) {\n * // Process each chunk (may contain partial lines)\n * buffer += chunk\n * }\n * } catch (error) {\n * console.error('Streaming fetch failed:', error.message)\n * }\n * ```\n */\n fetchStream(url: string, options?: RequestInit): AsyncGenerator<string, void, unknown>\n}\n\n/**\n * Settings API for reading/writing extension settings\n */\nexport interface SettingsAPI {\n /**\n * Get all settings for this extension\n */\n getAll<T extends Record<string, unknown>>(): Promise<T>\n\n /**\n * Get a specific setting value\n */\n get<T>(key: string): Promise<T | undefined>\n\n /**\n * Set a setting value\n */\n set(key: string, value: unknown): Promise<void>\n\n /**\n * Listen for setting changes\n */\n onChange(callback: (key: string, value: unknown) => void): Disposable\n}\n\n/**\n * Providers API for registering AI providers\n */\nexport interface ProvidersAPI {\n /**\n * Register an AI provider\n */\n register(provider: AIProvider): Disposable\n}\n\n/**\n * Tools API for registering tools\n */\nexport interface ToolsAPI {\n /**\n * Register a tool that Stina can use\n */\n register(tool: Tool): Disposable\n}\n\n/**\n * Actions API for registering UI actions\n */\nexport interface ActionsAPI {\n /**\n * Register an action that UI components can invoke\n */\n register(action: Action): Disposable\n}\n\n/**\n * Events API for notifying the host\n */\nexport interface EventsAPI {\n /**\n * Emit a named event with optional payload\n */\n emit(name: string, payload?: Record<string, unknown>): Promise<void>\n}\n\n/**\n * Scheduler schedule types\n */\nexport type SchedulerSchedule =\n | { type: 'at'; at: string }\n | { type: 'cron'; cron: string; timezone?: string }\n | { type: 'interval'; everyMs: number }\n\n/**\n * Scheduler job request\n */\nexport interface SchedulerJobRequest {\n id: string\n schedule: SchedulerSchedule\n payload?: Record<string, unknown>\n misfire?: 'run_once' | 'skip'\n /**\n * Optional user ID for user-scoped jobs.\n * If set, the job is associated with a specific user and the userId\n * will be passed to the extension when the job fires.\n */\n userId?: string\n}\n\n/**\n * Scheduler fire payload\n */\nexport interface SchedulerFirePayload {\n id: string\n payload?: Record<string, unknown>\n scheduledFor: string\n firedAt: string\n delayMs: number\n /** User ID if this is a user-scoped job, undefined if global */\n userId?: string\n}\n\n/**\n * Scheduler API for registering jobs\n */\nexport interface SchedulerAPI {\n schedule(job: SchedulerJobRequest): Promise<void>\n cancel(jobId: string): Promise<void>\n onFire(callback: (payload: SchedulerFirePayload) => void): Disposable\n}\n\n/**\n * User profile data\n */\nexport interface UserProfile {\n firstName?: string\n nickname?: string\n language?: string\n timezone?: string\n}\n\n/**\n * User API for profile access\n */\nexport interface UserAPI {\n getProfile(): Promise<UserProfile>\n}\n\n/**\n * Chat instruction message\n */\nexport interface ChatInstructionMessage {\n text: string\n conversationId?: string\n}\n\n/**\n * Chat API for appending instructions\n */\nexport interface ChatAPI {\n appendInstruction(message: ChatInstructionMessage): Promise<void>\n}\n\n/**\n * Database API for extension-specific tables\n */\nexport interface DatabaseAPI {\n /**\n * Execute a SQL query (only extension's prefixed tables allowed)\n */\n execute<T = unknown>(sql: string, params?: unknown[]): Promise<T[]>\n}\n\n/**\n * Simple key-value storage API with support for user-scoped storage.\n *\n * ## Global vs User-Scoped Storage\n *\n * Extensions have access to two types of storage:\n * - **Global storage**: Shared across all users, accessed via `get()`, `set()`, etc.\n * - **User-scoped storage**: Isolated per user, accessed via `getForUser()`, `setForUser()`, etc.\n *\n * Use global storage for extension-wide settings and user-scoped storage for\n * user preferences, session data, or any data that should be private to a user.\n *\n * @example\n * ```typescript\n * // Global storage (extension-wide)\n * await storage.set('apiEndpoint', 'https://api.example.com')\n * const endpoint = await storage.get<string>('apiEndpoint')\n *\n * // User-scoped storage (per-user)\n * if (context.userId) {\n * await storage.setForUser(context.userId, 'preferences', { theme: 'dark' })\n * const prefs = await storage.getForUser<Preferences>(context.userId, 'preferences')\n * }\n * ```\n */\nexport interface StorageAPI {\n /**\n * Get a value by key (global/extension-scoped)\n */\n get<T>(key: string): Promise<T | undefined>\n\n /**\n * Set a value (global/extension-scoped)\n */\n set(key: string, value: unknown): Promise<void>\n\n /**\n * Delete a key (global/extension-scoped)\n */\n delete(key: string): Promise<void>\n\n /**\n * Get all keys (global/extension-scoped)\n */\n keys(): Promise<string[]>\n\n /**\n * Get a value by key for a specific user (user-scoped)\n * @param userId The user ID\n * @param key The storage key\n */\n getForUser<T>(userId: string, key: string): Promise<T | undefined>\n\n /**\n * Set a value for a specific user (user-scoped)\n * @param userId The user ID\n * @param key The storage key\n * @param value The value to store\n */\n setForUser(userId: string, key: string, value: unknown): Promise<void>\n\n /**\n * Delete a key for a specific user (user-scoped)\n * @param userId The user ID\n * @param key The storage key\n */\n deleteForUser(userId: string, key: string): Promise<void>\n\n /**\n * Get all keys for a specific user (user-scoped)\n * @param userId The user ID\n */\n keysForUser(userId: string): Promise<string[]>\n}\n\n/**\n * Logging API\n */\nexport interface LogAPI {\n debug(message: string, data?: Record<string, unknown>): void\n info(message: string, data?: Record<string, unknown>): void\n warn(message: string, data?: Record<string, unknown>): void\n error(message: string, data?: Record<string, unknown>): void\n}\n\n// ============================================================================\n// AI Provider Types\n// ============================================================================\n\n/**\n * AI provider implementation\n */\nexport interface AIProvider {\n /** Provider ID (must match manifest) */\n id: string\n /** Display name */\n name: string\n\n /**\n * Get available models from this provider\n * @param options Optional settings for the provider (e.g., URL)\n */\n getModels(options?: GetModelsOptions): Promise<ModelInfo[]>\n\n /**\n * Chat completion with streaming\n */\n chat(\n messages: ChatMessage[],\n options: ChatOptions\n ): AsyncGenerator<StreamEvent, void, unknown>\n\n /**\n * Optional: Generate embeddings\n */\n embed?(texts: string[]): Promise<number[][]>\n}\n\n/**\n * Model information\n */\nexport interface ModelInfo {\n /** Model ID */\n id: string\n /** Display name */\n name: string\n /** Description */\n description?: string\n /** Context window size */\n contextLength?: number\n}\n\n/**\n * Chat message\n */\nexport interface ChatMessage {\n role: 'user' | 'assistant' | 'system' | 'tool'\n content: string\n /** For assistant messages: tool calls made by the model */\n tool_calls?: ToolCall[]\n /** For tool messages: the ID of the tool call this is a response to */\n tool_call_id?: string\n}\n\n/**\n * A tool call made by the model\n */\nexport interface ToolCall {\n /** Unique ID for this tool call */\n id: string\n /** Tool name/ID to invoke */\n name: string\n /** Arguments for the tool (as parsed object) */\n arguments: Record<string, unknown>\n}\n\n/**\n * Options for chat completion\n */\nexport interface ChatOptions {\n /** Model to use */\n model?: string\n /** Temperature (0-1) */\n temperature?: number\n /** Maximum tokens to generate */\n maxTokens?: number\n /** Abort signal for cancellation */\n signal?: AbortSignal\n /** Provider-specific settings from model configuration */\n settings?: Record<string, unknown>\n /** Available tools for this request */\n tools?: ToolDefinition[]\n}\n\n/**\n * Options for getModels\n */\nexport interface GetModelsOptions {\n /** Provider-specific settings (e.g., URL for Ollama) */\n settings?: Record<string, unknown>\n}\n\n/**\n * Streaming events from chat\n */\nexport type StreamEvent =\n | { type: 'content'; text: string }\n | { type: 'thinking'; text: string }\n | { type: 'tool_start'; name: string; input: unknown; toolCallId: string }\n | { type: 'tool_end'; name: string; output: unknown; toolCallId: string }\n | { type: 'done'; usage?: { inputTokens: number; outputTokens: number } }\n | { type: 'error'; message: string }\n\n// ============================================================================\n// Tool Types\n// ============================================================================\n\n/**\n * Tool implementation\n */\nexport interface Tool {\n /** Tool ID (must match manifest) */\n id: string\n /** Display name */\n name: string\n /** Description for Stina */\n description: string\n /** Parameter schema (JSON Schema) */\n parameters?: Record<string, unknown>\n\n /**\n * Execute the tool\n */\n execute(params: Record<string, unknown>): Promise<ToolResult>\n}\n\n/**\n * Tool execution result\n */\nexport interface ToolResult {\n /** Whether the tool succeeded */\n success: boolean\n /** Result data (for Stina to use) */\n data?: unknown\n /** Human-readable message */\n message?: string\n /** Error message if failed */\n error?: string\n}\n\n// ============================================================================\n// Action Types (for UI interactions, separate from Tools)\n// ============================================================================\n\n/**\n * Action implementation for UI interactions.\n * Actions are invoked by UI components, not by Stina (AI).\n */\nexport interface Action {\n /** Action ID (unique within the extension) */\n id: string\n\n /**\n * Execute the action\n * @param params Parameters from the UI component (with $-values already resolved)\n */\n execute(params: Record<string, unknown>): Promise<ActionResult>\n}\n\n/**\n * Action execution result\n */\nexport interface ActionResult {\n /** Whether the action succeeded */\n success: boolean\n /** Result data (returned to UI) */\n data?: unknown\n /** Error message if failed */\n error?: string\n}\n\n// ============================================================================\n// Extension Entry Point\n// ============================================================================\n\n/**\n * Extension entry point interface\n */\nexport interface ExtensionModule {\n /**\n * Called when extension is activated\n */\n activate(context: ExtensionContext): void | Disposable | Promise<void | Disposable>\n\n /**\n * Called when extension is deactivated\n */\n deactivate?(): void | Promise<void>\n}\n","/**\n * Message protocol between Extension Host and Extension Workers\n */\n\nimport type {\n ChatMessage,\n ChatOptions,\n GetModelsOptions,\n StreamEvent,\n ToolResult,\n ActionResult,\n ModelInfo,\n SchedulerFirePayload,\n} from './types.js'\n\n// ============================================================================\n// Host → Worker Messages\n// ============================================================================\n\nexport type HostToWorkerMessage =\n | ActivateMessage\n | DeactivateMessage\n | SettingsChangedMessage\n | SchedulerFireMessage\n | ProviderChatRequestMessage\n | ProviderModelsRequestMessage\n | ToolExecuteRequestMessage\n | ActionExecuteRequestMessage\n | ResponseMessage\n | StreamingFetchChunkMessage\n\nexport interface ActivateMessage {\n type: 'activate'\n id: string\n payload: {\n extensionId: string\n extensionVersion: string\n storagePath: string\n permissions: string[]\n settings: Record<string, unknown>\n }\n}\n\nexport interface DeactivateMessage {\n type: 'deactivate'\n id: string\n}\n\nexport interface SettingsChangedMessage {\n type: 'settings-changed'\n id: string\n payload: {\n key: string\n value: unknown\n }\n}\n\nexport interface SchedulerFireMessage {\n type: 'scheduler-fire'\n id: string\n payload: SchedulerFirePayload\n}\n\nexport interface ProviderChatRequestMessage {\n type: 'provider-chat-request'\n id: string\n payload: {\n providerId: string\n messages: ChatMessage[]\n options: ChatOptions\n }\n}\n\nexport interface ProviderModelsRequestMessage {\n type: 'provider-models-request'\n id: string\n payload: {\n providerId: string\n options?: GetModelsOptions\n }\n}\n\nexport interface ToolExecuteRequestMessage {\n type: 'tool-execute-request'\n id: string\n payload: {\n toolId: string\n params: Record<string, unknown>\n /** User ID if the tool is executed in a user context */\n userId?: string\n }\n}\n\nexport interface ActionExecuteRequestMessage {\n type: 'action-execute-request'\n id: string\n payload: {\n actionId: string\n params: Record<string, unknown>\n /** User ID if the action is executed in a user context */\n userId?: string\n }\n}\n\nexport interface ResponseMessage {\n type: 'response'\n id: string\n payload: {\n requestId: string\n success: boolean\n data?: unknown\n error?: string\n }\n}\n\n/**\n * Message sent from host to worker with streaming fetch data chunks.\n * Used for streaming network responses (e.g., NDJSON streams from Ollama).\n */\nexport interface StreamingFetchChunkMessage {\n type: 'streaming-fetch-chunk'\n id: string\n payload: {\n requestId: string\n chunk: string\n done: boolean\n error?: string\n }\n}\n\n// ============================================================================\n// Worker → Host Messages\n// ============================================================================\n\nexport type WorkerToHostMessage =\n | ReadyMessage\n | RequestMessage\n | ProviderRegisteredMessage\n | ToolRegisteredMessage\n | ActionRegisteredMessage\n | StreamEventMessage\n | LogMessage\n | ProviderModelsResponseMessage\n | ToolExecuteResponseMessage\n | ActionExecuteResponseMessage\n | StreamingFetchAckMessage\n\nexport interface ReadyMessage {\n type: 'ready'\n}\n\n/**\n * Message sent from worker to host to acknowledge receipt of a streaming fetch chunk.\n * This enables backpressure control to prevent unbounded memory growth.\n */\nexport interface StreamingFetchAckMessage {\n type: 'streaming-fetch-ack'\n payload: {\n requestId: string\n }\n}\n\nexport interface RequestMessage {\n type: 'request'\n id: string\n method: RequestMethod\n payload: unknown\n}\n\nexport type RequestMethod =\n | 'network.fetch'\n | 'network.fetch-stream'\n | 'settings.getAll'\n | 'settings.get'\n | 'settings.set'\n | 'user.getProfile'\n | 'events.emit'\n | 'scheduler.schedule'\n | 'scheduler.cancel'\n | 'chat.appendInstruction'\n | 'database.execute'\n | 'storage.get'\n | 'storage.set'\n | 'storage.delete'\n | 'storage.keys'\n | 'storage.getForUser'\n | 'storage.setForUser'\n | 'storage.deleteForUser'\n | 'storage.keysForUser'\n\nexport interface ProviderRegisteredMessage {\n type: 'provider-registered'\n payload: {\n id: string\n name: string\n }\n}\n\nexport interface ToolRegisteredMessage {\n type: 'tool-registered'\n payload: {\n id: string\n name: string\n description: string\n parameters?: Record<string, unknown>\n }\n}\n\nexport interface ActionRegisteredMessage {\n type: 'action-registered'\n payload: {\n id: string\n }\n}\n\nexport interface StreamEventMessage {\n type: 'stream-event'\n payload: {\n requestId: string\n event: StreamEvent\n }\n}\n\nexport interface ProviderModelsResponseMessage {\n type: 'provider-models-response'\n payload: {\n requestId: string\n models: ModelInfo[]\n error?: string\n }\n}\n\nexport interface ToolExecuteResponseMessage {\n type: 'tool-execute-response'\n payload: {\n requestId: string\n result: ToolResult\n error?: string\n }\n}\n\nexport interface ActionExecuteResponseMessage {\n type: 'action-execute-response'\n payload: {\n requestId: string\n result: ActionResult\n error?: string\n }\n}\n\nexport interface LogMessage {\n type: 'log'\n payload: {\n level: 'debug' | 'info' | 'warn' | 'error'\n message: string\n data?: Record<string, unknown>\n }\n}\n\n// ============================================================================\n// Utility Types\n// ============================================================================\n\nexport interface PendingRequest<T = unknown> {\n resolve: (value: T) => void\n reject: (error: Error) => void\n timeout: ReturnType<typeof setTimeout>\n}\n\n/**\n * Generate a unique message ID\n */\nexport function generateMessageId(): string {\n return `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACsBO,SAAS,uBACd,OACA,MACA,eAAe,MACP;AACR,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO;AAAA,EACT;AAEA,SAAO,MAAM,IAAI,KAAK,MAAM,YAAY,KAAK,OAAO,OAAO,KAAK,EAAE,CAAC,KAAK;AAC1E;;;ACgPO,SAAS,oBAA4B;AAC1C,SAAO,GAAG,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,EAAE,CAAC;AACjE;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/types.ts","../src/messages.ts"],"sourcesContent":["/**\n * @stina/extension-api\n *\n * Types and utilities for building Stina extensions.\n *\n * Extensions should import from this package for type definitions.\n * The runtime (worker-side code) should import from '@stina/extension-api/runtime'.\n */\n\n// Localization\nexport type { LocalizedString } from './types.js'\nexport { resolveLocalizedString } from './types.js'\n\n// Types\nexport type {\n // Manifest\n ExtensionManifest,\n Platform,\n ExtensionContributions,\n SettingDefinition,\n SettingOptionsMapping,\n SettingCreateMapping,\n ToolSettingsViewDefinition,\n ToolSettingsView,\n ToolSettingsListView,\n ToolSettingsListMapping,\n ToolSettingsComponentView,\n ToolSettingsActionDataSource,\n PanelDefinition,\n PanelView,\n PanelComponentView,\n PanelActionDataSource,\n PanelUnknownView,\n ProviderDefinition,\n PromptContribution,\n PromptSection,\n ToolDefinition,\n CommandDefinition,\n\n // Provider Configuration Schema\n ProviderConfigSchema,\n ProviderConfigProperty,\n ProviderConfigPropertyType,\n ProviderConfigSelectOption,\n ProviderConfigValidation,\n\n // Permissions\n Permission,\n NetworkPermission,\n StoragePermission,\n UserDataPermission,\n CapabilityPermission,\n SystemPermission,\n\n // Context\n ExtensionContext,\n Disposable,\n NetworkAPI,\n SettingsAPI,\n ProvidersAPI,\n ToolsAPI,\n ActionsAPI,\n EventsAPI,\n SchedulerAPI,\n SchedulerJobRequest,\n SchedulerSchedule,\n SchedulerFirePayload,\n UserAPI,\n UserProfile,\n ChatAPI,\n ChatInstructionMessage,\n DatabaseAPI,\n StorageAPI,\n LogAPI,\n\n // AI Provider\n AIProvider,\n ModelInfo,\n ChatMessage,\n ChatOptions,\n GetModelsOptions,\n StreamEvent,\n ToolCall,\n\n // Tools\n Tool,\n ToolResult,\n\n // Actions\n Action,\n ActionResult,\n\n // Entry point\n ExtensionModule,\n} from './types.js'\n\n// Messages (for host implementation)\nexport type {\n HostToWorkerMessage,\n WorkerToHostMessage,\n ActivateMessage,\n DeactivateMessage,\n SettingsChangedMessage,\n ProviderChatRequestMessage,\n ProviderModelsRequestMessage,\n ToolExecuteRequestMessage,\n ToolExecuteResponseMessage,\n ActionExecuteRequestMessage,\n ActionExecuteResponseMessage,\n ResponseMessage,\n ReadyMessage,\n RequestMessage,\n RequestMethod,\n ProviderRegisteredMessage,\n ToolRegisteredMessage,\n ActionRegisteredMessage,\n StreamEventMessage,\n LogMessage,\n PendingRequest,\n} from './messages.js'\n\nexport { generateMessageId } from './messages.js'\n\n// Component types (for extension UI components)\nexport type {\n // Styling\n AllowedCSSProperty,\n ExtensionComponentStyle,\n // Base types\n ExtensionComponentData,\n // Iteration & Children\n ExtensionComponentIterator,\n ExtensionComponentChildren,\n // Actions\n ExtensionActionCall,\n ExtensionActionRef,\n // Data Sources & Panel Definition\n ExtensionDataSource,\n ExtensionPanelDefinition,\n // Component Props\n HeaderProps,\n LabelProps,\n ParagraphProps,\n ButtonProps,\n TextInputProps,\n DateTimeInputProps,\n SelectProps,\n VerticalStackProps,\n HorizontalStackProps,\n GridProps,\n DividerProps,\n IconProps,\n IconButtonType,\n IconButtonProps,\n PanelAction,\n PanelProps,\n ToggleProps,\n CollapsibleProps,\n PillVariant,\n PillProps,\n CheckboxProps,\n MarkdownProps,\n ModalProps,\n} from './types.components.js'\n","/**\n * A string that can be either a simple string or a map of language codes to localized strings.\n * When a simple string is provided, it's used as the default/fallback value.\n * When a map is provided, the appropriate language is selected at runtime.\n *\n * @example\n * // Simple string (backwards compatible)\n * name: \"Get Weather\"\n *\n * @example\n * // Localized strings\n * name: { en: \"Get Weather\", sv: \"Hämta väder\", de: \"Wetter abrufen\" }\n */\nexport type LocalizedString = string | Record<string, string>\n\n/**\n * Resolves a LocalizedString to an actual string value.\n * @param value The LocalizedString to resolve\n * @param lang The preferred language code (e.g., \"sv\", \"en\")\n * @param fallbackLang The fallback language code (defaults to \"en\")\n * @returns The resolved string value\n */\nexport function resolveLocalizedString(\n value: LocalizedString,\n lang: string,\n fallbackLang = 'en'\n): string {\n if (typeof value === 'string') {\n return value\n }\n // Try preferred language first, then fallback language, then first available, then empty string\n return value[lang] ?? value[fallbackLang] ?? Object.values(value)[0] ?? ''\n}\n\n/**\n * Extension manifest format (manifest.json)\n */\nexport interface ExtensionManifest {\n /** Unique identifier (e.g., \"ollama-provider\") */\n id: string\n /** Human-readable name */\n name: string\n /** Version string (semver) */\n version: string\n /** Short description */\n description: string\n /** Author information */\n author: {\n name: string\n url?: string\n }\n /** Repository URL */\n repository?: string\n /** License identifier */\n license?: string\n /** Minimum Stina version required */\n engines?: {\n stina: string\n }\n /** Supported platforms */\n platforms?: Platform[]\n /** Entry point file (relative to extension root) */\n main: string\n /** Required permissions */\n permissions: Permission[]\n /** What the extension contributes */\n contributes?: ExtensionContributions\n}\n\nexport type Platform = 'web' | 'electron' | 'tui'\n\n/**\n * What an extension can contribute to Stina\n */\nexport interface ExtensionContributions {\n /** User-configurable settings */\n settings?: SettingDefinition[]\n /** Tool settings views for UI */\n toolSettings?: ToolSettingsViewDefinition[]\n /** Right panel contributions */\n panels?: PanelDefinition[]\n /** AI providers */\n providers?: ProviderDefinition[]\n /** Tools for Stina to use */\n tools?: ToolDefinition[]\n /** Slash commands */\n commands?: CommandDefinition[]\n /** Prompt contributions for the system prompt */\n prompts?: PromptContribution[]\n}\n\n/**\n * Setting definition for the UI\n */\nexport interface SettingDefinition {\n /** Setting ID (namespaced automatically) */\n id: string\n /** Display title */\n title: string\n /** Help text */\n description?: string\n /** Setting type */\n type: 'string' | 'number' | 'boolean' | 'select'\n /** Default value */\n default?: unknown\n /** For select type: available options */\n options?: { value: string; label: string }[]\n /** For select type: load options from tool */\n optionsToolId?: string\n /** Params for options tool */\n optionsParams?: Record<string, unknown>\n /** Mapping for options tool response */\n optionsMapping?: SettingOptionsMapping\n /** Tool ID for creating a new option */\n createToolId?: string\n /** Label for create action */\n createLabel?: string\n /** Fields for create form */\n createFields?: SettingDefinition[]\n /** Static params always sent to create tool */\n createParams?: Record<string, unknown>\n /** Mapping for create tool response */\n createMapping?: SettingCreateMapping\n /** Validation rules */\n validation?: {\n required?: boolean\n min?: number\n max?: number\n pattern?: string\n }\n}\n\n/**\n * Mapping for select field options from tool response\n */\nexport interface SettingOptionsMapping {\n /** Key for items array in tool result data */\n itemsKey: string\n /** Key for option value */\n valueKey: string\n /** Key for option label */\n labelKey: string\n /** Optional key for description */\n descriptionKey?: string\n}\n\n/**\n * Mapping for create tool response\n */\nexport interface SettingCreateMapping {\n /** Key for result data object */\n resultKey?: string\n /** Key for option value (defaults to \"id\") */\n valueKey: string\n}\n\n/**\n * Tool settings view definition (UI schema)\n */\nexport interface ToolSettingsViewDefinition {\n /** Unique view ID within the extension */\n id: string\n /** Display title */\n title: string\n /** Help text */\n description?: string\n /** View configuration */\n view: ToolSettingsView\n /** Fields for create/edit forms (uses SettingDefinition) */\n fields?: SettingDefinition[]\n}\n\n/**\n * Tool settings view types\n */\nexport type ToolSettingsView = ToolSettingsListView | ToolSettingsComponentView\n\n/**\n * List view backed by tools\n */\nexport interface ToolSettingsListView {\n /** View kind */\n kind: 'list'\n /** Tool ID for listing items */\n listToolId: string\n /** Tool ID for fetching details (optional) */\n getToolId?: string\n /** Tool ID for creating/updating items (optional) */\n upsertToolId?: string\n /** Tool ID for deleting items (optional) */\n deleteToolId?: string\n /** Mapping from tool data to UI fields */\n mapping: ToolSettingsListMapping\n /** Param name for search query (default: \"query\") */\n searchParam?: string\n /** Param name for limit (default: \"limit\") */\n limitParam?: string\n /** Param name for get/delete ID (default: \"id\") */\n idParam?: string\n /** Static params always sent to list tool */\n listParams?: Record<string, unknown>\n}\n\n/**\n * Mapping from tool list data to UI fields\n */\nexport interface ToolSettingsListMapping {\n /** Key for items array in tool result data */\n itemsKey: string\n /** Key for total count in tool result data */\n countKey?: string\n /** Key for item ID */\n idKey: string\n /** Key for item label */\n labelKey: string\n /** Key for item description */\n descriptionKey?: string\n /** Key for secondary label */\n secondaryKey?: string\n}\n\n/**\n * Component-based tool settings view using the declarative DSL.\n * Reuses the same structure as PanelComponentView for consistency.\n */\nexport interface ToolSettingsComponentView {\n /** View kind */\n kind: 'component'\n /** Data sources. Keys become scope variables (e.g., \"$settings\"). */\n data?: Record<string, ToolSettingsActionDataSource>\n /** Root component to render */\n content: import('./types.components.js').ExtensionComponentData\n}\n\n/**\n * Action-based data source for tool settings.\n */\nexport interface ToolSettingsActionDataSource {\n /** Action ID to call for fetching data */\n action: string\n /** Parameters to pass to the action */\n params?: Record<string, unknown>\n /** Event names that trigger refresh */\n refreshOn?: string[]\n}\n\n/**\n * Panel definition for right panel views\n */\nexport interface PanelDefinition {\n /** Unique panel ID within the extension */\n id: string\n /** Display title */\n title: string\n /** Icon name (from huge-icons) */\n icon?: string\n /** Panel view schema */\n view: PanelView\n}\n\n/**\n * Panel view schema (declarative)\n */\nexport type PanelView = PanelComponentView | PanelUnknownView\n\nexport interface PanelUnknownView {\n /** View kind */\n kind: string\n /** Additional view configuration */\n [key: string]: unknown\n}\n\n/**\n * Action-based data source for declarative panels.\n * Uses actions (not tools) to fetch data.\n */\nexport interface PanelActionDataSource {\n /** Action ID to call for fetching data */\n action: string\n /** Parameters to pass to the action */\n params?: Record<string, unknown>\n /** Event names that should trigger a refresh of this data */\n refreshOn?: string[]\n}\n\n/**\n * Component-based panel view using the declarative DSL.\n * Data is fetched via actions, content is rendered via ExtensionComponent.\n */\nexport interface PanelComponentView {\n kind: 'component'\n /** Data sources available in the panel. Keys become variable names (e.g., \"$projects\"). */\n data?: Record<string, PanelActionDataSource>\n /** Root component to render */\n content: import('./types.components.js').ExtensionComponentData\n}\n\n/**\n * Provider definition (metadata only, implementation in code)\n */\nexport interface ProviderDefinition {\n /** Provider ID */\n id: string\n /** Display name */\n name: string\n /** Description */\n description?: string\n /** Suggested default model when creating a new model configuration */\n suggestedDefaultModel?: string\n /** Default settings for this provider (e.g., { url: \"http://localhost:11434\" }) */\n defaultSettings?: Record<string, unknown>\n /** Schema for provider-specific configuration UI */\n configSchema?: ProviderConfigSchema\n}\n\n// ============================================================================\n// Prompt Contributions\n// ============================================================================\n\nexport type PromptSection = 'system' | 'behavior' | 'tools'\n\nexport interface PromptContribution {\n /** Unique ID within the extension */\n id: string\n /** Optional title for the prompt chunk */\n title?: string\n /** Prompt section placement */\n section?: PromptSection\n /** Plain text prompt content */\n text?: string\n /** Optional localized prompt content (keyed by locale, e.g. \"en\", \"sv\") */\n i18n?: Record<string, string>\n /** Optional ordering hint (lower comes first) */\n order?: number\n}\n\n// ============================================================================\n// Provider Configuration Schema\n// ============================================================================\n\n/**\n * Schema for provider-specific configuration.\n * Used to generate UI forms for configuring provider settings.\n */\nexport interface ProviderConfigSchema {\n /** Property definitions */\n properties: Record<string, ProviderConfigProperty>\n /** Display order of properties in UI (optional, defaults to object key order) */\n order?: string[]\n}\n\n/**\n * Property types for provider configuration\n */\nexport type ProviderConfigPropertyType =\n | 'string'\n | 'number'\n | 'boolean'\n | 'select'\n | 'password'\n | 'url'\n\n/**\n * Single property in a provider configuration schema.\n * Defines how a setting should be rendered and validated in the UI.\n */\nexport interface ProviderConfigProperty {\n /** Property type - determines UI control */\n type: ProviderConfigPropertyType\n /** Display label */\n title: string\n /** Help text shown below the input */\n description?: string\n /** Default value */\n default?: unknown\n /** Whether the field is required */\n required?: boolean\n /** Placeholder text for input fields */\n placeholder?: string\n /** For 'select' type: static options */\n options?: ProviderConfigSelectOption[]\n /** Validation rules */\n validation?: ProviderConfigValidation\n}\n\n/**\n * Option for select-type properties\n */\nexport interface ProviderConfigSelectOption {\n /** Value stored in settings */\n value: string\n /** Display label */\n label: string\n}\n\n/**\n * Validation rules for a property\n */\nexport interface ProviderConfigValidation {\n /** Regex pattern the value must match */\n pattern?: string\n /** Minimum string length */\n minLength?: number\n /** Maximum string length */\n maxLength?: number\n /** Minimum number value */\n min?: number\n /** Maximum number value */\n max?: number\n}\n\n/**\n * Tool definition (metadata only, implementation in code)\n */\nexport interface ToolDefinition {\n /** Tool ID */\n id: string\n /**\n * Display name - can be a simple string or localized strings.\n * @example \"Get Weather\"\n * @example { en: \"Get Weather\", sv: \"Hämta väder\" }\n */\n name: LocalizedString\n /**\n * Description for Stina - can be a simple string or localized strings.\n * Note: The AI always receives the English description (or fallback) for consistency.\n * Localized descriptions are used for UI display only.\n * @example \"Fetches current weather for a location\"\n * @example { en: \"Fetches current weather\", sv: \"Hämtar aktuellt väder\" }\n */\n description: LocalizedString\n /** Parameter schema (JSON Schema) */\n parameters?: Record<string, unknown>\n}\n\n/**\n * Command definition\n */\nexport interface CommandDefinition {\n /** Command ID (e.g., \"weather\" for /weather) */\n id: string\n /** Display name */\n name: string\n /** Description */\n description: string\n}\n\n// ============================================================================\n// Permissions\n// ============================================================================\n\nexport type Permission =\n | NetworkPermission\n | StoragePermission\n | UserDataPermission\n | CapabilityPermission\n | SystemPermission\n\n/** Network access permissions */\nexport type NetworkPermission =\n | 'network:*'\n | `network:localhost`\n | `network:localhost:${number}`\n | `network:${string}`\n\n/** Storage permissions */\nexport type StoragePermission = 'database.own' | 'storage.local'\n\n/** User data permissions */\nexport type UserDataPermission =\n | 'user.profile.read'\n | 'user.location.read'\n | 'chat.history.read'\n | 'chat.current.read'\n\n/** Capability permissions */\nexport type CapabilityPermission =\n | 'provider.register'\n | 'tools.register'\n | 'actions.register'\n | 'settings.register'\n | 'commands.register'\n | 'panels.register'\n | 'events.emit'\n | 'scheduler.register'\n | 'chat.message.write'\n\n/** System permissions */\nexport type SystemPermission =\n | 'files.read'\n | 'files.write'\n | 'clipboard.read'\n | 'clipboard.write'\n\n// ============================================================================\n// Extension Context (API available to extensions)\n// ============================================================================\n\n/**\n * Disposable resource that can be cleaned up\n */\nexport interface Disposable {\n dispose(): void\n}\n\n/**\n * Context provided to extension's activate function.\n *\n * ## User ID Context\n *\n * The `userId` field provides the current user context for extensions. It is set when:\n * - A tool is executed by a user\n * - An action is executed by a user\n * - A scheduled job fires (if the job was created with a userId)\n *\n * For extension activation, `userId` is undefined since activation happens at system level.\n * Extensions should check for `userId` in their tool/action handlers to access user-specific data.\n *\n * @example\n * ```typescript\n * // In a tool execute handler:\n * execute: async (params, context) => {\n * if (context.userId) {\n * // User-specific logic\n * const userData = await storage.getForUser(context.userId, 'preferences')\n * }\n * }\n * ```\n */\nexport interface ExtensionContext {\n /** Extension metadata */\n readonly extension: {\n readonly id: string\n readonly version: string\n readonly storagePath: string\n }\n\n /**\n * Current user ID if in a user context, undefined for global/system operations.\n * Set when tools or actions are executed by a user, or when a user-scoped job fires.\n */\n readonly userId?: string\n\n /** Network access (if permitted) */\n readonly network?: NetworkAPI\n\n /** Settings access (if permitted) */\n readonly settings?: SettingsAPI\n\n /** Provider registration (if permitted) */\n readonly providers?: ProvidersAPI\n\n /** Tool registration (if permitted) */\n readonly tools?: ToolsAPI\n\n /** Action registration (if permitted) */\n readonly actions?: ActionsAPI\n\n /** Event emission (if permitted) */\n readonly events?: EventsAPI\n\n /** Scheduler access (if permitted) */\n readonly scheduler?: SchedulerAPI\n\n /** User data access (if permitted) */\n readonly user?: UserAPI\n\n /** Chat access (if permitted) */\n readonly chat?: ChatAPI\n\n /** Database access (if permitted) */\n readonly database?: DatabaseAPI\n\n /** Local storage (if permitted) */\n readonly storage?: StorageAPI\n\n /** Logging (always available) */\n readonly log: LogAPI\n}\n\n/**\n * Network API for making HTTP requests\n */\nexport interface NetworkAPI {\n /**\n * Fetch a URL (permissions are enforced by host)\n */\n fetch(url: string, options?: RequestInit): Promise<Response>\n\n /**\n * Streaming fetch for responses like NDJSON or SSE.\n * Yields text chunks as they arrive from the server.\n *\n * @throws {Error} If the request fails or encounters a network error.\n * The error message will contain details about the failure.\n *\n * @example\n * ```typescript\n * try {\n * for await (const chunk of context.network.fetchStream(url, options)) {\n * // Process each chunk (may contain partial lines)\n * buffer += chunk\n * }\n * } catch (error) {\n * console.error('Streaming fetch failed:', error.message)\n * }\n * ```\n */\n fetchStream(url: string, options?: RequestInit): AsyncGenerator<string, void, unknown>\n}\n\n/**\n * Settings API for reading/writing extension settings\n */\nexport interface SettingsAPI {\n /**\n * Get all settings for this extension\n */\n getAll<T extends Record<string, unknown>>(): Promise<T>\n\n /**\n * Get a specific setting value\n */\n get<T>(key: string): Promise<T | undefined>\n\n /**\n * Set a setting value\n */\n set(key: string, value: unknown): Promise<void>\n\n /**\n * Listen for setting changes\n */\n onChange(callback: (key: string, value: unknown) => void): Disposable\n}\n\n/**\n * Providers API for registering AI providers\n */\nexport interface ProvidersAPI {\n /**\n * Register an AI provider\n */\n register(provider: AIProvider): Disposable\n}\n\n/**\n * Tools API for registering tools\n */\nexport interface ToolsAPI {\n /**\n * Register a tool that Stina can use\n */\n register(tool: Tool): Disposable\n}\n\n/**\n * Actions API for registering UI actions\n */\nexport interface ActionsAPI {\n /**\n * Register an action that UI components can invoke\n */\n register(action: Action): Disposable\n}\n\n/**\n * Events API for notifying the host\n */\nexport interface EventsAPI {\n /**\n * Emit a named event with optional payload\n */\n emit(name: string, payload?: Record<string, unknown>): Promise<void>\n}\n\n/**\n * Scheduler schedule types\n */\nexport type SchedulerSchedule =\n | { type: 'at'; at: string }\n | { type: 'cron'; cron: string; timezone?: string }\n | { type: 'interval'; everyMs: number }\n\n/**\n * Scheduler job request\n */\nexport interface SchedulerJobRequest {\n id: string\n schedule: SchedulerSchedule\n payload?: Record<string, unknown>\n misfire?: 'run_once' | 'skip'\n /**\n * Optional user ID for user-scoped jobs.\n * If set, the job is associated with a specific user and the userId\n * will be passed to the extension when the job fires.\n */\n userId?: string\n}\n\n/**\n * Scheduler fire payload\n */\nexport interface SchedulerFirePayload {\n id: string\n payload?: Record<string, unknown>\n scheduledFor: string\n firedAt: string\n delayMs: number\n /** User ID if this is a user-scoped job, undefined if global */\n userId?: string\n}\n\n/**\n * Scheduler API for registering jobs\n */\nexport interface SchedulerAPI {\n schedule(job: SchedulerJobRequest): Promise<void>\n cancel(jobId: string): Promise<void>\n onFire(callback: (payload: SchedulerFirePayload) => void | Promise<void>): Disposable\n}\n\n/**\n * User profile data\n */\nexport interface UserProfile {\n firstName?: string\n nickname?: string\n language?: string\n timezone?: string\n}\n\n/**\n * User API for profile access\n */\nexport interface UserAPI {\n getProfile(): Promise<UserProfile>\n}\n\n/**\n * Chat instruction message\n */\nexport interface ChatInstructionMessage {\n text: string\n conversationId?: string\n userId?: string\n}\n\n/**\n * Chat API for appending instructions\n */\nexport interface ChatAPI {\n appendInstruction(message: ChatInstructionMessage): Promise<void>\n}\n\n/**\n * Database API for extension-specific tables\n */\nexport interface DatabaseAPI {\n /**\n * Execute a SQL query (only extension's prefixed tables allowed)\n */\n execute<T = unknown>(sql: string, params?: unknown[]): Promise<T[]>\n}\n\n/**\n * Simple key-value storage API with support for user-scoped storage.\n *\n * ## Global vs User-Scoped Storage\n *\n * Extensions have access to two types of storage:\n * - **Global storage**: Shared across all users, accessed via `get()`, `set()`, etc.\n * - **User-scoped storage**: Isolated per user, accessed via `getForUser()`, `setForUser()`, etc.\n *\n * Use global storage for extension-wide settings and user-scoped storage for\n * user preferences, session data, or any data that should be private to a user.\n *\n * @example\n * ```typescript\n * // Global storage (extension-wide)\n * await storage.set('apiEndpoint', 'https://api.example.com')\n * const endpoint = await storage.get<string>('apiEndpoint')\n *\n * // User-scoped storage (per-user)\n * if (context.userId) {\n * await storage.setForUser(context.userId, 'preferences', { theme: 'dark' })\n * const prefs = await storage.getForUser<Preferences>(context.userId, 'preferences')\n * }\n * ```\n */\nexport interface StorageAPI {\n /**\n * Get a value by key (global/extension-scoped)\n */\n get<T>(key: string): Promise<T | undefined>\n\n /**\n * Set a value (global/extension-scoped)\n */\n set(key: string, value: unknown): Promise<void>\n\n /**\n * Delete a key (global/extension-scoped)\n */\n delete(key: string): Promise<void>\n\n /**\n * Get all keys (global/extension-scoped)\n */\n keys(): Promise<string[]>\n\n /**\n * Get a value by key for a specific user (user-scoped)\n * @param userId The user ID\n * @param key The storage key\n */\n getForUser<T>(userId: string, key: string): Promise<T | undefined>\n\n /**\n * Set a value for a specific user (user-scoped)\n * @param userId The user ID\n * @param key The storage key\n * @param value The value to store\n */\n setForUser(userId: string, key: string, value: unknown): Promise<void>\n\n /**\n * Delete a key for a specific user (user-scoped)\n * @param userId The user ID\n * @param key The storage key\n */\n deleteForUser(userId: string, key: string): Promise<void>\n\n /**\n * Get all keys for a specific user (user-scoped)\n * @param userId The user ID\n */\n keysForUser(userId: string): Promise<string[]>\n}\n\n/**\n * Logging API\n */\nexport interface LogAPI {\n debug(message: string, data?: Record<string, unknown>): void\n info(message: string, data?: Record<string, unknown>): void\n warn(message: string, data?: Record<string, unknown>): void\n error(message: string, data?: Record<string, unknown>): void\n}\n\n// ============================================================================\n// AI Provider Types\n// ============================================================================\n\n/**\n * AI provider implementation\n */\nexport interface AIProvider {\n /** Provider ID (must match manifest) */\n id: string\n /** Display name */\n name: string\n\n /**\n * Get available models from this provider\n * @param options Optional settings for the provider (e.g., URL)\n */\n getModels(options?: GetModelsOptions): Promise<ModelInfo[]>\n\n /**\n * Chat completion with streaming\n */\n chat(\n messages: ChatMessage[],\n options: ChatOptions\n ): AsyncGenerator<StreamEvent, void, unknown>\n\n /**\n * Optional: Generate embeddings\n */\n embed?(texts: string[]): Promise<number[][]>\n}\n\n/**\n * Model information\n */\nexport interface ModelInfo {\n /** Model ID */\n id: string\n /** Display name */\n name: string\n /** Description */\n description?: string\n /** Context window size */\n contextLength?: number\n}\n\n/**\n * Chat message\n */\nexport interface ChatMessage {\n role: 'user' | 'assistant' | 'system' | 'tool'\n content: string\n /** For assistant messages: tool calls made by the model */\n tool_calls?: ToolCall[]\n /** For tool messages: the ID of the tool call this is a response to */\n tool_call_id?: string\n}\n\n/**\n * A tool call made by the model\n */\nexport interface ToolCall {\n /** Unique ID for this tool call */\n id: string\n /** Tool name/ID to invoke */\n name: string\n /** Arguments for the tool (as parsed object) */\n arguments: Record<string, unknown>\n}\n\n/**\n * Options for chat completion\n */\nexport interface ChatOptions {\n /** Model to use */\n model?: string\n /** Temperature (0-1) */\n temperature?: number\n /** Maximum tokens to generate */\n maxTokens?: number\n /** Abort signal for cancellation */\n signal?: AbortSignal\n /** Provider-specific settings from model configuration */\n settings?: Record<string, unknown>\n /** Available tools for this request */\n tools?: ToolDefinition[]\n}\n\n/**\n * Options for getModels\n */\nexport interface GetModelsOptions {\n /** Provider-specific settings (e.g., URL for Ollama) */\n settings?: Record<string, unknown>\n}\n\n/**\n * Streaming events from chat\n */\nexport type StreamEvent =\n | { type: 'content'; text: string }\n | { type: 'thinking'; text: string }\n | { type: 'tool_start'; name: string; input: unknown; toolCallId: string }\n | { type: 'tool_end'; name: string; output: unknown; toolCallId: string }\n | { type: 'done'; usage?: { inputTokens: number; outputTokens: number } }\n | { type: 'error'; message: string }\n\n// ============================================================================\n// Tool Types\n// ============================================================================\n\n/**\n * Tool implementation\n */\nexport interface Tool {\n /** Tool ID (must match manifest) */\n id: string\n /** Display name */\n name: string\n /** Description for Stina */\n description: string\n /** Parameter schema (JSON Schema) */\n parameters?: Record<string, unknown>\n\n /**\n * Execute the tool\n */\n execute(params: Record<string, unknown>): Promise<ToolResult>\n}\n\n/**\n * Tool execution result\n */\nexport interface ToolResult {\n /** Whether the tool succeeded */\n success: boolean\n /** Result data (for Stina to use) */\n data?: unknown\n /** Human-readable message */\n message?: string\n /** Error message if failed */\n error?: string\n}\n\n// ============================================================================\n// Action Types (for UI interactions, separate from Tools)\n// ============================================================================\n\n/**\n * Action implementation for UI interactions.\n * Actions are invoked by UI components, not by Stina (AI).\n */\nexport interface Action {\n /** Action ID (unique within the extension) */\n id: string\n\n /**\n * Execute the action\n * @param params Parameters from the UI component (with $-values already resolved)\n */\n execute(params: Record<string, unknown>): Promise<ActionResult>\n}\n\n/**\n * Action execution result\n */\nexport interface ActionResult {\n /** Whether the action succeeded */\n success: boolean\n /** Result data (returned to UI) */\n data?: unknown\n /** Error message if failed */\n error?: string\n}\n\n// ============================================================================\n// Extension Entry Point\n// ============================================================================\n\n/**\n * Extension entry point interface\n */\nexport interface ExtensionModule {\n /**\n * Called when extension is activated\n */\n activate(context: ExtensionContext): void | Disposable | Promise<void | Disposable>\n\n /**\n * Called when extension is deactivated\n */\n deactivate?(): void | Promise<void>\n}\n","/**\n * Message protocol between Extension Host and Extension Workers\n */\n\nimport type {\n ChatMessage,\n ChatOptions,\n GetModelsOptions,\n StreamEvent,\n ToolResult,\n ActionResult,\n ModelInfo,\n SchedulerFirePayload,\n} from './types.js'\n\n// ============================================================================\n// Host → Worker Messages\n// ============================================================================\n\nexport type HostToWorkerMessage =\n | ActivateMessage\n | DeactivateMessage\n | SettingsChangedMessage\n | SchedulerFireMessage\n | ProviderChatRequestMessage\n | ProviderModelsRequestMessage\n | ToolExecuteRequestMessage\n | ActionExecuteRequestMessage\n | ResponseMessage\n | StreamingFetchChunkMessage\n\nexport interface ActivateMessage {\n type: 'activate'\n id: string\n payload: {\n extensionId: string\n extensionVersion: string\n storagePath: string\n permissions: string[]\n settings: Record<string, unknown>\n }\n}\n\nexport interface DeactivateMessage {\n type: 'deactivate'\n id: string\n}\n\nexport interface SettingsChangedMessage {\n type: 'settings-changed'\n id: string\n payload: {\n key: string\n value: unknown\n }\n}\n\nexport interface SchedulerFireMessage {\n type: 'scheduler-fire'\n id: string\n payload: SchedulerFirePayload\n}\n\nexport interface ProviderChatRequestMessage {\n type: 'provider-chat-request'\n id: string\n payload: {\n providerId: string\n messages: ChatMessage[]\n options: ChatOptions\n }\n}\n\nexport interface ProviderModelsRequestMessage {\n type: 'provider-models-request'\n id: string\n payload: {\n providerId: string\n options?: GetModelsOptions\n }\n}\n\nexport interface ToolExecuteRequestMessage {\n type: 'tool-execute-request'\n id: string\n payload: {\n toolId: string\n params: Record<string, unknown>\n /** User ID if the tool is executed in a user context */\n userId?: string\n }\n}\n\nexport interface ActionExecuteRequestMessage {\n type: 'action-execute-request'\n id: string\n payload: {\n actionId: string\n params: Record<string, unknown>\n /** User ID if the action is executed in a user context */\n userId?: string\n }\n}\n\nexport interface ResponseMessage {\n type: 'response'\n id: string\n payload: {\n requestId: string\n success: boolean\n data?: unknown\n error?: string\n }\n}\n\n/**\n * Message sent from host to worker with streaming fetch data chunks.\n * Used for streaming network responses (e.g., NDJSON streams from Ollama).\n */\nexport interface StreamingFetchChunkMessage {\n type: 'streaming-fetch-chunk'\n id: string\n payload: {\n requestId: string\n chunk: string\n done: boolean\n error?: string\n }\n}\n\n// ============================================================================\n// Worker → Host Messages\n// ============================================================================\n\nexport type WorkerToHostMessage =\n | ReadyMessage\n | RequestMessage\n | ProviderRegisteredMessage\n | ToolRegisteredMessage\n | ActionRegisteredMessage\n | StreamEventMessage\n | LogMessage\n | ProviderModelsResponseMessage\n | ToolExecuteResponseMessage\n | ActionExecuteResponseMessage\n | StreamingFetchAckMessage\n\nexport interface ReadyMessage {\n type: 'ready'\n}\n\n/**\n * Message sent from worker to host to acknowledge receipt of a streaming fetch chunk.\n * This enables backpressure control to prevent unbounded memory growth.\n */\nexport interface StreamingFetchAckMessage {\n type: 'streaming-fetch-ack'\n payload: {\n requestId: string\n }\n}\n\nexport interface RequestMessage {\n type: 'request'\n id: string\n method: RequestMethod\n payload: unknown\n}\n\nexport type RequestMethod =\n | 'network.fetch'\n | 'network.fetch-stream'\n | 'settings.getAll'\n | 'settings.get'\n | 'settings.set'\n | 'user.getProfile'\n | 'events.emit'\n | 'scheduler.schedule'\n | 'scheduler.cancel'\n | 'chat.appendInstruction'\n | 'database.execute'\n | 'storage.get'\n | 'storage.set'\n | 'storage.delete'\n | 'storage.keys'\n | 'storage.getForUser'\n | 'storage.setForUser'\n | 'storage.deleteForUser'\n | 'storage.keysForUser'\n\nexport interface ProviderRegisteredMessage {\n type: 'provider-registered'\n payload: {\n id: string\n name: string\n }\n}\n\nexport interface ToolRegisteredMessage {\n type: 'tool-registered'\n payload: {\n id: string\n name: string\n description: string\n parameters?: Record<string, unknown>\n }\n}\n\nexport interface ActionRegisteredMessage {\n type: 'action-registered'\n payload: {\n id: string\n }\n}\n\nexport interface StreamEventMessage {\n type: 'stream-event'\n payload: {\n requestId: string\n event: StreamEvent\n }\n}\n\nexport interface ProviderModelsResponseMessage {\n type: 'provider-models-response'\n payload: {\n requestId: string\n models: ModelInfo[]\n error?: string\n }\n}\n\nexport interface ToolExecuteResponseMessage {\n type: 'tool-execute-response'\n payload: {\n requestId: string\n result: ToolResult\n error?: string\n }\n}\n\nexport interface ActionExecuteResponseMessage {\n type: 'action-execute-response'\n payload: {\n requestId: string\n result: ActionResult\n error?: string\n }\n}\n\nexport interface LogMessage {\n type: 'log'\n payload: {\n level: 'debug' | 'info' | 'warn' | 'error'\n message: string\n data?: Record<string, unknown>\n }\n}\n\n// ============================================================================\n// Utility Types\n// ============================================================================\n\nexport interface PendingRequest<T = unknown> {\n resolve: (value: T) => void\n reject: (error: Error) => void\n timeout: ReturnType<typeof setTimeout>\n}\n\n/**\n * Generate a unique message ID\n */\nexport function generateMessageId(): string {\n return `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACsBO,SAAS,uBACd,OACA,MACA,eAAe,MACP;AACR,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO;AAAA,EACT;AAEA,SAAO,MAAM,IAAI,KAAK,MAAM,YAAY,KAAK,OAAO,OAAO,KAAK,EAAE,CAAC,KAAK;AAC1E;;;ACgPO,SAAS,oBAA4B;AAC1C,SAAO,GAAG,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,EAAE,CAAC;AACjE;","names":[]}
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
- import { S as SchedulerFirePayload, C as ChatMessage, a as ChatOptions, G as GetModelsOptions, b as StreamEvent, M as ModelInfo, T as ToolResult, A as ActionResult } from './types-kRt2DzdL.cjs';
2
- export { a7 as AIProvider, aa as Action, X as ActionsAPI, ac as AllowedCSSProperty, ao as ButtonProps, I as CapabilityPermission, a2 as ChatAPI, a3 as ChatInstructionMessage, aF as CheckboxProps, aC as CollapsibleProps, w as CommandDefinition, a4 as DatabaseAPI, aq as DateTimeInputProps, O as Disposable, av as DividerProps, Y as EventsAPI, ah as ExtensionActionCall, ai as ExtensionActionRef, ag as ExtensionComponentChildren, ae as ExtensionComponentData, af as ExtensionComponentIterator, ad as ExtensionComponentStyle, K as ExtensionContext, c as ExtensionContributions, aj as ExtensionDataSource, E as ExtensionManifest, ab as ExtensionModule, ak as ExtensionPanelDefinition, au as GridProps, al as HeaderProps, at as HorizontalStackProps, ay as IconButtonProps, ax as IconButtonType, aw as IconProps, am as LabelProps, L as LocalizedString, a6 as LogAPI, aG as MarkdownProps, aH as ModalProps, Q as NetworkAPI, N as NetworkPermission, az as PanelAction, p as PanelActionDataSource, o as PanelComponentView, m as PanelDefinition, aA as PanelProps, q as PanelUnknownView, n as PanelView, an as ParagraphProps, F as Permission, aE as PillProps, aD as PillVariant, P as Platform, t as PromptContribution, u as PromptSection, y as ProviderConfigProperty, z as ProviderConfigPropertyType, x as ProviderConfigSchema, B as ProviderConfigSelectOption, D as ProviderConfigValidation, s as ProviderDefinition, V as ProvidersAPI, Z as SchedulerAPI, _ as SchedulerJobRequest, $ as SchedulerSchedule, ar as SelectProps, f as SettingCreateMapping, d as SettingDefinition, e as SettingOptionsMapping, R as SettingsAPI, a5 as StorageAPI, H as StoragePermission, J as SystemPermission, ap as TextInputProps, aB as ToggleProps, a9 as Tool, a8 as ToolCall, v as ToolDefinition, l as ToolSettingsActionDataSource, k as ToolSettingsComponentView, j as ToolSettingsListMapping, i as ToolSettingsListView, h as ToolSettingsView, g as ToolSettingsViewDefinition, W as ToolsAPI, a0 as UserAPI, U as UserDataPermission, a1 as UserProfile, as as VerticalStackProps, r as resolveLocalizedString } from './types-kRt2DzdL.cjs';
1
+ import { S as SchedulerFirePayload, C as ChatMessage, a as ChatOptions, G as GetModelsOptions, b as StreamEvent, M as ModelInfo, T as ToolResult, A as ActionResult } from './types-CvfONPis.cjs';
2
+ export { a7 as AIProvider, aa as Action, X as ActionsAPI, ac as AllowedCSSProperty, ao as ButtonProps, I as CapabilityPermission, a2 as ChatAPI, a3 as ChatInstructionMessage, aF as CheckboxProps, aC as CollapsibleProps, w as CommandDefinition, a4 as DatabaseAPI, aq as DateTimeInputProps, O as Disposable, av as DividerProps, Y as EventsAPI, ah as ExtensionActionCall, ai as ExtensionActionRef, ag as ExtensionComponentChildren, ae as ExtensionComponentData, af as ExtensionComponentIterator, ad as ExtensionComponentStyle, K as ExtensionContext, c as ExtensionContributions, aj as ExtensionDataSource, E as ExtensionManifest, ab as ExtensionModule, ak as ExtensionPanelDefinition, au as GridProps, al as HeaderProps, at as HorizontalStackProps, ay as IconButtonProps, ax as IconButtonType, aw as IconProps, am as LabelProps, L as LocalizedString, a6 as LogAPI, aG as MarkdownProps, aH as ModalProps, Q as NetworkAPI, N as NetworkPermission, az as PanelAction, p as PanelActionDataSource, o as PanelComponentView, m as PanelDefinition, aA as PanelProps, q as PanelUnknownView, n as PanelView, an as ParagraphProps, F as Permission, aE as PillProps, aD as PillVariant, P as Platform, t as PromptContribution, u as PromptSection, y as ProviderConfigProperty, z as ProviderConfigPropertyType, x as ProviderConfigSchema, B as ProviderConfigSelectOption, D as ProviderConfigValidation, s as ProviderDefinition, V as ProvidersAPI, Z as SchedulerAPI, _ as SchedulerJobRequest, $ as SchedulerSchedule, ar as SelectProps, f as SettingCreateMapping, d as SettingDefinition, e as SettingOptionsMapping, R as SettingsAPI, a5 as StorageAPI, H as StoragePermission, J as SystemPermission, ap as TextInputProps, aB as ToggleProps, a9 as Tool, a8 as ToolCall, v as ToolDefinition, l as ToolSettingsActionDataSource, k as ToolSettingsComponentView, j as ToolSettingsListMapping, i as ToolSettingsListView, h as ToolSettingsView, g as ToolSettingsViewDefinition, W as ToolsAPI, a0 as UserAPI, U as UserDataPermission, a1 as UserProfile, as as VerticalStackProps, r as resolveLocalizedString } from './types-CvfONPis.cjs';
3
3
 
4
4
  /**
5
5
  * Message protocol between Extension Host and Extension Workers
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { S as SchedulerFirePayload, C as ChatMessage, a as ChatOptions, G as GetModelsOptions, b as StreamEvent, M as ModelInfo, T as ToolResult, A as ActionResult } from './types-kRt2DzdL.js';
2
- export { a7 as AIProvider, aa as Action, X as ActionsAPI, ac as AllowedCSSProperty, ao as ButtonProps, I as CapabilityPermission, a2 as ChatAPI, a3 as ChatInstructionMessage, aF as CheckboxProps, aC as CollapsibleProps, w as CommandDefinition, a4 as DatabaseAPI, aq as DateTimeInputProps, O as Disposable, av as DividerProps, Y as EventsAPI, ah as ExtensionActionCall, ai as ExtensionActionRef, ag as ExtensionComponentChildren, ae as ExtensionComponentData, af as ExtensionComponentIterator, ad as ExtensionComponentStyle, K as ExtensionContext, c as ExtensionContributions, aj as ExtensionDataSource, E as ExtensionManifest, ab as ExtensionModule, ak as ExtensionPanelDefinition, au as GridProps, al as HeaderProps, at as HorizontalStackProps, ay as IconButtonProps, ax as IconButtonType, aw as IconProps, am as LabelProps, L as LocalizedString, a6 as LogAPI, aG as MarkdownProps, aH as ModalProps, Q as NetworkAPI, N as NetworkPermission, az as PanelAction, p as PanelActionDataSource, o as PanelComponentView, m as PanelDefinition, aA as PanelProps, q as PanelUnknownView, n as PanelView, an as ParagraphProps, F as Permission, aE as PillProps, aD as PillVariant, P as Platform, t as PromptContribution, u as PromptSection, y as ProviderConfigProperty, z as ProviderConfigPropertyType, x as ProviderConfigSchema, B as ProviderConfigSelectOption, D as ProviderConfigValidation, s as ProviderDefinition, V as ProvidersAPI, Z as SchedulerAPI, _ as SchedulerJobRequest, $ as SchedulerSchedule, ar as SelectProps, f as SettingCreateMapping, d as SettingDefinition, e as SettingOptionsMapping, R as SettingsAPI, a5 as StorageAPI, H as StoragePermission, J as SystemPermission, ap as TextInputProps, aB as ToggleProps, a9 as Tool, a8 as ToolCall, v as ToolDefinition, l as ToolSettingsActionDataSource, k as ToolSettingsComponentView, j as ToolSettingsListMapping, i as ToolSettingsListView, h as ToolSettingsView, g as ToolSettingsViewDefinition, W as ToolsAPI, a0 as UserAPI, U as UserDataPermission, a1 as UserProfile, as as VerticalStackProps, r as resolveLocalizedString } from './types-kRt2DzdL.js';
1
+ import { S as SchedulerFirePayload, C as ChatMessage, a as ChatOptions, G as GetModelsOptions, b as StreamEvent, M as ModelInfo, T as ToolResult, A as ActionResult } from './types-CvfONPis.js';
2
+ export { a7 as AIProvider, aa as Action, X as ActionsAPI, ac as AllowedCSSProperty, ao as ButtonProps, I as CapabilityPermission, a2 as ChatAPI, a3 as ChatInstructionMessage, aF as CheckboxProps, aC as CollapsibleProps, w as CommandDefinition, a4 as DatabaseAPI, aq as DateTimeInputProps, O as Disposable, av as DividerProps, Y as EventsAPI, ah as ExtensionActionCall, ai as ExtensionActionRef, ag as ExtensionComponentChildren, ae as ExtensionComponentData, af as ExtensionComponentIterator, ad as ExtensionComponentStyle, K as ExtensionContext, c as ExtensionContributions, aj as ExtensionDataSource, E as ExtensionManifest, ab as ExtensionModule, ak as ExtensionPanelDefinition, au as GridProps, al as HeaderProps, at as HorizontalStackProps, ay as IconButtonProps, ax as IconButtonType, aw as IconProps, am as LabelProps, L as LocalizedString, a6 as LogAPI, aG as MarkdownProps, aH as ModalProps, Q as NetworkAPI, N as NetworkPermission, az as PanelAction, p as PanelActionDataSource, o as PanelComponentView, m as PanelDefinition, aA as PanelProps, q as PanelUnknownView, n as PanelView, an as ParagraphProps, F as Permission, aE as PillProps, aD as PillVariant, P as Platform, t as PromptContribution, u as PromptSection, y as ProviderConfigProperty, z as ProviderConfigPropertyType, x as ProviderConfigSchema, B as ProviderConfigSelectOption, D as ProviderConfigValidation, s as ProviderDefinition, V as ProvidersAPI, Z as SchedulerAPI, _ as SchedulerJobRequest, $ as SchedulerSchedule, ar as SelectProps, f as SettingCreateMapping, d as SettingDefinition, e as SettingOptionsMapping, R as SettingsAPI, a5 as StorageAPI, H as StoragePermission, J as SystemPermission, ap as TextInputProps, aB as ToggleProps, a9 as Tool, a8 as ToolCall, v as ToolDefinition, l as ToolSettingsActionDataSource, k as ToolSettingsComponentView, j as ToolSettingsListMapping, i as ToolSettingsListView, h as ToolSettingsView, g as ToolSettingsViewDefinition, W as ToolsAPI, a0 as UserAPI, U as UserDataPermission, a1 as UserProfile, as as VerticalStackProps, r as resolveLocalizedString } from './types-CvfONPis.js';
3
3
 
4
4
  /**
5
5
  * Message protocol between Extension Host and Extension Workers
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts"],"sourcesContent":["/**\n * A string that can be either a simple string or a map of language codes to localized strings.\n * When a simple string is provided, it's used as the default/fallback value.\n * When a map is provided, the appropriate language is selected at runtime.\n *\n * @example\n * // Simple string (backwards compatible)\n * name: \"Get Weather\"\n *\n * @example\n * // Localized strings\n * name: { en: \"Get Weather\", sv: \"Hämta väder\", de: \"Wetter abrufen\" }\n */\nexport type LocalizedString = string | Record<string, string>\n\n/**\n * Resolves a LocalizedString to an actual string value.\n * @param value The LocalizedString to resolve\n * @param lang The preferred language code (e.g., \"sv\", \"en\")\n * @param fallbackLang The fallback language code (defaults to \"en\")\n * @returns The resolved string value\n */\nexport function resolveLocalizedString(\n value: LocalizedString,\n lang: string,\n fallbackLang = 'en'\n): string {\n if (typeof value === 'string') {\n return value\n }\n // Try preferred language first, then fallback language, then first available, then empty string\n return value[lang] ?? value[fallbackLang] ?? Object.values(value)[0] ?? ''\n}\n\n/**\n * Extension manifest format (manifest.json)\n */\nexport interface ExtensionManifest {\n /** Unique identifier (e.g., \"ollama-provider\") */\n id: string\n /** Human-readable name */\n name: string\n /** Version string (semver) */\n version: string\n /** Short description */\n description: string\n /** Author information */\n author: {\n name: string\n url?: string\n }\n /** Repository URL */\n repository?: string\n /** License identifier */\n license?: string\n /** Minimum Stina version required */\n engines?: {\n stina: string\n }\n /** Supported platforms */\n platforms?: Platform[]\n /** Entry point file (relative to extension root) */\n main: string\n /** Required permissions */\n permissions: Permission[]\n /** What the extension contributes */\n contributes?: ExtensionContributions\n}\n\nexport type Platform = 'web' | 'electron' | 'tui'\n\n/**\n * What an extension can contribute to Stina\n */\nexport interface ExtensionContributions {\n /** User-configurable settings */\n settings?: SettingDefinition[]\n /** Tool settings views for UI */\n toolSettings?: ToolSettingsViewDefinition[]\n /** Right panel contributions */\n panels?: PanelDefinition[]\n /** AI providers */\n providers?: ProviderDefinition[]\n /** Tools for Stina to use */\n tools?: ToolDefinition[]\n /** Slash commands */\n commands?: CommandDefinition[]\n /** Prompt contributions for the system prompt */\n prompts?: PromptContribution[]\n}\n\n/**\n * Setting definition for the UI\n */\nexport interface SettingDefinition {\n /** Setting ID (namespaced automatically) */\n id: string\n /** Display title */\n title: string\n /** Help text */\n description?: string\n /** Setting type */\n type: 'string' | 'number' | 'boolean' | 'select'\n /** Default value */\n default?: unknown\n /** For select type: available options */\n options?: { value: string; label: string }[]\n /** For select type: load options from tool */\n optionsToolId?: string\n /** Params for options tool */\n optionsParams?: Record<string, unknown>\n /** Mapping for options tool response */\n optionsMapping?: SettingOptionsMapping\n /** Tool ID for creating a new option */\n createToolId?: string\n /** Label for create action */\n createLabel?: string\n /** Fields for create form */\n createFields?: SettingDefinition[]\n /** Static params always sent to create tool */\n createParams?: Record<string, unknown>\n /** Mapping for create tool response */\n createMapping?: SettingCreateMapping\n /** Validation rules */\n validation?: {\n required?: boolean\n min?: number\n max?: number\n pattern?: string\n }\n}\n\n/**\n * Mapping for select field options from tool response\n */\nexport interface SettingOptionsMapping {\n /** Key for items array in tool result data */\n itemsKey: string\n /** Key for option value */\n valueKey: string\n /** Key for option label */\n labelKey: string\n /** Optional key for description */\n descriptionKey?: string\n}\n\n/**\n * Mapping for create tool response\n */\nexport interface SettingCreateMapping {\n /** Key for result data object */\n resultKey?: string\n /** Key for option value (defaults to \"id\") */\n valueKey: string\n}\n\n/**\n * Tool settings view definition (UI schema)\n */\nexport interface ToolSettingsViewDefinition {\n /** Unique view ID within the extension */\n id: string\n /** Display title */\n title: string\n /** Help text */\n description?: string\n /** View configuration */\n view: ToolSettingsView\n /** Fields for create/edit forms (uses SettingDefinition) */\n fields?: SettingDefinition[]\n}\n\n/**\n * Tool settings view types\n */\nexport type ToolSettingsView = ToolSettingsListView | ToolSettingsComponentView\n\n/**\n * List view backed by tools\n */\nexport interface ToolSettingsListView {\n /** View kind */\n kind: 'list'\n /** Tool ID for listing items */\n listToolId: string\n /** Tool ID for fetching details (optional) */\n getToolId?: string\n /** Tool ID for creating/updating items (optional) */\n upsertToolId?: string\n /** Tool ID for deleting items (optional) */\n deleteToolId?: string\n /** Mapping from tool data to UI fields */\n mapping: ToolSettingsListMapping\n /** Param name for search query (default: \"query\") */\n searchParam?: string\n /** Param name for limit (default: \"limit\") */\n limitParam?: string\n /** Param name for get/delete ID (default: \"id\") */\n idParam?: string\n /** Static params always sent to list tool */\n listParams?: Record<string, unknown>\n}\n\n/**\n * Mapping from tool list data to UI fields\n */\nexport interface ToolSettingsListMapping {\n /** Key for items array in tool result data */\n itemsKey: string\n /** Key for total count in tool result data */\n countKey?: string\n /** Key for item ID */\n idKey: string\n /** Key for item label */\n labelKey: string\n /** Key for item description */\n descriptionKey?: string\n /** Key for secondary label */\n secondaryKey?: string\n}\n\n/**\n * Component-based tool settings view using the declarative DSL.\n * Reuses the same structure as PanelComponentView for consistency.\n */\nexport interface ToolSettingsComponentView {\n /** View kind */\n kind: 'component'\n /** Data sources. Keys become scope variables (e.g., \"$settings\"). */\n data?: Record<string, ToolSettingsActionDataSource>\n /** Root component to render */\n content: import('./types.components.js').ExtensionComponentData\n}\n\n/**\n * Action-based data source for tool settings.\n */\nexport interface ToolSettingsActionDataSource {\n /** Action ID to call for fetching data */\n action: string\n /** Parameters to pass to the action */\n params?: Record<string, unknown>\n /** Event names that trigger refresh */\n refreshOn?: string[]\n}\n\n/**\n * Panel definition for right panel views\n */\nexport interface PanelDefinition {\n /** Unique panel ID within the extension */\n id: string\n /** Display title */\n title: string\n /** Icon name (from huge-icons) */\n icon?: string\n /** Panel view schema */\n view: PanelView\n}\n\n/**\n * Panel view schema (declarative)\n */\nexport type PanelView = PanelComponentView | PanelUnknownView\n\nexport interface PanelUnknownView {\n /** View kind */\n kind: string\n /** Additional view configuration */\n [key: string]: unknown\n}\n\n/**\n * Action-based data source for declarative panels.\n * Uses actions (not tools) to fetch data.\n */\nexport interface PanelActionDataSource {\n /** Action ID to call for fetching data */\n action: string\n /** Parameters to pass to the action */\n params?: Record<string, unknown>\n /** Event names that should trigger a refresh of this data */\n refreshOn?: string[]\n}\n\n/**\n * Component-based panel view using the declarative DSL.\n * Data is fetched via actions, content is rendered via ExtensionComponent.\n */\nexport interface PanelComponentView {\n kind: 'component'\n /** Data sources available in the panel. Keys become variable names (e.g., \"$projects\"). */\n data?: Record<string, PanelActionDataSource>\n /** Root component to render */\n content: import('./types.components.js').ExtensionComponentData\n}\n\n/**\n * Provider definition (metadata only, implementation in code)\n */\nexport interface ProviderDefinition {\n /** Provider ID */\n id: string\n /** Display name */\n name: string\n /** Description */\n description?: string\n /** Suggested default model when creating a new model configuration */\n suggestedDefaultModel?: string\n /** Default settings for this provider (e.g., { url: \"http://localhost:11434\" }) */\n defaultSettings?: Record<string, unknown>\n /** Schema for provider-specific configuration UI */\n configSchema?: ProviderConfigSchema\n}\n\n// ============================================================================\n// Prompt Contributions\n// ============================================================================\n\nexport type PromptSection = 'system' | 'behavior' | 'tools'\n\nexport interface PromptContribution {\n /** Unique ID within the extension */\n id: string\n /** Optional title for the prompt chunk */\n title?: string\n /** Prompt section placement */\n section?: PromptSection\n /** Plain text prompt content */\n text?: string\n /** Optional localized prompt content (keyed by locale, e.g. \"en\", \"sv\") */\n i18n?: Record<string, string>\n /** Optional ordering hint (lower comes first) */\n order?: number\n}\n\n// ============================================================================\n// Provider Configuration Schema\n// ============================================================================\n\n/**\n * Schema for provider-specific configuration.\n * Used to generate UI forms for configuring provider settings.\n */\nexport interface ProviderConfigSchema {\n /** Property definitions */\n properties: Record<string, ProviderConfigProperty>\n /** Display order of properties in UI (optional, defaults to object key order) */\n order?: string[]\n}\n\n/**\n * Property types for provider configuration\n */\nexport type ProviderConfigPropertyType =\n | 'string'\n | 'number'\n | 'boolean'\n | 'select'\n | 'password'\n | 'url'\n\n/**\n * Single property in a provider configuration schema.\n * Defines how a setting should be rendered and validated in the UI.\n */\nexport interface ProviderConfigProperty {\n /** Property type - determines UI control */\n type: ProviderConfigPropertyType\n /** Display label */\n title: string\n /** Help text shown below the input */\n description?: string\n /** Default value */\n default?: unknown\n /** Whether the field is required */\n required?: boolean\n /** Placeholder text for input fields */\n placeholder?: string\n /** For 'select' type: static options */\n options?: ProviderConfigSelectOption[]\n /** Validation rules */\n validation?: ProviderConfigValidation\n}\n\n/**\n * Option for select-type properties\n */\nexport interface ProviderConfigSelectOption {\n /** Value stored in settings */\n value: string\n /** Display label */\n label: string\n}\n\n/**\n * Validation rules for a property\n */\nexport interface ProviderConfigValidation {\n /** Regex pattern the value must match */\n pattern?: string\n /** Minimum string length */\n minLength?: number\n /** Maximum string length */\n maxLength?: number\n /** Minimum number value */\n min?: number\n /** Maximum number value */\n max?: number\n}\n\n/**\n * Tool definition (metadata only, implementation in code)\n */\nexport interface ToolDefinition {\n /** Tool ID */\n id: string\n /**\n * Display name - can be a simple string or localized strings.\n * @example \"Get Weather\"\n * @example { en: \"Get Weather\", sv: \"Hämta väder\" }\n */\n name: LocalizedString\n /**\n * Description for Stina - can be a simple string or localized strings.\n * Note: The AI always receives the English description (or fallback) for consistency.\n * Localized descriptions are used for UI display only.\n * @example \"Fetches current weather for a location\"\n * @example { en: \"Fetches current weather\", sv: \"Hämtar aktuellt väder\" }\n */\n description: LocalizedString\n /** Parameter schema (JSON Schema) */\n parameters?: Record<string, unknown>\n}\n\n/**\n * Command definition\n */\nexport interface CommandDefinition {\n /** Command ID (e.g., \"weather\" for /weather) */\n id: string\n /** Display name */\n name: string\n /** Description */\n description: string\n}\n\n// ============================================================================\n// Permissions\n// ============================================================================\n\nexport type Permission =\n | NetworkPermission\n | StoragePermission\n | UserDataPermission\n | CapabilityPermission\n | SystemPermission\n\n/** Network access permissions */\nexport type NetworkPermission =\n | 'network:*'\n | `network:localhost`\n | `network:localhost:${number}`\n | `network:${string}`\n\n/** Storage permissions */\nexport type StoragePermission = 'database.own' | 'storage.local'\n\n/** User data permissions */\nexport type UserDataPermission =\n | 'user.profile.read'\n | 'user.location.read'\n | 'chat.history.read'\n | 'chat.current.read'\n\n/** Capability permissions */\nexport type CapabilityPermission =\n | 'provider.register'\n | 'tools.register'\n | 'actions.register'\n | 'settings.register'\n | 'commands.register'\n | 'panels.register'\n | 'events.emit'\n | 'scheduler.register'\n | 'chat.message.write'\n\n/** System permissions */\nexport type SystemPermission =\n | 'files.read'\n | 'files.write'\n | 'clipboard.read'\n | 'clipboard.write'\n\n// ============================================================================\n// Extension Context (API available to extensions)\n// ============================================================================\n\n/**\n * Disposable resource that can be cleaned up\n */\nexport interface Disposable {\n dispose(): void\n}\n\n/**\n * Context provided to extension's activate function.\n *\n * ## User ID Context\n *\n * The `userId` field provides the current user context for extensions. It is set when:\n * - A tool is executed by a user\n * - An action is executed by a user\n * - A scheduled job fires (if the job was created with a userId)\n *\n * For extension activation, `userId` is undefined since activation happens at system level.\n * Extensions should check for `userId` in their tool/action handlers to access user-specific data.\n *\n * @example\n * ```typescript\n * // In a tool execute handler:\n * execute: async (params, context) => {\n * if (context.userId) {\n * // User-specific logic\n * const userData = await storage.getForUser(context.userId, 'preferences')\n * }\n * }\n * ```\n */\nexport interface ExtensionContext {\n /** Extension metadata */\n readonly extension: {\n readonly id: string\n readonly version: string\n readonly storagePath: string\n }\n\n /**\n * Current user ID if in a user context, undefined for global/system operations.\n * Set when tools or actions are executed by a user, or when a user-scoped job fires.\n */\n readonly userId?: string\n\n /** Network access (if permitted) */\n readonly network?: NetworkAPI\n\n /** Settings access (if permitted) */\n readonly settings?: SettingsAPI\n\n /** Provider registration (if permitted) */\n readonly providers?: ProvidersAPI\n\n /** Tool registration (if permitted) */\n readonly tools?: ToolsAPI\n\n /** Action registration (if permitted) */\n readonly actions?: ActionsAPI\n\n /** Event emission (if permitted) */\n readonly events?: EventsAPI\n\n /** Scheduler access (if permitted) */\n readonly scheduler?: SchedulerAPI\n\n /** User data access (if permitted) */\n readonly user?: UserAPI\n\n /** Chat access (if permitted) */\n readonly chat?: ChatAPI\n\n /** Database access (if permitted) */\n readonly database?: DatabaseAPI\n\n /** Local storage (if permitted) */\n readonly storage?: StorageAPI\n\n /** Logging (always available) */\n readonly log: LogAPI\n}\n\n/**\n * Network API for making HTTP requests\n */\nexport interface NetworkAPI {\n /**\n * Fetch a URL (permissions are enforced by host)\n */\n fetch(url: string, options?: RequestInit): Promise<Response>\n\n /**\n * Streaming fetch for responses like NDJSON or SSE.\n * Yields text chunks as they arrive from the server.\n *\n * @throws {Error} If the request fails or encounters a network error.\n * The error message will contain details about the failure.\n *\n * @example\n * ```typescript\n * try {\n * for await (const chunk of context.network.fetchStream(url, options)) {\n * // Process each chunk (may contain partial lines)\n * buffer += chunk\n * }\n * } catch (error) {\n * console.error('Streaming fetch failed:', error.message)\n * }\n * ```\n */\n fetchStream(url: string, options?: RequestInit): AsyncGenerator<string, void, unknown>\n}\n\n/**\n * Settings API for reading/writing extension settings\n */\nexport interface SettingsAPI {\n /**\n * Get all settings for this extension\n */\n getAll<T extends Record<string, unknown>>(): Promise<T>\n\n /**\n * Get a specific setting value\n */\n get<T>(key: string): Promise<T | undefined>\n\n /**\n * Set a setting value\n */\n set(key: string, value: unknown): Promise<void>\n\n /**\n * Listen for setting changes\n */\n onChange(callback: (key: string, value: unknown) => void): Disposable\n}\n\n/**\n * Providers API for registering AI providers\n */\nexport interface ProvidersAPI {\n /**\n * Register an AI provider\n */\n register(provider: AIProvider): Disposable\n}\n\n/**\n * Tools API for registering tools\n */\nexport interface ToolsAPI {\n /**\n * Register a tool that Stina can use\n */\n register(tool: Tool): Disposable\n}\n\n/**\n * Actions API for registering UI actions\n */\nexport interface ActionsAPI {\n /**\n * Register an action that UI components can invoke\n */\n register(action: Action): Disposable\n}\n\n/**\n * Events API for notifying the host\n */\nexport interface EventsAPI {\n /**\n * Emit a named event with optional payload\n */\n emit(name: string, payload?: Record<string, unknown>): Promise<void>\n}\n\n/**\n * Scheduler schedule types\n */\nexport type SchedulerSchedule =\n | { type: 'at'; at: string }\n | { type: 'cron'; cron: string; timezone?: string }\n | { type: 'interval'; everyMs: number }\n\n/**\n * Scheduler job request\n */\nexport interface SchedulerJobRequest {\n id: string\n schedule: SchedulerSchedule\n payload?: Record<string, unknown>\n misfire?: 'run_once' | 'skip'\n /**\n * Optional user ID for user-scoped jobs.\n * If set, the job is associated with a specific user and the userId\n * will be passed to the extension when the job fires.\n */\n userId?: string\n}\n\n/**\n * Scheduler fire payload\n */\nexport interface SchedulerFirePayload {\n id: string\n payload?: Record<string, unknown>\n scheduledFor: string\n firedAt: string\n delayMs: number\n /** User ID if this is a user-scoped job, undefined if global */\n userId?: string\n}\n\n/**\n * Scheduler API for registering jobs\n */\nexport interface SchedulerAPI {\n schedule(job: SchedulerJobRequest): Promise<void>\n cancel(jobId: string): Promise<void>\n onFire(callback: (payload: SchedulerFirePayload) => void): Disposable\n}\n\n/**\n * User profile data\n */\nexport interface UserProfile {\n firstName?: string\n nickname?: string\n language?: string\n timezone?: string\n}\n\n/**\n * User API for profile access\n */\nexport interface UserAPI {\n getProfile(): Promise<UserProfile>\n}\n\n/**\n * Chat instruction message\n */\nexport interface ChatInstructionMessage {\n text: string\n conversationId?: string\n}\n\n/**\n * Chat API for appending instructions\n */\nexport interface ChatAPI {\n appendInstruction(message: ChatInstructionMessage): Promise<void>\n}\n\n/**\n * Database API for extension-specific tables\n */\nexport interface DatabaseAPI {\n /**\n * Execute a SQL query (only extension's prefixed tables allowed)\n */\n execute<T = unknown>(sql: string, params?: unknown[]): Promise<T[]>\n}\n\n/**\n * Simple key-value storage API with support for user-scoped storage.\n *\n * ## Global vs User-Scoped Storage\n *\n * Extensions have access to two types of storage:\n * - **Global storage**: Shared across all users, accessed via `get()`, `set()`, etc.\n * - **User-scoped storage**: Isolated per user, accessed via `getForUser()`, `setForUser()`, etc.\n *\n * Use global storage for extension-wide settings and user-scoped storage for\n * user preferences, session data, or any data that should be private to a user.\n *\n * @example\n * ```typescript\n * // Global storage (extension-wide)\n * await storage.set('apiEndpoint', 'https://api.example.com')\n * const endpoint = await storage.get<string>('apiEndpoint')\n *\n * // User-scoped storage (per-user)\n * if (context.userId) {\n * await storage.setForUser(context.userId, 'preferences', { theme: 'dark' })\n * const prefs = await storage.getForUser<Preferences>(context.userId, 'preferences')\n * }\n * ```\n */\nexport interface StorageAPI {\n /**\n * Get a value by key (global/extension-scoped)\n */\n get<T>(key: string): Promise<T | undefined>\n\n /**\n * Set a value (global/extension-scoped)\n */\n set(key: string, value: unknown): Promise<void>\n\n /**\n * Delete a key (global/extension-scoped)\n */\n delete(key: string): Promise<void>\n\n /**\n * Get all keys (global/extension-scoped)\n */\n keys(): Promise<string[]>\n\n /**\n * Get a value by key for a specific user (user-scoped)\n * @param userId The user ID\n * @param key The storage key\n */\n getForUser<T>(userId: string, key: string): Promise<T | undefined>\n\n /**\n * Set a value for a specific user (user-scoped)\n * @param userId The user ID\n * @param key The storage key\n * @param value The value to store\n */\n setForUser(userId: string, key: string, value: unknown): Promise<void>\n\n /**\n * Delete a key for a specific user (user-scoped)\n * @param userId The user ID\n * @param key The storage key\n */\n deleteForUser(userId: string, key: string): Promise<void>\n\n /**\n * Get all keys for a specific user (user-scoped)\n * @param userId The user ID\n */\n keysForUser(userId: string): Promise<string[]>\n}\n\n/**\n * Logging API\n */\nexport interface LogAPI {\n debug(message: string, data?: Record<string, unknown>): void\n info(message: string, data?: Record<string, unknown>): void\n warn(message: string, data?: Record<string, unknown>): void\n error(message: string, data?: Record<string, unknown>): void\n}\n\n// ============================================================================\n// AI Provider Types\n// ============================================================================\n\n/**\n * AI provider implementation\n */\nexport interface AIProvider {\n /** Provider ID (must match manifest) */\n id: string\n /** Display name */\n name: string\n\n /**\n * Get available models from this provider\n * @param options Optional settings for the provider (e.g., URL)\n */\n getModels(options?: GetModelsOptions): Promise<ModelInfo[]>\n\n /**\n * Chat completion with streaming\n */\n chat(\n messages: ChatMessage[],\n options: ChatOptions\n ): AsyncGenerator<StreamEvent, void, unknown>\n\n /**\n * Optional: Generate embeddings\n */\n embed?(texts: string[]): Promise<number[][]>\n}\n\n/**\n * Model information\n */\nexport interface ModelInfo {\n /** Model ID */\n id: string\n /** Display name */\n name: string\n /** Description */\n description?: string\n /** Context window size */\n contextLength?: number\n}\n\n/**\n * Chat message\n */\nexport interface ChatMessage {\n role: 'user' | 'assistant' | 'system' | 'tool'\n content: string\n /** For assistant messages: tool calls made by the model */\n tool_calls?: ToolCall[]\n /** For tool messages: the ID of the tool call this is a response to */\n tool_call_id?: string\n}\n\n/**\n * A tool call made by the model\n */\nexport interface ToolCall {\n /** Unique ID for this tool call */\n id: string\n /** Tool name/ID to invoke */\n name: string\n /** Arguments for the tool (as parsed object) */\n arguments: Record<string, unknown>\n}\n\n/**\n * Options for chat completion\n */\nexport interface ChatOptions {\n /** Model to use */\n model?: string\n /** Temperature (0-1) */\n temperature?: number\n /** Maximum tokens to generate */\n maxTokens?: number\n /** Abort signal for cancellation */\n signal?: AbortSignal\n /** Provider-specific settings from model configuration */\n settings?: Record<string, unknown>\n /** Available tools for this request */\n tools?: ToolDefinition[]\n}\n\n/**\n * Options for getModels\n */\nexport interface GetModelsOptions {\n /** Provider-specific settings (e.g., URL for Ollama) */\n settings?: Record<string, unknown>\n}\n\n/**\n * Streaming events from chat\n */\nexport type StreamEvent =\n | { type: 'content'; text: string }\n | { type: 'thinking'; text: string }\n | { type: 'tool_start'; name: string; input: unknown; toolCallId: string }\n | { type: 'tool_end'; name: string; output: unknown; toolCallId: string }\n | { type: 'done'; usage?: { inputTokens: number; outputTokens: number } }\n | { type: 'error'; message: string }\n\n// ============================================================================\n// Tool Types\n// ============================================================================\n\n/**\n * Tool implementation\n */\nexport interface Tool {\n /** Tool ID (must match manifest) */\n id: string\n /** Display name */\n name: string\n /** Description for Stina */\n description: string\n /** Parameter schema (JSON Schema) */\n parameters?: Record<string, unknown>\n\n /**\n * Execute the tool\n */\n execute(params: Record<string, unknown>): Promise<ToolResult>\n}\n\n/**\n * Tool execution result\n */\nexport interface ToolResult {\n /** Whether the tool succeeded */\n success: boolean\n /** Result data (for Stina to use) */\n data?: unknown\n /** Human-readable message */\n message?: string\n /** Error message if failed */\n error?: string\n}\n\n// ============================================================================\n// Action Types (for UI interactions, separate from Tools)\n// ============================================================================\n\n/**\n * Action implementation for UI interactions.\n * Actions are invoked by UI components, not by Stina (AI).\n */\nexport interface Action {\n /** Action ID (unique within the extension) */\n id: string\n\n /**\n * Execute the action\n * @param params Parameters from the UI component (with $-values already resolved)\n */\n execute(params: Record<string, unknown>): Promise<ActionResult>\n}\n\n/**\n * Action execution result\n */\nexport interface ActionResult {\n /** Whether the action succeeded */\n success: boolean\n /** Result data (returned to UI) */\n data?: unknown\n /** Error message if failed */\n error?: string\n}\n\n// ============================================================================\n// Extension Entry Point\n// ============================================================================\n\n/**\n * Extension entry point interface\n */\nexport interface ExtensionModule {\n /**\n * Called when extension is activated\n */\n activate(context: ExtensionContext): void | Disposable | Promise<void | Disposable>\n\n /**\n * Called when extension is deactivated\n */\n deactivate?(): void | Promise<void>\n}\n"],"mappings":";;;;;AAsBO,SAAS,uBACd,OACA,MACA,eAAe,MACP;AACR,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO;AAAA,EACT;AAEA,SAAO,MAAM,IAAI,KAAK,MAAM,YAAY,KAAK,OAAO,OAAO,KAAK,EAAE,CAAC,KAAK;AAC1E;","names":[]}
1
+ {"version":3,"sources":["../src/types.ts"],"sourcesContent":["/**\n * A string that can be either a simple string or a map of language codes to localized strings.\n * When a simple string is provided, it's used as the default/fallback value.\n * When a map is provided, the appropriate language is selected at runtime.\n *\n * @example\n * // Simple string (backwards compatible)\n * name: \"Get Weather\"\n *\n * @example\n * // Localized strings\n * name: { en: \"Get Weather\", sv: \"Hämta väder\", de: \"Wetter abrufen\" }\n */\nexport type LocalizedString = string | Record<string, string>\n\n/**\n * Resolves a LocalizedString to an actual string value.\n * @param value The LocalizedString to resolve\n * @param lang The preferred language code (e.g., \"sv\", \"en\")\n * @param fallbackLang The fallback language code (defaults to \"en\")\n * @returns The resolved string value\n */\nexport function resolveLocalizedString(\n value: LocalizedString,\n lang: string,\n fallbackLang = 'en'\n): string {\n if (typeof value === 'string') {\n return value\n }\n // Try preferred language first, then fallback language, then first available, then empty string\n return value[lang] ?? value[fallbackLang] ?? Object.values(value)[0] ?? ''\n}\n\n/**\n * Extension manifest format (manifest.json)\n */\nexport interface ExtensionManifest {\n /** Unique identifier (e.g., \"ollama-provider\") */\n id: string\n /** Human-readable name */\n name: string\n /** Version string (semver) */\n version: string\n /** Short description */\n description: string\n /** Author information */\n author: {\n name: string\n url?: string\n }\n /** Repository URL */\n repository?: string\n /** License identifier */\n license?: string\n /** Minimum Stina version required */\n engines?: {\n stina: string\n }\n /** Supported platforms */\n platforms?: Platform[]\n /** Entry point file (relative to extension root) */\n main: string\n /** Required permissions */\n permissions: Permission[]\n /** What the extension contributes */\n contributes?: ExtensionContributions\n}\n\nexport type Platform = 'web' | 'electron' | 'tui'\n\n/**\n * What an extension can contribute to Stina\n */\nexport interface ExtensionContributions {\n /** User-configurable settings */\n settings?: SettingDefinition[]\n /** Tool settings views for UI */\n toolSettings?: ToolSettingsViewDefinition[]\n /** Right panel contributions */\n panels?: PanelDefinition[]\n /** AI providers */\n providers?: ProviderDefinition[]\n /** Tools for Stina to use */\n tools?: ToolDefinition[]\n /** Slash commands */\n commands?: CommandDefinition[]\n /** Prompt contributions for the system prompt */\n prompts?: PromptContribution[]\n}\n\n/**\n * Setting definition for the UI\n */\nexport interface SettingDefinition {\n /** Setting ID (namespaced automatically) */\n id: string\n /** Display title */\n title: string\n /** Help text */\n description?: string\n /** Setting type */\n type: 'string' | 'number' | 'boolean' | 'select'\n /** Default value */\n default?: unknown\n /** For select type: available options */\n options?: { value: string; label: string }[]\n /** For select type: load options from tool */\n optionsToolId?: string\n /** Params for options tool */\n optionsParams?: Record<string, unknown>\n /** Mapping for options tool response */\n optionsMapping?: SettingOptionsMapping\n /** Tool ID for creating a new option */\n createToolId?: string\n /** Label for create action */\n createLabel?: string\n /** Fields for create form */\n createFields?: SettingDefinition[]\n /** Static params always sent to create tool */\n createParams?: Record<string, unknown>\n /** Mapping for create tool response */\n createMapping?: SettingCreateMapping\n /** Validation rules */\n validation?: {\n required?: boolean\n min?: number\n max?: number\n pattern?: string\n }\n}\n\n/**\n * Mapping for select field options from tool response\n */\nexport interface SettingOptionsMapping {\n /** Key for items array in tool result data */\n itemsKey: string\n /** Key for option value */\n valueKey: string\n /** Key for option label */\n labelKey: string\n /** Optional key for description */\n descriptionKey?: string\n}\n\n/**\n * Mapping for create tool response\n */\nexport interface SettingCreateMapping {\n /** Key for result data object */\n resultKey?: string\n /** Key for option value (defaults to \"id\") */\n valueKey: string\n}\n\n/**\n * Tool settings view definition (UI schema)\n */\nexport interface ToolSettingsViewDefinition {\n /** Unique view ID within the extension */\n id: string\n /** Display title */\n title: string\n /** Help text */\n description?: string\n /** View configuration */\n view: ToolSettingsView\n /** Fields for create/edit forms (uses SettingDefinition) */\n fields?: SettingDefinition[]\n}\n\n/**\n * Tool settings view types\n */\nexport type ToolSettingsView = ToolSettingsListView | ToolSettingsComponentView\n\n/**\n * List view backed by tools\n */\nexport interface ToolSettingsListView {\n /** View kind */\n kind: 'list'\n /** Tool ID for listing items */\n listToolId: string\n /** Tool ID for fetching details (optional) */\n getToolId?: string\n /** Tool ID for creating/updating items (optional) */\n upsertToolId?: string\n /** Tool ID for deleting items (optional) */\n deleteToolId?: string\n /** Mapping from tool data to UI fields */\n mapping: ToolSettingsListMapping\n /** Param name for search query (default: \"query\") */\n searchParam?: string\n /** Param name for limit (default: \"limit\") */\n limitParam?: string\n /** Param name for get/delete ID (default: \"id\") */\n idParam?: string\n /** Static params always sent to list tool */\n listParams?: Record<string, unknown>\n}\n\n/**\n * Mapping from tool list data to UI fields\n */\nexport interface ToolSettingsListMapping {\n /** Key for items array in tool result data */\n itemsKey: string\n /** Key for total count in tool result data */\n countKey?: string\n /** Key for item ID */\n idKey: string\n /** Key for item label */\n labelKey: string\n /** Key for item description */\n descriptionKey?: string\n /** Key for secondary label */\n secondaryKey?: string\n}\n\n/**\n * Component-based tool settings view using the declarative DSL.\n * Reuses the same structure as PanelComponentView for consistency.\n */\nexport interface ToolSettingsComponentView {\n /** View kind */\n kind: 'component'\n /** Data sources. Keys become scope variables (e.g., \"$settings\"). */\n data?: Record<string, ToolSettingsActionDataSource>\n /** Root component to render */\n content: import('./types.components.js').ExtensionComponentData\n}\n\n/**\n * Action-based data source for tool settings.\n */\nexport interface ToolSettingsActionDataSource {\n /** Action ID to call for fetching data */\n action: string\n /** Parameters to pass to the action */\n params?: Record<string, unknown>\n /** Event names that trigger refresh */\n refreshOn?: string[]\n}\n\n/**\n * Panel definition for right panel views\n */\nexport interface PanelDefinition {\n /** Unique panel ID within the extension */\n id: string\n /** Display title */\n title: string\n /** Icon name (from huge-icons) */\n icon?: string\n /** Panel view schema */\n view: PanelView\n}\n\n/**\n * Panel view schema (declarative)\n */\nexport type PanelView = PanelComponentView | PanelUnknownView\n\nexport interface PanelUnknownView {\n /** View kind */\n kind: string\n /** Additional view configuration */\n [key: string]: unknown\n}\n\n/**\n * Action-based data source for declarative panels.\n * Uses actions (not tools) to fetch data.\n */\nexport interface PanelActionDataSource {\n /** Action ID to call for fetching data */\n action: string\n /** Parameters to pass to the action */\n params?: Record<string, unknown>\n /** Event names that should trigger a refresh of this data */\n refreshOn?: string[]\n}\n\n/**\n * Component-based panel view using the declarative DSL.\n * Data is fetched via actions, content is rendered via ExtensionComponent.\n */\nexport interface PanelComponentView {\n kind: 'component'\n /** Data sources available in the panel. Keys become variable names (e.g., \"$projects\"). */\n data?: Record<string, PanelActionDataSource>\n /** Root component to render */\n content: import('./types.components.js').ExtensionComponentData\n}\n\n/**\n * Provider definition (metadata only, implementation in code)\n */\nexport interface ProviderDefinition {\n /** Provider ID */\n id: string\n /** Display name */\n name: string\n /** Description */\n description?: string\n /** Suggested default model when creating a new model configuration */\n suggestedDefaultModel?: string\n /** Default settings for this provider (e.g., { url: \"http://localhost:11434\" }) */\n defaultSettings?: Record<string, unknown>\n /** Schema for provider-specific configuration UI */\n configSchema?: ProviderConfigSchema\n}\n\n// ============================================================================\n// Prompt Contributions\n// ============================================================================\n\nexport type PromptSection = 'system' | 'behavior' | 'tools'\n\nexport interface PromptContribution {\n /** Unique ID within the extension */\n id: string\n /** Optional title for the prompt chunk */\n title?: string\n /** Prompt section placement */\n section?: PromptSection\n /** Plain text prompt content */\n text?: string\n /** Optional localized prompt content (keyed by locale, e.g. \"en\", \"sv\") */\n i18n?: Record<string, string>\n /** Optional ordering hint (lower comes first) */\n order?: number\n}\n\n// ============================================================================\n// Provider Configuration Schema\n// ============================================================================\n\n/**\n * Schema for provider-specific configuration.\n * Used to generate UI forms for configuring provider settings.\n */\nexport interface ProviderConfigSchema {\n /** Property definitions */\n properties: Record<string, ProviderConfigProperty>\n /** Display order of properties in UI (optional, defaults to object key order) */\n order?: string[]\n}\n\n/**\n * Property types for provider configuration\n */\nexport type ProviderConfigPropertyType =\n | 'string'\n | 'number'\n | 'boolean'\n | 'select'\n | 'password'\n | 'url'\n\n/**\n * Single property in a provider configuration schema.\n * Defines how a setting should be rendered and validated in the UI.\n */\nexport interface ProviderConfigProperty {\n /** Property type - determines UI control */\n type: ProviderConfigPropertyType\n /** Display label */\n title: string\n /** Help text shown below the input */\n description?: string\n /** Default value */\n default?: unknown\n /** Whether the field is required */\n required?: boolean\n /** Placeholder text for input fields */\n placeholder?: string\n /** For 'select' type: static options */\n options?: ProviderConfigSelectOption[]\n /** Validation rules */\n validation?: ProviderConfigValidation\n}\n\n/**\n * Option for select-type properties\n */\nexport interface ProviderConfigSelectOption {\n /** Value stored in settings */\n value: string\n /** Display label */\n label: string\n}\n\n/**\n * Validation rules for a property\n */\nexport interface ProviderConfigValidation {\n /** Regex pattern the value must match */\n pattern?: string\n /** Minimum string length */\n minLength?: number\n /** Maximum string length */\n maxLength?: number\n /** Minimum number value */\n min?: number\n /** Maximum number value */\n max?: number\n}\n\n/**\n * Tool definition (metadata only, implementation in code)\n */\nexport interface ToolDefinition {\n /** Tool ID */\n id: string\n /**\n * Display name - can be a simple string or localized strings.\n * @example \"Get Weather\"\n * @example { en: \"Get Weather\", sv: \"Hämta väder\" }\n */\n name: LocalizedString\n /**\n * Description for Stina - can be a simple string or localized strings.\n * Note: The AI always receives the English description (or fallback) for consistency.\n * Localized descriptions are used for UI display only.\n * @example \"Fetches current weather for a location\"\n * @example { en: \"Fetches current weather\", sv: \"Hämtar aktuellt väder\" }\n */\n description: LocalizedString\n /** Parameter schema (JSON Schema) */\n parameters?: Record<string, unknown>\n}\n\n/**\n * Command definition\n */\nexport interface CommandDefinition {\n /** Command ID (e.g., \"weather\" for /weather) */\n id: string\n /** Display name */\n name: string\n /** Description */\n description: string\n}\n\n// ============================================================================\n// Permissions\n// ============================================================================\n\nexport type Permission =\n | NetworkPermission\n | StoragePermission\n | UserDataPermission\n | CapabilityPermission\n | SystemPermission\n\n/** Network access permissions */\nexport type NetworkPermission =\n | 'network:*'\n | `network:localhost`\n | `network:localhost:${number}`\n | `network:${string}`\n\n/** Storage permissions */\nexport type StoragePermission = 'database.own' | 'storage.local'\n\n/** User data permissions */\nexport type UserDataPermission =\n | 'user.profile.read'\n | 'user.location.read'\n | 'chat.history.read'\n | 'chat.current.read'\n\n/** Capability permissions */\nexport type CapabilityPermission =\n | 'provider.register'\n | 'tools.register'\n | 'actions.register'\n | 'settings.register'\n | 'commands.register'\n | 'panels.register'\n | 'events.emit'\n | 'scheduler.register'\n | 'chat.message.write'\n\n/** System permissions */\nexport type SystemPermission =\n | 'files.read'\n | 'files.write'\n | 'clipboard.read'\n | 'clipboard.write'\n\n// ============================================================================\n// Extension Context (API available to extensions)\n// ============================================================================\n\n/**\n * Disposable resource that can be cleaned up\n */\nexport interface Disposable {\n dispose(): void\n}\n\n/**\n * Context provided to extension's activate function.\n *\n * ## User ID Context\n *\n * The `userId` field provides the current user context for extensions. It is set when:\n * - A tool is executed by a user\n * - An action is executed by a user\n * - A scheduled job fires (if the job was created with a userId)\n *\n * For extension activation, `userId` is undefined since activation happens at system level.\n * Extensions should check for `userId` in their tool/action handlers to access user-specific data.\n *\n * @example\n * ```typescript\n * // In a tool execute handler:\n * execute: async (params, context) => {\n * if (context.userId) {\n * // User-specific logic\n * const userData = await storage.getForUser(context.userId, 'preferences')\n * }\n * }\n * ```\n */\nexport interface ExtensionContext {\n /** Extension metadata */\n readonly extension: {\n readonly id: string\n readonly version: string\n readonly storagePath: string\n }\n\n /**\n * Current user ID if in a user context, undefined for global/system operations.\n * Set when tools or actions are executed by a user, or when a user-scoped job fires.\n */\n readonly userId?: string\n\n /** Network access (if permitted) */\n readonly network?: NetworkAPI\n\n /** Settings access (if permitted) */\n readonly settings?: SettingsAPI\n\n /** Provider registration (if permitted) */\n readonly providers?: ProvidersAPI\n\n /** Tool registration (if permitted) */\n readonly tools?: ToolsAPI\n\n /** Action registration (if permitted) */\n readonly actions?: ActionsAPI\n\n /** Event emission (if permitted) */\n readonly events?: EventsAPI\n\n /** Scheduler access (if permitted) */\n readonly scheduler?: SchedulerAPI\n\n /** User data access (if permitted) */\n readonly user?: UserAPI\n\n /** Chat access (if permitted) */\n readonly chat?: ChatAPI\n\n /** Database access (if permitted) */\n readonly database?: DatabaseAPI\n\n /** Local storage (if permitted) */\n readonly storage?: StorageAPI\n\n /** Logging (always available) */\n readonly log: LogAPI\n}\n\n/**\n * Network API for making HTTP requests\n */\nexport interface NetworkAPI {\n /**\n * Fetch a URL (permissions are enforced by host)\n */\n fetch(url: string, options?: RequestInit): Promise<Response>\n\n /**\n * Streaming fetch for responses like NDJSON or SSE.\n * Yields text chunks as they arrive from the server.\n *\n * @throws {Error} If the request fails or encounters a network error.\n * The error message will contain details about the failure.\n *\n * @example\n * ```typescript\n * try {\n * for await (const chunk of context.network.fetchStream(url, options)) {\n * // Process each chunk (may contain partial lines)\n * buffer += chunk\n * }\n * } catch (error) {\n * console.error('Streaming fetch failed:', error.message)\n * }\n * ```\n */\n fetchStream(url: string, options?: RequestInit): AsyncGenerator<string, void, unknown>\n}\n\n/**\n * Settings API for reading/writing extension settings\n */\nexport interface SettingsAPI {\n /**\n * Get all settings for this extension\n */\n getAll<T extends Record<string, unknown>>(): Promise<T>\n\n /**\n * Get a specific setting value\n */\n get<T>(key: string): Promise<T | undefined>\n\n /**\n * Set a setting value\n */\n set(key: string, value: unknown): Promise<void>\n\n /**\n * Listen for setting changes\n */\n onChange(callback: (key: string, value: unknown) => void): Disposable\n}\n\n/**\n * Providers API for registering AI providers\n */\nexport interface ProvidersAPI {\n /**\n * Register an AI provider\n */\n register(provider: AIProvider): Disposable\n}\n\n/**\n * Tools API for registering tools\n */\nexport interface ToolsAPI {\n /**\n * Register a tool that Stina can use\n */\n register(tool: Tool): Disposable\n}\n\n/**\n * Actions API for registering UI actions\n */\nexport interface ActionsAPI {\n /**\n * Register an action that UI components can invoke\n */\n register(action: Action): Disposable\n}\n\n/**\n * Events API for notifying the host\n */\nexport interface EventsAPI {\n /**\n * Emit a named event with optional payload\n */\n emit(name: string, payload?: Record<string, unknown>): Promise<void>\n}\n\n/**\n * Scheduler schedule types\n */\nexport type SchedulerSchedule =\n | { type: 'at'; at: string }\n | { type: 'cron'; cron: string; timezone?: string }\n | { type: 'interval'; everyMs: number }\n\n/**\n * Scheduler job request\n */\nexport interface SchedulerJobRequest {\n id: string\n schedule: SchedulerSchedule\n payload?: Record<string, unknown>\n misfire?: 'run_once' | 'skip'\n /**\n * Optional user ID for user-scoped jobs.\n * If set, the job is associated with a specific user and the userId\n * will be passed to the extension when the job fires.\n */\n userId?: string\n}\n\n/**\n * Scheduler fire payload\n */\nexport interface SchedulerFirePayload {\n id: string\n payload?: Record<string, unknown>\n scheduledFor: string\n firedAt: string\n delayMs: number\n /** User ID if this is a user-scoped job, undefined if global */\n userId?: string\n}\n\n/**\n * Scheduler API for registering jobs\n */\nexport interface SchedulerAPI {\n schedule(job: SchedulerJobRequest): Promise<void>\n cancel(jobId: string): Promise<void>\n onFire(callback: (payload: SchedulerFirePayload) => void | Promise<void>): Disposable\n}\n\n/**\n * User profile data\n */\nexport interface UserProfile {\n firstName?: string\n nickname?: string\n language?: string\n timezone?: string\n}\n\n/**\n * User API for profile access\n */\nexport interface UserAPI {\n getProfile(): Promise<UserProfile>\n}\n\n/**\n * Chat instruction message\n */\nexport interface ChatInstructionMessage {\n text: string\n conversationId?: string\n userId?: string\n}\n\n/**\n * Chat API for appending instructions\n */\nexport interface ChatAPI {\n appendInstruction(message: ChatInstructionMessage): Promise<void>\n}\n\n/**\n * Database API for extension-specific tables\n */\nexport interface DatabaseAPI {\n /**\n * Execute a SQL query (only extension's prefixed tables allowed)\n */\n execute<T = unknown>(sql: string, params?: unknown[]): Promise<T[]>\n}\n\n/**\n * Simple key-value storage API with support for user-scoped storage.\n *\n * ## Global vs User-Scoped Storage\n *\n * Extensions have access to two types of storage:\n * - **Global storage**: Shared across all users, accessed via `get()`, `set()`, etc.\n * - **User-scoped storage**: Isolated per user, accessed via `getForUser()`, `setForUser()`, etc.\n *\n * Use global storage for extension-wide settings and user-scoped storage for\n * user preferences, session data, or any data that should be private to a user.\n *\n * @example\n * ```typescript\n * // Global storage (extension-wide)\n * await storage.set('apiEndpoint', 'https://api.example.com')\n * const endpoint = await storage.get<string>('apiEndpoint')\n *\n * // User-scoped storage (per-user)\n * if (context.userId) {\n * await storage.setForUser(context.userId, 'preferences', { theme: 'dark' })\n * const prefs = await storage.getForUser<Preferences>(context.userId, 'preferences')\n * }\n * ```\n */\nexport interface StorageAPI {\n /**\n * Get a value by key (global/extension-scoped)\n */\n get<T>(key: string): Promise<T | undefined>\n\n /**\n * Set a value (global/extension-scoped)\n */\n set(key: string, value: unknown): Promise<void>\n\n /**\n * Delete a key (global/extension-scoped)\n */\n delete(key: string): Promise<void>\n\n /**\n * Get all keys (global/extension-scoped)\n */\n keys(): Promise<string[]>\n\n /**\n * Get a value by key for a specific user (user-scoped)\n * @param userId The user ID\n * @param key The storage key\n */\n getForUser<T>(userId: string, key: string): Promise<T | undefined>\n\n /**\n * Set a value for a specific user (user-scoped)\n * @param userId The user ID\n * @param key The storage key\n * @param value The value to store\n */\n setForUser(userId: string, key: string, value: unknown): Promise<void>\n\n /**\n * Delete a key for a specific user (user-scoped)\n * @param userId The user ID\n * @param key The storage key\n */\n deleteForUser(userId: string, key: string): Promise<void>\n\n /**\n * Get all keys for a specific user (user-scoped)\n * @param userId The user ID\n */\n keysForUser(userId: string): Promise<string[]>\n}\n\n/**\n * Logging API\n */\nexport interface LogAPI {\n debug(message: string, data?: Record<string, unknown>): void\n info(message: string, data?: Record<string, unknown>): void\n warn(message: string, data?: Record<string, unknown>): void\n error(message: string, data?: Record<string, unknown>): void\n}\n\n// ============================================================================\n// AI Provider Types\n// ============================================================================\n\n/**\n * AI provider implementation\n */\nexport interface AIProvider {\n /** Provider ID (must match manifest) */\n id: string\n /** Display name */\n name: string\n\n /**\n * Get available models from this provider\n * @param options Optional settings for the provider (e.g., URL)\n */\n getModels(options?: GetModelsOptions): Promise<ModelInfo[]>\n\n /**\n * Chat completion with streaming\n */\n chat(\n messages: ChatMessage[],\n options: ChatOptions\n ): AsyncGenerator<StreamEvent, void, unknown>\n\n /**\n * Optional: Generate embeddings\n */\n embed?(texts: string[]): Promise<number[][]>\n}\n\n/**\n * Model information\n */\nexport interface ModelInfo {\n /** Model ID */\n id: string\n /** Display name */\n name: string\n /** Description */\n description?: string\n /** Context window size */\n contextLength?: number\n}\n\n/**\n * Chat message\n */\nexport interface ChatMessage {\n role: 'user' | 'assistant' | 'system' | 'tool'\n content: string\n /** For assistant messages: tool calls made by the model */\n tool_calls?: ToolCall[]\n /** For tool messages: the ID of the tool call this is a response to */\n tool_call_id?: string\n}\n\n/**\n * A tool call made by the model\n */\nexport interface ToolCall {\n /** Unique ID for this tool call */\n id: string\n /** Tool name/ID to invoke */\n name: string\n /** Arguments for the tool (as parsed object) */\n arguments: Record<string, unknown>\n}\n\n/**\n * Options for chat completion\n */\nexport interface ChatOptions {\n /** Model to use */\n model?: string\n /** Temperature (0-1) */\n temperature?: number\n /** Maximum tokens to generate */\n maxTokens?: number\n /** Abort signal for cancellation */\n signal?: AbortSignal\n /** Provider-specific settings from model configuration */\n settings?: Record<string, unknown>\n /** Available tools for this request */\n tools?: ToolDefinition[]\n}\n\n/**\n * Options for getModels\n */\nexport interface GetModelsOptions {\n /** Provider-specific settings (e.g., URL for Ollama) */\n settings?: Record<string, unknown>\n}\n\n/**\n * Streaming events from chat\n */\nexport type StreamEvent =\n | { type: 'content'; text: string }\n | { type: 'thinking'; text: string }\n | { type: 'tool_start'; name: string; input: unknown; toolCallId: string }\n | { type: 'tool_end'; name: string; output: unknown; toolCallId: string }\n | { type: 'done'; usage?: { inputTokens: number; outputTokens: number } }\n | { type: 'error'; message: string }\n\n// ============================================================================\n// Tool Types\n// ============================================================================\n\n/**\n * Tool implementation\n */\nexport interface Tool {\n /** Tool ID (must match manifest) */\n id: string\n /** Display name */\n name: string\n /** Description for Stina */\n description: string\n /** Parameter schema (JSON Schema) */\n parameters?: Record<string, unknown>\n\n /**\n * Execute the tool\n */\n execute(params: Record<string, unknown>): Promise<ToolResult>\n}\n\n/**\n * Tool execution result\n */\nexport interface ToolResult {\n /** Whether the tool succeeded */\n success: boolean\n /** Result data (for Stina to use) */\n data?: unknown\n /** Human-readable message */\n message?: string\n /** Error message if failed */\n error?: string\n}\n\n// ============================================================================\n// Action Types (for UI interactions, separate from Tools)\n// ============================================================================\n\n/**\n * Action implementation for UI interactions.\n * Actions are invoked by UI components, not by Stina (AI).\n */\nexport interface Action {\n /** Action ID (unique within the extension) */\n id: string\n\n /**\n * Execute the action\n * @param params Parameters from the UI component (with $-values already resolved)\n */\n execute(params: Record<string, unknown>): Promise<ActionResult>\n}\n\n/**\n * Action execution result\n */\nexport interface ActionResult {\n /** Whether the action succeeded */\n success: boolean\n /** Result data (returned to UI) */\n data?: unknown\n /** Error message if failed */\n error?: string\n}\n\n// ============================================================================\n// Extension Entry Point\n// ============================================================================\n\n/**\n * Extension entry point interface\n */\nexport interface ExtensionModule {\n /**\n * Called when extension is activated\n */\n activate(context: ExtensionContext): void | Disposable | Promise<void | Disposable>\n\n /**\n * Called when extension is deactivated\n */\n deactivate?(): void | Promise<void>\n}\n"],"mappings":";;;;;AAsBO,SAAS,uBACd,OACA,MACA,eAAe,MACP;AACR,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO;AAAA,EACT;AAEA,SAAO,MAAM,IAAI,KAAK,MAAM,YAAY,KAAK,OAAO,OAAO,KAAK,EAAE,CAAC,KAAK;AAC1E;","names":[]}
package/dist/runtime.cjs CHANGED
@@ -90,7 +90,7 @@ async function handleHostMessage(message) {
90
90
  handleSettingsChanged(message.payload.key, message.payload.value);
91
91
  break;
92
92
  case "scheduler-fire":
93
- handleSchedulerFire(message.payload);
93
+ await handleSchedulerFire(message.payload);
94
94
  break;
95
95
  case "provider-chat-request":
96
96
  await handleProviderChatRequest(message.id, message.payload);
@@ -192,18 +192,19 @@ function handleSettingsChanged(key, value) {
192
192
  }
193
193
  }
194
194
  }
195
- function handleSchedulerFire(payload) {
195
+ async function handleSchedulerFire(payload) {
196
196
  if (extensionContext && payload.userId) {
197
197
  ;
198
198
  extensionContext.userId = payload.userId;
199
199
  }
200
- for (const callback of schedulerCallbacks) {
201
- try {
202
- callback(payload);
203
- } catch (error) {
204
- console.error("Error in scheduler callback:", error);
200
+ const results = await Promise.allSettled(
201
+ schedulerCallbacks.map((callback) => callback(payload))
202
+ );
203
+ results.forEach((result, index) => {
204
+ if (result.status === "rejected") {
205
+ console.error(`Error in scheduler callback ${index}:`, result.reason);
205
206
  }
206
- }
207
+ });
207
208
  if (extensionContext) {
208
209
  ;
209
210
  extensionContext.userId = void 0;
@@ -548,7 +549,9 @@ function buildContext(extensionId, extensionVersion, storagePath, permissions) {
548
549
  if (hasPermission("scheduler.register")) {
549
550
  const schedulerApi = {
550
551
  async schedule(job) {
551
- await sendRequest("scheduler.schedule", { job });
552
+ const userId = extensionContext.userId;
553
+ const jobWithUser = userId && !job.userId ? { ...job, userId } : job;
554
+ await sendRequest("scheduler.schedule", { job: jobWithUser });
552
555
  },
553
556
  async cancel(jobId) {
554
557
  await sendRequest("scheduler.cancel", { jobId });
@@ -576,7 +579,11 @@ function buildContext(extensionId, extensionVersion, storagePath, permissions) {
576
579
  if (hasPermission("chat.message.write")) {
577
580
  const chatApi = {
578
581
  async appendInstruction(message) {
579
- await sendRequest("chat.appendInstruction", message);
582
+ const contextUserId = extensionContext.userId;
583
+ await sendRequest("chat.appendInstruction", {
584
+ ...message,
585
+ userId: message.userId ?? contextUserId
586
+ });
580
587
  }
581
588
  };
582
589
  context.chat = chatApi;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/runtime.ts","../src/messages.ts"],"sourcesContent":["/**\n * Extension Runtime - Runs inside the worker\n *\n * This module handles communication with the Extension Host and provides\n * the ExtensionContext to the extension's activate function.\n */\n\nimport type {\n ExtensionContext,\n ExtensionModule,\n Disposable,\n NetworkAPI,\n SettingsAPI,\n ProvidersAPI,\n ToolsAPI,\n ActionsAPI,\n EventsAPI,\n SchedulerAPI,\n SchedulerJobRequest,\n SchedulerFirePayload,\n UserAPI,\n UserProfile,\n ChatAPI,\n ChatInstructionMessage,\n DatabaseAPI,\n StorageAPI,\n LogAPI,\n AIProvider,\n Tool,\n Action,\n ChatMessage,\n ChatOptions,\n GetModelsOptions,\n} from './types.js'\n\nimport type {\n HostToWorkerMessage,\n WorkerToHostMessage,\n RequestMessage,\n PendingRequest,\n} from './messages.js'\n\nimport { generateMessageId } from './messages.js'\n\n// ============================================================================\n// Environment Detection and Message Port\n// ============================================================================\n\n/**\n * Detect if we're in Node.js Worker Thread or Web Worker\n * and get the appropriate message port\n */\ninterface MessagePort {\n postMessage(message: WorkerToHostMessage): void\n onMessage(handler: (message: HostToWorkerMessage) => void): void\n}\n\nfunction getMessagePort(): MessagePort {\n // Check if we're in Node.js Worker Thread\n if (typeof process !== 'undefined' && process.versions?.node) {\n // Node.js Worker Thread - import parentPort dynamically\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const { parentPort } = require('node:worker_threads')\n return {\n postMessage: (message) => parentPort?.postMessage(message),\n onMessage: (handler) => parentPort?.on('message', handler),\n }\n }\n\n // Web Worker - use self\n return {\n postMessage: (message) => self.postMessage(message),\n onMessage: (handler) => {\n self.addEventListener('message', (event: MessageEvent<HostToWorkerMessage>) => {\n handler(event.data)\n })\n },\n }\n}\n\nconst messagePort = getMessagePort()\n\n// ============================================================================\n// Global State\n// ============================================================================\n\nlet extensionModule: ExtensionModule | null = null\nlet extensionDisposable: Disposable | null = null\nlet extensionContext: ExtensionContext | null = null\n\nconst pendingRequests = new Map<string, PendingRequest>()\nconst registeredProviders = new Map<string, AIProvider>()\nconst registeredTools = new Map<string, Tool>()\nconst registeredActions = new Map<string, Action>()\nconst settingsCallbacks: Array<(key: string, value: unknown) => void> = []\nconst schedulerCallbacks: Array<(payload: SchedulerFirePayload) => void> = []\n\n/**\n * Tracking for streaming fetch requests.\n * Each request stores incoming chunks and signals when new data arrives.\n */\ninterface StreamingFetchRequest {\n chunks: string[]\n done: boolean\n error?: string\n resolve?: () => void\n}\nconst streamingFetchRequests = new Map<string, StreamingFetchRequest>()\n\nconst REQUEST_TIMEOUT = 30000 // 30 seconds\n\n// ============================================================================\n// Message Handling\n// ============================================================================\n\n/**\n * Send a message to the host\n */\nfunction postMessage(message: WorkerToHostMessage): void {\n messagePort.postMessage(message)\n}\n\n/**\n * Send a request to the host and wait for response\n */\nasync function sendRequest<T>(method: RequestMessage['method'], payload: unknown): Promise<T> {\n const id = generateMessageId()\n\n return new Promise<T>((resolve, reject) => {\n const timeout = setTimeout(() => {\n pendingRequests.delete(id)\n reject(new Error(`Request ${method} timed out`))\n }, REQUEST_TIMEOUT)\n\n pendingRequests.set(id, { resolve: resolve as (value: unknown) => void, reject, timeout })\n\n postMessage({\n type: 'request',\n id,\n method,\n payload,\n })\n })\n}\n\n/**\n * Handle messages from the host\n */\nasync function handleHostMessage(message: HostToWorkerMessage): Promise<void> {\n switch (message.type) {\n case 'activate':\n await handleActivate(message.payload)\n break\n\n case 'deactivate':\n await handleDeactivate()\n break\n\n case 'settings-changed':\n handleSettingsChanged(message.payload.key, message.payload.value)\n break\n\n case 'scheduler-fire':\n handleSchedulerFire(message.payload)\n break\n\n case 'provider-chat-request':\n await handleProviderChatRequest(message.id, message.payload)\n break\n\n case 'provider-models-request':\n await handleProviderModelsRequest(message.id, message.payload)\n break\n\n case 'tool-execute-request':\n await handleToolExecuteRequest(message.id, message.payload)\n break\n\n case 'action-execute-request':\n await handleActionExecuteRequest(message.id, message.payload)\n break\n\n case 'response':\n handleResponse(message.payload)\n break\n\n case 'streaming-fetch-chunk':\n handleStreamingFetchChunk(message.payload)\n break\n }\n}\n\n/**\n * Handle incoming streaming fetch chunks from the host\n */\nfunction handleStreamingFetchChunk(payload: {\n requestId: string\n chunk: string\n done: boolean\n error?: string\n}): void {\n const request = streamingFetchRequests.get(payload.requestId)\n if (!request) return\n\n if (payload.error) {\n request.error = payload.error\n request.done = true\n } else if (payload.chunk) {\n request.chunks.push(payload.chunk)\n }\n\n if (payload.done) {\n request.done = true\n }\n\n // Signal that new data is available\n if (request.resolve) {\n const resolve = request.resolve\n request.resolve = undefined\n resolve()\n }\n\n // Send acknowledgment for backpressure control\n postMessage({\n type: 'streaming-fetch-ack',\n payload: { requestId: payload.requestId },\n })\n}\n\nfunction handleResponse(payload: { requestId: string; success: boolean; data?: unknown; error?: string }): void {\n const pending = pendingRequests.get(payload.requestId)\n if (!pending) return\n\n clearTimeout(pending.timeout)\n pendingRequests.delete(payload.requestId)\n\n if (payload.success) {\n pending.resolve(payload.data)\n } else {\n pending.reject(new Error(payload.error || 'Unknown error'))\n }\n}\n\n// ============================================================================\n// Activation / Deactivation\n// ============================================================================\n\nasync function handleActivate(payload: {\n extensionId: string\n extensionVersion: string\n storagePath: string\n permissions: string[]\n settings: Record<string, unknown>\n}): Promise<void> {\n const { extensionId, extensionVersion, storagePath, permissions } = payload\n\n // Build the context based on permissions\n extensionContext = buildContext(extensionId, extensionVersion, storagePath, permissions)\n\n // Import and activate the extension\n try {\n // The actual extension code should be bundled and available\n // This is called after the extension code has been loaded into the worker\n if (extensionModule?.activate) {\n const result = await extensionModule.activate(extensionContext)\n if (result && 'dispose' in result) {\n extensionDisposable = result\n }\n }\n } catch (error) {\n extensionContext.log.error('Failed to activate extension', {\n error: error instanceof Error ? error.message : String(error),\n })\n throw error\n }\n}\n\nasync function handleDeactivate(): Promise<void> {\n try {\n if (extensionModule?.deactivate) {\n await extensionModule.deactivate()\n }\n if (extensionDisposable) {\n extensionDisposable.dispose()\n }\n } catch (error) {\n console.error('Error during deactivation:', error)\n } finally {\n extensionModule = null\n extensionDisposable = null\n extensionContext = null\n registeredProviders.clear()\n registeredTools.clear()\n registeredActions.clear()\n settingsCallbacks.length = 0\n schedulerCallbacks.length = 0\n }\n}\n\nfunction handleSettingsChanged(key: string, value: unknown): void {\n for (const callback of settingsCallbacks) {\n try {\n callback(key, value)\n } catch (error) {\n console.error('Error in settings change callback:', error)\n }\n }\n}\n\nfunction handleSchedulerFire(payload: SchedulerFirePayload): void {\n // Set the userId in context if this is a user-scoped job\n if (extensionContext && payload.userId) {\n ;(extensionContext as { userId?: string }).userId = payload.userId\n }\n\n for (const callback of schedulerCallbacks) {\n try {\n callback(payload)\n } catch (error) {\n console.error('Error in scheduler callback:', error)\n }\n }\n\n // Reset userId after all callbacks\n if (extensionContext) {\n ;(extensionContext as { userId?: string }).userId = undefined\n }\n}\n\n// ============================================================================\n// Provider / Tool Requests\n// ============================================================================\n\nasync function handleProviderChatRequest(\n requestId: string,\n payload: { providerId: string; messages: ChatMessage[]; options: ChatOptions }\n): Promise<void> {\n const provider = registeredProviders.get(payload.providerId)\n if (!provider) {\n postMessage({\n type: 'request',\n id: generateMessageId(),\n method: 'network.fetch', // Dummy, we need a proper error response\n payload: { error: `Provider ${payload.providerId} not found` },\n })\n return\n }\n\n try {\n const generator = provider.chat(payload.messages, payload.options)\n let sawDone = false\n let sawError = false\n\n for await (const event of generator) {\n if (event.type === 'done') {\n sawDone = true\n } else if (event.type === 'error') {\n sawError = true\n }\n postMessage({\n type: 'stream-event',\n payload: { requestId, event },\n })\n }\n\n if (!sawDone && !sawError) {\n postMessage({\n type: 'stream-event',\n payload: {\n requestId,\n event: { type: 'done' },\n },\n })\n }\n } catch (error) {\n postMessage({\n type: 'stream-event',\n payload: {\n requestId,\n event: {\n type: 'error',\n message: error instanceof Error ? error.message : String(error),\n },\n },\n })\n }\n}\n\nasync function handleProviderModelsRequest(\n requestId: string,\n payload: { providerId: string; options?: GetModelsOptions }\n): Promise<void> {\n const provider = registeredProviders.get(payload.providerId)\n if (!provider) {\n // Send error response\n postMessage({\n type: 'provider-models-response',\n payload: {\n requestId,\n models: [],\n error: `Provider ${payload.providerId} not found`,\n },\n })\n return\n }\n\n try {\n // Pass options to getModels so provider can use settings (e.g., URL for Ollama)\n const models = await provider.getModels(payload.options)\n // Send response with models\n postMessage({\n type: 'provider-models-response',\n payload: {\n requestId,\n models,\n },\n })\n } catch (error) {\n postMessage({\n type: 'provider-models-response',\n payload: {\n requestId,\n models: [],\n error: error instanceof Error ? error.message : String(error),\n },\n })\n }\n}\n\nasync function handleToolExecuteRequest(\n requestId: string,\n payload: { toolId: string; params: Record<string, unknown>; userId?: string }\n): Promise<void> {\n const tool = registeredTools.get(payload.toolId)\n if (!tool) {\n // Send error response\n postMessage({\n type: 'tool-execute-response',\n payload: {\n requestId,\n result: { success: false, error: `Tool ${payload.toolId} not found` },\n error: `Tool ${payload.toolId} not found`,\n },\n })\n return\n }\n\n try {\n // Update the extension context with the userId if provided\n if (extensionContext && payload.userId) {\n // Create a new context with the userId for this execution\n ;(extensionContext as { userId?: string }).userId = payload.userId\n }\n\n const result = await tool.execute(payload.params)\n\n // Reset userId after execution\n if (extensionContext) {\n ;(extensionContext as { userId?: string }).userId = undefined\n }\n\n // Send response with result\n postMessage({\n type: 'tool-execute-response',\n payload: {\n requestId,\n result,\n },\n })\n } catch (error) {\n // Reset userId on error\n if (extensionContext) {\n ;(extensionContext as { userId?: string }).userId = undefined\n }\n\n const errorMessage = error instanceof Error ? error.message : String(error)\n postMessage({\n type: 'tool-execute-response',\n payload: {\n requestId,\n result: { success: false, error: errorMessage },\n error: errorMessage,\n },\n })\n }\n}\n\nasync function handleActionExecuteRequest(\n requestId: string,\n payload: { actionId: string; params: Record<string, unknown>; userId?: string }\n): Promise<void> {\n const action = registeredActions.get(payload.actionId)\n if (!action) {\n postMessage({\n type: 'action-execute-response',\n payload: {\n requestId,\n result: { success: false, error: `Action ${payload.actionId} not found` },\n error: `Action ${payload.actionId} not found`,\n },\n })\n return\n }\n\n try {\n // Update the extension context with the userId if provided\n if (extensionContext && payload.userId) {\n ;(extensionContext as { userId?: string }).userId = payload.userId\n }\n\n const result = await action.execute(payload.params)\n\n // Reset userId after execution\n if (extensionContext) {\n ;(extensionContext as { userId?: string }).userId = undefined\n }\n\n postMessage({\n type: 'action-execute-response',\n payload: {\n requestId,\n result,\n },\n })\n } catch (error) {\n // Reset userId on error\n if (extensionContext) {\n ;(extensionContext as { userId?: string }).userId = undefined\n }\n\n const errorMessage = error instanceof Error ? error.message : String(error)\n postMessage({\n type: 'action-execute-response',\n payload: {\n requestId,\n result: { success: false, error: errorMessage },\n error: errorMessage,\n },\n })\n }\n}\n\n// ============================================================================\n// Context Building\n// ============================================================================\n\nfunction buildContext(\n extensionId: string,\n extensionVersion: string,\n storagePath: string,\n permissions: string[]\n): ExtensionContext {\n const hasPermission = (perm: string): boolean => {\n return permissions.some((p) => {\n if (p === perm) return true\n if (p.endsWith(':*') && perm.startsWith(p.slice(0, -1))) return true\n return false\n })\n }\n\n const log: LogAPI = {\n debug: (message, data) => postMessage({ type: 'log', payload: { level: 'debug', message, data } }),\n info: (message, data) => postMessage({ type: 'log', payload: { level: 'info', message, data } }),\n warn: (message, data) => postMessage({ type: 'log', payload: { level: 'warn', message, data } }),\n error: (message, data) => postMessage({ type: 'log', payload: { level: 'error', message, data } }),\n }\n\n const context: ExtensionContext = {\n extension: {\n id: extensionId,\n version: extensionVersion,\n storagePath,\n },\n log,\n }\n\n // Add network API if permitted\n if (permissions.some((p) => p.startsWith('network:'))) {\n const networkApi: NetworkAPI = {\n async fetch(url: string, options?: RequestInit): Promise<Response> {\n const result = await sendRequest<{ status: number; statusText: string; headers: Record<string, string>; body: string }>('network.fetch', { url, options })\n return new Response(result.body, {\n status: result.status,\n statusText: result.statusText,\n headers: result.headers,\n })\n },\n\n async *fetchStream(url: string, options?: RequestInit): AsyncGenerator<string, void, unknown> {\n const requestId = generateMessageId()\n\n // Set up streaming request tracking\n const request: StreamingFetchRequest = {\n chunks: [],\n done: false,\n }\n streamingFetchRequests.set(requestId, request)\n\n // Send the streaming fetch request to the host\n postMessage({\n type: 'request',\n id: requestId,\n method: 'network.fetch-stream',\n payload: { url, options, requestId },\n })\n\n try {\n // Yield chunks as they arrive\n while (!request.done) {\n // Wait for new data\n await new Promise<void>((resolve) => {\n const resolver = () => {\n // Clear the stored resolver before resolving to avoid\n // stale callbacks being invoked for a new wait iteration.\n if (request.resolve === resolver) {\n request.resolve = undefined\n }\n resolve()\n }\n\n request.resolve = resolver\n\n // Check if we already have data or completion\n if (request.chunks.length > 0 || request.done) {\n resolver()\n }\n })\n\n // Check for errors first, before yielding any chunks\n if (request.error) {\n throw new Error(request.error)\n }\n\n // Yield all available chunks\n while (request.chunks.length > 0) {\n yield request.chunks.shift()!\n }\n }\n } finally {\n streamingFetchRequests.delete(requestId)\n }\n },\n }\n ;(context as { network: NetworkAPI }).network = networkApi\n }\n\n // Add settings API if permitted\n if (hasPermission('settings.register')) {\n const settingsApi: SettingsAPI = {\n async getAll<T extends Record<string, unknown>>(): Promise<T> {\n return sendRequest<T>('settings.getAll', {})\n },\n async get<T>(key: string): Promise<T | undefined> {\n return sendRequest<T | undefined>('settings.get', { key })\n },\n async set(key: string, value: unknown): Promise<void> {\n return sendRequest<void>('settings.set', { key, value })\n },\n onChange(callback: (key: string, value: unknown) => void): Disposable {\n settingsCallbacks.push(callback)\n return {\n dispose: () => {\n const index = settingsCallbacks.indexOf(callback)\n if (index >= 0) settingsCallbacks.splice(index, 1)\n },\n }\n },\n }\n ;(context as { settings: SettingsAPI }).settings = settingsApi\n }\n\n // Add providers API if permitted\n if (hasPermission('provider.register')) {\n const providersApi: ProvidersAPI = {\n register(provider: AIProvider): Disposable {\n registeredProviders.set(provider.id, provider)\n postMessage({\n type: 'provider-registered',\n payload: { id: provider.id, name: provider.name },\n })\n return {\n dispose: () => {\n registeredProviders.delete(provider.id)\n },\n }\n },\n }\n ;(context as { providers: ProvidersAPI }).providers = providersApi\n }\n\n // Add tools API if permitted\n if (hasPermission('tools.register')) {\n const toolsApi: ToolsAPI = {\n register(tool: Tool): Disposable {\n registeredTools.set(tool.id, tool)\n postMessage({\n type: 'tool-registered',\n payload: {\n id: tool.id,\n name: tool.name,\n description: tool.description,\n parameters: tool.parameters,\n },\n })\n return {\n dispose: () => {\n registeredTools.delete(tool.id)\n },\n }\n },\n }\n ;(context as { tools: ToolsAPI }).tools = toolsApi\n }\n\n // Add actions API if permitted\n if (hasPermission('actions.register')) {\n const actionsApi: ActionsAPI = {\n register(action: Action): Disposable {\n registeredActions.set(action.id, action)\n postMessage({\n type: 'action-registered',\n payload: {\n id: action.id,\n },\n })\n return {\n dispose: () => {\n registeredActions.delete(action.id)\n },\n }\n },\n }\n ;(context as { actions: ActionsAPI }).actions = actionsApi\n }\n\n // Add events API if permitted\n if (hasPermission('events.emit')) {\n const eventsApi: EventsAPI = {\n async emit(name: string, payload?: Record<string, unknown>): Promise<void> {\n await sendRequest<void>('events.emit', { name, payload })\n },\n }\n ;(context as { events: EventsAPI }).events = eventsApi\n }\n\n // Add scheduler API if permitted\n if (hasPermission('scheduler.register')) {\n const schedulerApi: SchedulerAPI = {\n async schedule(job: SchedulerJobRequest): Promise<void> {\n await sendRequest<void>('scheduler.schedule', { job })\n },\n async cancel(jobId: string): Promise<void> {\n await sendRequest<void>('scheduler.cancel', { jobId })\n },\n onFire(callback: (payload: SchedulerFirePayload) => void): Disposable {\n schedulerCallbacks.push(callback)\n return {\n dispose: () => {\n const index = schedulerCallbacks.indexOf(callback)\n if (index >= 0) schedulerCallbacks.splice(index, 1)\n },\n }\n },\n }\n ;(context as { scheduler: SchedulerAPI }).scheduler = schedulerApi\n }\n\n // Add user profile API if permitted\n if (hasPermission('user.profile.read')) {\n const userApi: UserAPI = {\n async getProfile(): Promise<UserProfile> {\n return sendRequest<UserProfile>('user.getProfile', {})\n },\n }\n ;(context as { user: UserAPI }).user = userApi\n }\n\n // Add chat API if permitted\n if (hasPermission('chat.message.write')) {\n const chatApi: ChatAPI = {\n async appendInstruction(message: ChatInstructionMessage): Promise<void> {\n await sendRequest<void>('chat.appendInstruction', message)\n },\n }\n ;(context as { chat: ChatAPI }).chat = chatApi\n }\n\n // Add database API if permitted\n if (hasPermission('database.own')) {\n const databaseApi: DatabaseAPI = {\n async execute<T = unknown>(sql: string, params?: unknown[]): Promise<T[]> {\n return sendRequest<T[]>('database.execute', { sql, params })\n },\n }\n ;(context as { database: DatabaseAPI }).database = databaseApi\n }\n\n // Add storage API if permitted\n if (hasPermission('storage.local')) {\n const storageApi: StorageAPI = {\n // Global/extension-scoped storage\n async get<T>(key: string): Promise<T | undefined> {\n return sendRequest<T | undefined>('storage.get', { key })\n },\n async set(key: string, value: unknown): Promise<void> {\n return sendRequest<void>('storage.set', { key, value })\n },\n async delete(key: string): Promise<void> {\n return sendRequest<void>('storage.delete', { key })\n },\n async keys(): Promise<string[]> {\n return sendRequest<string[]>('storage.keys', {})\n },\n // User-scoped storage\n async getForUser<T>(userId: string, key: string): Promise<T | undefined> {\n return sendRequest<T | undefined>('storage.getForUser', { userId, key })\n },\n async setForUser(userId: string, key: string, value: unknown): Promise<void> {\n return sendRequest<void>('storage.setForUser', { userId, key, value })\n },\n async deleteForUser(userId: string, key: string): Promise<void> {\n return sendRequest<void>('storage.deleteForUser', { userId, key })\n },\n async keysForUser(userId: string): Promise<string[]> {\n return sendRequest<string[]>('storage.keysForUser', { userId })\n },\n }\n ;(context as { storage: StorageAPI }).storage = storageApi\n }\n\n return context\n}\n\n// ============================================================================\n// Initialization\n// ============================================================================\n\n/**\n * Initialize the extension runtime\n * This should be called by the extension's entry point\n */\nexport function initializeExtension(module: ExtensionModule): void {\n extensionModule = module\n\n // Set up message listener using the appropriate message port\n messagePort.onMessage(async (message: HostToWorkerMessage) => {\n try {\n await handleHostMessage(message)\n } catch (error) {\n console.error('Error handling message:', error)\n }\n })\n\n // Signal that we're ready\n postMessage({ type: 'ready' })\n}\n\n// Re-export types for extensions to use\nexport type {\n ExtensionContext,\n ExtensionModule,\n Disposable,\n AIProvider,\n Tool,\n ToolDefinition,\n ToolResult,\n ToolCall,\n Action,\n ActionResult,\n ModelInfo,\n ChatMessage,\n ChatOptions,\n GetModelsOptions,\n StreamEvent,\n} from './types.js'\n","/**\n * Message protocol between Extension Host and Extension Workers\n */\n\nimport type {\n ChatMessage,\n ChatOptions,\n GetModelsOptions,\n StreamEvent,\n ToolResult,\n ActionResult,\n ModelInfo,\n SchedulerFirePayload,\n} from './types.js'\n\n// ============================================================================\n// Host → Worker Messages\n// ============================================================================\n\nexport type HostToWorkerMessage =\n | ActivateMessage\n | DeactivateMessage\n | SettingsChangedMessage\n | SchedulerFireMessage\n | ProviderChatRequestMessage\n | ProviderModelsRequestMessage\n | ToolExecuteRequestMessage\n | ActionExecuteRequestMessage\n | ResponseMessage\n | StreamingFetchChunkMessage\n\nexport interface ActivateMessage {\n type: 'activate'\n id: string\n payload: {\n extensionId: string\n extensionVersion: string\n storagePath: string\n permissions: string[]\n settings: Record<string, unknown>\n }\n}\n\nexport interface DeactivateMessage {\n type: 'deactivate'\n id: string\n}\n\nexport interface SettingsChangedMessage {\n type: 'settings-changed'\n id: string\n payload: {\n key: string\n value: unknown\n }\n}\n\nexport interface SchedulerFireMessage {\n type: 'scheduler-fire'\n id: string\n payload: SchedulerFirePayload\n}\n\nexport interface ProviderChatRequestMessage {\n type: 'provider-chat-request'\n id: string\n payload: {\n providerId: string\n messages: ChatMessage[]\n options: ChatOptions\n }\n}\n\nexport interface ProviderModelsRequestMessage {\n type: 'provider-models-request'\n id: string\n payload: {\n providerId: string\n options?: GetModelsOptions\n }\n}\n\nexport interface ToolExecuteRequestMessage {\n type: 'tool-execute-request'\n id: string\n payload: {\n toolId: string\n params: Record<string, unknown>\n /** User ID if the tool is executed in a user context */\n userId?: string\n }\n}\n\nexport interface ActionExecuteRequestMessage {\n type: 'action-execute-request'\n id: string\n payload: {\n actionId: string\n params: Record<string, unknown>\n /** User ID if the action is executed in a user context */\n userId?: string\n }\n}\n\nexport interface ResponseMessage {\n type: 'response'\n id: string\n payload: {\n requestId: string\n success: boolean\n data?: unknown\n error?: string\n }\n}\n\n/**\n * Message sent from host to worker with streaming fetch data chunks.\n * Used for streaming network responses (e.g., NDJSON streams from Ollama).\n */\nexport interface StreamingFetchChunkMessage {\n type: 'streaming-fetch-chunk'\n id: string\n payload: {\n requestId: string\n chunk: string\n done: boolean\n error?: string\n }\n}\n\n// ============================================================================\n// Worker → Host Messages\n// ============================================================================\n\nexport type WorkerToHostMessage =\n | ReadyMessage\n | RequestMessage\n | ProviderRegisteredMessage\n | ToolRegisteredMessage\n | ActionRegisteredMessage\n | StreamEventMessage\n | LogMessage\n | ProviderModelsResponseMessage\n | ToolExecuteResponseMessage\n | ActionExecuteResponseMessage\n | StreamingFetchAckMessage\n\nexport interface ReadyMessage {\n type: 'ready'\n}\n\n/**\n * Message sent from worker to host to acknowledge receipt of a streaming fetch chunk.\n * This enables backpressure control to prevent unbounded memory growth.\n */\nexport interface StreamingFetchAckMessage {\n type: 'streaming-fetch-ack'\n payload: {\n requestId: string\n }\n}\n\nexport interface RequestMessage {\n type: 'request'\n id: string\n method: RequestMethod\n payload: unknown\n}\n\nexport type RequestMethod =\n | 'network.fetch'\n | 'network.fetch-stream'\n | 'settings.getAll'\n | 'settings.get'\n | 'settings.set'\n | 'user.getProfile'\n | 'events.emit'\n | 'scheduler.schedule'\n | 'scheduler.cancel'\n | 'chat.appendInstruction'\n | 'database.execute'\n | 'storage.get'\n | 'storage.set'\n | 'storage.delete'\n | 'storage.keys'\n | 'storage.getForUser'\n | 'storage.setForUser'\n | 'storage.deleteForUser'\n | 'storage.keysForUser'\n\nexport interface ProviderRegisteredMessage {\n type: 'provider-registered'\n payload: {\n id: string\n name: string\n }\n}\n\nexport interface ToolRegisteredMessage {\n type: 'tool-registered'\n payload: {\n id: string\n name: string\n description: string\n parameters?: Record<string, unknown>\n }\n}\n\nexport interface ActionRegisteredMessage {\n type: 'action-registered'\n payload: {\n id: string\n }\n}\n\nexport interface StreamEventMessage {\n type: 'stream-event'\n payload: {\n requestId: string\n event: StreamEvent\n }\n}\n\nexport interface ProviderModelsResponseMessage {\n type: 'provider-models-response'\n payload: {\n requestId: string\n models: ModelInfo[]\n error?: string\n }\n}\n\nexport interface ToolExecuteResponseMessage {\n type: 'tool-execute-response'\n payload: {\n requestId: string\n result: ToolResult\n error?: string\n }\n}\n\nexport interface ActionExecuteResponseMessage {\n type: 'action-execute-response'\n payload: {\n requestId: string\n result: ActionResult\n error?: string\n }\n}\n\nexport interface LogMessage {\n type: 'log'\n payload: {\n level: 'debug' | 'info' | 'warn' | 'error'\n message: string\n data?: Record<string, unknown>\n }\n}\n\n// ============================================================================\n// Utility Types\n// ============================================================================\n\nexport interface PendingRequest<T = unknown> {\n resolve: (value: T) => void\n reject: (error: Error) => void\n timeout: ReturnType<typeof setTimeout>\n}\n\n/**\n * Generate a unique message ID\n */\nexport function generateMessageId(): string {\n return `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`\n}\n"],"mappings":"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","names":["module"]}
1
+ {"version":3,"sources":["../src/runtime.ts","../src/messages.ts"],"sourcesContent":["/**\n * Extension Runtime - Runs inside the worker\n *\n * This module handles communication with the Extension Host and provides\n * the ExtensionContext to the extension's activate function.\n */\n\nimport type {\n ExtensionContext,\n ExtensionModule,\n Disposable,\n NetworkAPI,\n SettingsAPI,\n ProvidersAPI,\n ToolsAPI,\n ActionsAPI,\n EventsAPI,\n SchedulerAPI,\n SchedulerJobRequest,\n SchedulerFirePayload,\n UserAPI,\n UserProfile,\n ChatAPI,\n ChatInstructionMessage,\n DatabaseAPI,\n StorageAPI,\n LogAPI,\n AIProvider,\n Tool,\n Action,\n ChatMessage,\n ChatOptions,\n GetModelsOptions,\n} from './types.js'\n\nimport type {\n HostToWorkerMessage,\n WorkerToHostMessage,\n RequestMessage,\n PendingRequest,\n} from './messages.js'\n\nimport { generateMessageId } from './messages.js'\n\n// ============================================================================\n// Environment Detection and Message Port\n// ============================================================================\n\n/**\n * Detect if we're in Node.js Worker Thread or Web Worker\n * and get the appropriate message port\n */\ninterface MessagePort {\n postMessage(message: WorkerToHostMessage): void\n onMessage(handler: (message: HostToWorkerMessage) => void): void\n}\n\nfunction getMessagePort(): MessagePort {\n // Check if we're in Node.js Worker Thread\n if (typeof process !== 'undefined' && process.versions?.node) {\n // Node.js Worker Thread - import parentPort dynamically\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const { parentPort } = require('node:worker_threads')\n return {\n postMessage: (message) => parentPort?.postMessage(message),\n onMessage: (handler) => parentPort?.on('message', handler),\n }\n }\n\n // Web Worker - use self\n return {\n postMessage: (message) => self.postMessage(message),\n onMessage: (handler) => {\n self.addEventListener('message', (event: MessageEvent<HostToWorkerMessage>) => {\n handler(event.data)\n })\n },\n }\n}\n\nconst messagePort = getMessagePort()\n\n// ============================================================================\n// Global State\n// ============================================================================\n\nlet extensionModule: ExtensionModule | null = null\nlet extensionDisposable: Disposable | null = null\nlet extensionContext: ExtensionContext | null = null\n\nconst pendingRequests = new Map<string, PendingRequest>()\nconst registeredProviders = new Map<string, AIProvider>()\nconst registeredTools = new Map<string, Tool>()\nconst registeredActions = new Map<string, Action>()\nconst settingsCallbacks: Array<(key: string, value: unknown) => void> = []\nconst schedulerCallbacks: Array<(payload: SchedulerFirePayload) => void | Promise<void>> = []\n\n/**\n * Tracking for streaming fetch requests.\n * Each request stores incoming chunks and signals when new data arrives.\n */\ninterface StreamingFetchRequest {\n chunks: string[]\n done: boolean\n error?: string\n resolve?: () => void\n}\nconst streamingFetchRequests = new Map<string, StreamingFetchRequest>()\n\nconst REQUEST_TIMEOUT = 30000 // 30 seconds\n\n// ============================================================================\n// Message Handling\n// ============================================================================\n\n/**\n * Send a message to the host\n */\nfunction postMessage(message: WorkerToHostMessage): void {\n messagePort.postMessage(message)\n}\n\n/**\n * Send a request to the host and wait for response\n */\nasync function sendRequest<T>(method: RequestMessage['method'], payload: unknown): Promise<T> {\n const id = generateMessageId()\n\n return new Promise<T>((resolve, reject) => {\n const timeout = setTimeout(() => {\n pendingRequests.delete(id)\n reject(new Error(`Request ${method} timed out`))\n }, REQUEST_TIMEOUT)\n\n pendingRequests.set(id, { resolve: resolve as (value: unknown) => void, reject, timeout })\n\n postMessage({\n type: 'request',\n id,\n method,\n payload,\n })\n })\n}\n\n/**\n * Handle messages from the host\n */\nasync function handleHostMessage(message: HostToWorkerMessage): Promise<void> {\n switch (message.type) {\n case 'activate':\n await handleActivate(message.payload)\n break\n\n case 'deactivate':\n await handleDeactivate()\n break\n\n case 'settings-changed':\n handleSettingsChanged(message.payload.key, message.payload.value)\n break\n\n case 'scheduler-fire':\n await handleSchedulerFire(message.payload)\n break\n\n case 'provider-chat-request':\n await handleProviderChatRequest(message.id, message.payload)\n break\n\n case 'provider-models-request':\n await handleProviderModelsRequest(message.id, message.payload)\n break\n\n case 'tool-execute-request':\n await handleToolExecuteRequest(message.id, message.payload)\n break\n\n case 'action-execute-request':\n await handleActionExecuteRequest(message.id, message.payload)\n break\n\n case 'response':\n handleResponse(message.payload)\n break\n\n case 'streaming-fetch-chunk':\n handleStreamingFetchChunk(message.payload)\n break\n }\n}\n\n/**\n * Handle incoming streaming fetch chunks from the host\n */\nfunction handleStreamingFetchChunk(payload: {\n requestId: string\n chunk: string\n done: boolean\n error?: string\n}): void {\n const request = streamingFetchRequests.get(payload.requestId)\n if (!request) return\n\n if (payload.error) {\n request.error = payload.error\n request.done = true\n } else if (payload.chunk) {\n request.chunks.push(payload.chunk)\n }\n\n if (payload.done) {\n request.done = true\n }\n\n // Signal that new data is available\n if (request.resolve) {\n const resolve = request.resolve\n request.resolve = undefined\n resolve()\n }\n\n // Send acknowledgment for backpressure control\n postMessage({\n type: 'streaming-fetch-ack',\n payload: { requestId: payload.requestId },\n })\n}\n\nfunction handleResponse(payload: { requestId: string; success: boolean; data?: unknown; error?: string }): void {\n const pending = pendingRequests.get(payload.requestId)\n if (!pending) return\n\n clearTimeout(pending.timeout)\n pendingRequests.delete(payload.requestId)\n\n if (payload.success) {\n pending.resolve(payload.data)\n } else {\n pending.reject(new Error(payload.error || 'Unknown error'))\n }\n}\n\n// ============================================================================\n// Activation / Deactivation\n// ============================================================================\n\nasync function handleActivate(payload: {\n extensionId: string\n extensionVersion: string\n storagePath: string\n permissions: string[]\n settings: Record<string, unknown>\n}): Promise<void> {\n const { extensionId, extensionVersion, storagePath, permissions } = payload\n\n // Build the context based on permissions\n extensionContext = buildContext(extensionId, extensionVersion, storagePath, permissions)\n\n // Import and activate the extension\n try {\n // The actual extension code should be bundled and available\n // This is called after the extension code has been loaded into the worker\n if (extensionModule?.activate) {\n const result = await extensionModule.activate(extensionContext)\n if (result && 'dispose' in result) {\n extensionDisposable = result\n }\n }\n } catch (error) {\n extensionContext.log.error('Failed to activate extension', {\n error: error instanceof Error ? error.message : String(error),\n })\n throw error\n }\n}\n\nasync function handleDeactivate(): Promise<void> {\n try {\n if (extensionModule?.deactivate) {\n await extensionModule.deactivate()\n }\n if (extensionDisposable) {\n extensionDisposable.dispose()\n }\n } catch (error) {\n console.error('Error during deactivation:', error)\n } finally {\n extensionModule = null\n extensionDisposable = null\n extensionContext = null\n registeredProviders.clear()\n registeredTools.clear()\n registeredActions.clear()\n settingsCallbacks.length = 0\n schedulerCallbacks.length = 0\n }\n}\n\nfunction handleSettingsChanged(key: string, value: unknown): void {\n for (const callback of settingsCallbacks) {\n try {\n callback(key, value)\n } catch (error) {\n console.error('Error in settings change callback:', error)\n }\n }\n}\n\nasync function handleSchedulerFire(payload: SchedulerFirePayload): Promise<void> {\n // Set the userId in context if this is a user-scoped job\n if (extensionContext && payload.userId) {\n ;(extensionContext as { userId?: string }).userId = payload.userId\n }\n\n // Run callbacks concurrently to avoid blocking\n const results = await Promise.allSettled(\n schedulerCallbacks.map((callback) => callback(payload)),\n )\n\n // Log any errors\n results.forEach((result, index) => {\n if (result.status === 'rejected') {\n console.error(`Error in scheduler callback ${index}:`, result.reason)\n }\n })\n\n // Reset userId after all callbacks have completed\n if (extensionContext) {\n ;(extensionContext as { userId?: string }).userId = undefined\n }\n}\n\n// ============================================================================\n// Provider / Tool Requests\n// ============================================================================\n\nasync function handleProviderChatRequest(\n requestId: string,\n payload: { providerId: string; messages: ChatMessage[]; options: ChatOptions }\n): Promise<void> {\n const provider = registeredProviders.get(payload.providerId)\n if (!provider) {\n postMessage({\n type: 'request',\n id: generateMessageId(),\n method: 'network.fetch', // Dummy, we need a proper error response\n payload: { error: `Provider ${payload.providerId} not found` },\n })\n return\n }\n\n try {\n const generator = provider.chat(payload.messages, payload.options)\n let sawDone = false\n let sawError = false\n\n for await (const event of generator) {\n if (event.type === 'done') {\n sawDone = true\n } else if (event.type === 'error') {\n sawError = true\n }\n postMessage({\n type: 'stream-event',\n payload: { requestId, event },\n })\n }\n\n if (!sawDone && !sawError) {\n postMessage({\n type: 'stream-event',\n payload: {\n requestId,\n event: { type: 'done' },\n },\n })\n }\n } catch (error) {\n postMessage({\n type: 'stream-event',\n payload: {\n requestId,\n event: {\n type: 'error',\n message: error instanceof Error ? error.message : String(error),\n },\n },\n })\n }\n}\n\nasync function handleProviderModelsRequest(\n requestId: string,\n payload: { providerId: string; options?: GetModelsOptions }\n): Promise<void> {\n const provider = registeredProviders.get(payload.providerId)\n if (!provider) {\n // Send error response\n postMessage({\n type: 'provider-models-response',\n payload: {\n requestId,\n models: [],\n error: `Provider ${payload.providerId} not found`,\n },\n })\n return\n }\n\n try {\n // Pass options to getModels so provider can use settings (e.g., URL for Ollama)\n const models = await provider.getModels(payload.options)\n // Send response with models\n postMessage({\n type: 'provider-models-response',\n payload: {\n requestId,\n models,\n },\n })\n } catch (error) {\n postMessage({\n type: 'provider-models-response',\n payload: {\n requestId,\n models: [],\n error: error instanceof Error ? error.message : String(error),\n },\n })\n }\n}\n\nasync function handleToolExecuteRequest(\n requestId: string,\n payload: { toolId: string; params: Record<string, unknown>; userId?: string }\n): Promise<void> {\n const tool = registeredTools.get(payload.toolId)\n if (!tool) {\n // Send error response\n postMessage({\n type: 'tool-execute-response',\n payload: {\n requestId,\n result: { success: false, error: `Tool ${payload.toolId} not found` },\n error: `Tool ${payload.toolId} not found`,\n },\n })\n return\n }\n\n try {\n // Update the extension context with the userId if provided\n if (extensionContext && payload.userId) {\n // Create a new context with the userId for this execution\n ;(extensionContext as { userId?: string }).userId = payload.userId\n }\n\n const result = await tool.execute(payload.params)\n\n // Reset userId after execution\n if (extensionContext) {\n ;(extensionContext as { userId?: string }).userId = undefined\n }\n\n // Send response with result\n postMessage({\n type: 'tool-execute-response',\n payload: {\n requestId,\n result,\n },\n })\n } catch (error) {\n // Reset userId on error\n if (extensionContext) {\n ;(extensionContext as { userId?: string }).userId = undefined\n }\n\n const errorMessage = error instanceof Error ? error.message : String(error)\n postMessage({\n type: 'tool-execute-response',\n payload: {\n requestId,\n result: { success: false, error: errorMessage },\n error: errorMessage,\n },\n })\n }\n}\n\nasync function handleActionExecuteRequest(\n requestId: string,\n payload: { actionId: string; params: Record<string, unknown>; userId?: string }\n): Promise<void> {\n const action = registeredActions.get(payload.actionId)\n if (!action) {\n postMessage({\n type: 'action-execute-response',\n payload: {\n requestId,\n result: { success: false, error: `Action ${payload.actionId} not found` },\n error: `Action ${payload.actionId} not found`,\n },\n })\n return\n }\n\n try {\n // Update the extension context with the userId if provided\n if (extensionContext && payload.userId) {\n ;(extensionContext as { userId?: string }).userId = payload.userId\n }\n\n const result = await action.execute(payload.params)\n\n // Reset userId after execution\n if (extensionContext) {\n ;(extensionContext as { userId?: string }).userId = undefined\n }\n\n postMessage({\n type: 'action-execute-response',\n payload: {\n requestId,\n result,\n },\n })\n } catch (error) {\n // Reset userId on error\n if (extensionContext) {\n ;(extensionContext as { userId?: string }).userId = undefined\n }\n\n const errorMessage = error instanceof Error ? error.message : String(error)\n postMessage({\n type: 'action-execute-response',\n payload: {\n requestId,\n result: { success: false, error: errorMessage },\n error: errorMessage,\n },\n })\n }\n}\n\n// ============================================================================\n// Context Building\n// ============================================================================\n\nfunction buildContext(\n extensionId: string,\n extensionVersion: string,\n storagePath: string,\n permissions: string[]\n): ExtensionContext {\n const hasPermission = (perm: string): boolean => {\n return permissions.some((p) => {\n if (p === perm) return true\n if (p.endsWith(':*') && perm.startsWith(p.slice(0, -1))) return true\n return false\n })\n }\n\n const log: LogAPI = {\n debug: (message, data) => postMessage({ type: 'log', payload: { level: 'debug', message, data } }),\n info: (message, data) => postMessage({ type: 'log', payload: { level: 'info', message, data } }),\n warn: (message, data) => postMessage({ type: 'log', payload: { level: 'warn', message, data } }),\n error: (message, data) => postMessage({ type: 'log', payload: { level: 'error', message, data } }),\n }\n\n const context: ExtensionContext = {\n extension: {\n id: extensionId,\n version: extensionVersion,\n storagePath,\n },\n log,\n }\n\n // Add network API if permitted\n if (permissions.some((p) => p.startsWith('network:'))) {\n const networkApi: NetworkAPI = {\n async fetch(url: string, options?: RequestInit): Promise<Response> {\n const result = await sendRequest<{ status: number; statusText: string; headers: Record<string, string>; body: string }>('network.fetch', { url, options })\n return new Response(result.body, {\n status: result.status,\n statusText: result.statusText,\n headers: result.headers,\n })\n },\n\n async *fetchStream(url: string, options?: RequestInit): AsyncGenerator<string, void, unknown> {\n const requestId = generateMessageId()\n\n // Set up streaming request tracking\n const request: StreamingFetchRequest = {\n chunks: [],\n done: false,\n }\n streamingFetchRequests.set(requestId, request)\n\n // Send the streaming fetch request to the host\n postMessage({\n type: 'request',\n id: requestId,\n method: 'network.fetch-stream',\n payload: { url, options, requestId },\n })\n\n try {\n // Yield chunks as they arrive\n while (!request.done) {\n // Wait for new data\n await new Promise<void>((resolve) => {\n const resolver = () => {\n // Clear the stored resolver before resolving to avoid\n // stale callbacks being invoked for a new wait iteration.\n if (request.resolve === resolver) {\n request.resolve = undefined\n }\n resolve()\n }\n\n request.resolve = resolver\n\n // Check if we already have data or completion\n if (request.chunks.length > 0 || request.done) {\n resolver()\n }\n })\n\n // Check for errors first, before yielding any chunks\n if (request.error) {\n throw new Error(request.error)\n }\n\n // Yield all available chunks\n while (request.chunks.length > 0) {\n yield request.chunks.shift()!\n }\n }\n } finally {\n streamingFetchRequests.delete(requestId)\n }\n },\n }\n ;(context as { network: NetworkAPI }).network = networkApi\n }\n\n // Add settings API if permitted\n if (hasPermission('settings.register')) {\n const settingsApi: SettingsAPI = {\n async getAll<T extends Record<string, unknown>>(): Promise<T> {\n return sendRequest<T>('settings.getAll', {})\n },\n async get<T>(key: string): Promise<T | undefined> {\n return sendRequest<T | undefined>('settings.get', { key })\n },\n async set(key: string, value: unknown): Promise<void> {\n return sendRequest<void>('settings.set', { key, value })\n },\n onChange(callback: (key: string, value: unknown) => void): Disposable {\n settingsCallbacks.push(callback)\n return {\n dispose: () => {\n const index = settingsCallbacks.indexOf(callback)\n if (index >= 0) settingsCallbacks.splice(index, 1)\n },\n }\n },\n }\n ;(context as { settings: SettingsAPI }).settings = settingsApi\n }\n\n // Add providers API if permitted\n if (hasPermission('provider.register')) {\n const providersApi: ProvidersAPI = {\n register(provider: AIProvider): Disposable {\n registeredProviders.set(provider.id, provider)\n postMessage({\n type: 'provider-registered',\n payload: { id: provider.id, name: provider.name },\n })\n return {\n dispose: () => {\n registeredProviders.delete(provider.id)\n },\n }\n },\n }\n ;(context as { providers: ProvidersAPI }).providers = providersApi\n }\n\n // Add tools API if permitted\n if (hasPermission('tools.register')) {\n const toolsApi: ToolsAPI = {\n register(tool: Tool): Disposable {\n registeredTools.set(tool.id, tool)\n postMessage({\n type: 'tool-registered',\n payload: {\n id: tool.id,\n name: tool.name,\n description: tool.description,\n parameters: tool.parameters,\n },\n })\n return {\n dispose: () => {\n registeredTools.delete(tool.id)\n },\n }\n },\n }\n ;(context as { tools: ToolsAPI }).tools = toolsApi\n }\n\n // Add actions API if permitted\n if (hasPermission('actions.register')) {\n const actionsApi: ActionsAPI = {\n register(action: Action): Disposable {\n registeredActions.set(action.id, action)\n postMessage({\n type: 'action-registered',\n payload: {\n id: action.id,\n },\n })\n return {\n dispose: () => {\n registeredActions.delete(action.id)\n },\n }\n },\n }\n ;(context as { actions: ActionsAPI }).actions = actionsApi\n }\n\n // Add events API if permitted\n if (hasPermission('events.emit')) {\n const eventsApi: EventsAPI = {\n async emit(name: string, payload?: Record<string, unknown>): Promise<void> {\n await sendRequest<void>('events.emit', { name, payload })\n },\n }\n ;(context as { events: EventsAPI }).events = eventsApi\n }\n\n // Add scheduler API if permitted\n if (hasPermission('scheduler.register')) {\n const schedulerApi: SchedulerAPI = {\n async schedule(job: SchedulerJobRequest): Promise<void> {\n // Automatically include userId from context if not explicitly set\n const userId = (extensionContext as { userId?: string }).userId\n const jobWithUser = userId && !job.userId ? { ...job, userId } : job\n await sendRequest<void>('scheduler.schedule', { job: jobWithUser })\n },\n async cancel(jobId: string): Promise<void> {\n await sendRequest<void>('scheduler.cancel', { jobId })\n },\n onFire(callback: (payload: SchedulerFirePayload) => void): Disposable {\n schedulerCallbacks.push(callback)\n return {\n dispose: () => {\n const index = schedulerCallbacks.indexOf(callback)\n if (index >= 0) schedulerCallbacks.splice(index, 1)\n },\n }\n },\n }\n ;(context as { scheduler: SchedulerAPI }).scheduler = schedulerApi\n }\n\n // Add user profile API if permitted\n if (hasPermission('user.profile.read')) {\n const userApi: UserAPI = {\n async getProfile(): Promise<UserProfile> {\n return sendRequest<UserProfile>('user.getProfile', {})\n },\n }\n ;(context as { user: UserAPI }).user = userApi\n }\n\n // Add chat API if permitted\n if (hasPermission('chat.message.write')) {\n const chatApi: ChatAPI = {\n async appendInstruction(message: ChatInstructionMessage): Promise<void> {\n const contextUserId = (extensionContext as { userId?: string }).userId\n await sendRequest<void>('chat.appendInstruction', {\n ...message,\n userId: message.userId ?? contextUserId,\n })\n },\n }\n ;(context as { chat: ChatAPI }).chat = chatApi\n }\n\n // Add database API if permitted\n if (hasPermission('database.own')) {\n const databaseApi: DatabaseAPI = {\n async execute<T = unknown>(sql: string, params?: unknown[]): Promise<T[]> {\n return sendRequest<T[]>('database.execute', { sql, params })\n },\n }\n ;(context as { database: DatabaseAPI }).database = databaseApi\n }\n\n // Add storage API if permitted\n if (hasPermission('storage.local')) {\n const storageApi: StorageAPI = {\n // Global/extension-scoped storage\n async get<T>(key: string): Promise<T | undefined> {\n return sendRequest<T | undefined>('storage.get', { key })\n },\n async set(key: string, value: unknown): Promise<void> {\n return sendRequest<void>('storage.set', { key, value })\n },\n async delete(key: string): Promise<void> {\n return sendRequest<void>('storage.delete', { key })\n },\n async keys(): Promise<string[]> {\n return sendRequest<string[]>('storage.keys', {})\n },\n // User-scoped storage\n async getForUser<T>(userId: string, key: string): Promise<T | undefined> {\n return sendRequest<T | undefined>('storage.getForUser', { userId, key })\n },\n async setForUser(userId: string, key: string, value: unknown): Promise<void> {\n return sendRequest<void>('storage.setForUser', { userId, key, value })\n },\n async deleteForUser(userId: string, key: string): Promise<void> {\n return sendRequest<void>('storage.deleteForUser', { userId, key })\n },\n async keysForUser(userId: string): Promise<string[]> {\n return sendRequest<string[]>('storage.keysForUser', { userId })\n },\n }\n ;(context as { storage: StorageAPI }).storage = storageApi\n }\n\n return context\n}\n\n// ============================================================================\n// Initialization\n// ============================================================================\n\n/**\n * Initialize the extension runtime\n * This should be called by the extension's entry point\n */\nexport function initializeExtension(module: ExtensionModule): void {\n extensionModule = module\n\n // Set up message listener using the appropriate message port\n messagePort.onMessage(async (message: HostToWorkerMessage) => {\n try {\n await handleHostMessage(message)\n } catch (error) {\n console.error('Error handling message:', error)\n }\n })\n\n // Signal that we're ready\n postMessage({ type: 'ready' })\n}\n\n// Re-export types for extensions to use\nexport type {\n ExtensionContext,\n ExtensionModule,\n Disposable,\n AIProvider,\n Tool,\n ToolDefinition,\n ToolResult,\n ToolCall,\n Action,\n ActionResult,\n ModelInfo,\n ChatMessage,\n ChatOptions,\n GetModelsOptions,\n StreamEvent,\n} from './types.js'\n","/**\n * Message protocol between Extension Host and Extension Workers\n */\n\nimport type {\n ChatMessage,\n ChatOptions,\n GetModelsOptions,\n StreamEvent,\n ToolResult,\n ActionResult,\n ModelInfo,\n SchedulerFirePayload,\n} from './types.js'\n\n// ============================================================================\n// Host → Worker Messages\n// ============================================================================\n\nexport type HostToWorkerMessage =\n | ActivateMessage\n | DeactivateMessage\n | SettingsChangedMessage\n | SchedulerFireMessage\n | ProviderChatRequestMessage\n | ProviderModelsRequestMessage\n | ToolExecuteRequestMessage\n | ActionExecuteRequestMessage\n | ResponseMessage\n | StreamingFetchChunkMessage\n\nexport interface ActivateMessage {\n type: 'activate'\n id: string\n payload: {\n extensionId: string\n extensionVersion: string\n storagePath: string\n permissions: string[]\n settings: Record<string, unknown>\n }\n}\n\nexport interface DeactivateMessage {\n type: 'deactivate'\n id: string\n}\n\nexport interface SettingsChangedMessage {\n type: 'settings-changed'\n id: string\n payload: {\n key: string\n value: unknown\n }\n}\n\nexport interface SchedulerFireMessage {\n type: 'scheduler-fire'\n id: string\n payload: SchedulerFirePayload\n}\n\nexport interface ProviderChatRequestMessage {\n type: 'provider-chat-request'\n id: string\n payload: {\n providerId: string\n messages: ChatMessage[]\n options: ChatOptions\n }\n}\n\nexport interface ProviderModelsRequestMessage {\n type: 'provider-models-request'\n id: string\n payload: {\n providerId: string\n options?: GetModelsOptions\n }\n}\n\nexport interface ToolExecuteRequestMessage {\n type: 'tool-execute-request'\n id: string\n payload: {\n toolId: string\n params: Record<string, unknown>\n /** User ID if the tool is executed in a user context */\n userId?: string\n }\n}\n\nexport interface ActionExecuteRequestMessage {\n type: 'action-execute-request'\n id: string\n payload: {\n actionId: string\n params: Record<string, unknown>\n /** User ID if the action is executed in a user context */\n userId?: string\n }\n}\n\nexport interface ResponseMessage {\n type: 'response'\n id: string\n payload: {\n requestId: string\n success: boolean\n data?: unknown\n error?: string\n }\n}\n\n/**\n * Message sent from host to worker with streaming fetch data chunks.\n * Used for streaming network responses (e.g., NDJSON streams from Ollama).\n */\nexport interface StreamingFetchChunkMessage {\n type: 'streaming-fetch-chunk'\n id: string\n payload: {\n requestId: string\n chunk: string\n done: boolean\n error?: string\n }\n}\n\n// ============================================================================\n// Worker → Host Messages\n// ============================================================================\n\nexport type WorkerToHostMessage =\n | ReadyMessage\n | RequestMessage\n | ProviderRegisteredMessage\n | ToolRegisteredMessage\n | ActionRegisteredMessage\n | StreamEventMessage\n | LogMessage\n | ProviderModelsResponseMessage\n | ToolExecuteResponseMessage\n | ActionExecuteResponseMessage\n | StreamingFetchAckMessage\n\nexport interface ReadyMessage {\n type: 'ready'\n}\n\n/**\n * Message sent from worker to host to acknowledge receipt of a streaming fetch chunk.\n * This enables backpressure control to prevent unbounded memory growth.\n */\nexport interface StreamingFetchAckMessage {\n type: 'streaming-fetch-ack'\n payload: {\n requestId: string\n }\n}\n\nexport interface RequestMessage {\n type: 'request'\n id: string\n method: RequestMethod\n payload: unknown\n}\n\nexport type RequestMethod =\n | 'network.fetch'\n | 'network.fetch-stream'\n | 'settings.getAll'\n | 'settings.get'\n | 'settings.set'\n | 'user.getProfile'\n | 'events.emit'\n | 'scheduler.schedule'\n | 'scheduler.cancel'\n | 'chat.appendInstruction'\n | 'database.execute'\n | 'storage.get'\n | 'storage.set'\n | 'storage.delete'\n | 'storage.keys'\n | 'storage.getForUser'\n | 'storage.setForUser'\n | 'storage.deleteForUser'\n | 'storage.keysForUser'\n\nexport interface ProviderRegisteredMessage {\n type: 'provider-registered'\n payload: {\n id: string\n name: string\n }\n}\n\nexport interface ToolRegisteredMessage {\n type: 'tool-registered'\n payload: {\n id: string\n name: string\n description: string\n parameters?: Record<string, unknown>\n }\n}\n\nexport interface ActionRegisteredMessage {\n type: 'action-registered'\n payload: {\n id: string\n }\n}\n\nexport interface StreamEventMessage {\n type: 'stream-event'\n payload: {\n requestId: string\n event: StreamEvent\n }\n}\n\nexport interface ProviderModelsResponseMessage {\n type: 'provider-models-response'\n payload: {\n requestId: string\n models: ModelInfo[]\n error?: string\n }\n}\n\nexport interface ToolExecuteResponseMessage {\n type: 'tool-execute-response'\n payload: {\n requestId: string\n result: ToolResult\n error?: string\n }\n}\n\nexport interface ActionExecuteResponseMessage {\n type: 'action-execute-response'\n payload: {\n requestId: string\n result: ActionResult\n error?: string\n }\n}\n\nexport interface LogMessage {\n type: 'log'\n payload: {\n level: 'debug' | 'info' | 'warn' | 'error'\n message: string\n data?: Record<string, unknown>\n }\n}\n\n// ============================================================================\n// Utility Types\n// ============================================================================\n\nexport interface PendingRequest<T = unknown> {\n resolve: (value: T) => void\n reject: (error: Error) => void\n timeout: ReturnType<typeof setTimeout>\n}\n\n/**\n * Generate a unique message ID\n */\nexport function generateMessageId(): string {\n return `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`\n}\n"],"mappings":"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","names":["module"]}
@@ -1,5 +1,5 @@
1
- import { ab as ExtensionModule } from './types-kRt2DzdL.cjs';
2
- export { a7 as AIProvider, aa as Action, A as ActionResult, C as ChatMessage, a as ChatOptions, O as Disposable, K as ExtensionContext, G as GetModelsOptions, M as ModelInfo, b as StreamEvent, a9 as Tool, a8 as ToolCall, v as ToolDefinition, T as ToolResult } from './types-kRt2DzdL.cjs';
1
+ import { ab as ExtensionModule } from './types-CvfONPis.cjs';
2
+ export { a7 as AIProvider, aa as Action, A as ActionResult, C as ChatMessage, a as ChatOptions, O as Disposable, K as ExtensionContext, G as GetModelsOptions, M as ModelInfo, b as StreamEvent, a9 as Tool, a8 as ToolCall, v as ToolDefinition, T as ToolResult } from './types-CvfONPis.cjs';
3
3
 
4
4
  /**
5
5
  * Extension Runtime - Runs inside the worker
package/dist/runtime.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { ab as ExtensionModule } from './types-kRt2DzdL.js';
2
- export { a7 as AIProvider, aa as Action, A as ActionResult, C as ChatMessage, a as ChatOptions, O as Disposable, K as ExtensionContext, G as GetModelsOptions, M as ModelInfo, b as StreamEvent, a9 as Tool, a8 as ToolCall, v as ToolDefinition, T as ToolResult } from './types-kRt2DzdL.js';
1
+ import { ab as ExtensionModule } from './types-CvfONPis.js';
2
+ export { a7 as AIProvider, aa as Action, A as ActionResult, C as ChatMessage, a as ChatOptions, O as Disposable, K as ExtensionContext, G as GetModelsOptions, M as ModelInfo, b as StreamEvent, a9 as Tool, a8 as ToolCall, v as ToolDefinition, T as ToolResult } from './types-CvfONPis.js';
3
3
 
4
4
  /**
5
5
  * Extension Runtime - Runs inside the worker
package/dist/runtime.js CHANGED
@@ -64,7 +64,7 @@ async function handleHostMessage(message) {
64
64
  handleSettingsChanged(message.payload.key, message.payload.value);
65
65
  break;
66
66
  case "scheduler-fire":
67
- handleSchedulerFire(message.payload);
67
+ await handleSchedulerFire(message.payload);
68
68
  break;
69
69
  case "provider-chat-request":
70
70
  await handleProviderChatRequest(message.id, message.payload);
@@ -166,18 +166,19 @@ function handleSettingsChanged(key, value) {
166
166
  }
167
167
  }
168
168
  }
169
- function handleSchedulerFire(payload) {
169
+ async function handleSchedulerFire(payload) {
170
170
  if (extensionContext && payload.userId) {
171
171
  ;
172
172
  extensionContext.userId = payload.userId;
173
173
  }
174
- for (const callback of schedulerCallbacks) {
175
- try {
176
- callback(payload);
177
- } catch (error) {
178
- console.error("Error in scheduler callback:", error);
174
+ const results = await Promise.allSettled(
175
+ schedulerCallbacks.map((callback) => callback(payload))
176
+ );
177
+ results.forEach((result, index) => {
178
+ if (result.status === "rejected") {
179
+ console.error(`Error in scheduler callback ${index}:`, result.reason);
179
180
  }
180
- }
181
+ });
181
182
  if (extensionContext) {
182
183
  ;
183
184
  extensionContext.userId = void 0;
@@ -522,7 +523,9 @@ function buildContext(extensionId, extensionVersion, storagePath, permissions) {
522
523
  if (hasPermission("scheduler.register")) {
523
524
  const schedulerApi = {
524
525
  async schedule(job) {
525
- await sendRequest("scheduler.schedule", { job });
526
+ const userId = extensionContext.userId;
527
+ const jobWithUser = userId && !job.userId ? { ...job, userId } : job;
528
+ await sendRequest("scheduler.schedule", { job: jobWithUser });
526
529
  },
527
530
  async cancel(jobId) {
528
531
  await sendRequest("scheduler.cancel", { jobId });
@@ -550,7 +553,11 @@ function buildContext(extensionId, extensionVersion, storagePath, permissions) {
550
553
  if (hasPermission("chat.message.write")) {
551
554
  const chatApi = {
552
555
  async appendInstruction(message) {
553
- await sendRequest("chat.appendInstruction", message);
556
+ const contextUserId = extensionContext.userId;
557
+ await sendRequest("chat.appendInstruction", {
558
+ ...message,
559
+ userId: message.userId ?? contextUserId
560
+ });
554
561
  }
555
562
  };
556
563
  context.chat = chatApi;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/runtime.ts"],"sourcesContent":["/**\n * Extension Runtime - Runs inside the worker\n *\n * This module handles communication with the Extension Host and provides\n * the ExtensionContext to the extension's activate function.\n */\n\nimport type {\n ExtensionContext,\n ExtensionModule,\n Disposable,\n NetworkAPI,\n SettingsAPI,\n ProvidersAPI,\n ToolsAPI,\n ActionsAPI,\n EventsAPI,\n SchedulerAPI,\n SchedulerJobRequest,\n SchedulerFirePayload,\n UserAPI,\n UserProfile,\n ChatAPI,\n ChatInstructionMessage,\n DatabaseAPI,\n StorageAPI,\n LogAPI,\n AIProvider,\n Tool,\n Action,\n ChatMessage,\n ChatOptions,\n GetModelsOptions,\n} from './types.js'\n\nimport type {\n HostToWorkerMessage,\n WorkerToHostMessage,\n RequestMessage,\n PendingRequest,\n} from './messages.js'\n\nimport { generateMessageId } from './messages.js'\n\n// ============================================================================\n// Environment Detection and Message Port\n// ============================================================================\n\n/**\n * Detect if we're in Node.js Worker Thread or Web Worker\n * and get the appropriate message port\n */\ninterface MessagePort {\n postMessage(message: WorkerToHostMessage): void\n onMessage(handler: (message: HostToWorkerMessage) => void): void\n}\n\nfunction getMessagePort(): MessagePort {\n // Check if we're in Node.js Worker Thread\n if (typeof process !== 'undefined' && process.versions?.node) {\n // Node.js Worker Thread - import parentPort dynamically\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const { parentPort } = require('node:worker_threads')\n return {\n postMessage: (message) => parentPort?.postMessage(message),\n onMessage: (handler) => parentPort?.on('message', handler),\n }\n }\n\n // Web Worker - use self\n return {\n postMessage: (message) => self.postMessage(message),\n onMessage: (handler) => {\n self.addEventListener('message', (event: MessageEvent<HostToWorkerMessage>) => {\n handler(event.data)\n })\n },\n }\n}\n\nconst messagePort = getMessagePort()\n\n// ============================================================================\n// Global State\n// ============================================================================\n\nlet extensionModule: ExtensionModule | null = null\nlet extensionDisposable: Disposable | null = null\nlet extensionContext: ExtensionContext | null = null\n\nconst pendingRequests = new Map<string, PendingRequest>()\nconst registeredProviders = new Map<string, AIProvider>()\nconst registeredTools = new Map<string, Tool>()\nconst registeredActions = new Map<string, Action>()\nconst settingsCallbacks: Array<(key: string, value: unknown) => void> = []\nconst schedulerCallbacks: Array<(payload: SchedulerFirePayload) => void> = []\n\n/**\n * Tracking for streaming fetch requests.\n * Each request stores incoming chunks and signals when new data arrives.\n */\ninterface StreamingFetchRequest {\n chunks: string[]\n done: boolean\n error?: string\n resolve?: () => void\n}\nconst streamingFetchRequests = new Map<string, StreamingFetchRequest>()\n\nconst REQUEST_TIMEOUT = 30000 // 30 seconds\n\n// ============================================================================\n// Message Handling\n// ============================================================================\n\n/**\n * Send a message to the host\n */\nfunction postMessage(message: WorkerToHostMessage): void {\n messagePort.postMessage(message)\n}\n\n/**\n * Send a request to the host and wait for response\n */\nasync function sendRequest<T>(method: RequestMessage['method'], payload: unknown): Promise<T> {\n const id = generateMessageId()\n\n return new Promise<T>((resolve, reject) => {\n const timeout = setTimeout(() => {\n pendingRequests.delete(id)\n reject(new Error(`Request ${method} timed out`))\n }, REQUEST_TIMEOUT)\n\n pendingRequests.set(id, { resolve: resolve as (value: unknown) => void, reject, timeout })\n\n postMessage({\n type: 'request',\n id,\n method,\n payload,\n })\n })\n}\n\n/**\n * Handle messages from the host\n */\nasync function handleHostMessage(message: HostToWorkerMessage): Promise<void> {\n switch (message.type) {\n case 'activate':\n await handleActivate(message.payload)\n break\n\n case 'deactivate':\n await handleDeactivate()\n break\n\n case 'settings-changed':\n handleSettingsChanged(message.payload.key, message.payload.value)\n break\n\n case 'scheduler-fire':\n handleSchedulerFire(message.payload)\n break\n\n case 'provider-chat-request':\n await handleProviderChatRequest(message.id, message.payload)\n break\n\n case 'provider-models-request':\n await handleProviderModelsRequest(message.id, message.payload)\n break\n\n case 'tool-execute-request':\n await handleToolExecuteRequest(message.id, message.payload)\n break\n\n case 'action-execute-request':\n await handleActionExecuteRequest(message.id, message.payload)\n break\n\n case 'response':\n handleResponse(message.payload)\n break\n\n case 'streaming-fetch-chunk':\n handleStreamingFetchChunk(message.payload)\n break\n }\n}\n\n/**\n * Handle incoming streaming fetch chunks from the host\n */\nfunction handleStreamingFetchChunk(payload: {\n requestId: string\n chunk: string\n done: boolean\n error?: string\n}): void {\n const request = streamingFetchRequests.get(payload.requestId)\n if (!request) return\n\n if (payload.error) {\n request.error = payload.error\n request.done = true\n } else if (payload.chunk) {\n request.chunks.push(payload.chunk)\n }\n\n if (payload.done) {\n request.done = true\n }\n\n // Signal that new data is available\n if (request.resolve) {\n const resolve = request.resolve\n request.resolve = undefined\n resolve()\n }\n\n // Send acknowledgment for backpressure control\n postMessage({\n type: 'streaming-fetch-ack',\n payload: { requestId: payload.requestId },\n })\n}\n\nfunction handleResponse(payload: { requestId: string; success: boolean; data?: unknown; error?: string }): void {\n const pending = pendingRequests.get(payload.requestId)\n if (!pending) return\n\n clearTimeout(pending.timeout)\n pendingRequests.delete(payload.requestId)\n\n if (payload.success) {\n pending.resolve(payload.data)\n } else {\n pending.reject(new Error(payload.error || 'Unknown error'))\n }\n}\n\n// ============================================================================\n// Activation / Deactivation\n// ============================================================================\n\nasync function handleActivate(payload: {\n extensionId: string\n extensionVersion: string\n storagePath: string\n permissions: string[]\n settings: Record<string, unknown>\n}): Promise<void> {\n const { extensionId, extensionVersion, storagePath, permissions } = payload\n\n // Build the context based on permissions\n extensionContext = buildContext(extensionId, extensionVersion, storagePath, permissions)\n\n // Import and activate the extension\n try {\n // The actual extension code should be bundled and available\n // This is called after the extension code has been loaded into the worker\n if (extensionModule?.activate) {\n const result = await extensionModule.activate(extensionContext)\n if (result && 'dispose' in result) {\n extensionDisposable = result\n }\n }\n } catch (error) {\n extensionContext.log.error('Failed to activate extension', {\n error: error instanceof Error ? error.message : String(error),\n })\n throw error\n }\n}\n\nasync function handleDeactivate(): Promise<void> {\n try {\n if (extensionModule?.deactivate) {\n await extensionModule.deactivate()\n }\n if (extensionDisposable) {\n extensionDisposable.dispose()\n }\n } catch (error) {\n console.error('Error during deactivation:', error)\n } finally {\n extensionModule = null\n extensionDisposable = null\n extensionContext = null\n registeredProviders.clear()\n registeredTools.clear()\n registeredActions.clear()\n settingsCallbacks.length = 0\n schedulerCallbacks.length = 0\n }\n}\n\nfunction handleSettingsChanged(key: string, value: unknown): void {\n for (const callback of settingsCallbacks) {\n try {\n callback(key, value)\n } catch (error) {\n console.error('Error in settings change callback:', error)\n }\n }\n}\n\nfunction handleSchedulerFire(payload: SchedulerFirePayload): void {\n // Set the userId in context if this is a user-scoped job\n if (extensionContext && payload.userId) {\n ;(extensionContext as { userId?: string }).userId = payload.userId\n }\n\n for (const callback of schedulerCallbacks) {\n try {\n callback(payload)\n } catch (error) {\n console.error('Error in scheduler callback:', error)\n }\n }\n\n // Reset userId after all callbacks\n if (extensionContext) {\n ;(extensionContext as { userId?: string }).userId = undefined\n }\n}\n\n// ============================================================================\n// Provider / Tool Requests\n// ============================================================================\n\nasync function handleProviderChatRequest(\n requestId: string,\n payload: { providerId: string; messages: ChatMessage[]; options: ChatOptions }\n): Promise<void> {\n const provider = registeredProviders.get(payload.providerId)\n if (!provider) {\n postMessage({\n type: 'request',\n id: generateMessageId(),\n method: 'network.fetch', // Dummy, we need a proper error response\n payload: { error: `Provider ${payload.providerId} not found` },\n })\n return\n }\n\n try {\n const generator = provider.chat(payload.messages, payload.options)\n let sawDone = false\n let sawError = false\n\n for await (const event of generator) {\n if (event.type === 'done') {\n sawDone = true\n } else if (event.type === 'error') {\n sawError = true\n }\n postMessage({\n type: 'stream-event',\n payload: { requestId, event },\n })\n }\n\n if (!sawDone && !sawError) {\n postMessage({\n type: 'stream-event',\n payload: {\n requestId,\n event: { type: 'done' },\n },\n })\n }\n } catch (error) {\n postMessage({\n type: 'stream-event',\n payload: {\n requestId,\n event: {\n type: 'error',\n message: error instanceof Error ? error.message : String(error),\n },\n },\n })\n }\n}\n\nasync function handleProviderModelsRequest(\n requestId: string,\n payload: { providerId: string; options?: GetModelsOptions }\n): Promise<void> {\n const provider = registeredProviders.get(payload.providerId)\n if (!provider) {\n // Send error response\n postMessage({\n type: 'provider-models-response',\n payload: {\n requestId,\n models: [],\n error: `Provider ${payload.providerId} not found`,\n },\n })\n return\n }\n\n try {\n // Pass options to getModels so provider can use settings (e.g., URL for Ollama)\n const models = await provider.getModels(payload.options)\n // Send response with models\n postMessage({\n type: 'provider-models-response',\n payload: {\n requestId,\n models,\n },\n })\n } catch (error) {\n postMessage({\n type: 'provider-models-response',\n payload: {\n requestId,\n models: [],\n error: error instanceof Error ? error.message : String(error),\n },\n })\n }\n}\n\nasync function handleToolExecuteRequest(\n requestId: string,\n payload: { toolId: string; params: Record<string, unknown>; userId?: string }\n): Promise<void> {\n const tool = registeredTools.get(payload.toolId)\n if (!tool) {\n // Send error response\n postMessage({\n type: 'tool-execute-response',\n payload: {\n requestId,\n result: { success: false, error: `Tool ${payload.toolId} not found` },\n error: `Tool ${payload.toolId} not found`,\n },\n })\n return\n }\n\n try {\n // Update the extension context with the userId if provided\n if (extensionContext && payload.userId) {\n // Create a new context with the userId for this execution\n ;(extensionContext as { userId?: string }).userId = payload.userId\n }\n\n const result = await tool.execute(payload.params)\n\n // Reset userId after execution\n if (extensionContext) {\n ;(extensionContext as { userId?: string }).userId = undefined\n }\n\n // Send response with result\n postMessage({\n type: 'tool-execute-response',\n payload: {\n requestId,\n result,\n },\n })\n } catch (error) {\n // Reset userId on error\n if (extensionContext) {\n ;(extensionContext as { userId?: string }).userId = undefined\n }\n\n const errorMessage = error instanceof Error ? error.message : String(error)\n postMessage({\n type: 'tool-execute-response',\n payload: {\n requestId,\n result: { success: false, error: errorMessage },\n error: errorMessage,\n },\n })\n }\n}\n\nasync function handleActionExecuteRequest(\n requestId: string,\n payload: { actionId: string; params: Record<string, unknown>; userId?: string }\n): Promise<void> {\n const action = registeredActions.get(payload.actionId)\n if (!action) {\n postMessage({\n type: 'action-execute-response',\n payload: {\n requestId,\n result: { success: false, error: `Action ${payload.actionId} not found` },\n error: `Action ${payload.actionId} not found`,\n },\n })\n return\n }\n\n try {\n // Update the extension context with the userId if provided\n if (extensionContext && payload.userId) {\n ;(extensionContext as { userId?: string }).userId = payload.userId\n }\n\n const result = await action.execute(payload.params)\n\n // Reset userId after execution\n if (extensionContext) {\n ;(extensionContext as { userId?: string }).userId = undefined\n }\n\n postMessage({\n type: 'action-execute-response',\n payload: {\n requestId,\n result,\n },\n })\n } catch (error) {\n // Reset userId on error\n if (extensionContext) {\n ;(extensionContext as { userId?: string }).userId = undefined\n }\n\n const errorMessage = error instanceof Error ? error.message : String(error)\n postMessage({\n type: 'action-execute-response',\n payload: {\n requestId,\n result: { success: false, error: errorMessage },\n error: errorMessage,\n },\n })\n }\n}\n\n// ============================================================================\n// Context Building\n// ============================================================================\n\nfunction buildContext(\n extensionId: string,\n extensionVersion: string,\n storagePath: string,\n permissions: string[]\n): ExtensionContext {\n const hasPermission = (perm: string): boolean => {\n return permissions.some((p) => {\n if (p === perm) return true\n if (p.endsWith(':*') && perm.startsWith(p.slice(0, -1))) return true\n return false\n })\n }\n\n const log: LogAPI = {\n debug: (message, data) => postMessage({ type: 'log', payload: { level: 'debug', message, data } }),\n info: (message, data) => postMessage({ type: 'log', payload: { level: 'info', message, data } }),\n warn: (message, data) => postMessage({ type: 'log', payload: { level: 'warn', message, data } }),\n error: (message, data) => postMessage({ type: 'log', payload: { level: 'error', message, data } }),\n }\n\n const context: ExtensionContext = {\n extension: {\n id: extensionId,\n version: extensionVersion,\n storagePath,\n },\n log,\n }\n\n // Add network API if permitted\n if (permissions.some((p) => p.startsWith('network:'))) {\n const networkApi: NetworkAPI = {\n async fetch(url: string, options?: RequestInit): Promise<Response> {\n const result = await sendRequest<{ status: number; statusText: string; headers: Record<string, string>; body: string }>('network.fetch', { url, options })\n return new Response(result.body, {\n status: result.status,\n statusText: result.statusText,\n headers: result.headers,\n })\n },\n\n async *fetchStream(url: string, options?: RequestInit): AsyncGenerator<string, void, unknown> {\n const requestId = generateMessageId()\n\n // Set up streaming request tracking\n const request: StreamingFetchRequest = {\n chunks: [],\n done: false,\n }\n streamingFetchRequests.set(requestId, request)\n\n // Send the streaming fetch request to the host\n postMessage({\n type: 'request',\n id: requestId,\n method: 'network.fetch-stream',\n payload: { url, options, requestId },\n })\n\n try {\n // Yield chunks as they arrive\n while (!request.done) {\n // Wait for new data\n await new Promise<void>((resolve) => {\n const resolver = () => {\n // Clear the stored resolver before resolving to avoid\n // stale callbacks being invoked for a new wait iteration.\n if (request.resolve === resolver) {\n request.resolve = undefined\n }\n resolve()\n }\n\n request.resolve = resolver\n\n // Check if we already have data or completion\n if (request.chunks.length > 0 || request.done) {\n resolver()\n }\n })\n\n // Check for errors first, before yielding any chunks\n if (request.error) {\n throw new Error(request.error)\n }\n\n // Yield all available chunks\n while (request.chunks.length > 0) {\n yield request.chunks.shift()!\n }\n }\n } finally {\n streamingFetchRequests.delete(requestId)\n }\n },\n }\n ;(context as { network: NetworkAPI }).network = networkApi\n }\n\n // Add settings API if permitted\n if (hasPermission('settings.register')) {\n const settingsApi: SettingsAPI = {\n async getAll<T extends Record<string, unknown>>(): Promise<T> {\n return sendRequest<T>('settings.getAll', {})\n },\n async get<T>(key: string): Promise<T | undefined> {\n return sendRequest<T | undefined>('settings.get', { key })\n },\n async set(key: string, value: unknown): Promise<void> {\n return sendRequest<void>('settings.set', { key, value })\n },\n onChange(callback: (key: string, value: unknown) => void): Disposable {\n settingsCallbacks.push(callback)\n return {\n dispose: () => {\n const index = settingsCallbacks.indexOf(callback)\n if (index >= 0) settingsCallbacks.splice(index, 1)\n },\n }\n },\n }\n ;(context as { settings: SettingsAPI }).settings = settingsApi\n }\n\n // Add providers API if permitted\n if (hasPermission('provider.register')) {\n const providersApi: ProvidersAPI = {\n register(provider: AIProvider): Disposable {\n registeredProviders.set(provider.id, provider)\n postMessage({\n type: 'provider-registered',\n payload: { id: provider.id, name: provider.name },\n })\n return {\n dispose: () => {\n registeredProviders.delete(provider.id)\n },\n }\n },\n }\n ;(context as { providers: ProvidersAPI }).providers = providersApi\n }\n\n // Add tools API if permitted\n if (hasPermission('tools.register')) {\n const toolsApi: ToolsAPI = {\n register(tool: Tool): Disposable {\n registeredTools.set(tool.id, tool)\n postMessage({\n type: 'tool-registered',\n payload: {\n id: tool.id,\n name: tool.name,\n description: tool.description,\n parameters: tool.parameters,\n },\n })\n return {\n dispose: () => {\n registeredTools.delete(tool.id)\n },\n }\n },\n }\n ;(context as { tools: ToolsAPI }).tools = toolsApi\n }\n\n // Add actions API if permitted\n if (hasPermission('actions.register')) {\n const actionsApi: ActionsAPI = {\n register(action: Action): Disposable {\n registeredActions.set(action.id, action)\n postMessage({\n type: 'action-registered',\n payload: {\n id: action.id,\n },\n })\n return {\n dispose: () => {\n registeredActions.delete(action.id)\n },\n }\n },\n }\n ;(context as { actions: ActionsAPI }).actions = actionsApi\n }\n\n // Add events API if permitted\n if (hasPermission('events.emit')) {\n const eventsApi: EventsAPI = {\n async emit(name: string, payload?: Record<string, unknown>): Promise<void> {\n await sendRequest<void>('events.emit', { name, payload })\n },\n }\n ;(context as { events: EventsAPI }).events = eventsApi\n }\n\n // Add scheduler API if permitted\n if (hasPermission('scheduler.register')) {\n const schedulerApi: SchedulerAPI = {\n async schedule(job: SchedulerJobRequest): Promise<void> {\n await sendRequest<void>('scheduler.schedule', { job })\n },\n async cancel(jobId: string): Promise<void> {\n await sendRequest<void>('scheduler.cancel', { jobId })\n },\n onFire(callback: (payload: SchedulerFirePayload) => void): Disposable {\n schedulerCallbacks.push(callback)\n return {\n dispose: () => {\n const index = schedulerCallbacks.indexOf(callback)\n if (index >= 0) schedulerCallbacks.splice(index, 1)\n },\n }\n },\n }\n ;(context as { scheduler: SchedulerAPI }).scheduler = schedulerApi\n }\n\n // Add user profile API if permitted\n if (hasPermission('user.profile.read')) {\n const userApi: UserAPI = {\n async getProfile(): Promise<UserProfile> {\n return sendRequest<UserProfile>('user.getProfile', {})\n },\n }\n ;(context as { user: UserAPI }).user = userApi\n }\n\n // Add chat API if permitted\n if (hasPermission('chat.message.write')) {\n const chatApi: ChatAPI = {\n async appendInstruction(message: ChatInstructionMessage): Promise<void> {\n await sendRequest<void>('chat.appendInstruction', message)\n },\n }\n ;(context as { chat: ChatAPI }).chat = chatApi\n }\n\n // Add database API if permitted\n if (hasPermission('database.own')) {\n const databaseApi: DatabaseAPI = {\n async execute<T = unknown>(sql: string, params?: unknown[]): Promise<T[]> {\n return sendRequest<T[]>('database.execute', { sql, params })\n },\n }\n ;(context as { database: DatabaseAPI }).database = databaseApi\n }\n\n // Add storage API if permitted\n if (hasPermission('storage.local')) {\n const storageApi: StorageAPI = {\n // Global/extension-scoped storage\n async get<T>(key: string): Promise<T | undefined> {\n return sendRequest<T | undefined>('storage.get', { key })\n },\n async set(key: string, value: unknown): Promise<void> {\n return sendRequest<void>('storage.set', { key, value })\n },\n async delete(key: string): Promise<void> {\n return sendRequest<void>('storage.delete', { key })\n },\n async keys(): Promise<string[]> {\n return sendRequest<string[]>('storage.keys', {})\n },\n // User-scoped storage\n async getForUser<T>(userId: string, key: string): Promise<T | undefined> {\n return sendRequest<T | undefined>('storage.getForUser', { userId, key })\n },\n async setForUser(userId: string, key: string, value: unknown): Promise<void> {\n return sendRequest<void>('storage.setForUser', { userId, key, value })\n },\n async deleteForUser(userId: string, key: string): Promise<void> {\n return sendRequest<void>('storage.deleteForUser', { userId, key })\n },\n async keysForUser(userId: string): Promise<string[]> {\n return sendRequest<string[]>('storage.keysForUser', { userId })\n },\n }\n ;(context as { storage: StorageAPI }).storage = storageApi\n }\n\n return context\n}\n\n// ============================================================================\n// Initialization\n// ============================================================================\n\n/**\n * Initialize the extension runtime\n * This should be called by the extension's entry point\n */\nexport function initializeExtension(module: ExtensionModule): void {\n extensionModule = module\n\n // Set up message listener using the appropriate message port\n messagePort.onMessage(async (message: HostToWorkerMessage) => {\n try {\n await handleHostMessage(message)\n } catch (error) {\n console.error('Error handling message:', error)\n }\n })\n\n // Signal that we're ready\n postMessage({ type: 'ready' })\n}\n\n// Re-export types for extensions to use\nexport type {\n ExtensionContext,\n ExtensionModule,\n Disposable,\n AIProvider,\n Tool,\n ToolDefinition,\n ToolResult,\n ToolCall,\n Action,\n ActionResult,\n ModelInfo,\n ChatMessage,\n ChatOptions,\n GetModelsOptions,\n StreamEvent,\n} from './types.js'\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/runtime.ts"],"sourcesContent":["/**\n * Extension Runtime - Runs inside the worker\n *\n * This module handles communication with the Extension Host and provides\n * the ExtensionContext to the extension's activate function.\n */\n\nimport type {\n ExtensionContext,\n ExtensionModule,\n Disposable,\n NetworkAPI,\n SettingsAPI,\n ProvidersAPI,\n ToolsAPI,\n ActionsAPI,\n EventsAPI,\n SchedulerAPI,\n SchedulerJobRequest,\n SchedulerFirePayload,\n UserAPI,\n UserProfile,\n ChatAPI,\n ChatInstructionMessage,\n DatabaseAPI,\n StorageAPI,\n LogAPI,\n AIProvider,\n Tool,\n Action,\n ChatMessage,\n ChatOptions,\n GetModelsOptions,\n} from './types.js'\n\nimport type {\n HostToWorkerMessage,\n WorkerToHostMessage,\n RequestMessage,\n PendingRequest,\n} from './messages.js'\n\nimport { generateMessageId } from './messages.js'\n\n// ============================================================================\n// Environment Detection and Message Port\n// ============================================================================\n\n/**\n * Detect if we're in Node.js Worker Thread or Web Worker\n * and get the appropriate message port\n */\ninterface MessagePort {\n postMessage(message: WorkerToHostMessage): void\n onMessage(handler: (message: HostToWorkerMessage) => void): void\n}\n\nfunction getMessagePort(): MessagePort {\n // Check if we're in Node.js Worker Thread\n if (typeof process !== 'undefined' && process.versions?.node) {\n // Node.js Worker Thread - import parentPort dynamically\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const { parentPort } = require('node:worker_threads')\n return {\n postMessage: (message) => parentPort?.postMessage(message),\n onMessage: (handler) => parentPort?.on('message', handler),\n }\n }\n\n // Web Worker - use self\n return {\n postMessage: (message) => self.postMessage(message),\n onMessage: (handler) => {\n self.addEventListener('message', (event: MessageEvent<HostToWorkerMessage>) => {\n handler(event.data)\n })\n },\n }\n}\n\nconst messagePort = getMessagePort()\n\n// ============================================================================\n// Global State\n// ============================================================================\n\nlet extensionModule: ExtensionModule | null = null\nlet extensionDisposable: Disposable | null = null\nlet extensionContext: ExtensionContext | null = null\n\nconst pendingRequests = new Map<string, PendingRequest>()\nconst registeredProviders = new Map<string, AIProvider>()\nconst registeredTools = new Map<string, Tool>()\nconst registeredActions = new Map<string, Action>()\nconst settingsCallbacks: Array<(key: string, value: unknown) => void> = []\nconst schedulerCallbacks: Array<(payload: SchedulerFirePayload) => void | Promise<void>> = []\n\n/**\n * Tracking for streaming fetch requests.\n * Each request stores incoming chunks and signals when new data arrives.\n */\ninterface StreamingFetchRequest {\n chunks: string[]\n done: boolean\n error?: string\n resolve?: () => void\n}\nconst streamingFetchRequests = new Map<string, StreamingFetchRequest>()\n\nconst REQUEST_TIMEOUT = 30000 // 30 seconds\n\n// ============================================================================\n// Message Handling\n// ============================================================================\n\n/**\n * Send a message to the host\n */\nfunction postMessage(message: WorkerToHostMessage): void {\n messagePort.postMessage(message)\n}\n\n/**\n * Send a request to the host and wait for response\n */\nasync function sendRequest<T>(method: RequestMessage['method'], payload: unknown): Promise<T> {\n const id = generateMessageId()\n\n return new Promise<T>((resolve, reject) => {\n const timeout = setTimeout(() => {\n pendingRequests.delete(id)\n reject(new Error(`Request ${method} timed out`))\n }, REQUEST_TIMEOUT)\n\n pendingRequests.set(id, { resolve: resolve as (value: unknown) => void, reject, timeout })\n\n postMessage({\n type: 'request',\n id,\n method,\n payload,\n })\n })\n}\n\n/**\n * Handle messages from the host\n */\nasync function handleHostMessage(message: HostToWorkerMessage): Promise<void> {\n switch (message.type) {\n case 'activate':\n await handleActivate(message.payload)\n break\n\n case 'deactivate':\n await handleDeactivate()\n break\n\n case 'settings-changed':\n handleSettingsChanged(message.payload.key, message.payload.value)\n break\n\n case 'scheduler-fire':\n await handleSchedulerFire(message.payload)\n break\n\n case 'provider-chat-request':\n await handleProviderChatRequest(message.id, message.payload)\n break\n\n case 'provider-models-request':\n await handleProviderModelsRequest(message.id, message.payload)\n break\n\n case 'tool-execute-request':\n await handleToolExecuteRequest(message.id, message.payload)\n break\n\n case 'action-execute-request':\n await handleActionExecuteRequest(message.id, message.payload)\n break\n\n case 'response':\n handleResponse(message.payload)\n break\n\n case 'streaming-fetch-chunk':\n handleStreamingFetchChunk(message.payload)\n break\n }\n}\n\n/**\n * Handle incoming streaming fetch chunks from the host\n */\nfunction handleStreamingFetchChunk(payload: {\n requestId: string\n chunk: string\n done: boolean\n error?: string\n}): void {\n const request = streamingFetchRequests.get(payload.requestId)\n if (!request) return\n\n if (payload.error) {\n request.error = payload.error\n request.done = true\n } else if (payload.chunk) {\n request.chunks.push(payload.chunk)\n }\n\n if (payload.done) {\n request.done = true\n }\n\n // Signal that new data is available\n if (request.resolve) {\n const resolve = request.resolve\n request.resolve = undefined\n resolve()\n }\n\n // Send acknowledgment for backpressure control\n postMessage({\n type: 'streaming-fetch-ack',\n payload: { requestId: payload.requestId },\n })\n}\n\nfunction handleResponse(payload: { requestId: string; success: boolean; data?: unknown; error?: string }): void {\n const pending = pendingRequests.get(payload.requestId)\n if (!pending) return\n\n clearTimeout(pending.timeout)\n pendingRequests.delete(payload.requestId)\n\n if (payload.success) {\n pending.resolve(payload.data)\n } else {\n pending.reject(new Error(payload.error || 'Unknown error'))\n }\n}\n\n// ============================================================================\n// Activation / Deactivation\n// ============================================================================\n\nasync function handleActivate(payload: {\n extensionId: string\n extensionVersion: string\n storagePath: string\n permissions: string[]\n settings: Record<string, unknown>\n}): Promise<void> {\n const { extensionId, extensionVersion, storagePath, permissions } = payload\n\n // Build the context based on permissions\n extensionContext = buildContext(extensionId, extensionVersion, storagePath, permissions)\n\n // Import and activate the extension\n try {\n // The actual extension code should be bundled and available\n // This is called after the extension code has been loaded into the worker\n if (extensionModule?.activate) {\n const result = await extensionModule.activate(extensionContext)\n if (result && 'dispose' in result) {\n extensionDisposable = result\n }\n }\n } catch (error) {\n extensionContext.log.error('Failed to activate extension', {\n error: error instanceof Error ? error.message : String(error),\n })\n throw error\n }\n}\n\nasync function handleDeactivate(): Promise<void> {\n try {\n if (extensionModule?.deactivate) {\n await extensionModule.deactivate()\n }\n if (extensionDisposable) {\n extensionDisposable.dispose()\n }\n } catch (error) {\n console.error('Error during deactivation:', error)\n } finally {\n extensionModule = null\n extensionDisposable = null\n extensionContext = null\n registeredProviders.clear()\n registeredTools.clear()\n registeredActions.clear()\n settingsCallbacks.length = 0\n schedulerCallbacks.length = 0\n }\n}\n\nfunction handleSettingsChanged(key: string, value: unknown): void {\n for (const callback of settingsCallbacks) {\n try {\n callback(key, value)\n } catch (error) {\n console.error('Error in settings change callback:', error)\n }\n }\n}\n\nasync function handleSchedulerFire(payload: SchedulerFirePayload): Promise<void> {\n // Set the userId in context if this is a user-scoped job\n if (extensionContext && payload.userId) {\n ;(extensionContext as { userId?: string }).userId = payload.userId\n }\n\n // Run callbacks concurrently to avoid blocking\n const results = await Promise.allSettled(\n schedulerCallbacks.map((callback) => callback(payload)),\n )\n\n // Log any errors\n results.forEach((result, index) => {\n if (result.status === 'rejected') {\n console.error(`Error in scheduler callback ${index}:`, result.reason)\n }\n })\n\n // Reset userId after all callbacks have completed\n if (extensionContext) {\n ;(extensionContext as { userId?: string }).userId = undefined\n }\n}\n\n// ============================================================================\n// Provider / Tool Requests\n// ============================================================================\n\nasync function handleProviderChatRequest(\n requestId: string,\n payload: { providerId: string; messages: ChatMessage[]; options: ChatOptions }\n): Promise<void> {\n const provider = registeredProviders.get(payload.providerId)\n if (!provider) {\n postMessage({\n type: 'request',\n id: generateMessageId(),\n method: 'network.fetch', // Dummy, we need a proper error response\n payload: { error: `Provider ${payload.providerId} not found` },\n })\n return\n }\n\n try {\n const generator = provider.chat(payload.messages, payload.options)\n let sawDone = false\n let sawError = false\n\n for await (const event of generator) {\n if (event.type === 'done') {\n sawDone = true\n } else if (event.type === 'error') {\n sawError = true\n }\n postMessage({\n type: 'stream-event',\n payload: { requestId, event },\n })\n }\n\n if (!sawDone && !sawError) {\n postMessage({\n type: 'stream-event',\n payload: {\n requestId,\n event: { type: 'done' },\n },\n })\n }\n } catch (error) {\n postMessage({\n type: 'stream-event',\n payload: {\n requestId,\n event: {\n type: 'error',\n message: error instanceof Error ? error.message : String(error),\n },\n },\n })\n }\n}\n\nasync function handleProviderModelsRequest(\n requestId: string,\n payload: { providerId: string; options?: GetModelsOptions }\n): Promise<void> {\n const provider = registeredProviders.get(payload.providerId)\n if (!provider) {\n // Send error response\n postMessage({\n type: 'provider-models-response',\n payload: {\n requestId,\n models: [],\n error: `Provider ${payload.providerId} not found`,\n },\n })\n return\n }\n\n try {\n // Pass options to getModels so provider can use settings (e.g., URL for Ollama)\n const models = await provider.getModels(payload.options)\n // Send response with models\n postMessage({\n type: 'provider-models-response',\n payload: {\n requestId,\n models,\n },\n })\n } catch (error) {\n postMessage({\n type: 'provider-models-response',\n payload: {\n requestId,\n models: [],\n error: error instanceof Error ? error.message : String(error),\n },\n })\n }\n}\n\nasync function handleToolExecuteRequest(\n requestId: string,\n payload: { toolId: string; params: Record<string, unknown>; userId?: string }\n): Promise<void> {\n const tool = registeredTools.get(payload.toolId)\n if (!tool) {\n // Send error response\n postMessage({\n type: 'tool-execute-response',\n payload: {\n requestId,\n result: { success: false, error: `Tool ${payload.toolId} not found` },\n error: `Tool ${payload.toolId} not found`,\n },\n })\n return\n }\n\n try {\n // Update the extension context with the userId if provided\n if (extensionContext && payload.userId) {\n // Create a new context with the userId for this execution\n ;(extensionContext as { userId?: string }).userId = payload.userId\n }\n\n const result = await tool.execute(payload.params)\n\n // Reset userId after execution\n if (extensionContext) {\n ;(extensionContext as { userId?: string }).userId = undefined\n }\n\n // Send response with result\n postMessage({\n type: 'tool-execute-response',\n payload: {\n requestId,\n result,\n },\n })\n } catch (error) {\n // Reset userId on error\n if (extensionContext) {\n ;(extensionContext as { userId?: string }).userId = undefined\n }\n\n const errorMessage = error instanceof Error ? error.message : String(error)\n postMessage({\n type: 'tool-execute-response',\n payload: {\n requestId,\n result: { success: false, error: errorMessage },\n error: errorMessage,\n },\n })\n }\n}\n\nasync function handleActionExecuteRequest(\n requestId: string,\n payload: { actionId: string; params: Record<string, unknown>; userId?: string }\n): Promise<void> {\n const action = registeredActions.get(payload.actionId)\n if (!action) {\n postMessage({\n type: 'action-execute-response',\n payload: {\n requestId,\n result: { success: false, error: `Action ${payload.actionId} not found` },\n error: `Action ${payload.actionId} not found`,\n },\n })\n return\n }\n\n try {\n // Update the extension context with the userId if provided\n if (extensionContext && payload.userId) {\n ;(extensionContext as { userId?: string }).userId = payload.userId\n }\n\n const result = await action.execute(payload.params)\n\n // Reset userId after execution\n if (extensionContext) {\n ;(extensionContext as { userId?: string }).userId = undefined\n }\n\n postMessage({\n type: 'action-execute-response',\n payload: {\n requestId,\n result,\n },\n })\n } catch (error) {\n // Reset userId on error\n if (extensionContext) {\n ;(extensionContext as { userId?: string }).userId = undefined\n }\n\n const errorMessage = error instanceof Error ? error.message : String(error)\n postMessage({\n type: 'action-execute-response',\n payload: {\n requestId,\n result: { success: false, error: errorMessage },\n error: errorMessage,\n },\n })\n }\n}\n\n// ============================================================================\n// Context Building\n// ============================================================================\n\nfunction buildContext(\n extensionId: string,\n extensionVersion: string,\n storagePath: string,\n permissions: string[]\n): ExtensionContext {\n const hasPermission = (perm: string): boolean => {\n return permissions.some((p) => {\n if (p === perm) return true\n if (p.endsWith(':*') && perm.startsWith(p.slice(0, -1))) return true\n return false\n })\n }\n\n const log: LogAPI = {\n debug: (message, data) => postMessage({ type: 'log', payload: { level: 'debug', message, data } }),\n info: (message, data) => postMessage({ type: 'log', payload: { level: 'info', message, data } }),\n warn: (message, data) => postMessage({ type: 'log', payload: { level: 'warn', message, data } }),\n error: (message, data) => postMessage({ type: 'log', payload: { level: 'error', message, data } }),\n }\n\n const context: ExtensionContext = {\n extension: {\n id: extensionId,\n version: extensionVersion,\n storagePath,\n },\n log,\n }\n\n // Add network API if permitted\n if (permissions.some((p) => p.startsWith('network:'))) {\n const networkApi: NetworkAPI = {\n async fetch(url: string, options?: RequestInit): Promise<Response> {\n const result = await sendRequest<{ status: number; statusText: string; headers: Record<string, string>; body: string }>('network.fetch', { url, options })\n return new Response(result.body, {\n status: result.status,\n statusText: result.statusText,\n headers: result.headers,\n })\n },\n\n async *fetchStream(url: string, options?: RequestInit): AsyncGenerator<string, void, unknown> {\n const requestId = generateMessageId()\n\n // Set up streaming request tracking\n const request: StreamingFetchRequest = {\n chunks: [],\n done: false,\n }\n streamingFetchRequests.set(requestId, request)\n\n // Send the streaming fetch request to the host\n postMessage({\n type: 'request',\n id: requestId,\n method: 'network.fetch-stream',\n payload: { url, options, requestId },\n })\n\n try {\n // Yield chunks as they arrive\n while (!request.done) {\n // Wait for new data\n await new Promise<void>((resolve) => {\n const resolver = () => {\n // Clear the stored resolver before resolving to avoid\n // stale callbacks being invoked for a new wait iteration.\n if (request.resolve === resolver) {\n request.resolve = undefined\n }\n resolve()\n }\n\n request.resolve = resolver\n\n // Check if we already have data or completion\n if (request.chunks.length > 0 || request.done) {\n resolver()\n }\n })\n\n // Check for errors first, before yielding any chunks\n if (request.error) {\n throw new Error(request.error)\n }\n\n // Yield all available chunks\n while (request.chunks.length > 0) {\n yield request.chunks.shift()!\n }\n }\n } finally {\n streamingFetchRequests.delete(requestId)\n }\n },\n }\n ;(context as { network: NetworkAPI }).network = networkApi\n }\n\n // Add settings API if permitted\n if (hasPermission('settings.register')) {\n const settingsApi: SettingsAPI = {\n async getAll<T extends Record<string, unknown>>(): Promise<T> {\n return sendRequest<T>('settings.getAll', {})\n },\n async get<T>(key: string): Promise<T | undefined> {\n return sendRequest<T | undefined>('settings.get', { key })\n },\n async set(key: string, value: unknown): Promise<void> {\n return sendRequest<void>('settings.set', { key, value })\n },\n onChange(callback: (key: string, value: unknown) => void): Disposable {\n settingsCallbacks.push(callback)\n return {\n dispose: () => {\n const index = settingsCallbacks.indexOf(callback)\n if (index >= 0) settingsCallbacks.splice(index, 1)\n },\n }\n },\n }\n ;(context as { settings: SettingsAPI }).settings = settingsApi\n }\n\n // Add providers API if permitted\n if (hasPermission('provider.register')) {\n const providersApi: ProvidersAPI = {\n register(provider: AIProvider): Disposable {\n registeredProviders.set(provider.id, provider)\n postMessage({\n type: 'provider-registered',\n payload: { id: provider.id, name: provider.name },\n })\n return {\n dispose: () => {\n registeredProviders.delete(provider.id)\n },\n }\n },\n }\n ;(context as { providers: ProvidersAPI }).providers = providersApi\n }\n\n // Add tools API if permitted\n if (hasPermission('tools.register')) {\n const toolsApi: ToolsAPI = {\n register(tool: Tool): Disposable {\n registeredTools.set(tool.id, tool)\n postMessage({\n type: 'tool-registered',\n payload: {\n id: tool.id,\n name: tool.name,\n description: tool.description,\n parameters: tool.parameters,\n },\n })\n return {\n dispose: () => {\n registeredTools.delete(tool.id)\n },\n }\n },\n }\n ;(context as { tools: ToolsAPI }).tools = toolsApi\n }\n\n // Add actions API if permitted\n if (hasPermission('actions.register')) {\n const actionsApi: ActionsAPI = {\n register(action: Action): Disposable {\n registeredActions.set(action.id, action)\n postMessage({\n type: 'action-registered',\n payload: {\n id: action.id,\n },\n })\n return {\n dispose: () => {\n registeredActions.delete(action.id)\n },\n }\n },\n }\n ;(context as { actions: ActionsAPI }).actions = actionsApi\n }\n\n // Add events API if permitted\n if (hasPermission('events.emit')) {\n const eventsApi: EventsAPI = {\n async emit(name: string, payload?: Record<string, unknown>): Promise<void> {\n await sendRequest<void>('events.emit', { name, payload })\n },\n }\n ;(context as { events: EventsAPI }).events = eventsApi\n }\n\n // Add scheduler API if permitted\n if (hasPermission('scheduler.register')) {\n const schedulerApi: SchedulerAPI = {\n async schedule(job: SchedulerJobRequest): Promise<void> {\n // Automatically include userId from context if not explicitly set\n const userId = (extensionContext as { userId?: string }).userId\n const jobWithUser = userId && !job.userId ? { ...job, userId } : job\n await sendRequest<void>('scheduler.schedule', { job: jobWithUser })\n },\n async cancel(jobId: string): Promise<void> {\n await sendRequest<void>('scheduler.cancel', { jobId })\n },\n onFire(callback: (payload: SchedulerFirePayload) => void): Disposable {\n schedulerCallbacks.push(callback)\n return {\n dispose: () => {\n const index = schedulerCallbacks.indexOf(callback)\n if (index >= 0) schedulerCallbacks.splice(index, 1)\n },\n }\n },\n }\n ;(context as { scheduler: SchedulerAPI }).scheduler = schedulerApi\n }\n\n // Add user profile API if permitted\n if (hasPermission('user.profile.read')) {\n const userApi: UserAPI = {\n async getProfile(): Promise<UserProfile> {\n return sendRequest<UserProfile>('user.getProfile', {})\n },\n }\n ;(context as { user: UserAPI }).user = userApi\n }\n\n // Add chat API if permitted\n if (hasPermission('chat.message.write')) {\n const chatApi: ChatAPI = {\n async appendInstruction(message: ChatInstructionMessage): Promise<void> {\n const contextUserId = (extensionContext as { userId?: string }).userId\n await sendRequest<void>('chat.appendInstruction', {\n ...message,\n userId: message.userId ?? contextUserId,\n })\n },\n }\n ;(context as { chat: ChatAPI }).chat = chatApi\n }\n\n // Add database API if permitted\n if (hasPermission('database.own')) {\n const databaseApi: DatabaseAPI = {\n async execute<T = unknown>(sql: string, params?: unknown[]): Promise<T[]> {\n return sendRequest<T[]>('database.execute', { sql, params })\n },\n }\n ;(context as { database: DatabaseAPI }).database = databaseApi\n }\n\n // Add storage API if permitted\n if (hasPermission('storage.local')) {\n const storageApi: StorageAPI = {\n // Global/extension-scoped storage\n async get<T>(key: string): Promise<T | undefined> {\n return sendRequest<T | undefined>('storage.get', { key })\n },\n async set(key: string, value: unknown): Promise<void> {\n return sendRequest<void>('storage.set', { key, value })\n },\n async delete(key: string): Promise<void> {\n return sendRequest<void>('storage.delete', { key })\n },\n async keys(): Promise<string[]> {\n return sendRequest<string[]>('storage.keys', {})\n },\n // User-scoped storage\n async getForUser<T>(userId: string, key: string): Promise<T | undefined> {\n return sendRequest<T | undefined>('storage.getForUser', { userId, key })\n },\n async setForUser(userId: string, key: string, value: unknown): Promise<void> {\n return sendRequest<void>('storage.setForUser', { userId, key, value })\n },\n async deleteForUser(userId: string, key: string): Promise<void> {\n return sendRequest<void>('storage.deleteForUser', { userId, key })\n },\n async keysForUser(userId: string): Promise<string[]> {\n return sendRequest<string[]>('storage.keysForUser', { userId })\n },\n }\n ;(context as { storage: StorageAPI }).storage = storageApi\n }\n\n return context\n}\n\n// ============================================================================\n// Initialization\n// ============================================================================\n\n/**\n * Initialize the extension runtime\n * This should be called by the extension's entry point\n */\nexport function initializeExtension(module: ExtensionModule): void {\n extensionModule = module\n\n // Set up message listener using the appropriate message port\n messagePort.onMessage(async (message: HostToWorkerMessage) => {\n try {\n await handleHostMessage(message)\n } catch (error) {\n console.error('Error handling message:', error)\n }\n })\n\n // Signal that we're ready\n postMessage({ type: 'ready' })\n}\n\n// Re-export types for extensions to use\nexport type {\n ExtensionContext,\n ExtensionModule,\n Disposable,\n AIProvider,\n Tool,\n ToolDefinition,\n ToolResult,\n ToolCall,\n Action,\n ActionResult,\n ModelInfo,\n ChatMessage,\n ChatOptions,\n GetModelsOptions,\n StreamEvent,\n} from './types.js'\n"],"mappings":"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","names":[]}
@@ -915,7 +915,7 @@ interface SchedulerFirePayload {
915
915
  interface SchedulerAPI {
916
916
  schedule(job: SchedulerJobRequest): Promise<void>;
917
917
  cancel(jobId: string): Promise<void>;
918
- onFire(callback: (payload: SchedulerFirePayload) => void): Disposable;
918
+ onFire(callback: (payload: SchedulerFirePayload) => void | Promise<void>): Disposable;
919
919
  }
920
920
  /**
921
921
  * User profile data
@@ -938,6 +938,7 @@ interface UserAPI {
938
938
  interface ChatInstructionMessage {
939
939
  text: string;
940
940
  conversationId?: string;
941
+ userId?: string;
941
942
  }
942
943
  /**
943
944
  * Chat API for appending instructions
@@ -915,7 +915,7 @@ interface SchedulerFirePayload {
915
915
  interface SchedulerAPI {
916
916
  schedule(job: SchedulerJobRequest): Promise<void>;
917
917
  cancel(jobId: string): Promise<void>;
918
- onFire(callback: (payload: SchedulerFirePayload) => void): Disposable;
918
+ onFire(callback: (payload: SchedulerFirePayload) => void | Promise<void>): Disposable;
919
919
  }
920
920
  /**
921
921
  * User profile data
@@ -938,6 +938,7 @@ interface UserAPI {
938
938
  interface ChatInstructionMessage {
939
939
  text: string;
940
940
  conversationId?: string;
941
+ userId?: string;
941
942
  }
942
943
  /**
943
944
  * Chat API for appending instructions
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stina/extension-api",
3
- "version": "0.18.0",
3
+ "version": "0.19.1",
4
4
  "private": false,
5
5
  "repository": {
6
6
  "type": "git",
package/src/runtime.ts CHANGED
@@ -93,7 +93,7 @@ const registeredProviders = new Map<string, AIProvider>()
93
93
  const registeredTools = new Map<string, Tool>()
94
94
  const registeredActions = new Map<string, Action>()
95
95
  const settingsCallbacks: Array<(key: string, value: unknown) => void> = []
96
- const schedulerCallbacks: Array<(payload: SchedulerFirePayload) => void> = []
96
+ const schedulerCallbacks: Array<(payload: SchedulerFirePayload) => void | Promise<void>> = []
97
97
 
98
98
  /**
99
99
  * Tracking for streaming fetch requests.
@@ -161,7 +161,7 @@ async function handleHostMessage(message: HostToWorkerMessage): Promise<void> {
161
161
  break
162
162
 
163
163
  case 'scheduler-fire':
164
- handleSchedulerFire(message.payload)
164
+ await handleSchedulerFire(message.payload)
165
165
  break
166
166
 
167
167
  case 'provider-chat-request':
@@ -307,21 +307,25 @@ function handleSettingsChanged(key: string, value: unknown): void {
307
307
  }
308
308
  }
309
309
 
310
- function handleSchedulerFire(payload: SchedulerFirePayload): void {
310
+ async function handleSchedulerFire(payload: SchedulerFirePayload): Promise<void> {
311
311
  // Set the userId in context if this is a user-scoped job
312
312
  if (extensionContext && payload.userId) {
313
313
  ;(extensionContext as { userId?: string }).userId = payload.userId
314
314
  }
315
315
 
316
- for (const callback of schedulerCallbacks) {
317
- try {
318
- callback(payload)
319
- } catch (error) {
320
- console.error('Error in scheduler callback:', error)
316
+ // Run callbacks concurrently to avoid blocking
317
+ const results = await Promise.allSettled(
318
+ schedulerCallbacks.map((callback) => callback(payload)),
319
+ )
320
+
321
+ // Log any errors
322
+ results.forEach((result, index) => {
323
+ if (result.status === 'rejected') {
324
+ console.error(`Error in scheduler callback ${index}:`, result.reason)
321
325
  }
322
- }
326
+ })
323
327
 
324
- // Reset userId after all callbacks
328
+ // Reset userId after all callbacks have completed
325
329
  if (extensionContext) {
326
330
  ;(extensionContext as { userId?: string }).userId = undefined
327
331
  }
@@ -747,7 +751,10 @@ function buildContext(
747
751
  if (hasPermission('scheduler.register')) {
748
752
  const schedulerApi: SchedulerAPI = {
749
753
  async schedule(job: SchedulerJobRequest): Promise<void> {
750
- await sendRequest<void>('scheduler.schedule', { job })
754
+ // Automatically include userId from context if not explicitly set
755
+ const userId = (extensionContext as { userId?: string }).userId
756
+ const jobWithUser = userId && !job.userId ? { ...job, userId } : job
757
+ await sendRequest<void>('scheduler.schedule', { job: jobWithUser })
751
758
  },
752
759
  async cancel(jobId: string): Promise<void> {
753
760
  await sendRequest<void>('scheduler.cancel', { jobId })
@@ -779,7 +786,11 @@ function buildContext(
779
786
  if (hasPermission('chat.message.write')) {
780
787
  const chatApi: ChatAPI = {
781
788
  async appendInstruction(message: ChatInstructionMessage): Promise<void> {
782
- await sendRequest<void>('chat.appendInstruction', message)
789
+ const contextUserId = (extensionContext as { userId?: string }).userId
790
+ await sendRequest<void>('chat.appendInstruction', {
791
+ ...message,
792
+ userId: message.userId ?? contextUserId,
793
+ })
783
794
  },
784
795
  }
785
796
  ;(context as { chat: ChatAPI }).chat = chatApi
package/src/types.ts CHANGED
@@ -717,7 +717,7 @@ export interface SchedulerFirePayload {
717
717
  export interface SchedulerAPI {
718
718
  schedule(job: SchedulerJobRequest): Promise<void>
719
719
  cancel(jobId: string): Promise<void>
720
- onFire(callback: (payload: SchedulerFirePayload) => void): Disposable
720
+ onFire(callback: (payload: SchedulerFirePayload) => void | Promise<void>): Disposable
721
721
  }
722
722
 
723
723
  /**
@@ -743,6 +743,7 @@ export interface UserAPI {
743
743
  export interface ChatInstructionMessage {
744
744
  text: string
745
745
  conversationId?: string
746
+ userId?: string
746
747
  }
747
748
 
748
749
  /**