diff --git a/package.json b/package.json index 8b985da13..cd6f763af 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ "smol-toml": "^1.6.0", "taze": "^19.9.2", "tinyexec": "^1.0.2", - "tsdown": "^0.17.4", + "tsdown": "catalog:", "tsx": "^4.21.0", "turbo": "^2.7.5", "typescript": "~5.9.3", diff --git a/packages/plugin-sdk/README.md b/packages/plugin-sdk/README.md new file mode 100644 index 000000000..1800c99d0 --- /dev/null +++ b/packages/plugin-sdk/README.md @@ -0,0 +1,3 @@ +# @proj-airi/plugin-sdk + +Runtime-agnostic SDK for AIRI plugins. diff --git a/packages/plugin-sdk/package.json b/packages/plugin-sdk/package.json index 6bdd6cf3f..c765319d8 100644 --- a/packages/plugin-sdk/package.json +++ b/packages/plugin-sdk/package.json @@ -31,5 +31,8 @@ "dev": "pnpm run build", "build": "tsdown", "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@moeru/eventa": "catalog:" } } diff --git a/packages/plugin-sdk/tsdown.config.ts b/packages/plugin-sdk/tsdown.config.ts new file mode 100644 index 000000000..5d4db3d25 --- /dev/null +++ b/packages/plugin-sdk/tsdown.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from 'tsdown' + +export default defineConfig({ + entry: [ + 'src/index.ts', + ], + dts: true, + format: 'esm', +}) diff --git a/packages/server-shared/package.json b/packages/server-shared/package.json index 18faded89..e304d076c 100644 --- a/packages/server-shared/package.json +++ b/packages/server-shared/package.json @@ -33,7 +33,7 @@ "typecheck": "tsc --noEmit" }, "dependencies": { - "@xsai/shared": "catalog:", + "@moeru/eventa": "catalog:", "@xsai/shared-chat": "catalog:" } } diff --git a/packages/server-shared/src/types/websocket/events.ts b/packages/server-shared/src/types/websocket/events.ts index 5411b3a93..ecb3dea17 100644 --- a/packages/server-shared/src/types/websocket/events.ts +++ b/packages/server-shared/src/types/websocket/events.ts @@ -1,5 +1,7 @@ import type { AssistantMessage, CommonContentPart, Message, ToolMessage, UserMessage } from '@xsai/shared-chat' +import { defineEventa } from '@moeru/eventa' + export interface DiscordGuildMember { nickname: string displayName: string @@ -13,22 +15,38 @@ export interface Discord { channelId?: string } -export interface MetadataEventSource { +export interface PluginIdentity { /** - * Stable module/plugin identifier (shared across instances). + * Stable plugin identifier (shared across instances). * Example: "telegram-bot", "stage-tamagotchi". */ - plugin: string + id: string /** - * Unique instance id for this module run (per process/deployment). - * Example: "telegram-01", "stage-ui-2f7c9". - */ - instanceId: string - /** - * Optional semantic version for the module/plugin. + * Optional semantic version for the plugin. * Example: "0.8.1-beta.7". */ version?: string + /** + * Optional labels attached to the plugin manifest. + * Example: { env: "prod", app: "telegram", devtools: "true" }. + */ + labels?: Record +} + +export interface ModuleIdentity { + /** + * Unique module instance id for this module run (per process/deployment). + * Example: "telegram-01", "stage-ui-2f7c9". + */ + id: string + /** + * Module identity kind. For now only plugin-backed modules are supported. + */ + kind: 'plugin' + /** + * Plugin identity associated with this module instance. + */ + plugin: PluginIdentity /** * K8s-style labels for routing and policy selectors. * Example: { env: "prod", app: "telegram", devtools: "true" }. @@ -36,6 +54,295 @@ export interface MetadataEventSource { labels?: Record } +export type MetadataEventSource = ModuleIdentity + +/** + * Static schema metadata for module configuration. + * This is transport-friendly and can be paired with a JSON Schema-like object. + * + * Example: + * { + * id: "airi.config.stage-ui", + * version: 2, + * schema: { type: "object", properties: { model: { type: "string" } }, required: ["model"] }, + * } + */ +export interface ModuleConfigSchema { + id: string + version: number + /** + * Optional JSON Schema-like descriptor for tooling/validation. + * Keep it JSON-serializable and avoid runtime-only values. + */ + schema?: Record +} + +/** + * Module dependency declaration. + * + * Use this during prepare/probe to describe what a module needs before + * it can decide its dynamic contributions. Dependencies can change at + * runtime if peers go offline. + * + * Example: + * { role: "llm:orchestrator", min: "v1", optional: true } + */ +export interface ModuleDependency { + /** + * Logical dependency role (preferred over hard-coded plugin ids). + * Example: "llm:orchestrator" + */ + role: string + /** + * Optional dependency flag. + */ + optional?: boolean + /** + * Version constraint hints. + */ + version?: string + min?: string + max?: string + /** + * Additional constraint metadata (JSON-serializable). + */ + constraints?: Record +} + +/** + * Dynamic contributions emitted by a module after configuration. + * + * Unlike static manifests, contributions can be updated or revoked at + * runtime. This is where capabilities, provider registrations, and UI + * extensions should be declared. + * + * Example: + * { + * capabilities: ["context.aggregate"], + * providers: [{ id: "vscode-context", type: "context-source" }], + * ui: { widgets: ["context-summary-panel"] } + * } + */ +export interface ModuleContribution { + /** + * Dynamic capabilities exposed by the module. + */ + capabilities?: string[] + /** + * Provider registry contributions (shape defined by the host). + */ + providers?: Array> + /** + * UI contribution descriptors (widgets, toolbar items, etc). + */ + ui?: Record + /** + * Hook registrations (event handlers, interceptors, etc). + */ + hooks?: Array> + /** + * Additional resources or metadata. + */ + resources?: Record +} + +/** + * Lifecycle phases for module orchestration and UX. + */ +export type ModulePhase + = | 'announced' + | 'preparing' + | 'prepared' + | 'configuration-needed' + | 'configured' + | 'ready' + | 'failed' + +export type Localizable + = | string + | { + /** + * Localization key owned by the module. + * Example: "config.deprecated.model_driver.legacy" + */ + key: string + /** + * Fallback display string when translation is unavailable. + */ + fallback?: string + /** + * Params for string interpolation. + */ + params?: Record + } + +export interface ModuleConfigNotice { + /** + * Machine-friendly key for analytics or client-side mapping. + */ + code?: string + /** + * Human readable message or localization key. + */ + message?: Localizable + /** + * JSON pointer or dotted path in config. + * Example: "driver.legacyModelPath" + */ + path?: string + /** + * Suggested replacement path or alternative. + */ + replacedBy?: string + /** + * Version since the notice applies. + */ + since?: number + /** + * Link to docs or migration guide. + */ + link?: string +} + +export interface ModuleConfigStep { + /** + * Suggested action to complete configuration. + * Use code for UI rendering or message for fallback. + */ + code?: string + message?: Localizable + /** + * Optional targeted field(s). + */ + paths?: string[] +} + +export interface ModuleConfigPlan { + /** + * Schema that this plan targets. + */ + schema: ModuleConfigSchema + /** + * Missing required paths for current schema/version. + */ + missing?: string[] + /** + * Invalid fields with reasons (runtime validation result). + */ + invalid?: Array<{ path: string, reason: string }> + /** + * Recommended defaults computed at runtime (may be environment-specific). + */ + defaults?: Record + /** + * Deprecated fields/behaviors detected in current config. + */ + deprecated?: Array + /** + * Suggested migration steps between schema versions. + */ + migrations?: Array<{ + from: number + to: number + steps?: Array + notes?: Array + }> + /** + * Human- or UI-friendly next actions to resolve partial config. + */ + nextSteps?: Array + /** + * Non-blocking issues that should be shown to the user/operator. + */ + warnings?: Array +} + +export interface ModuleConfigValidation { + /** + * Overall validation status. + * + * - valid: all required fields present and valid. + * - partial: config is structurally OK but missing required fields; can be fixed by patches. + * - invalid: one or more fields are present but invalid (type/range/format); requires correction. + */ + status: 'partial' | 'valid' | 'invalid' + /** + * Missing required fields (only for partial/invalid). + */ + missing?: string[] + /** + * Invalid fields with reasons (only for invalid). + */ + invalid?: Array<{ path: string, reason: Localizable }> + /** + * Non-blocking issues (e.g., deprecations, best-practice notices). + */ + warnings?: Array +} + +/** + * Config payload envelope for plan/apply/validate/commit. + * + * Example: + * { + * configId: "stage-ui-live2d", + * revision: 12, + * schemaVersion: 2, + * full: { model: "Hiyori", driver: { type: "live2d" } }, + * } + */ +export interface ModuleConfigEnvelope> { + configId: string + /** + * Monotonic revision number for this configId. + */ + revision: number + /** + * Schema version this config targets. + */ + schemaVersion: number + /** + * Optional source identity (who produced this config). + */ + source?: ModuleIdentity + /** + * Full config payload (use when first applying or rehydrating). + */ + full?: C + /** + * Partial patch payload (use when updating or filling missing fields). + */ + patch?: Partial + /** + * If patch is used, baseRevision should be set for optimistic concurrency. + */ + baseRevision?: number +} + +export interface ModuleCapability { + /** + * Stable capability id within a module. + * Example: "memory.write", "vision.ocr". + */ + id: string + /** + * Human-friendly name. + */ + name?: string + /** + * Optional localized description. + */ + description?: Localizable + /** + * Capability-specific config schema (if needed). + */ + configSchema?: ModuleConfigSchema + /** + * Additional metadata for tooling/UI. + */ + metadata?: Record +} + export type RouteTargetExpression = | { type: 'and', all: RouteTargetExpression[] } | { type: 'or', any: RouteTargetExpression[] } @@ -162,7 +469,7 @@ export type WebSocketEventDataInputs = WebSocketEventInputText | WebSocketEventI export type WebSocketEventInputs = WebSocketEventOf<'input:text'> | WebSocketEventOf<'input:text:voice'> | WebSocketEventOf<'input:voice'> export interface WebSocketEventBaseMetadata { - source?: MetadataEventSource + source?: ModuleIdentity event?: { id?: string parentId?: string @@ -177,7 +484,7 @@ export interface WebSocketBaseEvent { */ source?: WebSocketEventSource | S metadata: { - source: MetadataEventSource + source: ModuleIdentity event: { id: string parentId?: string @@ -194,56 +501,468 @@ export type WithOutputSource = { [S in Source]: OutputSource[S] } +// Module orchestration (local or remote transport): +// +// 1) module:authenticate → module:authenticated +// 2) registry:modules:sync (host → module bootstrap) +// 3) module:announce (identity, deps, config schema) +// 4) module:prepared +// 5) module:configuration:* (validate/plan/commit flow) +// 6) module:configuration:configured +// 7) module:contribute:capability:offer (repeat per capability) +// 8) module:contribute:capability:configuration:* (optional) +// 9) module:contribute:capability:activated +// 10) module:status (ready) +// 11) module:status:change (to re-run phases) + +interface ModuleAuthenticateEvent { + token: string +} + +interface ModuleAuthenticatedEvent { + authenticated: boolean +} + +interface RegistryModulesSyncEvent { + modules: Array<{ + name: string + index?: number + identity: ModuleIdentity + }> +} + +interface ErrorEvent { + message: string +} + +interface ModuleAnnounceEvent { + name: string + identity: ModuleIdentity + possibleEvents: Array<(keyof WebSocketEvents)> + configSchema?: ModuleConfigSchema + dependencies?: ModuleDependency[] +} + +interface ModulePreparedEvent { + identity: ModuleIdentity + missingDependencies?: ModuleDependency[] +} + +interface ModuleConfigurationNeededEvent { + identity: ModuleIdentity + schema?: ModuleConfigSchema + current?: ModuleConfigEnvelope + reason?: string +} + +interface ModuleStatusEvent { + identity: ModuleIdentity + phase: ModulePhase + reason?: string + details?: Record +} + +interface ModuleConfigurationValidateRequestEvent { + identity: ModuleIdentity + current?: ModuleConfigEnvelope +} + +interface ModuleConfigurationValidateResponseEvent { + identity: ModuleIdentity + validation: ModuleConfigValidation + plan?: ModuleConfigPlan + current?: ModuleConfigEnvelope +} + +interface ModuleConfigurationValidateStatusEvent { + identity: ModuleIdentity + state: 'queued' | 'working' | 'done' | 'failed' + note?: string + progress?: number +} + +interface ModuleConfigurationPlanRequestEvent { + identity: ModuleIdentity + plan?: ModuleConfigPlan + current?: ModuleConfigEnvelope +} + +interface ModuleConfigurationPlanResponseEvent { + identity: ModuleIdentity + plan: ModuleConfigPlan + current?: ModuleConfigEnvelope +} + +interface ModuleConfigurationPlanStatusEvent { + identity: ModuleIdentity + state: 'queued' | 'working' | 'done' | 'failed' + note?: string + progress?: number +} + +interface ModuleConfigurationCommitEvent { + identity: ModuleIdentity + config: ModuleConfigEnvelope +} + +interface ModuleConfigurationCommitStatusEvent { + identity: ModuleIdentity + state: 'queued' | 'working' | 'done' | 'failed' + note?: string + progress?: number +} + +interface ModuleConfigurationConfiguredEvent { + identity: ModuleIdentity + config: ModuleConfigEnvelope +} + +interface ModuleContributeCapabilityOfferEvent { + identity: ModuleIdentity + capability: ModuleCapability +} + +interface ModuleContributeCapabilityConfigurationNeededEvent { + identity: ModuleIdentity + capabilityId: string + schema?: ModuleConfigSchema + current?: ModuleConfigEnvelope + reason?: string +} + +interface ModuleContributeCapabilityConfigurationValidateRequestEvent { + identity: ModuleIdentity + capabilityId: string + current?: ModuleConfigEnvelope +} + +interface ModuleContributeCapabilityConfigurationValidateResponseEvent { + identity: ModuleIdentity + capabilityId: string + validation: ModuleConfigValidation + plan?: ModuleConfigPlan + current?: ModuleConfigEnvelope +} + +interface ModuleContributeCapabilityConfigurationValidateStatusEvent { + identity: ModuleIdentity + capabilityId: string + state: 'queued' | 'working' | 'done' | 'failed' + note?: string + progress?: number +} + +interface ModuleContributeCapabilityConfigurationPlanRequestEvent { + identity: ModuleIdentity + capabilityId: string + plan?: ModuleConfigPlan + current?: ModuleConfigEnvelope +} + +interface ModuleContributeCapabilityConfigurationPlanResponseEvent { + identity: ModuleIdentity + capabilityId: string + plan: ModuleConfigPlan + current?: ModuleConfigEnvelope +} + +interface ModuleContributeCapabilityConfigurationPlanStatusEvent { + identity: ModuleIdentity + capabilityId: string + state: 'queued' | 'working' | 'done' | 'failed' + note?: string + progress?: number +} + +interface ModuleContributeCapabilityConfigurationCommitEvent { + identity: ModuleIdentity + capabilityId: string + config: ModuleConfigEnvelope +} + +interface ModuleContributeCapabilityConfigurationCommitStatusEvent { + identity: ModuleIdentity + capabilityId: string + state: 'queued' | 'working' | 'done' | 'failed' + note?: string + progress?: number +} + +interface ModuleContributeCapabilityConfigurationConfiguredEvent { + identity: ModuleIdentity + capabilityId: string + config: ModuleConfigEnvelope +} + +interface ModuleContributeCapabilityActivatedEvent { + identity: ModuleIdentity + capabilityId: string + active: boolean + reason?: string +} + +interface ModuleStatusChangeEvent { + identity: ModuleIdentity + phase: ModulePhase + reason?: string + details?: Record +} + +interface UiConfigureEvent { + moduleName: string + moduleIndex?: number + config: C | Record +} + +type OutputGenAiChatToolCallEvent = { + toolCalls: ToolMessage[] +} & Partial> & Partial> + +type OutputGenAiChatMessageEvent = { + message: AssistantMessage +} & Partial> & Partial> + +interface OutputGenAiChatUsage { + promptTokens: number + completionTokens: number + totalTokens: number + source: 'provider-based' | 'estimate-based' +} + +type OutputGenAiChatCompleteEvent = { + message: AssistantMessage + toolCalls: ToolMessage[] + usage: OutputGenAiChatUsage +} & Partial> & Partial> + +interface SparkNotifyEvent { + id: string + eventId: string + lane?: string + kind: 'alarm' | 'ping' | 'reminder' + urgency: 'immediate' | 'soon' | 'later' + headline: string + note?: string + payload?: Record + ttlMs?: number + requiresAck?: boolean + destinations: Array + metadata?: Record +} + +interface SparkEmitEvent { + id: string + eventId?: string + state: 'queued' | 'working' | 'done' | 'dropped' | 'blocked' | 'expired' + note?: string + destinations: Array + metadata?: Record +} + +interface SparkCommandGuidanceOption { + label: string + steps: Array + rationale?: string + possibleOutcome?: Array + risk?: 'high' | 'medium' | 'low' | 'none' + fallback?: Array + triggers?: Array +} + +interface SparkCommandGuidance { + type: 'proposal' | 'instruction' | 'memory-recall' + /** + * Personas can be used to adjust the behavior of sub-agents. + * For example, when using as NPC in games, or player in Minecraft, + * the persona can help define the character's traits and decision-making style. + * + * Example: + * persona: { + * "bravery": "high", + * "cautiousness": "low", + * "friendliness": "medium" + * } + */ + persona?: Record + options: Array +} + +interface SparkCommandEvent { + id: string + eventId?: string + parentEventId?: string + commandId: string + interrupt: 'force' | 'soft' | false + priority: 'critical' | 'high' | 'normal' | 'low' + intent: 'plan' | 'proposal' | 'action' | 'pause' | 'resume' | 'reroute' | 'context' + ack?: string + guidance?: SparkCommandGuidance + contexts?: Array + destinations: Array +} + +interface TransportConnectionHeartbeatEvent { + kind: MessageHeartbeatKind + message: MessageHeartbeat | string + at?: number +} + +type ContextUpdateEvent = ContextUpdate + +export const moduleAuthenticate = defineEventa('module:authenticate') +export const moduleAuthenticated = defineEventa('module:authenticated') +export const registryModulesSync = defineEventa('registry:modules:sync') + +export const error = defineEventa('error') + +export const moduleAnnounce = defineEventa('module:announce') +export const modulePrepared = defineEventa('module:prepared') +export const moduleConfigurationNeeded = defineEventa('module:configuration:needed') +export const moduleStatus = defineEventa('module:status') + +export const moduleConfigurationValidateRequest = defineEventa('module:configuration:validate:request') +export const moduleConfigurationValidateResponse = defineEventa('module:configuration:validate:response') +export const moduleConfigurationValidateStatus = defineEventa('module:configuration:validate:status') +export const moduleConfigurationPlanRequest = defineEventa('module:configuration:plan:request') +export const moduleConfigurationPlanResponse = defineEventa('module:configuration:plan:response') +export const moduleConfigurationPlanStatus = defineEventa('module:configuration:plan:status') +export const moduleConfigurationCommit = defineEventa('module:configuration:commit') +export const moduleConfigurationCommitStatus = defineEventa('module:configuration:commit:status') +export const moduleConfigurationConfigured = defineEventa('module:configuration:configured') + +export const moduleContributeCapabilityOffer = defineEventa('module:contribute:capability:offer') +export const moduleContributeCapabilityConfigurationNeeded = defineEventa('module:contribute:capability:configuration:needed') +export const moduleContributeCapabilityConfigurationValidateRequest = defineEventa('module:contribute:capability:configuration:validate:request') +export const moduleContributeCapabilityConfigurationValidateResponse = defineEventa('module:contribute:capability:configuration:validate:response') +export const moduleContributeCapabilityConfigurationValidateStatus = defineEventa('module:contribute:capability:configuration:validate:status') +export const moduleContributeCapabilityConfigurationPlanRequest = defineEventa('module:contribute:capability:configuration:plan:request') +export const moduleContributeCapabilityConfigurationPlanResponse = defineEventa('module:contribute:capability:configuration:plan:response') +export const moduleContributeCapabilityConfigurationPlanStatus = defineEventa('module:contribute:capability:configuration:plan:status') +export const moduleContributeCapabilityConfigurationCommit = defineEventa('module:contribute:capability:configuration:commit') +export const moduleContributeCapabilityConfigurationCommitStatus = defineEventa('module:contribute:capability:configuration:commit:status') +export const moduleContributeCapabilityConfigurationConfigured = defineEventa('module:contribute:capability:configuration:configured') +export const moduleContributeCapabilityActivated = defineEventa('module:contribute:capability:activated') + +export const moduleStatusChange = defineEventa('module:status:change') + +export const uiConfigure = defineEventa('ui:configure') + +export const inputText = defineEventa('input:text') +export const inputTextVoice = defineEventa('input:text:voice') +export const inputVoice = defineEventa('input:voice') + +export const outputGenAiChatToolCall = defineEventa('output:gen-ai:chat:tool-call') +export const outputGenAiChatMessage = defineEventa('output:gen-ai:chat:message') +export const outputGenAiChatComplete = defineEventa('output:gen-ai:chat:complete') + +export const sparkNotify = defineEventa('spark:notify') +export const sparkEmit = defineEventa('spark:emit') +export const sparkCommand = defineEventa('spark:command') + +export const transportConnectionHeartbeat = defineEventa('transport:connection:heartbeat') +export const contextUpdate = defineEventa('context:update') + // Thanks to: // // A little hack for creating extensible discriminated unions : r/typescript // https://www.reddit.com/r/typescript/comments/1064ibt/a_little_hack_for_creating_extensible/ export interface WebSocketEvents { - 'error': { - message: string - } + 'error': ErrorEvent - 'module:authenticate': { - token: string - } - 'module:authenticated': { - authenticated: boolean - } - 'module:announce': { - name: string - identity?: MetadataEventSource - possibleEvents: Array<(keyof WebSocketEvents)> - } - 'module:configure': { - config: C - } + 'module:authenticate': ModuleAuthenticateEvent + 'module:authenticated': ModuleAuthenticatedEvent + /** + * Server-side registry sync for known online modules. + * Sent to newly authenticated peers to bootstrap module discovery. + */ + 'registry:modules:sync': RegistryModulesSyncEvent + 'module:announce': ModuleAnnounceEvent + /** + * Prepare completed. Host can move into config apply/validate. + * + * Example: + * module:prepared { missingDependencies: [] } + */ + 'module:prepared': ModulePreparedEvent + /** + * Module needs configuration to proceed to prepared/configured. + */ + 'module:configuration:needed': ModuleConfigurationNeededEvent + /** + * Lifecycle status updates for orchestration/UX. + * + * Example: + * module:status { phase: "ready" } + */ + 'module:status': ModuleStatusEvent + /** + * Ask the module to validate current config (host → module). + */ + 'module:configuration:validate:request': ModuleConfigurationValidateRequestEvent + /** + * Validation response (module → host), with optional plan suggestions. + */ + 'module:configuration:validate:response': ModuleConfigurationValidateResponseEvent + /** + * Status updates for validation (module → host). + */ + 'module:configuration:validate:status': ModuleConfigurationValidateStatusEvent + /** + * Configuration planning request (host → module). + */ + 'module:configuration:plan:request': ModuleConfigurationPlanRequestEvent + /** + * Configuration planning response (module → host). + */ + 'module:configuration:plan:response': ModuleConfigurationPlanResponseEvent + /** + * Status updates for planning (module → host). + */ + 'module:configuration:plan:status': ModuleConfigurationPlanStatusEvent + /** + * Commit a config as "active" (host → module). + */ + 'module:configuration:commit': ModuleConfigurationCommitEvent + /** + * Status updates for commit (module → host). + */ + 'module:configuration:commit:status': ModuleConfigurationCommitStatusEvent + /** + * Configuration fully applied and active (module → host). + */ + 'module:configuration:configured': ModuleConfigurationConfiguredEvent + /** + * Capability offer emitted after module configuration. + */ + 'module:contribute:capability:offer': ModuleContributeCapabilityOfferEvent + /** + * Capability needs configuration before activation. + */ + 'module:contribute:capability:configuration:needed': ModuleContributeCapabilityConfigurationNeededEvent + 'module:contribute:capability:configuration:validate:request': ModuleContributeCapabilityConfigurationValidateRequestEvent + 'module:contribute:capability:configuration:validate:response': ModuleContributeCapabilityConfigurationValidateResponseEvent + 'module:contribute:capability:configuration:validate:status': ModuleContributeCapabilityConfigurationValidateStatusEvent + 'module:contribute:capability:configuration:plan:request': ModuleContributeCapabilityConfigurationPlanRequestEvent + 'module:contribute:capability:configuration:plan:response': ModuleContributeCapabilityConfigurationPlanResponseEvent + 'module:contribute:capability:configuration:plan:status': ModuleContributeCapabilityConfigurationPlanStatusEvent + 'module:contribute:capability:configuration:commit': ModuleContributeCapabilityConfigurationCommitEvent + 'module:contribute:capability:configuration:commit:status': ModuleContributeCapabilityConfigurationCommitStatusEvent + 'module:contribute:capability:configuration:configured': ModuleContributeCapabilityConfigurationConfiguredEvent + 'module:contribute:capability:activated': ModuleContributeCapabilityActivatedEvent + /** + * Request a phase transition (module → host). + */ + 'module:status:change': ModuleStatusChangeEvent - 'ui:configure': { - moduleName: string - moduleIndex?: number - config: C | Record - } + 'ui:configure': UiConfigureEvent 'input:text': WebSocketEventInputText 'input:text:voice': WebSocketEventInputTextVoice 'input:voice': WebSocketEventInputVoice - 'output:gen-ai:chat:tool-call': { - toolCalls: ToolMessage[] - } & Partial> & Partial> - 'output:gen-ai:chat:message': { - message: AssistantMessage - } & Partial> & Partial> - 'output:gen-ai:chat:complete': { - message: AssistantMessage - toolCalls: ToolMessage[] - usage: { - promptTokens: number - completionTokens: number - totalTokens: number - source: 'provider-based' | 'estimate-based' - } - } & Partial> & Partial> + 'output:gen-ai:chat:tool-call': OutputGenAiChatToolCallEvent + 'output:gen-ai:chat:message': OutputGenAiChatMessageEvent + 'output:gen-ai:chat:complete': OutputGenAiChatCompleteEvent /** * Spark used for allowing agents in a network to raise an event toward the other destinations (e.g. character). @@ -270,20 +989,7 @@ export interface WebSocketEvents { * * destinations controls routing (e.g. ['character'], ['character','minecraft-agent']). */ - 'spark:notify': { - id: string - eventId: string - lane?: string - kind: 'alarm' | 'ping' | 'reminder' - urgency: 'immediate' | 'soon' | 'later' - headline: string - note?: string - payload?: Record - ttlMs?: number - requiresAck?: boolean - destinations: Array - metadata?: Record - } + 'spark:notify': SparkNotifyEvent /** * Acknowledgement/progress/state for a spark or command (bidirectional). @@ -293,14 +999,7 @@ export interface WebSocketEvents { * - Sub-agent: state=blocked/dropped with note when it cannot comply. * - Minecraft: state=working, note="Pillared up; healing" in reply to a command. */ - 'spark:emit': { - id: string - eventId?: string - state: 'queued' | 'working' | 'done' | 'dropped' | 'blocked' | 'expired' - note?: string - destinations: Array - metadata?: Record - } + 'spark:emit': SparkEmitEvent /** * Character issues instructions or context to a sub-agent. @@ -311,51 +1010,11 @@ export interface WebSocketEvents { * - Prep plan: interrupt=soft, priority=high, intent=plan with steps/fallbacks. * - Contextual hints: intent=context with contextPatch ideas/hints. */ - 'spark:command': { - id: string - eventId?: string - parentEventId?: string - commandId: string - interrupt: 'force' | 'soft' | false - priority: 'critical' | 'high' | 'normal' | 'low' - intent: 'plan' | 'proposal' | 'action' | 'pause' | 'resume' | 'reroute' | 'context' - ack?: string - guidance?: { - type: 'proposal' | 'instruction' | 'memory-recall' - /** - * Personas can be used to adjust the behavior of sub-agents. - * For example, when using as NPC in games, or player in Minecraft, - * the persona can help define the character's traits and decision-making style. - * - * Example: - * persona: { - * "bravery": "high", - * "cautiousness": "low", - * "friendliness": "medium" - * } - */ - persona?: Record - options: Array<{ - label: string - steps: Array - rationale?: string - possibleOutcome?: Array - risk?: 'high' | 'medium' | 'low' | 'none' - fallback?: Array - triggers?: Array - }> - } - contexts?: Array - destinations: Array - } + 'spark:command': SparkCommandEvent - 'transport:connection:heartbeat': { - kind: MessageHeartbeatKind - message: MessageHeartbeat | string - at?: number - } + 'transport:connection:heartbeat': TransportConnectionHeartbeatEvent - 'context:update': ContextUpdate + 'context:update': ContextUpdateEvent } export type WebSocketEvent = { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5e25e8ae1..59736aece 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -40,7 +40,7 @@ catalogs: specifier: 0.1.0-beta.15 version: 0.1.0-beta.15 '@moeru/eventa': - specifier: 1.0.0-alpha.10 + specifier: 'catalog:' version: 1.0.0-alpha.10 '@moeru/std': specifier: 0.1.0-beta.14 @@ -171,6 +171,9 @@ catalogs: superjson: specifier: ^2.2.6 version: 2.2.6 + tsdown: + specifier: ^0.17.4 + version: 0.17.4 tsx: specifier: ^4.21.0 version: 4.21.0 @@ -315,7 +318,7 @@ importers: specifier: ^1.0.2 version: 1.0.2 tsdown: - specifier: ^0.17.4 + specifier: 'catalog:' version: 0.17.4(@arethetypeswrong/core@0.18.2)(oxc-resolver@11.16.2)(publint@0.3.16)(synckit@0.11.12)(typescript@5.9.3)(unplugin-lightningcss@0.4.4)(unplugin-unused@0.5.6) tsx: specifier: ^4.21.0 @@ -1967,7 +1970,11 @@ importers: specifier: ^1.0.2 version: 1.0.2 - packages/plugin-sdk: {} + packages/plugin-sdk: + dependencies: + '@moeru/eventa': + specifier: 'catalog:' + version: 1.0.0-alpha.10(electron@40.0.0) packages/server-runtime: dependencies: @@ -2001,9 +2008,6 @@ importers: crossws: specifier: ^0.4.3 version: 0.4.3(srvx@0.10.1) - defu: - specifier: ^6.1.4 - version: 6.1.4 superjson: specifier: 'catalog:' version: 2.2.6 @@ -2014,9 +2018,9 @@ importers: packages/server-shared: dependencies: - '@xsai/shared': + '@moeru/eventa': specifier: 'catalog:' - version: 0.4.0-beta.13 + version: 1.0.0-alpha.10(electron@40.0.0) '@xsai/shared-chat': specifier: 'catalog:' version: 0.4.0-beta.13 @@ -3020,7 +3024,7 @@ importers: version: 14.1.0(vue@3.5.26(typescript@5.9.3)) '@wxt-dev/module-vue': specifier: ^1.0.3 - version: 1.0.3(vite@7.3.1(@types/node@24.10.9)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.26(typescript@5.9.3))(wxt@0.20.13(@types/node@24.10.9)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.2)(rollup@4.55.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) + version: 1.0.3(vite@8.0.0-beta.9(@types/node@24.10.9)(esbuild@0.27.2)(jiti@2.6.1)(less@4.5.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.26(typescript@5.9.3))(wxt@0.20.13(@types/node@24.10.9)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.2)(rollup@4.55.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2)) nanoid: specifier: ^5.1.6 version: 5.1.6 @@ -19698,6 +19702,13 @@ snapshots: optionalDependencies: electron: 39.2.7 + '@moeru/eventa@1.0.0-alpha.10(electron@40.0.0)': + dependencies: + nanoid: 5.1.6 + picomatch: 4.0.3 + optionalDependencies: + electron: 40.0.0 + '@moeru/eventa@1.0.0-alpha.11(electron@40.0.0)(h3@2.0.1-rc.5(crossws@0.4.3(srvx@0.10.1)))': dependencies: nanoid: 5.1.6 @@ -23283,9 +23294,9 @@ snapshots: '@types/filesystem': 0.0.36 '@types/har-format': 1.2.16 - '@wxt-dev/module-vue@1.0.3(vite@7.3.1(@types/node@24.10.9)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.26(typescript@5.9.3))(wxt@0.20.13(@types/node@24.10.9)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.2)(rollup@4.55.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))': + '@wxt-dev/module-vue@1.0.3(vite@8.0.0-beta.9(@types/node@24.10.9)(esbuild@0.27.2)(jiti@2.6.1)(less@4.5.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.26(typescript@5.9.3))(wxt@0.20.13(@types/node@24.10.9)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.2)(rollup@4.55.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))': dependencies: - '@vitejs/plugin-vue': 6.0.3(vite@7.3.1(@types/node@24.10.9)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.26(typescript@5.9.3)) + '@vitejs/plugin-vue': 6.0.3(vite@8.0.0-beta.9(@types/node@24.10.9)(esbuild@0.27.2)(jiti@2.6.1)(less@4.5.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2))(vue@3.5.26(typescript@5.9.3)) wxt: 0.20.13(@types/node@24.10.9)(jiti@2.6.1)(less@4.5.1)(lightningcss@1.30.2)(rollup@4.55.2)(terser@5.44.1)(tsx@4.21.0)(yaml@2.8.2) transitivePeerDependencies: - vite diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 1548aa65b..663e787ad 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -36,7 +36,7 @@ catalog: '@iconify-json/logos': ^1.2.10 '@iconify-json/tabler': ^1.2.26 '@moeru/eslint-config': 0.1.0-beta.15 - '@moeru/eventa': 1.0.0-alpha.10 + '@moeru/eventa': 'catalog:' '@moeru/std': 0.1.0-beta.14 '@nekopaw/tempora': 0.4.0-alpha.1 '@pinia/testing': ^1.0.3 @@ -80,6 +80,7 @@ catalog: splitpanes: ^4.0.4 std-env: ^3.10.0 superjson: ^2.2.6 + tsdown: ^0.17.4 tsx: ^4.21.0 uncrypto: ^0.1.3 unplugin-info: ^1.2.4 diff --git a/vitest.config.ts b/vitest.config.ts index b589679ed..0324ab497 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -5,6 +5,7 @@ export default defineConfig({ projects: [ 'apps/server', 'packages/stage-ui', + 'packages/plugin-sdk', 'packages/vite-plugin-warpdrive', 'packages/audio-pipelines-transcribe', 'packages/server-runtime',