diff --git a/extensions/qa-channel/src/protocol.ts b/extensions/qa-channel/src/protocol.ts index 8be30daea7f..6ded2033ae1 100644 --- a/extensions/qa-channel/src/protocol.ts +++ b/extensions/qa-channel/src/protocol.ts @@ -1 +1,173 @@ -export type * from "openclaw/plugin-sdk/qa-channel-protocol"; +export type QaBusConversationKind = "direct" | "channel"; + +export type QaBusConversation = { + id: string; + kind: QaBusConversationKind; + title?: string; +}; + +export type QaBusAttachment = { + id: string; + kind: "image" | "video" | "audio" | "file"; + mimeType: string; + fileName?: string; + inline?: boolean; + url?: string; + contentBase64?: string; + width?: number; + height?: number; + durationMs?: number; + altText?: string; + transcript?: string; +}; + +export type QaBusMessage = { + id: string; + accountId: string; + direction: "inbound" | "outbound"; + conversation: QaBusConversation; + senderId: string; + senderName?: string; + text: string; + timestamp: number; + threadId?: string; + threadTitle?: string; + replyToId?: string; + deleted?: boolean; + editedAt?: number; + attachments?: QaBusAttachment[]; + reactions: Array<{ + emoji: string; + senderId: string; + timestamp: number; + }>; +}; + +export type QaBusThread = { + id: string; + accountId: string; + conversationId: string; + title: string; + createdAt: number; + createdBy: string; +}; + +export type QaBusEvent = + | { cursor: number; kind: "inbound-message"; accountId: string; message: QaBusMessage } + | { cursor: number; kind: "outbound-message"; accountId: string; message: QaBusMessage } + | { cursor: number; kind: "thread-created"; accountId: string; thread: QaBusThread } + | { cursor: number; kind: "message-edited"; accountId: string; message: QaBusMessage } + | { cursor: number; kind: "message-deleted"; accountId: string; message: QaBusMessage } + | { + cursor: number; + kind: "reaction-added"; + accountId: string; + message: QaBusMessage; + emoji: string; + senderId: string; + }; + +export type QaBusInboundMessageInput = { + accountId?: string; + conversation: QaBusConversation; + senderId: string; + senderName?: string; + text: string; + timestamp?: number; + threadId?: string; + threadTitle?: string; + replyToId?: string; + attachments?: QaBusAttachment[]; +}; + +export type QaBusOutboundMessageInput = { + accountId?: string; + to: string; + senderId?: string; + senderName?: string; + text: string; + timestamp?: number; + threadId?: string; + replyToId?: string; + attachments?: QaBusAttachment[]; +}; + +export type QaBusCreateThreadInput = { + accountId?: string; + conversationId: string; + title: string; + createdBy?: string; + timestamp?: number; +}; + +export type QaBusReactToMessageInput = { + accountId?: string; + messageId: string; + emoji: string; + senderId?: string; + timestamp?: number; +}; + +export type QaBusEditMessageInput = { + accountId?: string; + messageId: string; + text: string; + timestamp?: number; +}; + +export type QaBusDeleteMessageInput = { + accountId?: string; + messageId: string; + timestamp?: number; +}; + +export type QaBusSearchMessagesInput = { + accountId?: string; + query?: string; + conversationId?: string; + threadId?: string; + limit?: number; +}; + +export type QaBusReadMessageInput = { + accountId?: string; + messageId: string; +}; + +export type QaBusPollInput = { + accountId?: string; + cursor?: number; + timeoutMs?: number; + limit?: number; +}; + +export type QaBusPollResult = { + cursor: number; + events: QaBusEvent[]; +}; + +export type QaBusStateSnapshot = { + cursor: number; + conversations: QaBusConversation[]; + threads: QaBusThread[]; + messages: QaBusMessage[]; + events: QaBusEvent[]; +}; + +export type QaBusWaitForInput = + | { + timeoutMs?: number; + kind: "event-kind"; + eventKind: QaBusEvent["kind"]; + } + | { + timeoutMs?: number; + kind: "message-text"; + textIncludes: string; + direction?: QaBusMessage["direction"]; + } + | { + timeoutMs?: number; + kind: "thread-id"; + threadId: string; + }; diff --git a/extensions/qa-lab/package.json b/extensions/qa-lab/package.json index 7e864ab2de3..95fb99b3bc1 100644 --- a/extensions/qa-lab/package.json +++ b/extensions/qa-lab/package.json @@ -7,6 +7,7 @@ "dependencies": { "@copilotkit/aimock": "1.15.1", "@modelcontextprotocol/sdk": "1.29.0", + "@openclaw/qa-channel": "workspace:*", "playwright-core": "1.59.1", "yaml": "^2.8.3", "zod": "^4.3.6" diff --git a/extensions/qa-lab/src/lab-server.test.ts b/extensions/qa-lab/src/lab-server.test.ts index 038da1da940..c66841dcb2e 100644 --- a/extensions/qa-lab/src/lab-server.test.ts +++ b/extensions/qa-lab/src/lab-server.test.ts @@ -6,7 +6,7 @@ import { setTimeout as sleep } from "node:timers/promises"; import { afterEach, describe, expect, it, vi } from "vitest"; import { startQaLabServer } from "./lab-server.js"; -vi.mock("openclaw/plugin-sdk/qa-channel", async () => await import("../../qa-channel/api.js")); +vi.mock("@openclaw/qa-channel/api.js", async () => await import("../../qa-channel/api.js")); const captureMock = vi.hoisted(() => { const sessions: Array> = []; diff --git a/extensions/qa-lab/src/protocol.ts b/extensions/qa-lab/src/protocol.ts new file mode 100644 index 00000000000..6ded2033ae1 --- /dev/null +++ b/extensions/qa-lab/src/protocol.ts @@ -0,0 +1,173 @@ +export type QaBusConversationKind = "direct" | "channel"; + +export type QaBusConversation = { + id: string; + kind: QaBusConversationKind; + title?: string; +}; + +export type QaBusAttachment = { + id: string; + kind: "image" | "video" | "audio" | "file"; + mimeType: string; + fileName?: string; + inline?: boolean; + url?: string; + contentBase64?: string; + width?: number; + height?: number; + durationMs?: number; + altText?: string; + transcript?: string; +}; + +export type QaBusMessage = { + id: string; + accountId: string; + direction: "inbound" | "outbound"; + conversation: QaBusConversation; + senderId: string; + senderName?: string; + text: string; + timestamp: number; + threadId?: string; + threadTitle?: string; + replyToId?: string; + deleted?: boolean; + editedAt?: number; + attachments?: QaBusAttachment[]; + reactions: Array<{ + emoji: string; + senderId: string; + timestamp: number; + }>; +}; + +export type QaBusThread = { + id: string; + accountId: string; + conversationId: string; + title: string; + createdAt: number; + createdBy: string; +}; + +export type QaBusEvent = + | { cursor: number; kind: "inbound-message"; accountId: string; message: QaBusMessage } + | { cursor: number; kind: "outbound-message"; accountId: string; message: QaBusMessage } + | { cursor: number; kind: "thread-created"; accountId: string; thread: QaBusThread } + | { cursor: number; kind: "message-edited"; accountId: string; message: QaBusMessage } + | { cursor: number; kind: "message-deleted"; accountId: string; message: QaBusMessage } + | { + cursor: number; + kind: "reaction-added"; + accountId: string; + message: QaBusMessage; + emoji: string; + senderId: string; + }; + +export type QaBusInboundMessageInput = { + accountId?: string; + conversation: QaBusConversation; + senderId: string; + senderName?: string; + text: string; + timestamp?: number; + threadId?: string; + threadTitle?: string; + replyToId?: string; + attachments?: QaBusAttachment[]; +}; + +export type QaBusOutboundMessageInput = { + accountId?: string; + to: string; + senderId?: string; + senderName?: string; + text: string; + timestamp?: number; + threadId?: string; + replyToId?: string; + attachments?: QaBusAttachment[]; +}; + +export type QaBusCreateThreadInput = { + accountId?: string; + conversationId: string; + title: string; + createdBy?: string; + timestamp?: number; +}; + +export type QaBusReactToMessageInput = { + accountId?: string; + messageId: string; + emoji: string; + senderId?: string; + timestamp?: number; +}; + +export type QaBusEditMessageInput = { + accountId?: string; + messageId: string; + text: string; + timestamp?: number; +}; + +export type QaBusDeleteMessageInput = { + accountId?: string; + messageId: string; + timestamp?: number; +}; + +export type QaBusSearchMessagesInput = { + accountId?: string; + query?: string; + conversationId?: string; + threadId?: string; + limit?: number; +}; + +export type QaBusReadMessageInput = { + accountId?: string; + messageId: string; +}; + +export type QaBusPollInput = { + accountId?: string; + cursor?: number; + timeoutMs?: number; + limit?: number; +}; + +export type QaBusPollResult = { + cursor: number; + events: QaBusEvent[]; +}; + +export type QaBusStateSnapshot = { + cursor: number; + conversations: QaBusConversation[]; + threads: QaBusThread[]; + messages: QaBusMessage[]; + events: QaBusEvent[]; +}; + +export type QaBusWaitForInput = + | { + timeoutMs?: number; + kind: "event-kind"; + eventKind: QaBusEvent["kind"]; + } + | { + timeoutMs?: number; + kind: "message-text"; + textIncludes: string; + direction?: QaBusMessage["direction"]; + } + | { + timeoutMs?: number; + kind: "thread-id"; + threadId: string; + }; diff --git a/extensions/qa-lab/src/runtime-api.ts b/extensions/qa-lab/src/runtime-api.ts index 3a6e2499ec9..de44ddf1bdf 100644 --- a/extensions/qa-lab/src/runtime-api.ts +++ b/extensions/qa-lab/src/runtime-api.ts @@ -20,7 +20,7 @@ export { searchQaBusMessages, sendQaBusMessage, setQaChannelRuntime, -} from "openclaw/plugin-sdk/qa-channel"; +} from "@openclaw/qa-channel/api.js"; export type { QaBusAttachment, QaBusConversation, @@ -39,4 +39,4 @@ export type { QaBusStateSnapshot, QaBusThread, QaBusWaitForInput, -} from "openclaw/plugin-sdk/qa-channel"; +} from "./protocol.js"; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e6758a1e05b..242eba3ad4b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1117,6 +1117,9 @@ importers: '@modelcontextprotocol/sdk': specifier: 1.29.0 version: 1.29.0(zod@4.3.6) + '@openclaw/qa-channel': + specifier: workspace:* + version: link:../qa-channel playwright-core: specifier: 1.59.1 version: 1.59.1 diff --git a/scripts/e2e/npm-telegram-live-docker.sh b/scripts/e2e/npm-telegram-live-docker.sh index 1fa6946da86..143fd7809b6 100755 --- a/scripts/e2e/npm-telegram-live-docker.sh +++ b/scripts/e2e/npm-telegram-live-docker.sh @@ -232,6 +232,9 @@ ln -sfnT "$openclaw_package_dir/dist" /app/dist cp "$openclaw_package_dir/package.json" /app/package.json rm -rf "$openclaw_package_dir/extensions" ln -sfnT /app/extensions "$openclaw_package_dir/extensions" +mkdir -p /app/node_modules/@openclaw +rm -rf /app/node_modules/@openclaw/qa-channel +ln -sfnT /app/extensions/qa-channel /app/node_modules/@openclaw/qa-channel node --input-type=module <<'NODE' import fs from "node:fs"; @@ -241,14 +244,6 @@ for (const packageJsonPath of [ ]) { const pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf8")); pkg.exports = pkg.exports && typeof pkg.exports === "object" ? pkg.exports : {}; - pkg.exports["./plugin-sdk/qa-channel"] = { - types: "./extensions/qa-channel/api.ts", - default: "./extensions/qa-channel/api.ts", - }; - pkg.exports["./plugin-sdk/qa-channel-protocol"] = { - types: "./extensions/qa-channel/src/protocol.ts", - default: "./extensions/qa-channel/src/protocol.ts", - }; if (!pkg.exports["./plugin-sdk/gateway-runtime"]) { pkg.exports["./plugin-sdk/gateway-runtime"] = { types: "./dist/plugin-sdk/gateway-runtime.d.ts", diff --git a/scripts/lib/plugin-sdk-private-local-only-subpaths.json b/scripts/lib/plugin-sdk-private-local-only-subpaths.json index 5e7ea8c64ca..6b4a7af24a7 100644 --- a/scripts/lib/plugin-sdk-private-local-only-subpaths.json +++ b/scripts/lib/plugin-sdk-private-local-only-subpaths.json @@ -1 +1 @@ -["qa-channel", "qa-channel-protocol", "qa-lab", "qa-runtime"] +["qa-lab", "qa-runtime"]