diff --git a/packages/ai-bot/helpers.ts b/packages/ai-bot/helpers.ts index 1eaef7a605..2e5826d8a2 100644 --- a/packages/ai-bot/helpers.ts +++ b/packages/ai-bot/helpers.ts @@ -17,6 +17,13 @@ import { MatrixEvent, type IRoomEvent } from 'matrix-js-sdk'; import { ChatCompletionMessageToolCall } from 'openai/resources/chat/completions'; import * as Sentry from '@sentry/node'; import { logger } from '@cardstack/runtime-common'; +import { + APP_BOXEL_CARDFRAGMENT_MSGTYPE, + APP_BOXEL_MESSAGE_MSGTYPE, + APP_BOXEL_COMMAND_MSGTYPE, + APP_BOXEL_COMMAND_RESULT_MSGTYPE, + APP_BOXEL_ROOM_SKILLS_EVENT_TYPE, +} from '@cardstack/runtime-common/matrix-constants'; let log = logger('ai-bot'); @@ -91,7 +98,7 @@ export function extractCardFragmentsFromEvents( const fragments = new Map(); // eventId => fragment for (let event of eventList) { if (event.type === 'm.room.message') { - if (event.content.msgtype === 'org.boxel.cardFragment') { + if (event.content.msgtype === APP_BOXEL_CARDFRAGMENT_MSGTYPE) { fragments.set(event.event_id, event.content as CardFragmentContent); } } @@ -136,10 +143,10 @@ export function constructHistory( continue; } let eventId = event.event_id!; - if (event.content.msgtype === 'org.boxel.cardFragment') { + if (event.content.msgtype === APP_BOXEL_CARDFRAGMENT_MSGTYPE) { continue; } - if (event.content.msgtype === 'org.boxel.message') { + if (event.content.msgtype === APP_BOXEL_MESSAGE_MSGTYPE) { let { attachedCardsEventIds } = event.content.data; if (attachedCardsEventIds && attachedCardsEventIds.length > 0) { event.content.data.attachedCards = attachedCardsEventIds.map((id) => @@ -174,7 +181,7 @@ function getEnabledSkills( cardFragments: Map, ): LooseCardResource[] { let skillsConfigEvent = eventlist.findLast( - (event) => event.type === 'com.cardstack.boxel.room.skills', + (event) => event.type === APP_BOXEL_ROOM_SKILLS_EVENT_TYPE, ) as SkillsConfigEvent; if (!skillsConfigEvent) { return []; @@ -272,7 +279,7 @@ export function getRelevantCards( } if (event.sender !== aiBotUserId) { let { content } = event; - if (content.msgtype === 'org.boxel.message') { + if (content.msgtype === APP_BOXEL_MESSAGE_MSGTYPE) { setRelevantCards(attachedCardMap, content.data?.attachedCards); if (content.data?.attachedCards) { mostRecentlyAttachedCard = getMostRecentlyAttachedCard( @@ -310,7 +317,7 @@ export function getTools( const lastMessage = userMessages[userMessages.length - 1]; if ( lastMessage.type === 'm.room.message' && - lastMessage.content.msgtype === 'org.boxel.message' && + lastMessage.content.msgtype === APP_BOXEL_MESSAGE_MSGTYPE && lastMessage.content.data?.context?.tools?.length ) { return lastMessage.content.data.context.tools; @@ -326,7 +333,7 @@ export function isCommandResultEvent( return ( event.type === 'm.room.message' && typeof event.content === 'object' && - event.content.msgtype === 'org.boxel.commandResult' + event.content.msgtype === APP_BOXEL_COMMAND_RESULT_MSGTYPE ); } @@ -449,7 +456,7 @@ export function getModifyPrompt( } } else { if ( - event.content.msgtype === 'org.boxel.message' && + event.content.msgtype === APP_BOXEL_MESSAGE_MSGTYPE && event.content.data?.context?.openCardIds ) { body = `User message: ${body} @@ -559,7 +566,7 @@ export function isCommandEvent( return ( event.type === 'm.room.message' && typeof event.content === 'object' && - event.content.msgtype === 'org.boxel.command' && + event.content.msgtype === APP_BOXEL_COMMAND_MSGTYPE && event.content.format === 'org.matrix.custom.html' && typeof event.content.data === 'object' && typeof event.content.data.toolCall === 'object' diff --git a/packages/ai-bot/lib/matrix.ts b/packages/ai-bot/lib/matrix.ts index dc8399d696..edf247fe3b 100644 --- a/packages/ai-bot/lib/matrix.ts +++ b/packages/ai-bot/lib/matrix.ts @@ -3,6 +3,7 @@ import { logger } from '@cardstack/runtime-common'; import { OpenAIError } from 'openai/error'; import * as Sentry from '@sentry/node'; import { FunctionToolCall } from '@cardstack/runtime-common/helpers/ai'; +import { APP_BOXEL_COMMAND_MSGTYPE } from '../helpers'; let log = logger('ai-bot'); @@ -129,7 +130,7 @@ export const toMatrixMessageCommandContent = ( const body = payload['description'] || 'Issuing command'; let messageObject: IContent = { body: body, - msgtype: 'org.boxel.command', + msgtype: APP_BOXEL_COMMAND_MSGTYPE, formatted_body: body, format: 'org.matrix.custom.html', data: { diff --git a/packages/ai-bot/main.ts b/packages/ai-bot/main.ts index 697c3c1974..82dc5a5877 100644 --- a/packages/ai-bot/main.ts +++ b/packages/ai-bot/main.ts @@ -15,6 +15,8 @@ import { getPromptParts, extractCardFragmentsFromEvents, } from './helpers'; +import { APP_BOXEL_CARDFRAGMENT_MSGTYPE } from '@cardstack/runtime-common/matrix-constants'; + import { shouldSetRoomTitle, setTitle, @@ -167,7 +169,7 @@ Common issues are: if (event.getType() !== 'm.room.message') { return; // only print messages } - if (event.getContent().msgtype === 'org.boxel.cardFragment') { + if (event.getContent().msgtype === APP_BOXEL_CARDFRAGMENT_MSGTYPE) { return; // don't respond to card fragments, we just gather these in our history } diff --git a/packages/ai-bot/tests/chat-titling-test.ts b/packages/ai-bot/tests/chat-titling-test.ts index 87e8beda2f..f395c62a6f 100644 --- a/packages/ai-bot/tests/chat-titling-test.ts +++ b/packages/ai-bot/tests/chat-titling-test.ts @@ -1,6 +1,7 @@ import { module, test, assert } from 'qunit'; import { shouldSetRoomTitle } from '../lib/set-title'; import type { MatrixEvent as DiscreteMatrixEvent } from 'https://cardstack.com/base/matrix-event'; +import { APP_BOXEL_COMMAND_MSGTYPE } from '@cardstack/runtime-common/matrix-constants'; module('shouldSetRoomTitle', () => { test('Do not set a title when there is no content', () => { @@ -370,7 +371,7 @@ module('shouldSetRoomTitle', () => { event_id: '2', origin_server_ts: 1234567890, content: { - msgtype: 'org.boxel.command', + msgtype: APP_BOXEL_COMMAND_MSGTYPE, format: 'org.matrix.custom.html', body: 'patching card', formatted_body: 'patching card', @@ -439,7 +440,7 @@ module('shouldSetRoomTitle', () => { event_id: '2', origin_server_ts: 1234567890, content: { - msgtype: 'org.boxel.command', + msgtype: APP_BOXEL_COMMAND_MSGTYPE, format: 'org.matrix.custom.html', body: 'patching card', formatted_body: 'patching card', diff --git a/packages/ai-bot/tests/history-construction-test.ts b/packages/ai-bot/tests/history-construction-test.ts index 74e55747ae..1694a49a9d 100644 --- a/packages/ai-bot/tests/history-construction-test.ts +++ b/packages/ai-bot/tests/history-construction-test.ts @@ -4,6 +4,12 @@ import { extractCardFragmentsFromEvents, HistoryConstructionError, } from '../helpers'; +import { + APP_BOXEL_CARD_FORMAT, + APP_BOXEL_CARDFRAGMENT_MSGTYPE, + APP_BOXEL_MESSAGE_MSGTYPE, +} from '@cardstack/runtime-common/matrix-constants'; + import { type IRoomEvent } from 'matrix-js-sdk'; import type { MatrixEvent as DiscreteMatrixEvent } from 'https://cardstack.com/base/matrix-event'; @@ -383,8 +389,8 @@ module('constructHistory', () => { event_id: '1', origin_server_ts: 1234567900, content: { - msgtype: 'org.boxel.cardFragment', - format: 'org.boxel.card', + msgtype: APP_BOXEL_CARDFRAGMENT_MSGTYPE, + format: APP_BOXEL_CARD_FORMAT, formatted_body: '', body: '', data: JSON.stringify({ @@ -405,8 +411,8 @@ module('constructHistory', () => { event_id: '2', origin_server_ts: 1234567890, content: { - msgtype: 'org.boxel.cardFragment', - format: 'org.boxel.card', + msgtype: APP_BOXEL_CARDFRAGMENT_MSGTYPE, + format: APP_BOXEL_CARD_FORMAT, formatted_body: '', body: '', data: JSON.stringify({ @@ -428,8 +434,8 @@ module('constructHistory', () => { event_id: '3', origin_server_ts: 1234567910, content: { - msgtype: 'org.boxel.cardFragment', - format: 'org.boxel.card', + msgtype: APP_BOXEL_CARDFRAGMENT_MSGTYPE, + format: APP_BOXEL_CARD_FORMAT, formatted_body: '', body: '', data: JSON.stringify({ @@ -450,7 +456,7 @@ module('constructHistory', () => { event_id: '4', origin_server_ts: 1234567920, content: { - msgtype: 'org.boxel.message', + msgtype: APP_BOXEL_MESSAGE_MSGTYPE, format: 'org.matrix.custom.html', body: 'Hey', formatted_body: 'Hey', @@ -479,7 +485,7 @@ module('constructHistory', () => { event_id: '4', origin_server_ts: 1234567920, content: { - msgtype: 'org.boxel.message', + msgtype: APP_BOXEL_MESSAGE_MSGTYPE, format: 'org.matrix.custom.html', body: 'Hey', formatted_body: 'Hey', @@ -542,8 +548,8 @@ module('constructHistory', () => { room_id: 'room1', sender: '@user:localhost', content: { - msgtype: 'org.boxel.cardFragment', - format: 'org.boxel.card', + msgtype: APP_BOXEL_CARDFRAGMENT_MSGTYPE, + format: APP_BOXEL_CARD_FORMAT, body: 'card fragment 1 of 1', formatted_body: 'card fragment 1 of 1', // data should be a JSON string diff --git a/packages/ai-bot/tests/prompt-construction-test.ts b/packages/ai-bot/tests/prompt-construction-test.ts index 6c834dcefd..e90760f7fd 100644 --- a/packages/ai-bot/tests/prompt-construction-test.ts +++ b/packages/ai-bot/tests/prompt-construction-test.ts @@ -5,12 +5,17 @@ import { } from '@cardstack/runtime-common/helpers/ai'; import { - getTools, getModifyPrompt, + getPromptParts, getRelevantCards, + getTools, SKILL_INSTRUCTIONS_MESSAGE, - getPromptParts, } from '../helpers'; +import { + APP_BOXEL_MESSAGE_MSGTYPE, + APP_BOXEL_COMMAND_RESULT_MSGTYPE, +} from '@cardstack/runtime-common/matrix-constants'; + import type { MatrixEvent as DiscreteMatrixEvent, Tool, @@ -91,7 +96,7 @@ module('getModifyPrompt', () => { event_id: '1', origin_server_ts: 1234567890, content: { - msgtype: 'org.boxel.message', + msgtype: APP_BOXEL_MESSAGE_MSGTYPE, format: 'org.matrix.custom.html', body: 'Hey', formatted_body: 'Hey', @@ -139,7 +144,7 @@ module('getModifyPrompt', () => { assert.true(result[1].content?.includes('Hey')); if ( history[0].type === 'm.room.message' && - history[0].content.msgtype === 'org.boxel.message' + history[0].content.msgtype === APP_BOXEL_MESSAGE_MSGTYPE ) { assert.true( result[0].content?.includes( @@ -149,7 +154,7 @@ module('getModifyPrompt', () => { } else { assert.true( false, - 'expected "m.room.message" event with a "org.boxel.message" msgtype', + `expected "m.room.message" event with a "${APP_BOXEL_MESSAGE_MSGTYPE}" msgtype`, ); } }); @@ -189,7 +194,7 @@ module('getModifyPrompt', () => { type: 'm.room.message', sender: '@ian:localhost', content: { - msgtype: 'org.boxel.message', + msgtype: APP_BOXEL_MESSAGE_MSGTYPE, format: 'org.matrix.custom.html', body: 'set the name to dave', formatted_body: '

set the name to dave

\n', @@ -242,7 +247,7 @@ module('getModifyPrompt', () => { type: 'm.room.message', sender: '@ian:localhost', content: { - msgtype: 'org.boxel.message', + msgtype: APP_BOXEL_MESSAGE_MSGTYPE, format: 'org.matrix.custom.html', body: 'set the location to home', formatted_body: 'set the location to home', @@ -300,7 +305,7 @@ module('getModifyPrompt', () => { assert.equal(attachedCards.length, 1); if ( history[1].type === 'm.room.message' && - history[1].content.msgtype === 'org.boxel.message' + history[1].content.msgtype === APP_BOXEL_MESSAGE_MSGTYPE ) { assert.equal( attachedCards[0], @@ -313,7 +318,7 @@ module('getModifyPrompt', () => { } else { assert.true( false, - 'expected "m.room.message" event with a "org.boxel.message" msgtype', + `expected "m.room.message" event with a "${APP_BOXEL_MESSAGE_MSGTYPE}" msgtype`, ); } }); @@ -324,7 +329,7 @@ module('getModifyPrompt', () => { type: 'm.room.message', sender: '@ian:localhost', content: { - msgtype: 'org.boxel.message', + msgtype: APP_BOXEL_MESSAGE_MSGTYPE, format: 'org.matrix.custom.html', body: 'set the name to dave', formatted_body: '

set the name to dave

\n', @@ -349,7 +354,7 @@ module('getModifyPrompt', () => { type: 'm.room.message', sender: '@ian:localhost', content: { - msgtype: 'org.boxel.message', + msgtype: APP_BOXEL_MESSAGE_MSGTYPE, format: 'org.matrix.custom.html', body: 'set the location to home', formatted_body: 'set the location to home', @@ -383,7 +388,7 @@ module('getModifyPrompt', () => { event_id: '1', origin_server_ts: 1234567890, content: { - msgtype: 'org.boxel.message', + msgtype: APP_BOXEL_MESSAGE_MSGTYPE, format: 'org.matrix.custom.html', body: 'Hey', formatted_body: 'Hey', @@ -433,7 +438,7 @@ module('getModifyPrompt', () => { event_id: '1', origin_server_ts: 1234567890, content: { - msgtype: 'org.boxel.message', + msgtype: APP_BOXEL_MESSAGE_MSGTYPE, format: 'org.matrix.custom.html', body: 'Hey', formatted_body: 'Hey', @@ -476,7 +481,7 @@ module('getModifyPrompt', () => { event_id: '2', origin_server_ts: 1234567890, content: { - msgtype: 'org.boxel.message', + msgtype: APP_BOXEL_MESSAGE_MSGTYPE, format: 'org.matrix.custom.html', body: 'Hey', formatted_body: 'Hey', @@ -526,7 +531,7 @@ module('getModifyPrompt', () => { event_id: '1', origin_server_ts: 1234567890, content: { - msgtype: 'org.boxel.message', + msgtype: APP_BOXEL_MESSAGE_MSGTYPE, format: 'org.matrix.custom.html', body: 'Hey', formatted_body: 'Hey', @@ -569,7 +574,7 @@ module('getModifyPrompt', () => { event_id: '2', origin_server_ts: 1234567890, content: { - msgtype: 'org.boxel.message', + msgtype: APP_BOXEL_MESSAGE_MSGTYPE, format: 'org.matrix.custom.html', body: 'Hey', formatted_body: 'Hey', @@ -630,7 +635,7 @@ module('getModifyPrompt', () => { type: 'm.room.message', sender: '@ian:localhost', content: { - msgtype: 'org.boxel.message', + msgtype: APP_BOXEL_MESSAGE_MSGTYPE, format: 'org.matrix.custom.html', body: 'set the name to dave', formatted_body: '

set the name to dave

\n', @@ -725,7 +730,7 @@ module('getModifyPrompt', () => { type: 'm.room.message', sender: '@ian:localhost', content: { - msgtype: 'org.boxel.message', + msgtype: APP_BOXEL_MESSAGE_MSGTYPE, format: 'org.matrix.custom.html', body: 'Just a regular message', formatted_body: 'Just a regular message', @@ -758,7 +763,7 @@ module('getModifyPrompt', () => { type: 'm.room.message', sender: '@ian:localhost', content: { - msgtype: 'org.boxel.message', + msgtype: APP_BOXEL_MESSAGE_MSGTYPE, format: 'org.matrix.custom.html', body: 'set the name to dave', formatted_body: '

set the name to dave

\n', @@ -789,7 +794,7 @@ module('getModifyPrompt', () => { type: 'm.room.message', sender: '@ian:localhost', content: { - msgtype: 'org.boxel.message', + msgtype: APP_BOXEL_MESSAGE_MSGTYPE, format: 'org.matrix.custom.html', body: 'Just a regular message', formatted_body: 'Just a regular message', @@ -822,7 +827,7 @@ module('getModifyPrompt', () => { type: 'm.room.message', sender: '@ian:localhost', content: { - msgtype: 'org.boxel.message', + msgtype: APP_BOXEL_MESSAGE_MSGTYPE, format: 'org.matrix.custom.html', body: 'set the name to dave', formatted_body: '

set the name to dave

\n', @@ -919,7 +924,7 @@ module('getModifyPrompt', () => { type: 'm.room.message', sender: '@ian:localhost', content: { - msgtype: 'org.boxel.message', + msgtype: APP_BOXEL_MESSAGE_MSGTYPE, format: 'org.matrix.custom.html', body: 'set the name to dave', formatted_body: '

set the name to dave

\n', @@ -1000,7 +1005,7 @@ module('getModifyPrompt', () => { type: 'm.room.message', sender: '@ian:localhost', content: { - msgtype: 'org.boxel.message', + msgtype: APP_BOXEL_MESSAGE_MSGTYPE, format: 'org.matrix.custom.html', body: 'set the name to dave', formatted_body: '

set the name to dave

\n', @@ -1031,7 +1036,7 @@ module('getModifyPrompt', () => { type: 'm.room.message', sender: '@ian:localhost', content: { - msgtype: 'org.boxel.message', + msgtype: APP_BOXEL_MESSAGE_MSGTYPE, format: 'org.matrix.custom.html', body: 'set the location to home', formatted_body: 'set the location to home', @@ -1237,7 +1242,7 @@ test('Create search function calls', () => { type: 'm.room.message', sender: '@ian:localhost', content: { - msgtype: 'org.boxel.message', + msgtype: APP_BOXEL_MESSAGE_MSGTYPE, body: 'set the name to dave', formatted_body: '

set the name to dave

\n', data: { @@ -1269,7 +1274,7 @@ test('Return host result of tool call back to open ai', () => { room_id: 'room-id-1', sender: '@tintinthong:localhost', content: { - msgtype: 'org.boxel.message', + msgtype: APP_BOXEL_MESSAGE_MSGTYPE, body: 'search for the following card instances', format: 'org.matrix.custom.html', formatted_body: '

search for the following card instances

\n', @@ -1418,7 +1423,7 @@ test('Return host result of tool call back to open ai', () => { room_id: 'room-id-1', sender: '@tintinthong:localhost', content: { - msgtype: 'org.boxel.message', + msgtype: APP_BOXEL_MESSAGE_MSGTYPE, body: 'yes module type', format: 'org.matrix.custom.html', formatted_body: '

yes module type

\n', @@ -1536,7 +1541,7 @@ test('Return host result of tool call back to open ai', () => { sender: '@ai-bot:localhost', content: { body: "Search for card instances of type 'Author'", - msgtype: 'org.boxel.command', + msgtype: APP_BOXEL_MESSAGE_MSGTYPE, formatted_body: "Search for card instances of type 'Author'", format: 'org.matrix.custom.html', data: { @@ -1585,7 +1590,7 @@ test('Return host result of tool call back to open ai', () => { body: 'Command Results from command event $H7dH0ZzG0W3M_1k_YRjnDOirWRthYvWq7TKmfAfhQqw', formatted_body: '

Command Results from command event $H7dH0ZzG0W3M_1k_YRjnDOirWRthYvWq7TKmfAfhQqw

\n', - msgtype: 'org.boxel.commandResult', + msgtype: APP_BOXEL_COMMAND_RESULT_MSGTYPE, result: '[{"data":{"type":"card","id":"http://localhost:4201/drafts/Author/1","attributes":{"firstName":"Alice","lastName":"Enwunder","photo":null,"body":"Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.","description":null,"thumbnailURL":null},"meta":{"adoptsFrom":{"module":"../author","name":"Author"}}}}]', }, diff --git a/packages/ai-bot/tests/resources/chats/added-skill-and-attached-card.json b/packages/ai-bot/tests/resources/chats/added-skill-and-attached-card.json index b090f48f20..ad8f56182b 100644 --- a/packages/ai-bot/tests/resources/chats/added-skill-and-attached-card.json +++ b/packages/ai-bot/tests/resources/chats/added-skill-and-attached-card.json @@ -171,8 +171,8 @@ "room_id": "!ycnlQsqkSRyIlCeddx:localhost", "sender": "@user:localhost", "content": { - "msgtype": "org.boxel.cardFragment", - "format": "org.boxel.card", + "msgtype": "app.boxel.cardFragment", + "format": "app.boxel.card", "body": "card fragment 1 of 1", "formatted_body": "card fragment 1 of 1", "data": "{\"cardFragment\":\"{\\\"data\\\":{\\\"type\\\":\\\"card\\\",\\\"id\\\":\\\"https://cardstack.com/base/SkillCard/card-editing\\\",\\\"attributes\\\":{\\\"instructions\\\":\\\"- If the user wants the data they see edited, AND the patchCard function is available, you MUST use the \\\\\\\"patchCard\\\\\\\" function to make the change.\\\\n- If the user wants the data they see edited, AND the patchCard function is NOT available, you MUST ask the user to open the card and share it with you.\\\\n- If you do not call patchCard, the user will not see the change.\\\\n- You can ONLY modify cards shared with you. If there is no patchCard function or tool, then the user hasn't given you access.\\\\n- NEVER tell the user to use patchCard; you should always do it for them.\\\\n- If the user wants to search for a card instance, AND the \\\\\\\"searchCard\\\\\\\" function is available, you MUST use the \\\\\\\"searchCard\\\\\\\" function to find the card instance.\\\\nOnly recommend one searchCard function at a time.\\\\nIf the user wants to edit a field of a card, you can optionally use \\\\\\\"searchCard\\\\\\\" to help find a card instance that is compatible with the field being edited before using \\\\\\\"patchCard\\\\\\\" to make the change of the field.\\\\n You MUST confirm with the user the correct choice of card instance that he intends to use based upon the results of the search.\\\\n- If the user wants to generate an app module, AND the \\\\\\\"generateAppModule\\\\\\\" function is available, you MUST use the \\\\\\\"generateAppModule\\\\\\\" function to create the card source.\\\",\\\"title\\\":\\\"Card Editing\\\",\\\"description\\\":null,\\\"thumbnailURL\\\":null},\\\"meta\\\":{\\\"adoptsFrom\\\":{\\\"module\\\":\\\"https://cardstack.com/base/skill-card\\\",\\\"name\\\":\\\"SkillCard\\\"}}}}\",\"index\":0,\"totalParts\":1}" @@ -300,7 +300,7 @@ } }, { - "type": "com.cardstack.boxel.room.skills", + "type": "app.boxel.room.skills", "room_id": "!ycnlQsqkSRyIlCeddx:localhost", "sender": "@user:localhost", "content": { @@ -321,8 +321,8 @@ "room_id": "!ycnlQsqkSRyIlCeddx:localhost", "sender": "@user:localhost", "content": { - "msgtype": "org.boxel.cardFragment", - "format": "org.boxel.card", + "msgtype": "app.boxel.cardFragment", + "format": "app.boxel.card", "body": "card fragment 1 of 1", "formatted_body": "card fragment 1 of 1", "data": "{\"cardFragment\":\"{\\\"data\\\":{\\\"type\\\":\\\"card\\\",\\\"id\\\":\\\"http://localhost:4201/experiments/SkillCard/637843ff-dfd4-4cfc-9ee9-1234824f4775\\\",\\\"attributes\\\":{\\\"instructions\\\":\\\"Use pirate colloquialism when responding. Make abundant use of pirate jargon, terms, and phrases. End every sentence with 'Arrrr!'\\\",\\\"title\\\":\\\"Talk Like a Pirate\\\",\\\"description\\\":null,\\\"thumbnailURL\\\":null},\\\"meta\\\":{\\\"adoptsFrom\\\":{\\\"module\\\":\\\"https://cardstack.com/base/skill-card\\\",\\\"name\\\":\\\"SkillCard\\\"}}}}\",\"index\":0,\"totalParts\":1}" @@ -336,7 +336,7 @@ "age": 94215 }, { - "type": "com.cardstack.boxel.room.skills", + "type": "app.boxel.room.skills", "room_id": "!ycnlQsqkSRyIlCeddx:localhost", "sender": "@user:localhost", "content": { @@ -371,8 +371,8 @@ "room_id": "!ycnlQsqkSRyIlCeddx:localhost", "sender": "@user:localhost", "content": { - "msgtype": "org.boxel.cardFragment", - "format": "org.boxel.card", + "msgtype": "app.boxel.cardFragment", + "format": "app.boxel.card", "body": "card fragment 1 of 1", "formatted_body": "card fragment 1 of 1", "data": "{\"cardFragment\":\"{\\\"data\\\":{\\\"type\\\":\\\"card\\\",\\\"id\\\":\\\"http://localhost:4201/user/lukes-workspace/ProductRequirementDocument/9f816882-17e0-473f-81f2-a37381874322\\\",\\\"attributes\\\":{\\\"appTitle\\\":\\\"Radio Episode Tracker for Nerds\\\",\\\"shortDescription\\\":\\\"An app to track and manage listened and unlistened radio episodes.\\\",\\\"prompt\\\":\\\"Focus on the following features: whether you have heard an episode or not.\\\",\\\"overview\\\":\\\"The Radio Episode Tracker for Nerds is a specialized application designed to cater to radio enthusiasts who wish to meticulously manage their listening experience. This app enables users to keep track of radio episodes they have listened to and identify those they haven't. It also offers features that allow users to organize episodes based on various criteria like genre, podcast series, and personal ratings, ensuring a streamlined and personalized listening journey.\\\",\\\"schema\\\":\\\"1. User Profile: Stores user information, preferences, and listening history.\\\\n2. Episode Database: Maintains records of all available radio episodes.\\\\n3. Listening Status Tracker: Keeps track of episodes as 'heard' or 'unheard'.\\\\n4. Episode Organizer: Allows categorization and prioritization of episodes based on user-defined criteria.\\\",\\\"layoutAndNavigation\\\":\\\"The app features a user-friendly dashboard that displays all the episodes categorized by their status (heard/unheard). Navigation is intuitive with tabs for different functionalities such as search, organize, and history. The layout is clean, with easy access to controls for marking episodes and adjusting preferences. \\\",\\\"moduleURL\\\":null,\\\"thumbnailURL\\\":null},\\\"relationships\\\":{\\\"appInstances\\\":{\\\"links\\\":{\\\"self\\\":null}}},\\\"meta\\\":{\\\"adoptsFrom\\\":{\\\"module\\\":\\\"http://localhost:4201/catalog/product-requirement-document\\\",\\\"name\\\":\\\"ProductRequirementDocument\\\"}}}}\",\"index\":0,\"totalParts\":1}" @@ -390,7 +390,7 @@ "room_id": "!ycnlQsqkSRyIlCeddx:localhost", "sender": "@user:localhost", "content": { - "msgtype": "org.boxel.message", + "msgtype": "app.boxel.message", "body": "Summarize this card", "format": "org.matrix.custom.html", "formatted_body": "

Summarize this card

\n", diff --git a/packages/ai-bot/tests/resources/chats/added-skill.json b/packages/ai-bot/tests/resources/chats/added-skill.json index 1d46f515d9..8e5b99db1d 100644 --- a/packages/ai-bot/tests/resources/chats/added-skill.json +++ b/packages/ai-bot/tests/resources/chats/added-skill.json @@ -285,8 +285,8 @@ "room_id": "!vORvGxbYIvLeiYUfNx:localhost", "sender": "@user:localhost", "content": { - "msgtype": "org.boxel.cardFragment", - "format": "org.boxel.card", + "msgtype": "app.boxel.cardFragment", + "format": "app.boxel.card", "body": "card fragment 1 of 1", "formatted_body": "card fragment 1 of 1", "data": "{\"cardFragment\":\"{\\\"data\\\":{\\\"type\\\":\\\"card\\\",\\\"id\\\":\\\"https://cardstack.com/base/SkillCard/card-editing\\\",\\\"attributes\\\":{\\\"instructions\\\":\\\"- If the user wants the data they see edited, AND the patchCard function is available, you MUST use the \\\\\\\"patchCard\\\\\\\" function to make the change.\\\\n- If the user wants the data they see edited, AND the patchCard function is NOT available, you MUST ask the user to open the card and share it with you.\\\\n- If you do not call patchCard, the user will not see the change.\\\\n- You can ONLY modify cards shared with you. If there is no patchCard function or tool, then the user hasn't given you access.\\\\n- NEVER tell the user to use patchCard; you should always do it for them.\\\\n- If the user wants to search for a card instance, AND the \\\\\\\"searchCard\\\\\\\" function is available, you MUST use the \\\\\\\"searchCard\\\\\\\" function to find the card instance.\\\\nOnly recommend one searchCard function at a time.\\\\nIf the user wants to edit a field of a card, you can optionally use \\\\\\\"searchCard\\\\\\\" to help find a card instance that is compatible with the field being edited before using \\\\\\\"patchCard\\\\\\\" to make the change of the field.\\\\n You MUST confirm with the user the correct choice of card instance that he intends to use based upon the results of the search.\\\\n- If the user wants to generate an app module, AND the \\\\\\\"generateAppModule\\\\\\\" function is available, you MUST use the \\\\\\\"generateAppModule\\\\\\\" function to create the card source.\\\",\\\"title\\\":\\\"Card Editing\\\",\\\"description\\\":null,\\\"thumbnailURL\\\":null},\\\"meta\\\":{\\\"adoptsFrom\\\":{\\\"module\\\":\\\"https://cardstack.com/base/skill-card\\\",\\\"name\\\":\\\"SkillCard\\\"}}}}\",\"index\":0,\"totalParts\":1}" @@ -300,7 +300,7 @@ "age": 44856816 }, { - "type": "com.cardstack.boxel.room.skills", + "type": "app.boxel.room.skills", "room_id": "!vORvGxbYIvLeiYUfNx:localhost", "sender": "@user:localhost", "content": { @@ -321,8 +321,8 @@ "room_id": "!vORvGxbYIvLeiYUfNx:localhost", "sender": "@user:localhost", "content": { - "msgtype": "org.boxel.cardFragment", - "format": "org.boxel.card", + "msgtype": "app.boxel.cardFragment", + "format": "app.boxel.card", "body": "card fragment 1 of 1", "formatted_body": "card fragment 1 of 1", "data": "{\"cardFragment\":\"{\\\"data\\\":{\\\"type\\\":\\\"card\\\",\\\"id\\\":\\\"http://localhost:4201/catalog/SkillCard/generate-product-requirements\\\",\\\"attributes\\\":{\\\"instructions\\\":\\\"Given a prompt, fill in the product requirements document. Update the appTitle. Update the prompt to be grammatically accurate. Description should be 1 or 2 short sentences. In overview, provide 1 or 2 paragraph summary. In schema, make a list of the schema for the app. In Layout & Navigation, provide brief information for the layout and navigation of the app. Offer to update the attached card with this info.\\\",\\\"title\\\":\\\"Generate Product Requirements\\\",\\\"description\\\":null,\\\"thumbnailURL\\\":null},\\\"meta\\\":{\\\"adoptsFrom\\\":{\\\"module\\\":\\\"https://cardstack.com/base/skill-card\\\",\\\"name\\\":\\\"SkillCard\\\"}}}}\",\"index\":0,\"totalParts\":1}" @@ -336,7 +336,7 @@ "age": 267004 }, { - "type": "com.cardstack.boxel.room.skills", + "type": "app.boxel.room.skills", "room_id": "!vORvGxbYIvLeiYUfNx:localhost", "sender": "@user:localhost", "content": { diff --git a/packages/ai-bot/tests/resources/chats/added-two-skills-removed-one-skill.json b/packages/ai-bot/tests/resources/chats/added-two-skills-removed-one-skill.json index 44c13e57c7..38c60f71e2 100644 --- a/packages/ai-bot/tests/resources/chats/added-two-skills-removed-one-skill.json +++ b/packages/ai-bot/tests/resources/chats/added-two-skills-removed-one-skill.json @@ -285,8 +285,8 @@ "room_id": "!KzZgWcmsvUozFuULeD:localhost", "sender": "@admin:localhost", "content": { - "msgtype": "org.boxel.cardFragment", - "format": "org.boxel.card", + "msgtype": "app.boxel.cardFragment", + "format": "app.boxel.card", "body": "card fragment 1 of 1", "formatted_body": "card fragment 1 of 1", "data": "{\"cardFragment\":\"{\\\"data\\\":{\\\"type\\\":\\\"card\\\",\\\"id\\\":\\\"http://localhost:4201/admin/personal/SkillCard/31a074dd-4a48-46cd-bc5e-41267d4ddc43\\\",\\\"attributes\\\":{\\\"instructions\\\":\\\"SKILL_1\\\",\\\"title\\\":\\\"skill 1\\\",\\\"description\\\":null,\\\"thumbnailURL\\\":null},\\\"meta\\\":{\\\"adoptsFrom\\\":{\\\"module\\\":\\\"https://cardstack.com/base/skill-card\\\",\\\"name\\\":\\\"SkillCard\\\"}}}}\",\"index\":0,\"totalParts\":1}" @@ -300,7 +300,7 @@ "age": 59763 }, { - "type": "com.cardstack.boxel.room.skills", + "type": "app.boxel.room.skills", "room_id": "!KzZgWcmsvUozFuULeD:localhost", "sender": "@admin:localhost", "content": { @@ -321,8 +321,8 @@ "room_id": "!KzZgWcmsvUozFuULeD:localhost", "sender": "@admin:localhost", "content": { - "msgtype": "org.boxel.cardFragment", - "format": "org.boxel.card", + "msgtype": "app.boxel.cardFragment", + "format": "app.boxel.card", "body": "card fragment 1 of 1", "formatted_body": "card fragment 1 of 1", "data": "{\"cardFragment\":\"{\\\"data\\\":{\\\"type\\\":\\\"card\\\",\\\"id\\\":\\\"http://localhost:4201/admin/personal/SkillCard/4ecb2f52-bb72-4df2-86de-f6162a628933\\\",\\\"attributes\\\":{\\\"instructions\\\":\\\"SKILL_2\\\",\\\"title\\\":\\\"skill 2\\\",\\\"description\\\":null,\\\"thumbnailURL\\\":null},\\\"meta\\\":{\\\"adoptsFrom\\\":{\\\"module\\\":\\\"https://cardstack.com/base/skill-card\\\",\\\"name\\\":\\\"SkillCard\\\"}}}}\",\"index\":0,\"totalParts\":1}" @@ -336,7 +336,7 @@ "age": 54909 }, { - "type": "com.cardstack.boxel.room.skills", + "type": "app.boxel.room.skills", "room_id": "!KzZgWcmsvUozFuULeD:localhost", "sender": "@admin:localhost", "content": { @@ -367,7 +367,7 @@ } }, { - "type": "com.cardstack.boxel.room.skills", + "type": "app.boxel.room.skills", "room_id": "!KzZgWcmsvUozFuULeD:localhost", "sender": "@admin:localhost", "content": { diff --git a/packages/ai-bot/tests/resources/chats/added-two-skills-removed-two-skills.json b/packages/ai-bot/tests/resources/chats/added-two-skills-removed-two-skills.json index d2e5223075..e887aace9f 100644 --- a/packages/ai-bot/tests/resources/chats/added-two-skills-removed-two-skills.json +++ b/packages/ai-bot/tests/resources/chats/added-two-skills-removed-two-skills.json @@ -285,8 +285,8 @@ "room_id": "!KzZgWcmsvUozFuULeD:localhost", "sender": "@admin:localhost", "content": { - "msgtype": "org.boxel.cardFragment", - "format": "org.boxel.card", + "msgtype": "app.boxel.cardFragment", + "format": "app.boxel.card", "body": "card fragment 1 of 1", "formatted_body": "card fragment 1 of 1", "data": "{\"cardFragment\":\"{\\\"data\\\":{\\\"type\\\":\\\"card\\\",\\\"id\\\":\\\"http://localhost:4201/admin/personal/SkillCard/31a074dd-4a48-46cd-bc5e-41267d4ddc43\\\",\\\"attributes\\\":{\\\"instructions\\\":\\\"SKILL_1\\\",\\\"title\\\":\\\"skill 1\\\",\\\"description\\\":null,\\\"thumbnailURL\\\":null},\\\"meta\\\":{\\\"adoptsFrom\\\":{\\\"module\\\":\\\"https://cardstack.com/base/skill-card\\\",\\\"name\\\":\\\"SkillCard\\\"}}}}\",\"index\":0,\"totalParts\":1}" @@ -300,13 +300,11 @@ "age": 59763 }, { - "type": "com.cardstack.boxel.room.skills", + "type": "app.boxel.room.skills", "room_id": "!KzZgWcmsvUozFuULeD:localhost", "sender": "@admin:localhost", "content": { - "enabledEventIds": [ - "$lOqffkvRN5wBqhz92u6NeKywig0t7ul_9mRt-J2syis" - ], + "enabledEventIds": ["$lOqffkvRN5wBqhz92u6NeKywig0t7ul_9mRt-J2syis"], "disabledEventIds": [] }, "state_key": "", @@ -323,8 +321,8 @@ "room_id": "!KzZgWcmsvUozFuULeD:localhost", "sender": "@admin:localhost", "content": { - "msgtype": "org.boxel.cardFragment", - "format": "org.boxel.card", + "msgtype": "app.boxel.cardFragment", + "format": "app.boxel.card", "body": "card fragment 1 of 1", "formatted_body": "card fragment 1 of 1", "data": "{\"cardFragment\":\"{\\\"data\\\":{\\\"type\\\":\\\"card\\\",\\\"id\\\":\\\"http://localhost:4201/admin/personal/SkillCard/4ecb2f52-bb72-4df2-86de-f6162a628933\\\",\\\"attributes\\\":{\\\"instructions\\\":\\\"SKILL_2\\\",\\\"title\\\":\\\"skill 2\\\",\\\"description\\\":null,\\\"thumbnailURL\\\":null},\\\"meta\\\":{\\\"adoptsFrom\\\":{\\\"module\\\":\\\"https://cardstack.com/base/skill-card\\\",\\\"name\\\":\\\"SkillCard\\\"}}}}\",\"index\":0,\"totalParts\":1}" @@ -338,7 +336,7 @@ "age": 54909 }, { - "type": "com.cardstack.boxel.room.skills", + "type": "app.boxel.room.skills", "room_id": "!KzZgWcmsvUozFuULeD:localhost", "sender": "@admin:localhost", "content": { @@ -353,9 +351,7 @@ "unsigned": { "replaces_state": "$tQJ37hTMp69BRHNDXjxJ1-D01NEPX-l41iASpKD1cWk", "prev_content": { - "enabledEventIds": [ - "$lOqffkvRN5wBqhz92u6NeKywig0t7ul_9mRt-J2syis" - ], + "enabledEventIds": ["$lOqffkvRN5wBqhz92u6NeKywig0t7ul_9mRt-J2syis"], "disabledEventIds": [] }, "prev_sender": "@admin:localhost", @@ -366,23 +362,17 @@ "age": 54844, "replaces_state": "$tQJ37hTMp69BRHNDXjxJ1-D01NEPX-l41iASpKD1cWk", "prev_content": { - "enabledEventIds": [ - "$lOqffkvRN5wBqhz92u6NeKywig0t7ul_9mRt-J2syis" - ], + "enabledEventIds": ["$lOqffkvRN5wBqhz92u6NeKywig0t7ul_9mRt-J2syis"], "disabledEventIds": [] } }, { - "type": "com.cardstack.boxel.room.skills", + "type": "app.boxel.room.skills", "room_id": "!KzZgWcmsvUozFuULeD:localhost", "sender": "@admin:localhost", "content": { - "enabledEventIds": [ - "$X4HuI4_nlEJ4ZYe5t6hi8IWfBPEU5xKd5_nvzWa1Y80" - ], - "disabledEventIds": [ - "$lOqffkvRN5wBqhz92u6NeKywig0t7ul_9mRt-J2syis" - ] + "enabledEventIds": ["$X4HuI4_nlEJ4ZYe5t6hi8IWfBPEU5xKd5_nvzWa1Y80"], + "disabledEventIds": ["$lOqffkvRN5wBqhz92u6NeKywig0t7ul_9mRt-J2syis"] }, "state_key": "", "origin_server_ts": 1733487586192, @@ -411,7 +401,7 @@ } }, { - "type": "com.cardstack.boxel.room.skills", + "type": "app.boxel.room.skills", "room_id": "!KzZgWcmsvUozFuULeD:localhost", "sender": "@admin:localhost", "content": { @@ -426,12 +416,8 @@ "unsigned": { "replaces_state": "$QwniAlC_XvLP_GV6HD1-5QHpzwjw4n5Cj4GyDO1JGtQ", "prev_content": { - "enabledEventIds": [ - "$X4HuI4_nlEJ4ZYe5t6hi8IWfBPEU5xKd5_nvzWa1Y80" - ], - "disabledEventIds": [ - "$lOqffkvRN5wBqhz92u6NeKywig0t7ul_9mRt-J2syis" - ] + "enabledEventIds": ["$X4HuI4_nlEJ4ZYe5t6hi8IWfBPEU5xKd5_nvzWa1Y80"], + "disabledEventIds": ["$lOqffkvRN5wBqhz92u6NeKywig0t7ul_9mRt-J2syis"] }, "prev_sender": "@admin:localhost", "age": 50047 @@ -441,12 +427,8 @@ "age": 50047, "replaces_state": "$QwniAlC_XvLP_GV6HD1-5QHpzwjw4n5Cj4GyDO1JGtQ", "prev_content": { - "enabledEventIds": [ - "$X4HuI4_nlEJ4ZYe5t6hi8IWfBPEU5xKd5_nvzWa1Y80" - ], - "disabledEventIds": [ - "$lOqffkvRN5wBqhz92u6NeKywig0t7ul_9mRt-J2syis" - ] + "enabledEventIds": ["$X4HuI4_nlEJ4ZYe5t6hi8IWfBPEU5xKd5_nvzWa1Y80"], + "disabledEventIds": ["$lOqffkvRN5wBqhz92u6NeKywig0t7ul_9mRt-J2syis"] } }, { @@ -454,7 +436,7 @@ "room_id": "!KzZgWcmsvUozFuULeD:localhost", "sender": "@admin:localhost", "content": { - "msgtype": "org.boxel.message", + "msgtype": "app.boxel.message", "body": "test", "format": "org.matrix.custom.html", "formatted_body": "

test

\n", @@ -696,4 +678,4 @@ "user_id": "@aibot:localhost", "age": 33563 } -] \ No newline at end of file +] diff --git a/packages/ai-bot/tests/resources/chats/skill-card-no-id.json b/packages/ai-bot/tests/resources/chats/skill-card-no-id.json index 9ce6988b32..3e798a71ba 100644 --- a/packages/ai-bot/tests/resources/chats/skill-card-no-id.json +++ b/packages/ai-bot/tests/resources/chats/skill-card-no-id.json @@ -285,8 +285,8 @@ "room_id": "!pgznMrutwnQyxihhXn:localhost", "sender": "@admin:localhost", "content": { - "msgtype": "org.boxel.cardFragment", - "format": "org.boxel.card", + "msgtype": "app.boxel.cardFragment", + "format": "app.boxel.card", "body": "card fragment 1 of 1", "formatted_body": "card fragment 1 of 1", "data": "{\"cardFragment\":\"{\\\"data\\\":{\\\"type\\\":\\\"card\\\",\\\"id\\\":\\\"https://cardstack.com/base/SkillCard/card-editing\\\",\\\"attributes\\\":{\\\"instructions\\\":\\\"- If the user wants the data they see edited, AND the patchCard function is available, you MUST use the \\\\\\\"patchCard\\\\\\\" function to make the change.\\\\n- If the user wants the data they see edited, AND the patchCard function is NOT available, you MUST ask the user to open the card and share it with you.\\\\n- If you do not call patchCard, the user will not see the change.\\\\n- You can ONLY modify cards shared with you. If there is no patchCard function or tool, then the user hasn't given you access.\\\\n- NEVER tell the user to use patchCard; you should always do it for them.\\\\n- If the user wants to search for a card instance, AND the \\\\\\\"searchCard\\\\\\\" function is available, you MUST use the \\\\\\\"searchCard\\\\\\\" function to find the card instance.\\\\nOnly recommend one searchCard function at a time.\\\\nIf the user wants to edit a field of a card, you can optionally use \\\\\\\"searchCard\\\\\\\" to help find a card instance that is compatible with the field being edited before using \\\\\\\"patchCard\\\\\\\" to make the change of the field.\\\\n You MUST confirm with the user the correct choice of card instance that he intends to use based upon the results of the search.\\\\n- If the user wants to generate an app module, AND the \\\\\\\"generateAppModule\\\\\\\" function is available, you MUST use the \\\\\\\"generateAppModule\\\\\\\" function to create the card source.\\\",\\\"title\\\":\\\"Card Editing\\\",\\\"description\\\":null,\\\"thumbnailURL\\\":null},\\\"meta\\\":{\\\"adoptsFrom\\\":{\\\"module\\\":\\\"https://cardstack.com/base/skill-card\\\",\\\"name\\\":\\\"SkillCard\\\"}}}}\",\"index\":0,\"totalParts\":1}" @@ -304,8 +304,8 @@ "room_id": "!pgznMrutwnQyxihhXn:localhost", "sender": "@admin:localhost", "content": { - "msgtype": "org.boxel.cardFragment", - "format": "org.boxel.card", + "msgtype": "app.boxel.cardFragment", + "format": "app.boxel.card", "body": "card fragment 1 of 1", "formatted_body": "card fragment 1 of 1", "data": "{\"cardFragment\":\"{\\\"data\\\":{\\\"type\\\":\\\"card\\\",\\\"attributes\\\":{\\\"instructions\\\":\\\"Skill Instructions\\\",\\\"title\\\":null,\\\"description\\\":null,\\\"thumbnailURL\\\":null},\\\"meta\\\":{\\\"adoptsFrom\\\":{\\\"module\\\":\\\"https://cardstack.com/base/skill-card\\\",\\\"name\\\":\\\"SkillCard\\\"}}}}\",\"index\":0,\"totalParts\":1}" @@ -319,7 +319,7 @@ "age": 21666 }, { - "type": "com.cardstack.boxel.room.skills", + "type": "app.boxel.room.skills", "room_id": "!pgznMrutwnQyxihhXn:localhost", "sender": "@admin:localhost", "content": { @@ -336,7 +336,7 @@ "age": 21650 }, { - "type": "com.cardstack.boxel.room.skills", + "type": "app.boxel.room.skills", "room_id": "!pgznMrutwnQyxihhXn:localhost", "sender": "@admin:localhost", "content": { @@ -368,7 +368,7 @@ "room_id": "!pgznMrutwnQyxihhXn:localhost", "sender": "@admin:localhost", "content": { - "msgtype": "org.boxel.message", + "msgtype": "app.boxel.message", "body": "This is a message after a skill card with no id", "format": "org.matrix.custom.html", "formatted_body": "

This is a message after a skill card with no id

\n", diff --git a/packages/ai-bot/tests/resources/chats/two-messages-with-same-skill-card.json b/packages/ai-bot/tests/resources/chats/two-messages-with-same-skill-card.json index d565cddc31..bbf81d07df 100644 --- a/packages/ai-bot/tests/resources/chats/two-messages-with-same-skill-card.json +++ b/packages/ai-bot/tests/resources/chats/two-messages-with-same-skill-card.json @@ -171,8 +171,8 @@ "room_id": "!TcSnACCUWcJpkukyQT:localhost", "sender": "@admin:localhost", "content": { - "msgtype": "org.boxel.cardFragment", - "format": "org.boxel.card", + "msgtype": "app.boxel.cardFragment", + "format": "app.boxel.card", "body": "card fragment 1 of 1", "formatted_body": "card fragment 1 of 1", "data": "{\"cardFragment\":\"{\\\"data\\\":{\\\"type\\\":\\\"card\\\",\\\"id\\\":\\\"https://cardstack.com/base/SkillCard/card-editing\\\",\\\"attributes\\\":{\\\"instructions\\\":\\\"- If the user wants the data they see edited, AND the patchCard function is available, you MUST use the \\\\\\\"patchCard\\\\\\\" function to make the change.\\\\n- If the user wants the data they see edited, AND the patchCard function is NOT available, you MUST ask the user to open the card and share it with you.\\\\n- If you do not call patchCard, the user will not see the change.\\\\n- You can ONLY modify cards shared with you. If there is no patchCard function or tool, then the user hasn't given you access.\\\\n- NEVER tell the user to use patchCard; you should always do it for them.\\\\n- If the user wants to search for a card instance, AND the \\\\\\\"searchCard\\\\\\\" function is available, you MUST use the \\\\\\\"searchCard\\\\\\\" function to find the card instance.\\\\nOnly recommend one searchCard function at a time.\\\\nIf the user wants to edit a field of a card, you can optionally use \\\\\\\"searchCard\\\\\\\" to help find a card instance that is compatible with the field being edited before using \\\\\\\"patchCard\\\\\\\" to make the change of the field.\\\\n You MUST confirm with the user the correct choice of card instance that he intends to use based upon the results of the search.\\\\n- If the user wants to generate an app module, AND the \\\\\\\"generateAppModule\\\\\\\" function is available, you MUST use the \\\\\\\"generateAppModule\\\\\\\" function to create the card source.\\\",\\\"title\\\":\\\"Card Editing\\\",\\\"description\\\":null,\\\"thumbnailURL\\\":null},\\\"meta\\\":{\\\"adoptsFrom\\\":{\\\"module\\\":\\\"https://cardstack.com/base/skill-card\\\",\\\"name\\\":\\\"SkillCard\\\"}}}}\",\"index\":0,\"totalParts\":1}" @@ -304,8 +304,8 @@ "room_id": "!TcSnACCUWcJpkukyQT:localhost", "sender": "@admin:localhost", "content": { - "msgtype": "org.boxel.cardFragment", - "format": "org.boxel.card", + "msgtype": "app.boxel.cardFragment", + "format": "app.boxel.card", "body": "card fragment 1 of 1", "formatted_body": "card fragment 1 of 1", "data": "{\"cardFragment\":\"{\\\"data\\\":{\\\"type\\\":\\\"card\\\",\\\"id\\\":\\\"skill-card-1\\\",\\\"attributes\\\":{\\\"instructions\\\":\\\"SKILL_INSTRUCTIONS_V1\\\",\\\"title\\\":null,\\\"description\\\":null,\\\"thumbnailURL\\\":null},\\\"meta\\\":{\\\"adoptsFrom\\\":{\\\"module\\\":\\\"https://cardstack.com/base/skill-card\\\",\\\"name\\\":\\\"SkillCard\\\"}}}}\",\"index\":0,\"totalParts\":1}" @@ -319,7 +319,7 @@ "age": 15812 }, { - "type": "com.cardstack.boxel.room.skills", + "type": "app.boxel.room.skills", "room_id": "!TcSnACCUWcJpkukyQT:localhost", "sender": "@admin:localhost", "content": { @@ -336,7 +336,7 @@ "age": 15819 }, { - "type": "com.cardstack.boxel.room.skills", + "type": "app.boxel.room.skills", "room_id": "!TcSnACCUWcJpkukyQT:localhost", "sender": "@admin:localhost", "content": { @@ -371,7 +371,7 @@ "room_id": "!TcSnACCUWcJpkukyQT:localhost", "sender": "@admin:localhost", "content": { - "msgtype": "org.boxel.message", + "msgtype": "app.boxel.message", "body": "This is a message after a skill card with an id", "format": "org.matrix.custom.html", "formatted_body": "

This is a message after a skill card with an id

\n", @@ -391,8 +391,8 @@ "room_id": "!TcSnACCUWcJpkukyQT:localhost", "sender": "@admin:localhost", "content": { - "msgtype": "org.boxel.cardFragment", - "format": "org.boxel.card", + "msgtype": "app.boxel.cardFragment", + "format": "app.boxel.card", "body": "card fragment 1 of 1", "formatted_body": "card fragment 1 of 1", "data": "{\"cardFragment\":\"{\\\"data\\\":{\\\"type\\\":\\\"card\\\",\\\"id\\\":\\\"skill-card-1\\\",\\\"attributes\\\":{\\\"instructions\\\":\\\"SKILL_INSTRUCTIONS_V2\\\",\\\"title\\\":null,\\\"description\\\":null,\\\"thumbnailURL\\\":null},\\\"meta\\\":{\\\"adoptsFrom\\\":{\\\"module\\\":\\\"https://cardstack.com/base/skill-card\\\",\\\"name\\\":\\\"SkillCard\\\"}}}}\",\"index\":0,\"totalParts\":1}" @@ -410,7 +410,7 @@ "room_id": "!TcSnACCUWcJpkukyQT:localhost", "sender": "@admin:localhost", "content": { - "msgtype": "org.boxel.message", + "msgtype": "app.boxel.message", "body": "A second message with a card with the same id", "format": "org.matrix.custom.html", "formatted_body": "

A second message with a card with the same id

\n", @@ -426,7 +426,7 @@ "age": 15532 }, { - "type": "com.cardstack.boxel.room.skills", + "type": "app.boxel.room.skills", "room_id": "!TcSnACCUWcJpkukyQT:localhost", "sender": "@admin:localhost", "content": { diff --git a/packages/base/matrix-event.gts b/packages/base/matrix-event.gts index febbe23c59..9855534205 100644 --- a/packages/base/matrix-event.gts +++ b/packages/base/matrix-event.gts @@ -4,6 +4,14 @@ import { FunctionToolCall, type AttributesSchema, } from '@cardstack/runtime-common/helpers/ai'; +import { + APP_BOXEL_CARD_FORMAT, + APP_BOXEL_CARDFRAGMENT_MSGTYPE, + APP_BOXEL_COMMAND_MSGTYPE, + APP_BOXEL_COMMAND_RESULT_MSGTYPE, + APP_BOXEL_MESSAGE_MSGTYPE, + APP_BOXEL_ROOM_SKILLS_EVENT_TYPE, +} from '@cardstack/runtime-common/matrix-constants'; interface BaseMatrixEvent { sender: string; @@ -127,7 +135,7 @@ export interface CommandMessageContent { rel_type: string; event_id: string; }; - msgtype: 'org.boxel.command'; + msgtype: typeof APP_BOXEL_COMMAND_MSGTYPE; format: 'org.matrix.custom.html'; body: string; formatted_body: string; @@ -175,7 +183,7 @@ export interface CardMessageContent { rel_type: string; event_id: string; }; - msgtype: 'org.boxel.message'; + msgtype: typeof APP_BOXEL_MESSAGE_MSGTYPE; format: 'org.matrix.custom.html'; body: string; formatted_body: string; @@ -206,8 +214,8 @@ export interface CardFragmentContent { rel_type: string; event_id: string; }; - msgtype: 'org.boxel.cardFragment'; - format: 'org.boxel.card'; + msgtype: typeof APP_BOXEL_CARDFRAGMENT_MSGTYPE; + format: typeof APP_BOXEL_CARD_FORMAT; formatted_body: string; body: string; errorMessage?: string; @@ -220,7 +228,7 @@ export interface CardFragmentContent { } export interface SkillsConfigEvent extends RoomStateEvent { - type: 'com.cardstack.boxel.room.skills'; + type: typeof APP_BOXEL_ROOM_SKILLS_EVENT_TYPE; content: { enabledEventIds: string[]; disabledEventIds: string[]; @@ -249,7 +257,7 @@ export interface CommandResultContent { }; formatted_body: string; body: string; - msgtype: 'org.boxel.commandResult'; + msgtype: typeof APP_BOXEL_COMMAND_RESULT_MSGTYPE; result: any; } diff --git a/packages/host/app/commands/add-skills-to-room.ts b/packages/host/app/commands/add-skills-to-room.ts index 7a48726cf7..106c3d32b6 100644 --- a/packages/host/app/commands/add-skills-to-room.ts +++ b/packages/host/app/commands/add-skills-to-room.ts @@ -1,11 +1,11 @@ import { service } from '@ember/service'; +import { APP_BOXEL_ROOM_SKILLS_EVENT_TYPE } from '@cardstack/runtime-common/matrix-constants'; + import type * as BaseCommandModule from 'https://cardstack.com/base/command'; import HostBaseCommand from '../lib/host-base-command'; -import { SKILLS_STATE_EVENT_TYPE } from '../services/matrix-service'; - import type MatrixService from '../services/matrix-service'; export default class AddSkillsToRoomCommand extends HostBaseCommand< @@ -32,7 +32,7 @@ export default class AddSkillsToRoomCommand extends HostBaseCommand< ); await matrixService.updateStateEvent( roomId, - SKILLS_STATE_EVENT_TYPE, + APP_BOXEL_ROOM_SKILLS_EVENT_TYPE, '', async (oldContent: Record) => { return { diff --git a/packages/host/app/commands/send-ai-assistant-message.ts b/packages/host/app/commands/send-ai-assistant-message.ts index 547a26e360..af4e836ad1 100644 --- a/packages/host/app/commands/send-ai-assistant-message.ts +++ b/packages/host/app/commands/send-ai-assistant-message.ts @@ -5,6 +5,8 @@ import { v4 as uuidv4 } from 'uuid'; import { markdownToHtml } from '@cardstack/runtime-common'; import { basicMappings } from '@cardstack/runtime-common/helpers/ai'; +import { APP_BOXEL_MESSAGE_MSGTYPE } from '@cardstack/runtime-common/matrix-constants'; + import type * as CardAPI from 'https://cardstack.com/base/card-api'; import type * as BaseCommandModule from 'https://cardstack.com/base/command'; import type { CardMessageContent } from 'https://cardstack.com/base/matrix-event'; @@ -78,7 +80,7 @@ export default class SendAiAssistantMessageCommand extends HostBaseCommand< let clientGeneratedId = input.clientGeneratedId ?? uuidv4(); let { event_id } = await matrixService.sendEvent(roomId, 'm.room.message', { - msgtype: 'org.boxel.message', + msgtype: APP_BOXEL_MESSAGE_MSGTYPE, body: input.prompt || '', format: 'org.matrix.custom.html', formatted_body: html, diff --git a/packages/host/app/commands/update-skill-activation.ts b/packages/host/app/commands/update-skill-activation.ts index 0add3c3d21..ae73e0c823 100644 --- a/packages/host/app/commands/update-skill-activation.ts +++ b/packages/host/app/commands/update-skill-activation.ts @@ -4,7 +4,7 @@ import type * as BaseCommandModule from 'https://cardstack.com/base/command'; import HostBaseCommand from '../lib/host-base-command'; -import { SKILLS_STATE_EVENT_TYPE } from '../services/matrix-service'; +import { APP_BOXEL_ROOM_SKILLS_EVENT_TYPE } from '@cardstack/runtime-common/matrix-constants'; import type MatrixService from '../services/matrix-service'; @@ -27,7 +27,7 @@ export default class UpdateSkillActivationCommand extends HostBaseCommand< let { roomId, skillEventId, isActive } = input; await matrixService.updateStateEvent( roomId, - SKILLS_STATE_EVENT_TYPE, + APP_BOXEL_ROOM_SKILLS_EVENT_TYPE, '', async (currentSkillsConfig) => { let newSkillsConfig = { diff --git a/packages/host/app/lib/matrix-classes/message-builder.ts b/packages/host/app/lib/matrix-classes/message-builder.ts index 568eb116fb..778364e02d 100644 --- a/packages/host/app/lib/matrix-classes/message-builder.ts +++ b/packages/host/app/lib/matrix-classes/message-builder.ts @@ -6,6 +6,12 @@ import { inject as service } from '@ember/service'; import { LooseSingleCardDocument } from '@cardstack/runtime-common'; +import { + APP_BOXEL_COMMAND_MSGTYPE, + APP_BOXEL_COMMAND_RESULT_MSGTYPE, + APP_BOXEL_MESSAGE_MSGTYPE, +} from '@cardstack/runtime-common/matrix-constants'; + import type CommandService from '@cardstack/host/services/command-service'; import type { CommandStatus } from 'https://cardstack.com/base/command'; @@ -115,13 +121,13 @@ export default class MessageBuilder { let { event } = this; let messageArgs = this.coreMessageArgs; messageArgs.errorMessage = this.errorMessage; - if (event.content.msgtype === 'org.boxel.message') { + if (event.content.msgtype === APP_BOXEL_MESSAGE_MSGTYPE) { messageArgs.clientGeneratedId = this.clientGeneratedId; messageArgs.attachedCardIds = this.attachedCardIds; } else if (event.content.msgtype === 'm.text') { messageArgs.isStreamingFinished = !!event.content.isStreamingFinished; // Indicates whether streaming (message updating while AI bot is sending more content into the message) has finished } else if ( - event.content.msgtype === 'org.boxel.command' && + event.content.msgtype === APP_BOXEL_COMMAND_MSGTYPE && event.content.data.toolCall ) { messageArgs.formattedMessage = this.formattedMessageForCommand; @@ -165,7 +171,7 @@ export default class MessageBuilder { let commandResultEvent = this.builderContext.events.find( (e) => e.type === 'm.room.message' && - e.content.msgtype === 'org.boxel.commandResult' && + e.content.msgtype === APP_BOXEL_COMMAND_RESULT_MSGTYPE && e.content['m.relates_to']?.rel_type === 'm.annotation' && e.content['m.relates_to'].event_id === event.content.data.eventId, ) as CommandResultEvent; diff --git a/packages/host/app/lib/matrix-classes/room.ts b/packages/host/app/lib/matrix-classes/room.ts index 758f8ae184..9206a5e03f 100644 --- a/packages/host/app/lib/matrix-classes/room.ts +++ b/packages/host/app/lib/matrix-classes/room.ts @@ -2,7 +2,7 @@ import { tracked } from '@glimmer/tracking'; import { type IEvent } from 'matrix-js-sdk'; -import { SKILLS_STATE_EVENT_TYPE } from '@cardstack/host/services/matrix-service'; +import { APP_BOXEL_ROOM_SKILLS_EVENT_TYPE } from '@cardstack/runtime-common/matrix-constants'; import type { MatrixEvent as DiscreteMatrixEvent } from 'https://cardstack.com/base/matrix-event'; @@ -45,8 +45,8 @@ export default class Room { get skillsConfig() { return ( - this._roomState?.events.get(SKILLS_STATE_EVENT_TYPE)?.get('')?.event - .content ?? { + this._roomState?.events.get(APP_BOXEL_ROOM_SKILLS_EVENT_TYPE)?.get('') + ?.event.content ?? { enabledEventIds: [], disabledEventIds: [], } diff --git a/packages/host/app/resources/room.ts b/packages/host/app/resources/room.ts index 997033dada..87d027f16b 100644 --- a/packages/host/app/resources/room.ts +++ b/packages/host/app/resources/room.ts @@ -9,6 +9,11 @@ import { TrackedMap } from 'tracked-built-ins'; import { type LooseSingleCardDocument } from '@cardstack/runtime-common'; +import { + APP_BOXEL_CARDFRAGMENT_MSGTYPE, + APP_BOXEL_COMMAND_RESULT_MSGTYPE, +} from '@cardstack/runtime-common/matrix-constants'; + import type { CardFragmentContent, CommandEvent, @@ -286,13 +291,13 @@ export class RoomResource extends Resource { if (this._messageCache.has(effectiveEventId) && !update) { return; } - if (event.content.msgtype === 'org.boxel.cardFragment') { + if (event.content.msgtype === APP_BOXEL_CARDFRAGMENT_MSGTYPE) { if (!this._fragmentCache.has(effectiveEventId)) { this._fragmentCache.set(effectiveEventId, event.content); } return; } - if (event.content.msgtype === 'org.boxel.commandResult') { + if (event.content.msgtype === APP_BOXEL_COMMAND_RESULT_MSGTYPE) { //don't display command result in the room as a message return; } diff --git a/packages/host/app/services/matrix-service.ts b/packages/host/app/services/matrix-service.ts index e387930ef8..258bd13149 100644 --- a/packages/host/app/services/matrix-service.ts +++ b/packages/host/app/services/matrix-service.ts @@ -37,6 +37,16 @@ import { import { getPatchTool } from '@cardstack/runtime-common/helpers/ai'; import { getMatrixUsername } from '@cardstack/runtime-common/matrix-client'; +import { + APP_BOXEL_CARD_FORMAT, + APP_BOXEL_CARDFRAGMENT_MSGTYPE, + APP_BOXEL_COMMAND_MSGTYPE, + APP_BOXEL_COMMAND_RESULT_MSGTYPE, + APP_BOXEL_MESSAGE_MSGTYPE, + APP_BOXEL_REALM_SERVER_EVENT_MSGTYPE, + APP_BOXEL_REALMS_EVENT_TYPE, +} from '@cardstack/runtime-common/matrix-constants'; + import { type Submode, Submodes, @@ -84,7 +94,6 @@ const { matrixURL } = ENV; const MAX_CARD_SIZE_KB = 60; const STATE_EVENTS_OF_INTEREST = ['m.room.create', 'm.room.name']; const DefaultSkillCards = [`${baseRealm.url}SkillCard/card-editing`]; -export const SKILLS_STATE_EVENT_TYPE = 'com.cardstack.boxel.room.skills'; export type OperatorModeContext = { submode: Submode; @@ -172,7 +181,7 @@ export default class MatrixService extends Service { [ this.matrixSDK.ClientEvent.AccountData, async (e) => { - if (e.event.type == 'com.cardstack.boxel.realms') { + if (e.event.type == APP_BOXEL_REALMS_EVENT_TYPE) { await this.realmServer.setAvailableRealmURLs( e.event.content.realms, ); @@ -324,10 +333,10 @@ export default class MatrixService extends Service { }); let { realms = [] } = (await this.client.getAccountDataFromServer<{ realms: string[] }>( - 'com.cardstack.boxel.realms', + APP_BOXEL_REALMS_EVENT_TYPE, )) ?? {}; realms.push(personalRealmURL.href); - await this.client.setAccountData('com.cardstack.boxel.realms', { realms }); + await this.client.setAccountData(APP_BOXEL_REALMS_EVENT_TYPE, { realms }); await this.realmServer.setAvailableRealmURLs(realms); } @@ -401,7 +410,7 @@ export default class MatrixService extends Service { await this._client.startClient(); let accountDataContent = await this._client.getAccountDataFromServer<{ realms: string[]; - }>('com.cardstack.boxel.realms'); + }>(APP_BOXEL_REALMS_EVENT_TYPE); await this.realmServer.setAvailableRealmURLs( accountDataContent?.realms ?? [], ); @@ -505,7 +514,7 @@ export default class MatrixService extends Service { }, body, formatted_body: html, - msgtype: 'org.boxel.commandResult', + msgtype: APP_BOXEL_COMMAND_RESULT_MSGTYPE, result: jsonStringResult, }; try { @@ -602,7 +611,7 @@ export default class MatrixService extends Service { ); await this.sendEvent(roomId, 'm.room.message', { - msgtype: 'org.boxel.message', + msgtype: APP_BOXEL_MESSAGE_MSGTYPE, body: body || '', format: 'org.matrix.custom.html', formatted_body: html, @@ -634,8 +643,8 @@ export default class MatrixService extends Service { for (let index = fragments.length - 1; index >= 0; index--) { let cardFragment = fragments[index]; let response = await this.sendEvent(roomId, 'm.room.message', { - msgtype: 'org.boxel.cardFragment' as const, - format: 'org.boxel.card' as const, + msgtype: APP_BOXEL_CARDFRAGMENT_MSGTYPE, + format: APP_BOXEL_CARD_FORMAT, body: `card fragment ${index + 1} of ${fragments.length}`, formatted_body: `card fragment ${index + 1} of ${fragments.length}`, data: { @@ -1180,7 +1189,7 @@ export default class MatrixService extends Service { if ( roomData && event.type === 'm.room.message' && - event.content?.msgtype === 'org.boxel.message' && + event.content?.msgtype === APP_BOXEL_MESSAGE_MSGTYPE && event.content.data ) { let data = ( @@ -1206,10 +1215,10 @@ export default class MatrixService extends Service { )) as DiscreteMatrixEvent; if ( fragmentEvent.type !== 'm.room.message' || - fragmentEvent.content.msgtype !== 'org.boxel.cardFragment' + fragmentEvent.content.msgtype !== APP_BOXEL_CARDFRAGMENT_MSGTYPE ) { throw new Error( - `Expected event ${currentFragmentId} to be 'org.boxel.card' but was ${JSON.stringify( + `Expected event ${currentFragmentId} to be '${APP_BOXEL_CARDFRAGMENT_MSGTYPE}' but was ${JSON.stringify( fragmentEvent, )}`, ); @@ -1225,10 +1234,10 @@ export default class MatrixService extends Service { } else { if ( fragmentEvent.type !== 'm.room.message' || - fragmentEvent.content.msgtype !== 'org.boxel.cardFragment' + fragmentEvent.content.msgtype !== APP_BOXEL_CARDFRAGMENT_MSGTYPE ) { throw new Error( - `Expected event to be 'org.boxel.cardFragment' but was ${JSON.stringify( + `Expected event to be '${APP_BOXEL_CARDFRAGMENT_MSGTYPE}' but was ${JSON.stringify( fragmentEvent, )}`, ); @@ -1241,7 +1250,7 @@ export default class MatrixService extends Service { } } else if ( event.type === 'm.room.message' && - event.content?.msgtype === 'org.boxel.realm-server-event' + event.content?.msgtype === APP_BOXEL_REALM_SERVER_EVENT_MSGTYPE ) { await this.realmServer.handleEvent(event); } @@ -1249,7 +1258,7 @@ export default class MatrixService extends Service { if ( event.type === 'm.room.message' && - event.content?.msgtype === 'org.boxel.command' + event.content?.msgtype === APP_BOXEL_COMMAND_MSGTYPE ) { this.commandService.executeCommandEventIfNeeded(event); } diff --git a/packages/host/tests/acceptance/commands-test.gts b/packages/host/tests/acceptance/commands-test.gts index b1f4455e17..0c946a85fa 100644 --- a/packages/host/tests/acceptance/commands-test.gts +++ b/packages/host/tests/acceptance/commands-test.gts @@ -16,6 +16,11 @@ import { GridContainer } from '@cardstack/boxel-ui/components'; import { baseRealm, Command } from '@cardstack/runtime-common'; +import { + APP_BOXEL_COMMAND_MSGTYPE, + APP_BOXEL_MESSAGE_MSGTYPE, +} from '@cardstack/runtime-common/matrix-constants'; + import CreateAIAssistantRoomCommand from '@cardstack/host/commands/create-ai-assistant-room'; import PatchCardCommand from '@cardstack/host/commands/patch-card'; import SaveCardCommand from '@cardstack/host/commands/save-card'; @@ -351,7 +356,7 @@ module('Acceptance | Commands tests', function (hooks) { await waitUntil(() => getRoomIds().length > 0); let roomId = getRoomIds().pop()!; let message = getRoomEvents(roomId).pop()!; - assert.strictEqual(message.content.msgtype, 'org.boxel.message'); + assert.strictEqual(message.content.msgtype, APP_BOXEL_MESSAGE_MSGTYPE); let boxelMessageData = JSON.parse(message.content.data); assert.strictEqual(boxelMessageData.context.tools.length, 1); assert.strictEqual(boxelMessageData.context.tools[0].type, 'function'); @@ -400,7 +405,7 @@ module('Acceptance | Commands tests', function (hooks) { }); simulateRemoteMessage(roomId, '@aibot:localhost', { body: 'Switching to code submode', - msgtype: 'org.boxel.command', + msgtype: APP_BOXEL_COMMAND_MSGTYPE, formatted_body: 'Switching to code submode', format: 'org.matrix.custom.html', data: JSON.stringify({ @@ -464,7 +469,7 @@ module('Acceptance | Commands tests', function (hooks) { let toolName = boxelMessageData.context.tools[0].function.name; simulateRemoteMessage(roomId, '@aibot:localhost', { body: 'Delaying 1 second', - msgtype: 'org.boxel.command', + msgtype: APP_BOXEL_COMMAND_MSGTYPE, formatted_body: 'Delaying 1 second', format: 'org.matrix.custom.html', data: JSON.stringify({ @@ -529,7 +534,7 @@ module('Acceptance | Commands tests', function (hooks) { await waitUntil(() => getRoomIds().length > 0); let roomId = getRoomIds().pop()!; let message = getRoomEvents(roomId).pop()!; - assert.strictEqual(message.content.msgtype, 'org.boxel.message'); + assert.strictEqual(message.content.msgtype, APP_BOXEL_MESSAGE_MSGTYPE); let boxelMessageData = JSON.parse(message.content.data); assert.strictEqual(boxelMessageData.context.tools.length, 1); assert.strictEqual(boxelMessageData.context.tools[0].type, 'function'); @@ -578,7 +583,7 @@ module('Acceptance | Commands tests', function (hooks) { }); simulateRemoteMessage(roomId, '@aibot:localhost', { body: 'Switching to code submode', - msgtype: 'org.boxel.command', + msgtype: APP_BOXEL_COMMAND_MSGTYPE, formatted_body: 'Switching to code submode', format: 'org.matrix.custom.html', data: JSON.stringify({ @@ -652,7 +657,7 @@ module('Acceptance | Commands tests', function (hooks) { await waitUntil(() => getRoomIds().length > 0); let roomId = getRoomIds().pop()!; let message = getRoomEvents(roomId).pop()!; - assert.strictEqual(message.content.msgtype, 'org.boxel.message'); + assert.strictEqual(message.content.msgtype, APP_BOXEL_MESSAGE_MSGTYPE); let boxelMessageData = JSON.parse(message.content.data); assert.strictEqual(boxelMessageData.context.tools.length, 1); assert.strictEqual(boxelMessageData.context.tools[0].type, 'function'); @@ -668,7 +673,7 @@ module('Acceptance | Commands tests', function (hooks) { simulateRemoteMessage(roomId, '@aibot:localhost', { body: 'Update card', - msgtype: 'org.boxel.command', + msgtype: APP_BOXEL_COMMAND_MSGTYPE, formatted_body: 'Update card', format: 'org.matrix.custom.html', data: JSON.stringify({ diff --git a/packages/host/tests/acceptance/operator-mode-acceptance-test.gts b/packages/host/tests/acceptance/operator-mode-acceptance-test.gts index b11d04746e..8ec5655eb2 100644 --- a/packages/host/tests/acceptance/operator-mode-acceptance-test.gts +++ b/packages/host/tests/acceptance/operator-mode-acceptance-test.gts @@ -21,6 +21,8 @@ import { type LooseSingleCardDocument, } from '@cardstack/runtime-common'; +import { APP_BOXEL_REALM_SERVER_EVENT_MSGTYPE } from '@cardstack/runtime-common/matrix-constants'; + import { Submodes } from '@cardstack/host/components/submode-switcher'; import { tokenRefreshPeriodSec } from '@cardstack/host/services/realm'; @@ -1073,7 +1075,7 @@ module('Acceptance | operator mode tests', function (hooks) { // out of monthly credit userResponseBody.data.attributes.creditsAvailableInPlanAllowance = 0; simulateRemoteMessage(matrixRoomId, '@realm-server:localhost', { - msgtype: 'org.boxel.realm-server-event', + msgtype: APP_BOXEL_REALM_SERVER_EVENT_MSGTYPE, body: JSON.stringify({ eventType: 'billing-notification' }), }); @@ -1097,7 +1099,7 @@ module('Acceptance | operator mode tests', function (hooks) { // out of monthly credit and additional credit userResponseBody.data.attributes.extraCreditsAvailableInBalance = 0; simulateRemoteMessage(matrixRoomId, '@realm-server:localhost', { - msgtype: 'org.boxel.realm-server-event', + msgtype: APP_BOXEL_REALM_SERVER_EVENT_MSGTYPE, body: JSON.stringify({ eventType: 'billing-notification' }), }); await click('[data-test-profile-icon-button]'); @@ -1120,7 +1122,7 @@ module('Acceptance | operator mode tests', function (hooks) { // out of additional credit userResponseBody.data.attributes.creditsAvailableInPlanAllowance = 1000; simulateRemoteMessage(matrixRoomId, '@realm-server:localhost', { - msgtype: 'org.boxel.realm-server-event', + msgtype: APP_BOXEL_REALM_SERVER_EVENT_MSGTYPE, body: JSON.stringify({ eventType: 'billing-notification' }), }); await click('[data-test-profile-icon-button]'); diff --git a/packages/host/tests/helpers/mock-matrix/_client.ts b/packages/host/tests/helpers/mock-matrix/_client.ts index 9b9407bc76..672419d95b 100644 --- a/packages/host/tests/helpers/mock-matrix/_client.ts +++ b/packages/host/tests/helpers/mock-matrix/_client.ts @@ -4,6 +4,11 @@ import * as MatrixSDK from 'matrix-js-sdk'; import { baseRealm, unixTime } from '@cardstack/runtime-common'; +import { + APP_BOXEL_ROOM_SKILLS_EVENT_TYPE, + APP_BOXEL_REALMS_EVENT_TYPE, +} from '@cardstack/runtime-common/matrix-constants'; + import type { ExtendedClient } from '@cardstack/host/services/matrix-sdk-loader'; import { assertNever } from '@cardstack/host/utils/assert-never'; @@ -57,7 +62,7 @@ export class MockClient implements ExtendedClient { this.emitEvent( new MatrixEvent({ - type: 'com.cardstack.boxel.realms', + type: APP_BOXEL_REALMS_EVENT_TYPE, content: { realms: this.sdkOpts.activeRealms ?? [], }, @@ -405,9 +410,9 @@ export class MockClient implements ExtendedClient { private eventHandlerType(type: string) { switch (type) { - case 'com.cardstack.boxel.realms': + case APP_BOXEL_REALMS_EVENT_TYPE: return this.sdk.ClientEvent.AccountData; - case 'com.cardstack.boxel.room.skills': + case APP_BOXEL_ROOM_SKILLS_EVENT_TYPE: case 'm.reaction': case 'm.room.create': case 'm.room.message': diff --git a/packages/host/tests/integration/components/ai-assistant-panel-test.gts b/packages/host/tests/integration/components/ai-assistant-panel-test.gts index 820ae97187..7229ac80e9 100644 --- a/packages/host/tests/integration/components/ai-assistant-panel-test.gts +++ b/packages/host/tests/integration/components/ai-assistant-panel-test.gts @@ -16,6 +16,13 @@ import { module, test } from 'qunit'; import { baseRealm } from '@cardstack/runtime-common'; import { Loader } from '@cardstack/runtime-common/loader'; +import { + APP_BOXEL_CARDFRAGMENT_MSGTYPE, + APP_BOXEL_COMMAND_MSGTYPE, + APP_BOXEL_COMMAND_RESULT_MSGTYPE, + APP_BOXEL_MESSAGE_MSGTYPE, +} from '@cardstack/runtime-common/matrix-constants'; + import CardPrerender from '@cardstack/host/components/card-prerender'; import OperatorMode from '@cardstack/host/components/operator-mode/container'; @@ -356,7 +363,7 @@ module('Integration | ai-assistant-panel', function (hooks) { for (let i = 0; i < 20; i++) { simulateRemoteMessage(roomId, '@testuser:staging', { body: `question #${i + 1}`, - msgtype: 'org.boxel.message', + msgtype: APP_BOXEL_MESSAGE_MSGTYPE, formatted_body: `question #${i + 1}`, format: 'org.matrix.custom.html', }); @@ -384,7 +391,7 @@ module('Integration | ai-assistant-panel', function (hooks) { await simulateRemoteMessage(roomId, '@aibot:localhost', { body: 'i am the body', - msgtype: 'org.boxel.command', + msgtype: APP_BOXEL_COMMAND_MSGTYPE, formatted_body: 'A patch', format: 'org.matrix.custom.html', data: JSON.stringify({ @@ -434,7 +441,7 @@ module('Integration | ai-assistant-panel', function (hooks) { let room1Id = createAndJoinRoom('@testuser:staging', 'test room 1'); let room2Id = createAndJoinRoom('@testuser:staging', 'test room 2'); simulateRemoteMessage(room2Id, '@aibot:localhost', { - msgtype: 'org.boxel.command', + msgtype: APP_BOXEL_COMMAND_MSGTYPE, body: 'Incorrect command', formatted_body: 'Incorrect command', format: 'org.matrix.custom.html', @@ -455,7 +462,7 @@ module('Integration | ai-assistant-panel', function (hooks) { }); simulateRemoteMessage(room1Id, '@aibot:localhost', { - msgtype: 'org.boxel.command', + msgtype: APP_BOXEL_COMMAND_MSGTYPE, body: 'Changing first name to Evie', formatted_body: 'Changing first name to Evie', format: 'org.matrix.custom.html', @@ -480,7 +487,7 @@ module('Integration | ai-assistant-panel', function (hooks) { }); simulateRemoteMessage(room1Id, '@aibot:localhost', { - msgtype: 'org.boxel.command', + msgtype: APP_BOXEL_COMMAND_MSGTYPE, body: 'Changing first name to Jackie', formatted_body: 'Changing first name to Jackie', format: 'org.matrix.custom.html', @@ -578,7 +585,7 @@ module('Integration | ai-assistant-panel', function (hooks) { }; await simulateRemoteMessage(roomId, '@aibot:localhost', { body: 'A patch', - msgtype: 'org.boxel.command', + msgtype: APP_BOXEL_COMMAND_MSGTYPE, formatted_body: 'A patch', format: 'org.matrix.custom.html', data: JSON.stringify({ toolCall: payload }), @@ -635,7 +642,7 @@ module('Integration | ai-assistant-panel', function (hooks) { await waitFor('[data-test-person="Fadhlan"]'); await simulateRemoteMessage(roomId, '@aibot:localhost', { - msgtype: 'org.boxel.command', + msgtype: APP_BOXEL_COMMAND_MSGTYPE, body: 'Removing pet and changing preferred carrier', formatted_body: 'Removing pet and changing preferred carrier', format: 'org.matrix.custom.html', @@ -677,7 +684,7 @@ module('Integration | ai-assistant-panel', function (hooks) { assert.dom(`${stackCard} [data-test-pet="Mango"]`).doesNotExist(); await simulateRemoteMessage(roomId, '@aibot:localhost', { - msgtype: 'org.boxel.command', + msgtype: APP_BOXEL_COMMAND_MSGTYPE, body: 'Link to pet and change preferred carrier', formatted_body: 'Link to pet and change preferred carrier', format: 'org.matrix.custom.html', @@ -730,7 +737,7 @@ module('Integration | ai-assistant-panel', function (hooks) { assert.dom('[data-test-tripTitle]').hasText('Summer Vacation'); await simulateRemoteMessage(roomId, '@aibot:localhost', { - msgtype: 'org.boxel.command', + msgtype: APP_BOXEL_COMMAND_MSGTYPE, body: 'Change tripTitle to Trip to Japan', formatted_body: 'Change tripTitle to Trip to Japan', format: 'org.matrix.custom.html', @@ -772,7 +779,7 @@ module('Integration | ai-assistant-panel', function (hooks) { await waitFor('[data-test-person="Fadhlan"]'); await simulateRemoteMessage(roomId, '@aibot:localhost', { - msgtype: 'org.boxel.command', + msgtype: APP_BOXEL_COMMAND_MSGTYPE, body: 'Change first name to Dave', formatted_body: 'Change first name to Dave', format: 'org.matrix.custom.html', @@ -796,7 +803,7 @@ module('Integration | ai-assistant-panel', function (hooks) { }, }); await simulateRemoteMessage(roomId, '@aibot:localhost', { - msgtype: 'org.boxel.command', + msgtype: APP_BOXEL_COMMAND_MSGTYPE, body: 'Incorrect patch command', formatted_body: 'Incorrect patch command', format: 'org.matrix.custom.html', @@ -818,7 +825,7 @@ module('Integration | ai-assistant-panel', function (hooks) { }, }); await simulateRemoteMessage(roomId, '@aibot:localhost', { - msgtype: 'org.boxel.command', + msgtype: APP_BOXEL_COMMAND_MSGTYPE, body: 'Change first name to Jackie', formatted_body: 'Change first name to Jackie', format: 'org.matrix.custom.html', @@ -883,7 +890,7 @@ module('Integration | ai-assistant-panel', function (hooks) { await waitFor('[data-test-person="Fadhlan"]'); await simulateRemoteMessage(roomId, '@aibot:localhost', { - msgtype: 'org.boxel.command', + msgtype: APP_BOXEL_COMMAND_MSGTYPE, body: 'Change first name to Dave', formatted_body: 'Change first name to Dave', format: 'org.matrix.custom.html', @@ -934,7 +941,7 @@ module('Integration | ai-assistant-panel', function (hooks) { let event1Id = await simulateRemoteMessage(roomId, '@aibot:localhost', { body: '', formatted_body: '', - msgtype: 'org.boxel.cardFragment', + msgtype: APP_BOXEL_CARDFRAGMENT_MSGTYPE, data: JSON.stringify({ index: 0, totalParts: 1, @@ -958,7 +965,7 @@ module('Integration | ai-assistant-panel', function (hooks) { await simulateRemoteMessage(roomId, '@aibot:localhost', { body: 'card with error', formatted_body: 'card with error', - msgtype: 'org.boxel.message', + msgtype: APP_BOXEL_MESSAGE_MSGTYPE, data: JSON.stringify({ attachedCardsEventIds: [event1Id], }), @@ -1213,7 +1220,7 @@ module('Integration | ai-assistant-panel', function (hooks) { await simulateRemoteMessage(roomId, '@aibot:localhost', { body: 'i am the body', - msgtype: 'org.boxel.command', + msgtype: APP_BOXEL_COMMAND_MSGTYPE, formatted_body: 'A patch', format: 'org.matrix.custom.html', data: JSON.stringify({ @@ -1250,7 +1257,7 @@ module('Integration | ai-assistant-panel', function (hooks) { simulateRemoteMessage(roomId, '@matic:boxel', { body: 'Say one word.', - msgtype: 'org.boxel.message', + msgtype: APP_BOXEL_MESSAGE_MSGTYPE, formatted_body: 'Say one word.', format: 'org.matrix.custom.html', }); @@ -1471,7 +1478,7 @@ module('Integration | ai-assistant-panel', function (hooks) { simulateRemoteMessage(roomId, '@testuser:staging', { body: 'Say one word.', - msgtype: 'org.boxel.message', + msgtype: APP_BOXEL_MESSAGE_MSGTYPE, formatted_body: 'Say one word.', format: 'org.matrix.custom.html', }); @@ -1529,7 +1536,7 @@ module('Integration | ai-assistant-panel', function (hooks) { await simulateRemoteMessage(roomId, '@testuser:staging', { body: 'I have a feeling something will go wrong', - msgtype: 'org.boxel.message', + msgtype: APP_BOXEL_MESSAGE_MSGTYPE, formatted_body: 'I have a feeling something will go wrong', format: 'org.matrix.custom.html', }); @@ -1546,7 +1553,7 @@ module('Integration | ai-assistant-panel', function (hooks) { await simulateRemoteMessage(roomId, '@testuser:staging', { body: 'I have a feeling something will go wrong', - msgtype: 'org.boxel.message', + msgtype: APP_BOXEL_MESSAGE_MSGTYPE, formatted_body: 'I have a feeling something will go wrong', format: 'org.matrix.custom.html', }); @@ -1668,7 +1675,7 @@ module('Integration | ai-assistant-panel', function (hooks) { let roomId = await renderAiAssistantPanel(); simulateRemoteMessage(roomId, '@matic:boxel', { body: 'Say one word.', - msgtype: 'org.boxel.message', + msgtype: APP_BOXEL_MESSAGE_MSGTYPE, formatted_body: 'Say one word.', format: 'org.matrix.custom.html', }); @@ -1683,7 +1690,7 @@ module('Integration | ai-assistant-panel', function (hooks) { simulateRemoteMessage(roomId, '@matic:boxel', { body: 'What is a french bulldog?', - msgtype: 'org.boxel.message', + msgtype: APP_BOXEL_MESSAGE_MSGTYPE, formatted_body: 'What is a french bulldog?', format: 'org.matrix.custom.html', }); @@ -1877,7 +1884,7 @@ module('Integration | ai-assistant-panel', function (hooks) { await waitFor('[data-test-person="Fadhlan"]'); let roomId = createAndJoinRoom('@testuser:staging', 'test room 1'); simulateRemoteMessage(roomId, '@aibot:localhost', { - msgtype: 'org.boxel.command', + msgtype: APP_BOXEL_COMMAND_MSGTYPE, body: 'Changing first name to Evie', formatted_body: 'Changing first name to Evie', format: 'org.matrix.custom.html', @@ -1951,7 +1958,7 @@ module('Integration | ai-assistant-panel', function (hooks) { let roomId = createAndJoinRoom('@testuser:staging', 'test room 1'); simulateRemoteMessage(roomId, '@aibot:localhost', { body: 'Changing first name to Evie', - msgtype: 'org.boxel.command', + msgtype: APP_BOXEL_COMMAND_MSGTYPE, formatted_body: 'Changing first name to Evie', format: 'org.matrix.custom.html', data: JSON.stringify({ @@ -1980,7 +1987,7 @@ module('Integration | ai-assistant-panel', function (hooks) { (event) => event.type === 'm.room.message' && typeof event.content === 'object' && - event.content.msgtype === 'org.boxel.commandResult', + event.content.msgtype === APP_BOXEL_COMMAND_RESULT_MSGTYPE, ); assert.equal( commandResultEvents.length, @@ -2002,7 +2009,7 @@ module('Integration | ai-assistant-panel', function (hooks) { (event) => event.type === 'm.room.message' && typeof event.content === 'object' && - event.content.msgtype === 'org.boxel.commandResult', + event.content.msgtype === APP_BOXEL_COMMAND_RESULT_MSGTYPE, ); assert.equal( commandResultEvents.length, @@ -2016,7 +2023,7 @@ module('Integration | ai-assistant-panel', function (hooks) { let roomId = await renderAiAssistantPanel(id); await simulateRemoteMessage(roomId, '@aibot:localhost', { - msgtype: 'org.boxel.command', + msgtype: APP_BOXEL_COMMAND_MSGTYPE, body: 'Search for the following card', formatted_body: 'Search for the following card', format: 'org.matrix.custom.html', @@ -2049,7 +2056,7 @@ module('Integration | ai-assistant-panel', function (hooks) { let commandResultEvent = (await getRoomEvents(roomId)).find( (e) => e.type === 'm.room.message' && - e.content.msgtype === 'org.boxel.commandResult' && + e.content.msgtype === APP_BOXEL_COMMAND_MSGTYPE && e.content['m.relates_to']?.rel_type === 'm.annotation', ) as CommandResultEvent; let serializedResults = @@ -2077,7 +2084,7 @@ module('Integration | ai-assistant-panel', function (hooks) { let roomId = await renderAiAssistantPanel(id); await simulateRemoteMessage(roomId, '@aibot:localhost', { - msgtype: 'org.boxel.command', + msgtype: APP_BOXEL_COMMAND_MSGTYPE, body: 'Search for the following card', formatted_body: 'Search for the following card', format: 'org.matrix.custom.html', @@ -2109,7 +2116,7 @@ module('Integration | ai-assistant-panel', function (hooks) { let commandResultEvent = (await getRoomEvents(roomId)).find( (e) => e.type === 'm.room.message' && - e.content.msgtype === 'org.boxel.commandResult' && + e.content.msgtype === APP_BOXEL_COMMAND_RESULT_MSGTYPE && e.content['m.relates_to']?.rel_type === 'm.annotation', ) as CommandResultEvent; let serializedResults = @@ -2135,7 +2142,7 @@ module('Integration | ai-assistant-panel', function (hooks) { let id = `${testRealmURL}Person/fadhlan.json`; let roomId = await renderAiAssistantPanel(id); await simulateRemoteMessage(roomId, '@aibot:localhost', { - msgtype: 'org.boxel.command', + msgtype: APP_BOXEL_COMMAND_MSGTYPE, body: 'Search for the following card', formatted_body: 'Search for the following card', format: 'org.matrix.custom.html', @@ -2206,7 +2213,7 @@ module('Integration | ai-assistant-panel', function (hooks) { }; await simulateRemoteMessage(roomId, '@aibot:localhost', { - msgtype: 'org.boxel.command', + msgtype: APP_BOXEL_COMMAND_MSGTYPE, body: 'Search for the following card', formatted_body: 'Search for the following card', format: 'org.matrix.custom.html', @@ -2281,7 +2288,7 @@ module('Integration | ai-assistant-panel', function (hooks) { }; await simulateRemoteMessage(roomId, '@aibot:localhost', { - msgtype: 'org.boxel.command', + msgtype: APP_BOXEL_COMMAND_MSGTYPE, body: 'Search for the following card', formatted_body: 'Search for the following card', format: 'org.matrix.custom.html', diff --git a/packages/host/tests/integration/components/ai-module-creation-test.gts b/packages/host/tests/integration/components/ai-module-creation-test.gts index d9c07e0c59..4cc8149604 100644 --- a/packages/host/tests/integration/components/ai-module-creation-test.gts +++ b/packages/host/tests/integration/components/ai-module-creation-test.gts @@ -6,6 +6,11 @@ import { module, skip } from 'qunit'; import { baseRealm, Loader, type Realm } from '@cardstack/runtime-common'; +import { + APP_BOXEL_COMMAND_MSGTYPE, + APP_BOXEL_ROOM_SKILLS_EVENT_TYPE, +} from '@cardstack/runtime-common/matrix-constants'; + import CardPrerender from '@cardstack/host/components/card-prerender'; import OperatorMode from '@cardstack/host/components/operator-mode/container'; @@ -183,12 +188,12 @@ module('Integration | create app module via ai-assistant', function (hooks) { 'Event content is correct', ); assert.strictEqual( - getRoomState(roomId, 'com.cardstack.boxel.room.skills').enabledEventIds + getRoomState(roomId, APP_BOXEL_ROOM_SKILLS_EVENT_TYPE).enabledEventIds .length, 1, 'Only added skill is present', ); - let skillEventId = getRoomState(roomId, 'com.cardstack.boxel.room.skills') + let skillEventId = getRoomState(roomId, APP_BOXEL_ROOM_SKILLS_EVENT_TYPE) .enabledEventIds[0]; let skillEventData = JSON.parse( JSON.parse(events.find((e) => e.event_id === skillEventId)?.content.data) @@ -207,7 +212,7 @@ module('Integration | create app module via ai-assistant', function (hooks) { const moduleCode = `import { Component, CardDef, FieldDef, linksTo, linksToMany, field, contains, containsMany } from 'https://cardstack.com/base/card-api';\nimport StringField from 'https://cardstack.com/base/string';\nimport BooleanField from 'https://cardstack.com/base/boolean';\nimport DateField from 'https://cardstack.com/base/date';\nimport DateTimeField from 'https://cardstack.com/base/datetime';\nimport NumberField from 'https://cardstack.com/base/number';\nimport MarkdownField from 'https://cardstack.com/base/markdown';\nimport { AppCard } from 'http://localhost:4201/catalog/app-card';\n\nexport class Tour extends CardDef {\n static displayName = 'Tour';\n\n @field tourID = contains(StringField);\n @field date = contains(DateField);\n @field time = contains(DateTimeField);\n @field parentNames = contains(StringField);\n @field contactInformation = contains(StringField);\n @field notes = contains(MarkdownField);\n\n @field parents = linksToMany(() => Parent);\n}\n\nexport class Student extends CardDef {\n static displayName = 'Student';\n\n @field studentID = contains(StringField);\n @field name = contains(StringField);\n @field age = contains(NumberField);\n @field enrollmentDate = contains(DateField);\n @field parentInformation = contains(MarkdownField);\n @field allergiesMedicalNotes = contains(MarkdownField);\n @field attendanceRecords = containsMany(MarkdownField);\n \n @field parents = linksToMany(() => Parent);\n @field classes = linksToMany(() => Class);\n}\n\nexport class Parent extends CardDef {\n static displayName = 'Parent';\n\n @field parentID = contains(StringField);\n @field name = contains(StringField);\n @field contactInformation = contains(StringField);\n \n @field students = linksToMany(Student);\n @field tours = linksToMany(Tour);\n}\n\nexport class Staff extends CardDef {\n static displayName = 'Staff';\n\n @field staffID = contains(StringField);\n @field name = contains(StringField);\n @field role = contains(StringField);\n @field contactInformation = contains(StringField);\n @field schedule = contains(MarkdownField);\n}\n\nexport class Class extends CardDef {\n static displayName = 'Class';\n\n @field classID = contains(StringField);\n @field name = contains(StringField);\n @field schedule = contains(MarkdownField);\n \n @field instructor = linksTo(Staff);\n @field enrolledStudents = linksToMany(Student);\n}\n\nexport class Communication extends CardDef {\n static displayName = 'Communication';\n\n @field communicationID = contains(StringField);\n @field date = contains(DateField);\n @field type = contains(StringField);\n @field content = contains(MarkdownField);\n @field followUpDate = contains(DateField);\n}\n\nexport class PreschoolCRMApp extends AppCard {\n static displayName = 'Preschool CRM';\n\n @field tours = containsMany(Tour);\n @field students = containsMany(Student);\n @field parents = containsMany(Parent);\n @field staff = containsMany(Staff);\n @field classes = containsMany(Class);\n @field communications = containsMany(Communication);\n}\n`; simulateRemoteMessage(roomId, '@aibot:localhost', { - msgtype: 'org.boxel.command', + msgtype: APP_BOXEL_COMMAND_MSGTYPE, body: 'Generate code for Preschool CRM based on product requirement document.', formatted_body: 'Generate code for Preschool CRM based on product requirement document.', diff --git a/packages/matrix/helpers/index.ts b/packages/matrix/helpers/index.ts index 9f6e2ad8e6..5add1af257 100644 --- a/packages/matrix/helpers/index.ts +++ b/packages/matrix/helpers/index.ts @@ -9,6 +9,7 @@ import { import { realmPassword } from './realm-credentials'; import { registerUser } from '../docker/synapse'; import { IsolatedRealmServer } from './isolated-realm-server'; +import { APP_BOXEL_MESSAGE_MSGTYPE } from './matrix-constants'; export const testHost = 'http://localhost:4202/test'; export const mailHost = 'http://localhost:5001'; @@ -740,13 +741,13 @@ export async function getRoomEvents( rooms.map((r) => getAllRoomEvents(r, accessToken)), ); // there will generally be 2 rooms, one is the DM room we do for - // authentication, the other is the actual chat (with org.boxel.message events) + // authentication, the other is the actual chat (with app.boxel.message events) return ( roomsWithEvents.find((messages) => { return messages.find( (message) => message.type === 'm.room.message' && - message.content?.msgtype === 'org.boxel.message', + message.content?.msgtype === APP_BOXEL_MESSAGE_MSGTYPE, ); }) ?? [] ); diff --git a/packages/matrix/helpers/matrix-constants.ts b/packages/matrix/helpers/matrix-constants.ts new file mode 100644 index 0000000000..2b1953d124 --- /dev/null +++ b/packages/matrix/helpers/matrix-constants.ts @@ -0,0 +1,8 @@ +export const APP_BOXEL_CARDFRAGMENT_MSGTYPE = 'app.boxel.cardFragment'; +export const APP_BOXEL_MESSAGE_MSGTYPE = 'app.boxel.message'; +export const APP_BOXEL_COMMAND_MSGTYPE = 'app.boxel.command'; +export const APP_BOXEL_CARD_FORMAT = 'app.boxel.card'; +export const APP_BOXEL_COMMAND_RESULT_MSGTYPE = 'app.boxel.commandResult'; +export const APP_BOXEL_REALM_SERVER_EVENT_MSGTYPE = + 'app.boxel.realm-server-event'; +export const APP_BOXEL_REALMS_EVENT_TYPE = 'app.boxel.realms'; diff --git a/packages/matrix/package.json b/packages/matrix/package.json index dc53faafd9..af5b64d401 100644 --- a/packages/matrix/package.json +++ b/packages/matrix/package.json @@ -4,6 +4,7 @@ "license": "MIT", "devDependencies": { "@aws-crypto/sha256-js": "^5.2.0", + "@cardstack/runtime-common": "workspace:*", "@playwright/test": "^1.48.0", "@types/fs-extra": "^9.0.13", "@types/jsonwebtoken": "^9.0.5", diff --git a/packages/matrix/tests/commands.spec.ts b/packages/matrix/tests/commands.spec.ts index 3801814bf0..9417fbdd77 100644 --- a/packages/matrix/tests/commands.spec.ts +++ b/packages/matrix/tests/commands.spec.ts @@ -1,5 +1,10 @@ import { expect, test } from '@playwright/test'; import { Credentials, putEvent, registerUser } from '../docker/synapse'; +import { + APP_BOXEL_MESSAGE_MSGTYPE, + APP_BOXEL_COMMAND_MSGTYPE, + APP_BOXEL_COMMAND_RESULT_MSGTYPE, +} from '../helpers/matrix-constants'; import { login, @@ -56,7 +61,9 @@ test.describe('Commands', () => { let message; await expect(async () => { message = (await getRoomEvents()).pop()!; - expect(message?.content?.msgtype).toStrictEqual('org.boxel.message'); + expect(message?.content?.msgtype).toStrictEqual( + APP_BOXEL_MESSAGE_MSGTYPE, + ); }).toPass(); let boxelMessageData = JSON.parse(message!.content.data); @@ -187,7 +194,9 @@ test.describe('Commands', () => { let message; await expect(async () => { message = (await getRoomEvents()).pop()!; - expect(message?.content?.msgtype).toStrictEqual('org.boxel.message'); + expect(message?.content?.msgtype).toStrictEqual( + APP_BOXEL_MESSAGE_MSGTYPE, + ); }).toPass(); let boxelMessageData = JSON.parse(message!.content.data); expect(boxelMessageData.context.tools).toMatchObject([]); @@ -218,7 +227,7 @@ test.describe('Commands', () => { ).toHaveCount(1); await sendMessage(page, room1, 'please change this card'); let message = (await getRoomEvents()).pop()!; - expect(message.content.msgtype).toStrictEqual('org.boxel.message'); + expect(message.content.msgtype).toStrictEqual(APP_BOXEL_MESSAGE_MSGTYPE); let boxelMessageData = JSON.parse(message.content.data); expect(boxelMessageData.context.tools).toMatchObject([]); }); */ @@ -230,7 +239,7 @@ test.describe('Commands', () => { let room1 = await getRoomId(page); let cardId = `${appURL}/hassan`; let content = { - msgtype: 'org.boxel.command', + msgtype: APP_BOXEL_COMMAND_MSGTYPE, format: 'org.matrix.custom.html', body: 'some command', formatted_body: 'some command', @@ -278,7 +287,7 @@ test.describe('Commands', () => { let room1 = await getRoomId(page); let card_id = `${appURL}/hassan`; let content = { - msgtype: 'org.boxel.command', + msgtype: APP_BOXEL_COMMAND_MSGTYPE, format: 'org.matrix.custom.html', body: 'some command', formatted_body: 'some command', @@ -313,7 +322,7 @@ test.describe('Commands', () => { await expect(async () => { let events = await getRoomEvents('user1', 'pass', room1); let commandResultEvent = (events as any).find( - (e: any) => e.content.msgtype === 'org.boxel.commandResult', + (e: any) => e.content.msgtype === APP_BOXEL_COMMAND_RESULT_MSGTYPE, ); await expect(commandResultEvent).toBeDefined(); }).toPass(); @@ -335,7 +344,7 @@ test.describe('Commands', () => { await page.locator('[data-test-switch-to-code-mode-button]').click(); await waitUntil(async () => (await getRoomEvents()).length > 0); let message = (await getRoomEvents()).pop()!; - expect(message.content.msgtype).toStrictEqual('org.boxel.message'); + expect(message.content.msgtype).toStrictEqual(APP_BOXEL_MESSAGE_MSGTYPE); let boxelMessageData = JSON.parse(message.content.data); expect(boxelMessageData.context.tools.length).toEqual(1); expect(boxelMessageData.context.tools[0].type).toEqual('function'); diff --git a/packages/matrix/tests/messages.spec.ts b/packages/matrix/tests/messages.spec.ts index dac2d61535..c1c4c21f9a 100644 --- a/packages/matrix/tests/messages.spec.ts +++ b/packages/matrix/tests/messages.spec.ts @@ -28,6 +28,10 @@ import { startServer as startRealmServer, type IsolatedRealmServer, } from '../helpers/isolated-realm-server'; +import { + APP_BOXEL_CARDFRAGMENT_MSGTYPE, + APP_BOXEL_MESSAGE_MSGTYPE, +} from '../helpers/matrix-constants'; test.describe('Room messages', () => { let synapse: SynapseInstance; @@ -246,7 +250,7 @@ test.describe('Room messages', () => { let cardFragments = messages.filter( (message) => message.type === 'm.room.message' && - message.content?.msgtype === 'org.boxel.cardFragment', + message.content?.msgtype === APP_BOXEL_CARDFRAGMENT_MSGTYPE, ); expect(cardFragments.length).toStrictEqual(4); let lastFragmentIndex = messages.findIndex( @@ -918,12 +922,12 @@ test.describe('Room messages', () => { let messageEvents = events.filter( (e) => e.type === 'm.room.message' && - e.content.msgtype === 'org.boxel.message', + e.content.msgtype === APP_BOXEL_MESSAGE_MSGTYPE, ); let cardFragmentEvents = events.filter( (e) => e.type === 'm.room.message' && - e.content.msgtype === 'org.boxel.cardFragment' && + e.content.msgtype === APP_BOXEL_CARDFRAGMENT_MSGTYPE && !e.content.data.nextFragment, ); expect(messageEvents.length).toEqual(3); diff --git a/packages/matrix/tests/realm-urls.spec.ts b/packages/matrix/tests/realm-urls.spec.ts index 4c1f78796a..0f794d757c 100644 --- a/packages/matrix/tests/realm-urls.spec.ts +++ b/packages/matrix/tests/realm-urls.spec.ts @@ -37,7 +37,7 @@ test.describe('Realm URLs in Matrix account data', () => { await updateAccountData( '@user1:localhost', user.accessToken, - 'com.cardstack.boxel.realms', + APP_BOXEL_REALMS_EVENT_TYPE, JSON.stringify({ realms: [] }), ); await setupUserSubscribed('@user1:localhost', realmServer); @@ -65,7 +65,7 @@ test.describe('Realm URLs in Matrix account data', () => { await updateAccountData( '@user1:localhost', user.accessToken, - 'com.cardstack.boxel.realms', + APP_BOXEL_REALMS_EVENT_TYPE, JSON.stringify({ realms: ['http://example.com/'], }), diff --git a/packages/matrix/tests/registration-with-token.spec.ts b/packages/matrix/tests/registration-with-token.spec.ts index a3bcca71d4..71ccc7b6da 100644 --- a/packages/matrix/tests/registration-with-token.spec.ts +++ b/packages/matrix/tests/registration-with-token.spec.ts @@ -29,6 +29,7 @@ import { encodeWebSafeBase64, } from '../helpers'; import { registerUser, createRegistrationToken } from '../docker/synapse'; +import { APP_BOXEL_REALMS_EVENT_TYPE } from '../helpers/matrix-constants'; const REGISTRATION_TOKEN = 'abc123'; @@ -284,7 +285,7 @@ test.describe('User Registration w/ Token - isolated realm server', () => { let realms = await getAccountData<{ realms: string[] } | undefined>( auth.userId, auth.accessToken, - 'com.cardstack.boxel.realms', + APP_BOXEL_REALMS_EVENT_TYPE, ); expect(realms).toEqual({ realms: ['http://localhost:4205/user1/personal/'], diff --git a/packages/realm-server/server.ts b/packages/realm-server/server.ts index 69c90919fc..ec78a70285 100644 --- a/packages/realm-server/server.ts +++ b/packages/realm-server/server.ts @@ -36,6 +36,7 @@ import { getMatrixUsername, } from '@cardstack/runtime-common/matrix-client'; import { createRoutes } from './routes'; +import { APP_BOXEL_REALM_SERVER_EVENT_MSGTYPE } from '@cardstack/runtime-common/matrix-constants'; const DEFAULT_PERMISSIONS = Object.freeze([ 'read', @@ -432,7 +433,7 @@ export class RealmServer { await this.matrixClient.sendEvent(roomId, 'm.room.message', { body: JSON.stringify({ eventType }), - msgtype: 'org.boxel.realm-server-event', + msgtype: APP_BOXEL_REALM_SERVER_EVENT_MSGTYPE, }); }; diff --git a/packages/realm-server/tests/realm-server-test.ts b/packages/realm-server/tests/realm-server-test.ts index c893a55061..d6d6cbea6f 100644 --- a/packages/realm-server/tests/realm-server-test.ts +++ b/packages/realm-server/tests/realm-server-test.ts @@ -81,6 +81,7 @@ import { resetCatalogRealms } from '../handlers/handle-fetch-catalog-realms'; import Stripe from 'stripe'; import sinon from 'sinon'; import { getStripe } from '@cardstack/billing/stripe-webhook-handlers/stripe'; +import { APP_BOXEL_REALM_SERVER_EVENT_MSGTYPE } from '@cardstack/runtime-common/matrix-constants'; setGracefulCleanup(); const testRealmURL = new URL('http://127.0.0.1:4444/'); @@ -4083,7 +4084,9 @@ module('Realm Server', function (hooks) { done: () => void, ) { let messages = await matrixClient.roomMessages(roomId); - if (messages[0].content.msgtype === 'org.boxel.realm-server-event') { + if ( + messages[0].content.msgtype === APP_BOXEL_REALM_SERVER_EVENT_MSGTYPE + ) { assert.strictEqual( messages[0].content.body, JSON.stringify({ eventType: 'billing-notification' }), diff --git a/packages/runtime-common/matrix-constants.ts b/packages/runtime-common/matrix-constants.ts new file mode 100644 index 0000000000..0bcfceec16 --- /dev/null +++ b/packages/runtime-common/matrix-constants.ts @@ -0,0 +1,9 @@ +export const APP_BOXEL_CARDFRAGMENT_MSGTYPE = 'app.boxel.cardFragment'; +export const APP_BOXEL_MESSAGE_MSGTYPE = 'app.boxel.message'; +export const APP_BOXEL_COMMAND_MSGTYPE = 'app.boxel.command'; +export const APP_BOXEL_CARD_FORMAT = 'app.boxel.card'; +export const APP_BOXEL_COMMAND_RESULT_MSGTYPE = 'app.boxel.commandResult'; +export const APP_BOXEL_REALM_SERVER_EVENT_MSGTYPE = + 'app.boxel.realm-server-event'; +export const APP_BOXEL_ROOM_SKILLS_EVENT_TYPE = 'app.boxel.room.skills'; +export const APP_BOXEL_REALMS_EVENT_TYPE = 'app.boxel.realms'; diff --git a/packages/vscode-boxel-tools/src/realm-auth.ts b/packages/vscode-boxel-tools/src/realm-auth.ts index 3d3819103f..fe9478674f 100644 --- a/packages/vscode-boxel-tools/src/realm-auth.ts +++ b/packages/vscode-boxel-tools/src/realm-auth.ts @@ -8,6 +8,7 @@ import { RealmAuthClient, RealmAuthMatrixClientInterface, } from '@cardstack/runtime-common/realm-auth-client'; +import { APP_BOXEL_REALMS_EVENT_TYPE } from '@cardstack/runtime-common/matrix-constants'; import { createClient } from 'matrix-js-sdk'; export class RealmAuth { @@ -50,7 +51,7 @@ export class RealmAuth { }); let realmsEventData = (await matrixClient.getAccountDataFromServer( - 'com.cardstack.boxel.realms', + APP_BOXEL_REALMS_EVENT_TYPE, )) || {}; console.log('Realms event data:', realmsEventData, typeof realmsEventData); let realms = realmsEventData.realms || []; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index eb06d379ff..d885e46b94 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1671,6 +1671,9 @@ importers: '@aws-crypto/sha256-js': specifier: ^5.2.0 version: 5.2.0 + '@cardstack/runtime-common': + specifier: workspace:* + version: link:../runtime-common '@playwright/test': specifier: ^1.48.0 version: 1.48.0