From ba3d66de86a631605fb32b58fa8d093793966114 Mon Sep 17 00:00:00 2001 From: RainbowBird Date: Fri, 27 Mar 2026 01:07:18 +0800 Subject: [PATCH] feat(server-protocol): introduce shared protocol types for AIRI server clients and frontends - Added package.json for @proj-airi/server-protocol with necessary configurations. - Implemented chat event types including WireMessage, SendMessagesRequest, and PullMessagesRequest. - Defined WebSocket event types and structures for better integration with AIRI components. - Updated server-runtime and server-sdk to utilize the new server-protocol package. - Refactored imports across various packages to replace server-shared types with server-protocol types. - Enhanced type definitions and added TypeScript configurations for better development experience. --- apps/server/Dockerfile | 9 ++- apps/server/docker-compose.yml | 37 ++++++++++- .../docs/ai-context/workers-and-runtime.md | 2 +- apps/server/package.json | 4 +- apps/server/src/bin/__test__/run.test.ts | 15 +++-- apps/server/src/bin/run.ts | 64 ++++++++++++------- apps/server/src/libs/otel.ts | 6 +- apps/server/src/routes/chat-ws.ts | 2 +- apps/server/src/services/chats.ts | 3 +- apps/server/tsconfig.json | 10 +++ packages/plugin-sdk/package.json | 1 - packages/server-protocol/README.md | 20 ++++++ packages/server-protocol/package.json | 43 +++++++++++++ packages/server-protocol/src/events/chat.ts | 45 +++++++++++++ packages/server-protocol/src/index.ts | 2 + .../src/types/index.ts | 0 .../src/types/websocket/events.ts | 0 .../src/types/websocket/index.ts | 0 packages/server-protocol/tsconfig.json | 18 ++++++ packages/server-protocol/tsdown.config.ts | 11 ++++ packages/server-runtime/package.json | 1 + packages/server-runtime/src/index.ts | 4 +- .../src/middlewares/route.test.ts | 2 +- .../server-runtime/src/middlewares/route.ts | 2 +- .../src/middlewares/route/match-expression.ts | 2 +- packages/server-runtime/src/types/conn.ts | 2 +- packages/server-runtime/tsconfig.json | 8 +-- packages/server-sdk/package.json | 1 + packages/server-sdk/src/client.ts | 4 +- packages/server-sdk/src/events/chat.ts | 46 +------------ packages/server-sdk/src/index.ts | 4 +- packages/server-sdk/test/client.test.ts | 2 +- packages/server-shared/README.md | 45 +------------ packages/server-shared/package.json | 8 +-- packages/server-shared/src/index.ts | 1 - packages/server-shared/tsdown.config.ts | 3 +- packages/stage-pages/package.json | 1 + .../components/context-flow-actions.vue | 2 +- .../use-context-flow-formatters.ts | 2 +- .../context-flow/context-flow-types.ts | 2 +- .../src/pages/devtools/context-flow/index.vue | 4 +- packages/stage-ui/package.json | 1 + .../event-handler-spark-notify/index.ts | 2 +- .../character/orchestrator/index.test.ts | 2 +- .../stores/character/orchestrator/store.ts | 2 +- packages/stage-ui/src/stores/chat.ts | 2 +- .../stores/chat/context-providers/datetime.ts | 2 +- .../stage-ui/src/stores/chat/context-store.ts | 2 +- .../stores/devtools/websocket-inspector.ts | 2 +- .../src/stores/mods/api/channel-server.ts | 5 +- .../src/stores/modules/vision/orchestrator.ts | 2 +- packages/stage-ui/src/types/chat.ts | 2 +- packages/stage-ui/src/utils/event-source.ts | 2 +- pnpm-lock.yaml | 48 ++++++++++---- pnpm-workspace.yaml | 4 +- services/discord-bot/package.json | 2 +- .../discord-bot/src/adapters/airi-adapter.ts | 4 +- .../src/bots/discord/commands/summon.ts | 2 +- 58 files changed, 329 insertions(+), 195 deletions(-) create mode 100644 packages/server-protocol/README.md create mode 100644 packages/server-protocol/package.json create mode 100644 packages/server-protocol/src/events/chat.ts create mode 100644 packages/server-protocol/src/index.ts rename packages/{server-shared => server-protocol}/src/types/index.ts (100%) rename packages/{server-shared => server-protocol}/src/types/websocket/events.ts (100%) rename packages/{server-shared => server-protocol}/src/types/websocket/index.ts (100%) create mode 100644 packages/server-protocol/tsconfig.json create mode 100644 packages/server-protocol/tsdown.config.ts diff --git a/apps/server/Dockerfile b/apps/server/Dockerfile index 58f67c29d..6520e476b 100644 --- a/apps/server/Dockerfile +++ b/apps/server/Dockerfile @@ -4,17 +4,20 @@ WORKDIR /app RUN corepack enable -COPY pnpm-lock.yaml pnpm-workspace.yaml package.json ./ +COPY pnpm-lock.yaml pnpm-workspace.yaml package.json tsconfig.json ./ COPY patches/ ./patches/ COPY apps/server apps/server +COPY packages/server-protocol packages/server-protocol COPY packages/server-schema packages/server-schema -COPY packages/server-sdk packages/server-sdk +COPY packages/plugin-protocol packages/plugin-protocol RUN --mount=type=cache,id=pnpm-store,target=/root/.pnpm-store \ pnpm install --frozen-lockfile --ignore-scripts RUN pnpm -F @proj-airi/server-schema run build -RUN pnpm -F @proj-airi/server-sdk run build +RUN pnpm -F @proj-airi/server-protocol run build +RUN pnpm -F @proj-airi/plugin-protocol run build +RUN pnpm -F @proj-airi/server run build EXPOSE 3000 diff --git a/apps/server/docker-compose.yml b/apps/server/docker-compose.yml index 7f8bd4408..884374a2f 100644 --- a/apps/server/docker-compose.yml +++ b/apps/server/docker-compose.yml @@ -30,10 +30,11 @@ services: timeout: 5s retries: 10 - server: + api: build: context: ../.. dockerfile: apps/server/Dockerfile + command: ['pnpm', '-F', '@proj-airi/server', 'run', 'server', 'api'] depends_on: db: condition: service_healthy @@ -52,6 +53,40 @@ services: timeout: 5s retries: 5 + outbox-dispatcher: + build: + context: ../.. + dockerfile: apps/server/Dockerfile + command: ['pnpm', '-F', '@proj-airi/server', 'run', 'server', 'outbox-dispatcher'] + depends_on: + db: + condition: service_healthy + redis: + condition: service_healthy + env_file: + - path: .env + required: false + - path: .env.local + required: false + restart: unless-stopped + + cache-sync-consumer: + build: + context: ../.. + dockerfile: apps/server/Dockerfile + command: ['pnpm', '-F', '@proj-airi/server', 'run', 'server', 'cache-sync-consumer'] + depends_on: + db: + condition: service_healthy + redis: + condition: service_healthy + env_file: + - path: .env + required: false + - path: .env.local + required: false + restart: unless-stopped + volumes: db_data: driver: local diff --git a/apps/server/docs/ai-context/workers-and-runtime.md b/apps/server/docs/ai-context/workers-and-runtime.md index 1b318fca2..04c1433dc 100644 --- a/apps/server/docs/ai-context/workers-and-runtime.md +++ b/apps/server/docs/ai-context/workers-and-runtime.md @@ -125,7 +125,7 @@ - WS 广播不具备持久化和重放能力 - 真正补齐消息还是靠 `pullMessages` -- 广播只是加速客户端同步 +- 广播只是为了降低拉取延迟,不代表存在旧式 `sync` 端点 ## OpenTelemetry diff --git a/apps/server/package.json b/apps/server/package.json index c5204e37d..97dadd519 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -9,6 +9,7 @@ "dev": "pnpm run apply:env -- tsx --watch src/bin/run.ts api", "start": "pnpm run apply:env -- tsx src/bin/run.ts api", "server": "pnpm run apply:env -- tsx src/bin/run.ts", + "build": "tsc -b", "typecheck": "tsc --noEmit", "db:generate": "drizzle-kit generate", "db:push": "pnpm run apply:env -- drizzle-kit push" @@ -37,9 +38,10 @@ "@opentelemetry/sdk-trace-node": "^2.6.1", "@opentelemetry/semantic-conventions": "^1.40.0", "@proj-airi/drizzle-orm-browser-migrator": "^0.1.6", + "@proj-airi/server-protocol": "workspace:*", "@proj-airi/server-schema": "workspace:*", - "@proj-airi/server-sdk": "workspace:", "better-auth": "^1.5.6", + "cac": "catalog:", "drizzle-orm": "^0.45.1", "drizzle-valibot": "catalog:", "hono": "catalog:", diff --git a/apps/server/src/bin/__test__/run.test.ts b/apps/server/src/bin/__test__/run.test.ts index 4920b5b82..fce4cbaa7 100644 --- a/apps/server/src/bin/__test__/run.test.ts +++ b/apps/server/src/bin/__test__/run.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from 'vitest' -import { getServerCliHelpText, parseServerRole } from '../run' +import { createServerCli, parseServerRole } from '../run' describe('server cli', () => { it('parses supported roles', () => { @@ -14,10 +14,13 @@ describe('server cli', () => { expect(parseServerRole(['unknown'])).toBeNull() }) - it('returns help text that lists all supported roles', () => { - expect(getServerCliHelpText()).toContain('Usage: server ') - expect(getServerCliHelpText()).toContain('api') - expect(getServerCliHelpText()).toContain('cache-sync-consumer') - expect(getServerCliHelpText()).toContain('outbox-dispatcher') + it('registers all supported roles with cac', () => { + const cli = createServerCli() + + expect(cli.commands.map(command => command.name)).toEqual(expect.arrayContaining([ + 'api', + 'cache-sync-consumer', + 'outbox-dispatcher', + ])) }) }) diff --git a/apps/server/src/bin/run.ts b/apps/server/src/bin/run.ts index 6a041053b..a7006a025 100644 --- a/apps/server/src/bin/run.ts +++ b/apps/server/src/bin/run.ts @@ -5,6 +5,7 @@ import process from 'node:process' import { pathToFileURL } from 'node:url' import { errorMessageFrom } from '@moeru/std' +import { cac } from 'cac' import { runApiServer } from '../app' import { handleCacheSyncMessage, runBillingEventsConsumer } from './run-billing-events-consumer' @@ -14,26 +15,6 @@ const serverRoles = ['api', 'cache-sync-consumer', 'outbox-dispatcher'] as const type ServerRole = typeof serverRoles[number] -export function getServerCliHelpText(): string { - return [ - 'Usage: server ', - '', - 'Roles:', - ' api Start the HTTP/WebSocket API process', - ' cache-sync-consumer Start the cache-sync Redis Streams consumer', - ' outbox-dispatcher Publish DB outbox events to Redis Streams', - ].join('\n') -} - -export function parseServerRole(args: string[]): ServerRole | null { - const role = args[0] - if (!role) { - return null - } - - return serverRoles.includes(role as ServerRole) ? role as ServerRole : null -} - async function runServerRole(role: ServerRole): Promise { switch (role) { case 'api': @@ -51,15 +32,50 @@ async function runServerRole(role: ServerRole): Promise { } } -async function main(): Promise { - const role = parseServerRole(process.argv.slice(2)) +export function createServerCli() { + const cli = cac('server') + + cli + .usage('') + .command('api', 'Start the HTTP/WebSocket API process') + .action(() => runServerRole('api')) + + cli + .command('cache-sync-consumer', 'Start the cache-sync Redis Streams consumer') + .action(() => runServerRole('cache-sync-consumer')) + + cli + .command('outbox-dispatcher', 'Publish DB outbox events to Redis Streams') + .action(() => runServerRole('outbox-dispatcher')) + + cli.help() + + return cli +} + +export function parseServerRole(args: string[]): ServerRole | null { + const cli = createServerCli() + cli.parse(['node', 'server', ...args], { run: false }) + + const role = cli.matchedCommandName if (!role) { - process.stdout.write(`${getServerCliHelpText()}\n`) + return null + } + + return serverRoles.includes(role as ServerRole) ? role as ServerRole : null +} + +async function main(): Promise { + const cli = createServerCli() + cli.parse(process.argv, { run: false }) + + if (!cli.matchedCommand) { + cli.outputHelp() process.exitCode = 1 return } - await runServerRole(role) + await cli.runMatchedCommand() } function isExecutedAsMainModule(): boolean { diff --git a/apps/server/src/libs/otel.ts b/apps/server/src/libs/otel.ts index 11bd30009..345545b7f 100644 --- a/apps/server/src/libs/otel.ts +++ b/apps/server/src/libs/otel.ts @@ -38,7 +38,6 @@ export interface AuthMetrics { } export interface EngagementMetrics { - chatSync: Counter chatMessages: Counter characterCreated: Counter characterDeleted: Counter @@ -200,11 +199,8 @@ export function initOtel(env: Env): OtelInstance | undefined { // Engagement metrics const engagement: EngagementMetrics = { - chatSync: meter.createCounter('chat.sync', { - description: 'Number of chat sync operations', - }), chatMessages: meter.createCounter('chat.messages', { - description: 'Number of chat messages synced', + description: 'Number of chat messages written or pulled', }), characterCreated: meter.createCounter('character.created', { description: 'Number of characters created', diff --git a/apps/server/src/routes/chat-ws.ts b/apps/server/src/routes/chat-ws.ts index 9584a298a..47125e696 100644 --- a/apps/server/src/routes/chat-ws.ts +++ b/apps/server/src/routes/chat-ws.ts @@ -6,7 +6,7 @@ import type { ChatService } from '../services/chats' import { useLogger } from '@guiiai/logg' import { defineInvokeHandler } from '@moeru/eventa' -import { newMessages, pullMessages, sendMessages } from '@proj-airi/server-sdk' +import { newMessages, pullMessages, sendMessages } from '@proj-airi/server-protocol' import { createPeerHooks, wsDisconnectedEvent } from '../libs/eventa-hono-adapter' diff --git a/apps/server/src/services/chats.ts b/apps/server/src/services/chats.ts index 56afb6acb..8401ef892 100644 --- a/apps/server/src/services/chats.ts +++ b/apps/server/src/services/chats.ts @@ -1,4 +1,4 @@ -import type { MessageRole, WireMessage } from '@proj-airi/server-sdk' +import type { MessageRole, WireMessage } from '@proj-airi/server-protocol' import type { Database } from '../libs/db' import type { EngagementMetrics } from '../libs/otel' @@ -281,7 +281,6 @@ export function createChatService(db: Database, metrics?: EngagementMetrics | nu } }) - metrics?.chatSync.add(1) if (result.totalCount > 0) { metrics?.chatMessages.add(result.totalCount) } diff --git a/apps/server/tsconfig.json b/apps/server/tsconfig.json index 4c4e5f9a4..be2537d47 100644 --- a/apps/server/tsconfig.json +++ b/apps/server/tsconfig.json @@ -8,8 +8,17 @@ "ESNext" ], "useDefineForClassFields": true, + "baseUrl": ".", "module": "ESNext", "moduleResolution": "Bundler", + "paths": { + "@proj-airi/server-protocol": [ + "../../packages/server-protocol/src/index.ts" + ], + "@proj-airi/server-protocol/*": [ + "../../packages/server-protocol/src/*" + ] + }, "resolveJsonModule": true, "types": [ "vitest", @@ -20,6 +29,7 @@ "skipLibCheck": true }, "include": [ + "../../packages/server-protocol/src/**/*.ts", "src/**/*.ts", "src/**/*.d.ts" ] diff --git a/packages/plugin-sdk/package.json b/packages/plugin-sdk/package.json index 494333b10..cdda13872 100644 --- a/packages/plugin-sdk/package.json +++ b/packages/plugin-sdk/package.json @@ -42,7 +42,6 @@ "dependencies": { "@moeru/eventa": "catalog:", "@proj-airi/plugin-protocol": "workspace:*", - "@proj-airi/server-shared": "workspace:*", "nanoid": "catalog:", "valibot": "^1.3.1", "xstate": "^5.28.0" diff --git a/packages/server-protocol/README.md b/packages/server-protocol/README.md new file mode 100644 index 000000000..eaf7f09f7 --- /dev/null +++ b/packages/server-protocol/README.md @@ -0,0 +1,20 @@ +# @proj-airi/server-protocol + +Shared protocol types for AIRI server runtimes, SDKs, and frontend consumers. + +## Usage + +```shell +ni @proj-airi/server-protocol -D # from @antfu/ni, can be installed via `npm i -g @antfu/ni` +pnpm i @proj-airi/server-protocol -D +yarn i @proj-airi/server-protocol -D +npm i @proj-airi/server-protocol -D +``` + +```typescript +import type { WebSocketEvents } from '@proj-airi/server-protocol' +``` + +## License + +[MIT](../../LICENSE) diff --git a/packages/server-protocol/package.json b/packages/server-protocol/package.json new file mode 100644 index 000000000..8fa61f53f --- /dev/null +++ b/packages/server-protocol/package.json @@ -0,0 +1,43 @@ +{ + "name": "@proj-airi/server-protocol", + "type": "module", + "version": "0.9.0-alpha.22", + "description": "Shared protocol types for AIRI server clients, frontends, and runtimes", + "author": { + "name": "Moeru AI Project AIRI Team", + "email": "airi@moeru.ai", + "url": "https://github.com/moeru-ai" + }, + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/moeru-ai/airi.git", + "directory": "packages/server-protocol" + }, + "exports": { + ".": { + "types": "./dist/index.d.mts", + "default": "./dist/index.mjs" + }, + "./types": { + "types": "./dist/types/index.d.mts", + "default": "./dist/types/index.mjs" + } + }, + "main": "./dist/index.mjs", + "types": "./dist/index.d.mts", + "files": [ + "README.md", + "dist", + "package.json" + ], + "scripts": { + "dev": "pnpm run build", + "build": "tsdown", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "@moeru/eventa": "catalog:", + "@proj-airi/plugin-protocol": "workspace:*" + } +} diff --git a/packages/server-protocol/src/events/chat.ts b/packages/server-protocol/src/events/chat.ts new file mode 100644 index 000000000..ad14cd3e8 --- /dev/null +++ b/packages/server-protocol/src/events/chat.ts @@ -0,0 +1,45 @@ +import { defineInvokeEventa, defineOutboundEventa } from '@moeru/eventa' + +export interface WireMessage { + id: string + chatId: string + senderId: string | null + role: 'system' | 'user' | 'assistant' | 'tool' | 'error' + content: string + seq: number + createdAt: number + updatedAt: number +} + +export type MessageRole = WireMessage['role'] + +export interface SendMessagesRequest { + chatId: string + messages: { id: string, role: string, content: string }[] +} + +export interface SendMessagesResponse { + seq: number +} + +export interface PullMessagesRequest { + chatId: string + afterSeq: number + limit?: number +} + +export interface PullMessagesResponse { + messages: WireMessage[] + seq: number +} + +export interface NewMessagesPayload { + chatId: string + messages: WireMessage[] + fromSeq: number + toSeq: number +} + +export const sendMessages = defineInvokeEventa('chat:send-messages') +export const pullMessages = defineInvokeEventa('chat:pull-messages') +export const newMessages = defineOutboundEventa('chat:new-messages') diff --git a/packages/server-protocol/src/index.ts b/packages/server-protocol/src/index.ts new file mode 100644 index 000000000..9cce78989 --- /dev/null +++ b/packages/server-protocol/src/index.ts @@ -0,0 +1,2 @@ +export * from './events/chat' +export * from './types' diff --git a/packages/server-shared/src/types/index.ts b/packages/server-protocol/src/types/index.ts similarity index 100% rename from packages/server-shared/src/types/index.ts rename to packages/server-protocol/src/types/index.ts diff --git a/packages/server-shared/src/types/websocket/events.ts b/packages/server-protocol/src/types/websocket/events.ts similarity index 100% rename from packages/server-shared/src/types/websocket/events.ts rename to packages/server-protocol/src/types/websocket/events.ts diff --git a/packages/server-shared/src/types/websocket/index.ts b/packages/server-protocol/src/types/websocket/index.ts similarity index 100% rename from packages/server-shared/src/types/websocket/index.ts rename to packages/server-protocol/src/types/websocket/index.ts diff --git a/packages/server-protocol/tsconfig.json b/packages/server-protocol/tsconfig.json new file mode 100644 index 000000000..00dcfd807 --- /dev/null +++ b/packages/server-protocol/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "target": "ESNext", + "lib": [ + "ESNext" + ], + "module": "ESNext", + "moduleResolution": "bundler", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "isolatedModules": true, + "verbatimModuleSyntax": true, + "skipLibCheck": true + }, + "include": [ + "src/**/*.ts" + ] +} diff --git a/packages/server-protocol/tsdown.config.ts b/packages/server-protocol/tsdown.config.ts new file mode 100644 index 000000000..ddbc43fc5 --- /dev/null +++ b/packages/server-protocol/tsdown.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from 'tsdown' + +export default defineConfig({ + entry: { + 'index': 'src/index.ts', + 'types/index': 'src/types/index.ts', + }, + sourcemap: true, + unused: true, + inlineOnly: false, +}) diff --git a/packages/server-runtime/package.json b/packages/server-runtime/package.json index 67b3d6fbe..59d95e84e 100644 --- a/packages/server-runtime/package.json +++ b/packages/server-runtime/package.json @@ -41,6 +41,7 @@ "dependencies": { "@guiiai/logg": "catalog:", "@moeru/std": "catalog:", + "@proj-airi/server-protocol": "workspace:^", "@proj-airi/server-shared": "workspace:^", "crossws": "^0.4.4", "h3": "^2.0.1-rc.19", diff --git a/packages/server-runtime/src/index.ts b/packages/server-runtime/src/index.ts index 8270e13c4..a7bd994d4 100644 --- a/packages/server-runtime/src/index.ts +++ b/packages/server-runtime/src/index.ts @@ -1,4 +1,4 @@ -import type { MetadataEventSource, WebSocketEvent } from '@proj-airi/server-shared/types' +import type { MetadataEventSource, WebSocketEvent } from '@proj-airi/server-protocol' import type { RouteContext, @@ -11,11 +11,11 @@ import type { AuthenticatedPeer, Peer } from './types' import { timingSafeEqual } from 'node:crypto' import { availableLogLevelStrings, Format, LogLevelString, logLevelStringToLogLevelMap, useLogg } from '@guiiai/logg' +import { MessageHeartbeat, MessageHeartbeatKind, WebSocketEventSource } from '@proj-airi/server-protocol' import { createInvalidJsonServerErrorMessage, ServerErrorMessages, } from '@proj-airi/server-shared' -import { MessageHeartbeat, MessageHeartbeatKind, WebSocketEventSource } from '@proj-airi/server-shared/types' import { defineWebSocketHandler, H3 } from 'h3' import { nanoid } from 'nanoid' import { parse, stringify } from 'superjson' diff --git a/packages/server-runtime/src/middlewares/route.test.ts b/packages/server-runtime/src/middlewares/route.test.ts index 05fb0ced4..2c7c641df 100644 --- a/packages/server-runtime/src/middlewares/route.test.ts +++ b/packages/server-runtime/src/middlewares/route.test.ts @@ -1,4 +1,4 @@ -import type { RouteTargetExpression, WebSocketBaseEvent, WebSocketEventOf, WebSocketEvents } from '@proj-airi/server-shared/types' +import type { RouteTargetExpression, WebSocketBaseEvent, WebSocketEventOf, WebSocketEvents } from '@proj-airi/server-protocol' import type { AuthenticatedPeer } from '../types' diff --git a/packages/server-runtime/src/middlewares/route.ts b/packages/server-runtime/src/middlewares/route.ts index 0bbef2960..f0c215549 100644 --- a/packages/server-runtime/src/middlewares/route.ts +++ b/packages/server-runtime/src/middlewares/route.ts @@ -1,4 +1,4 @@ -import type { RouteTargetExpression, WebSocketEvent } from '@proj-airi/server-shared/types' +import type { RouteTargetExpression, WebSocketEvent } from '@proj-airi/server-protocol' import type { AuthenticatedPeer } from '../types' diff --git a/packages/server-runtime/src/middlewares/route/match-expression.ts b/packages/server-runtime/src/middlewares/route/match-expression.ts index a1d6201fb..3fe59e458 100644 --- a/packages/server-runtime/src/middlewares/route/match-expression.ts +++ b/packages/server-runtime/src/middlewares/route/match-expression.ts @@ -1,4 +1,4 @@ -import type { RouteTargetExpression } from '@proj-airi/server-shared/types' +import type { RouteTargetExpression } from '@proj-airi/server-protocol' import type { AuthenticatedPeer } from '../../types' diff --git a/packages/server-runtime/src/types/conn.ts b/packages/server-runtime/src/types/conn.ts index e5702c1aa..495ec69c7 100644 --- a/packages/server-runtime/src/types/conn.ts +++ b/packages/server-runtime/src/types/conn.ts @@ -1,4 +1,4 @@ -import type { MetadataEventSource } from '@proj-airi/server-shared/types' +import type { MetadataEventSource } from '@proj-airi/server-protocol' export interface Peer { /** diff --git a/packages/server-runtime/tsconfig.json b/packages/server-runtime/tsconfig.json index 7ac9034c1..6e8cedbfe 100644 --- a/packages/server-runtime/tsconfig.json +++ b/packages/server-runtime/tsconfig.json @@ -8,11 +8,11 @@ "module": "ESNext", "moduleResolution": "bundler", "paths": { - "@proj-airi/server-shared": [ - "../server-shared/src/index.ts" + "@proj-airi/server-protocol": [ + "../server-protocol/src/index.ts" ], - "@proj-airi/server-shared/*": [ - "../server-shared/src/*" + "@proj-airi/server-protocol/*": [ + "../server-protocol/src/*" ] }, "esModuleInterop": true, diff --git a/packages/server-sdk/package.json b/packages/server-sdk/package.json index 6d709e0e2..fc4104bfd 100644 --- a/packages/server-sdk/package.json +++ b/packages/server-sdk/package.json @@ -39,6 +39,7 @@ "dependencies": { "@moeru/eventa": "catalog:", "@moeru/std": "catalog:", + "@proj-airi/server-protocol": "workspace:^", "@proj-airi/server-shared": "workspace:^", "crossws": "^0.4.4", "superjson": "catalog:" diff --git a/packages/server-sdk/src/client.ts b/packages/server-sdk/src/client.ts index c57e866ac..b017c304d 100644 --- a/packages/server-sdk/src/client.ts +++ b/packages/server-sdk/src/client.ts @@ -6,14 +6,14 @@ import type { WebSocketEvent, WebSocketEventOptionalSource, WebSocketEvents, -} from '@proj-airi/server-shared/types' +} from '@proj-airi/server-protocol' import WebSocket from 'crossws/websocket' import superjson from 'superjson' import { errorMessageFrom, sleep } from '@moeru/std' +import { MessageHeartbeat, MessageHeartbeatKind } from '@proj-airi/server-protocol' import { isTerminalAuthenticationServerErrorMessage, parseServerErrorMessage } from '@proj-airi/server-shared' -import { MessageHeartbeat, MessageHeartbeatKind } from '@proj-airi/server-shared/types' export type ClientStatus = | 'idle' diff --git a/packages/server-sdk/src/events/chat.ts b/packages/server-sdk/src/events/chat.ts index ad14cd3e8..2b9041801 100644 --- a/packages/server-sdk/src/events/chat.ts +++ b/packages/server-sdk/src/events/chat.ts @@ -1,45 +1 @@ -import { defineInvokeEventa, defineOutboundEventa } from '@moeru/eventa' - -export interface WireMessage { - id: string - chatId: string - senderId: string | null - role: 'system' | 'user' | 'assistant' | 'tool' | 'error' - content: string - seq: number - createdAt: number - updatedAt: number -} - -export type MessageRole = WireMessage['role'] - -export interface SendMessagesRequest { - chatId: string - messages: { id: string, role: string, content: string }[] -} - -export interface SendMessagesResponse { - seq: number -} - -export interface PullMessagesRequest { - chatId: string - afterSeq: number - limit?: number -} - -export interface PullMessagesResponse { - messages: WireMessage[] - seq: number -} - -export interface NewMessagesPayload { - chatId: string - messages: WireMessage[] - fromSeq: number - toSeq: number -} - -export const sendMessages = defineInvokeEventa('chat:send-messages') -export const pullMessages = defineInvokeEventa('chat:pull-messages') -export const newMessages = defineOutboundEventa('chat:new-messages') +export * from '@proj-airi/server-protocol' diff --git a/packages/server-sdk/src/index.ts b/packages/server-sdk/src/index.ts index 8b471545a..49df2a0ec 100644 --- a/packages/server-sdk/src/index.ts +++ b/packages/server-sdk/src/index.ts @@ -1,4 +1,4 @@ export * from './client' export * from './events/chat' -export type * from '@proj-airi/server-shared/types' -export { ContextUpdateStrategy, WebSocketEventSource } from '@proj-airi/server-shared/types' +export type * from '@proj-airi/server-protocol' +export { ContextUpdateStrategy, WebSocketEventSource } from '@proj-airi/server-protocol' diff --git a/packages/server-sdk/test/client.test.ts b/packages/server-sdk/test/client.test.ts index 653b67373..2d9fdbcb2 100644 --- a/packages/server-sdk/test/client.test.ts +++ b/packages/server-sdk/test/client.test.ts @@ -1,4 +1,4 @@ -import type { WebSocketEvent } from '@proj-airi/server-shared/types' +import type { WebSocketEvent } from '@proj-airi/server-protocol' import superjson from 'superjson' diff --git a/packages/server-shared/README.md b/packages/server-shared/README.md index b84ee5c5d..bcefc30b6 100644 --- a/packages/server-shared/README.md +++ b/packages/server-shared/README.md @@ -1,6 +1,6 @@ # @proj-airi/server-shared -The shared type definitions for all server-side packages of Project AIRI. +Server-side shared utilities for AIRI runtimes. ## Usage @@ -12,50 +12,9 @@ npm i @proj-airi/server-shared -D ``` ```typescript -import type { WebSocketEvents } from '@proj-airi/server-shared' +import { parseServerErrorMessage } from '@proj-airi/server-shared' ``` -## How to use the events in distributed use cases? - -### Scenarios - -#### Minecraft agent - -##### 1. Urgent combat (witch attack) - -- Minecraft sends `spark:notify` (kind=alarm, urgency=immediate, payload hp/location/gear, destinations=["character"]). -- Character `spark:emit` working ("Seen it"). -- Character issues `spark:command` with interrupt=force and options (retreat vs push). -- Minecraft `spark:emit` working ("Pillared up; healing") then done/blocked as it executes. -- Optional `context:update` for summary/memory. - -##### 2. Prep plan (Ender Dragon) - -- Discord/user intent triggers character `spark:command` to Minecraft (intent=plan, interrupt=soft, steps gather beds/pots/gear, fallback). -- Optional `context:update` with tips (lane='game'). -- Minecraft streams `spark:emit` progress. -- If ambushed, Minecraft raises new `spark:notify` (alarm/immediate) to preempt. -- Character revises with another `spark:command`. -- Completion via `spark:emit` done + summary note. - -##### 3. Routine nudge - -- Minecraft signals low food via `spark:notify` (kind=reminder, urgency=soon, destinations=["character"]). -- Character defers to next tick and sends `spark:command` (interrupt=soft, intent=plan: "gather food nearby"). -- Minecraft `spark:emit` queued/working then done. - -##### 4. Multi-step command while researching (plan + live control) - -> [!NOTE] -> Using `intent=plan` keeps the loop alive even with un-finalized ideas—similar to TODO scaffolding in coding agents—while richer guidance is still being researched. - -- Character receives a user goal (e.g., fortify base) and issues an initial `spark:command` to Minecraft (interrupt=soft, intent=plan, steps to gather materials) so the agent keeps working. -- Character simultaneously performs memory/search/design tasks outside the game loop (wiki lookup, prior notes). -- As insights arrive, character sends `context:update` (lane='game', hints/ideas) to enrich the sub-agent without preemption. -- If an urgent event occurs during prep, Minecraft raises `spark:notify` (alarm) → character responds with a short `spark:emit` working and a `spark:command` (interrupt=force) to handle it (e.g., retreat, block up). -- Once design is ready, character sends a refined `spark:command` (`intent=proposal` (or `action`), `interrupt=soft`) with structured options/steps/fallbacks. -- Minecraft streams `spark:emit` progress; when complete, character summarizes via `spark:emit` or `context:update` for memory. - ## License [MIT](../../LICENSE) diff --git a/packages/server-shared/package.json b/packages/server-shared/package.json index 8537077f3..770f7230d 100644 --- a/packages/server-shared/package.json +++ b/packages/server-shared/package.json @@ -18,10 +18,6 @@ ".": { "types": "./dist/index.d.mts", "default": "./dist/index.mjs" - }, - "./types": { - "types": "./dist/types/index.d.mts", - "default": "./dist/types/index.mjs" } }, "main": "./dist/index.mjs", @@ -36,7 +32,5 @@ "build": "tsdown", "typecheck": "tsc --noEmit" }, - "dependencies": { - "@proj-airi/plugin-protocol": "workspace:*" - } + "dependencies": {} } diff --git a/packages/server-shared/src/index.ts b/packages/server-shared/src/index.ts index 979493029..183e8bd09 100644 --- a/packages/server-shared/src/index.ts +++ b/packages/server-shared/src/index.ts @@ -1,2 +1 @@ export * from './errors' -export * from './types' diff --git a/packages/server-shared/tsdown.config.ts b/packages/server-shared/tsdown.config.ts index ddbc43fc5..e69541b02 100644 --- a/packages/server-shared/tsdown.config.ts +++ b/packages/server-shared/tsdown.config.ts @@ -2,8 +2,7 @@ import { defineConfig } from 'tsdown' export default defineConfig({ entry: { - 'index': 'src/index.ts', - 'types/index': 'src/types/index.ts', + index: 'src/index.ts', }, sourcemap: true, unused: true, diff --git a/packages/stage-pages/package.json b/packages/stage-pages/package.json index efd865941..01c4009b2 100644 --- a/packages/stage-pages/package.json +++ b/packages/stage-pages/package.json @@ -26,6 +26,7 @@ "@moeru/std": "catalog:", "@proj-airi/ccc": "workspace:*", "@proj-airi/i18n": "workspace:*", + "@proj-airi/server-protocol": "workspace:*", "@proj-airi/server-sdk": "workspace:*", "@proj-airi/stage-ui": "workspace:*", "@proj-airi/stage-ui-live2d": "workspace:*", diff --git a/packages/stage-pages/src/pages/devtools/context-flow/components/context-flow-actions.vue b/packages/stage-pages/src/pages/devtools/context-flow/components/context-flow-actions.vue index 8ac547684..1dca0bdeb 100644 --- a/packages/stage-pages/src/pages/devtools/context-flow/components/context-flow-actions.vue +++ b/packages/stage-pages/src/pages/devtools/context-flow/components/context-flow-actions.vue @@ -1,5 +1,5 @@