Skip to content

Commit 8fa0992

Browse files
committed
Improve use of msgtype on CommandResult matrix events
- now used to identify results with a result card vs those without
1 parent 5ab1eca commit 8fa0992

File tree

9 files changed

+97
-72
lines changed

9 files changed

+97
-72
lines changed

packages/ai-bot/helpers.ts

+16-9
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@ import { MatrixEvent, type IRoomEvent } from 'matrix-js-sdk';
1616
import { ChatCompletionMessageToolCall } from 'openai/resources/chat/completions';
1717
import * as Sentry from '@sentry/node';
1818
import { logger } from '@cardstack/runtime-common';
19-
import { APP_BOXEL_COMMAND_RESULT_EVENT_TYPE } from '../runtime-common/matrix-constants';
19+
import {
20+
APP_BOXEL_COMMAND_RESULT_EVENT_TYPE,
21+
APP_BOXEL_COMMAND_RESULT_WITH_OUTPUT_MSGTYPE,
22+
} from '../runtime-common/matrix-constants';
2023
import {
2124
APP_BOXEL_CARDFRAGMENT_MSGTYPE,
2225
APP_BOXEL_MESSAGE_MSGTYPE,
@@ -139,14 +142,15 @@ export function constructHistory(
139142
}
140143
}
141144
let event = { ...rawEvent } as DiscreteMatrixEvent;
142-
if (event.type === APP_BOXEL_COMMAND_RESULT_EVENT_TYPE) {
145+
if (
146+
event.type === APP_BOXEL_COMMAND_RESULT_EVENT_TYPE &&
147+
event.content.msgtype == APP_BOXEL_COMMAND_RESULT_WITH_OUTPUT_MSGTYPE
148+
) {
143149
let { cardEventId } = event.content.data;
144-
if (cardEventId) {
145-
event.content.data.card = serializedCardFromFragments(
146-
cardEventId,
147-
cardFragments,
148-
);
149-
}
150+
event.content.data.card = serializedCardFromFragments(
151+
cardEventId,
152+
cardFragments,
153+
);
150154
}
151155
if (event.type !== 'm.room.message') {
152156
continue;
@@ -401,7 +405,10 @@ function toPromptMessageWithToolResult(
401405
let content = 'pending';
402406
if (commandResult) {
403407
let status = commandResult.content['m.relates_to']?.key;
404-
if (commandResult.content.data.card) {
408+
if (
409+
commandResult.content.msgtype ===
410+
APP_BOXEL_COMMAND_RESULT_WITH_OUTPUT_MSGTYPE
411+
) {
405412
content = `Command ${status}, with result card: ${JSON.stringify(
406413
commandResult.content.data.card,
407414
)}.\n`;

packages/ai-bot/tests/chat-titling-test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type { MatrixEvent as DiscreteMatrixEvent } from 'https://cardstack.com/b
44
import {
55
APP_BOXEL_COMMAND_MSGTYPE,
66
APP_BOXEL_COMMAND_RESULT_EVENT_TYPE,
7+
APP_BOXEL_COMMAND_RESULT_WITH_NO_OUTPUT_MSGTYPE,
78
} from '@cardstack/runtime-common/matrix-constants';
89
import { IEvent, IRoomEvent, MatrixEvent } from 'matrix-js-sdk';
910

@@ -415,8 +416,7 @@ module('shouldSetRoomTitle', () => {
415416
key: 'applied',
416417
rel_type: 'm.annotation',
417418
},
418-
data: {},
419-
msgtype: APP_BOXEL_COMMAND_RESULT_EVENT_TYPE,
419+
msgtype: APP_BOXEL_COMMAND_RESULT_WITH_NO_OUTPUT_MSGTYPE,
420420
},
421421
};
422422
const eventLog: IRoomEvent[] = [

packages/ai-bot/tests/prompt-construction-test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ import {
1313
} from '../helpers';
1414
import {
1515
APP_BOXEL_MESSAGE_MSGTYPE,
16-
APP_BOXEL_COMMAND_RESULT_MSGTYPE,
1716
APP_BOXEL_COMMAND_MSGTYPE,
1817
APP_BOXEL_COMMAND_RESULT_EVENT_TYPE,
18+
APP_BOXEL_COMMAND_RESULT_WITH_OUTPUT_MSGTYPE,
1919
} from '@cardstack/runtime-common/matrix-constants';
2020

2121
import type {
@@ -1510,7 +1510,7 @@ test('Return host result of tool call back to open ai', () => {
15101510
rel_type: 'm.annotation',
15111511
key: 'applied',
15121512
},
1513-
msgtype: APP_BOXEL_COMMAND_RESULT_MSGTYPE,
1513+
msgtype: APP_BOXEL_COMMAND_RESULT_WITH_OUTPUT_MSGTYPE,
15141514
data: {
15151515
card: JSON.stringify({
15161516
data: {

packages/base/matrix-event.gts

+16-11
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ import {
1010
APP_BOXEL_CARDFRAGMENT_MSGTYPE,
1111
APP_BOXEL_COMMAND_MSGTYPE,
1212
APP_BOXEL_COMMAND_RESULT_EVENT_TYPE,
13-
APP_BOXEL_COMMAND_RESULT_MSGTYPE,
13+
APP_BOXEL_COMMAND_RESULT_WITH_NO_OUTPUT_MSGTYPE,
14+
APP_BOXEL_COMMAND_RESULT_WITH_OUTPUT_MSGTYPE,
1415
APP_BOXEL_MESSAGE_MSGTYPE,
1516
APP_BOXEL_ROOM_SKILLS_EVENT_TYPE,
1617
} from '@cardstack/runtime-common/matrix-constants';
@@ -228,7 +229,7 @@ export interface SkillsConfigEvent extends RoomStateEvent {
228229

229230
export interface CommandResultEvent extends BaseMatrixEvent {
230231
type: typeof APP_BOXEL_COMMAND_RESULT_EVENT_TYPE;
231-
content: CommandResultContent;
232+
content: CommandResultWithOutputContent | CommandResultWithNoOutputContent;
232233
unsigned: {
233234
age: number;
234235
transaction_id: string;
@@ -237,23 +238,27 @@ export interface CommandResultEvent extends BaseMatrixEvent {
237238
};
238239
}
239240

240-
export interface CommandResultContent {
241-
'm.relates_to'?: {
241+
export interface CommandResultWithOutputContent {
242+
'm.relates_to': {
242243
rel_type: 'm.annotation';
243244
key: string;
244245
event_id: string;
245-
'm.in_reply_to'?: {
246-
event_id: string;
247-
};
248246
};
249247
data: {
250-
// we use this field over the wire since the matrix message protocol
251-
// limits us to 65KB per message
252-
cardEventId?: string;
248+
cardEventId: string;
253249
// we materialize this field on the server
254250
card?: LooseSingleCardDocument;
255251
};
256-
msgtype: typeof APP_BOXEL_COMMAND_RESULT_MSGTYPE;
252+
msgtype: typeof APP_BOXEL_COMMAND_RESULT_WITH_OUTPUT_MSGTYPE;
253+
}
254+
255+
export interface CommandResultWithNoOutputContent {
256+
'm.relates_to': {
257+
rel_type: 'm.annotation';
258+
key: string;
259+
event_id: string;
260+
};
261+
msgtype: typeof APP_BOXEL_COMMAND_RESULT_WITH_NO_OUTPUT_MSGTYPE;
257262
}
258263

259264
export type MatrixEvent =

packages/host/app/lib/matrix-classes/message-builder.ts

+13-17
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { LooseSingleCardDocument } from '@cardstack/runtime-common';
99
import {
1010
APP_BOXEL_COMMAND_MSGTYPE,
1111
APP_BOXEL_COMMAND_RESULT_EVENT_TYPE,
12-
APP_BOXEL_COMMAND_RESULT_MSGTYPE,
12+
APP_BOXEL_COMMAND_RESULT_WITH_OUTPUT_MSGTYPE,
1313
APP_BOXEL_MESSAGE_MSGTYPE,
1414
} from '@cardstack/runtime-common/matrix-constants';
1515

@@ -22,7 +22,6 @@ import type {
2222
CardMessageEvent,
2323
CommandEvent,
2424
CommandResultEvent,
25-
CommandResultContent,
2625
MatrixEvent as DiscreteMatrixEvent,
2726
MessageEvent,
2827
} from 'https://cardstack.com/base/matrix-event';
@@ -138,26 +137,23 @@ export default class MessageBuilder {
138137
private async buildMessageCommand(message: Message) {
139138
let event = this.event as CommandEvent;
140139
let command = event.content.data.toolCall;
141-
let annotation = this.builderContext.events.find((e: any) => {
140+
let commandResultEvent = this.builderContext.events.find((e: any) => {
142141
let r = e.content['m.relates_to'];
143142
return (
144143
e.type === APP_BOXEL_COMMAND_RESULT_EVENT_TYPE &&
145-
e.content.msgtype === APP_BOXEL_COMMAND_RESULT_MSGTYPE &&
146-
r?.rel_type === 'm.annotation' &&
147-
(r?.event_id === event.content.data.eventId ||
148-
r?.event_id === event.event_id ||
149-
r?.event_id === this.builderContext.effectiveEventId)
144+
r.rel_type === 'm.annotation' &&
145+
(r.event_id === event.content.data.eventId ||
146+
r.event_id === event.event_id ||
147+
r.event_id === this.builderContext.effectiveEventId)
150148
);
151149
}) as CommandResultEvent | undefined;
152-
let status: CommandStatus = 'ready';
153-
let commandResultContent = annotation?.content as
154-
| CommandResultContent
155-
| undefined;
156-
if (commandResultContent?.['m.relates_to']?.key === 'applied') {
157-
status = 'applied';
158-
}
159-
let commandResultCardEventId: string | undefined =
160-
commandResultContent?.data.cardEventId ?? undefined;
150+
let status = (commandResultEvent?.content?.['m.relates_to']?.key ||
151+
'ready') as CommandStatus;
152+
let commandResultCardEventId =
153+
commandResultEvent?.content?.msgtype ===
154+
APP_BOXEL_COMMAND_RESULT_WITH_OUTPUT_MSGTYPE
155+
? commandResultEvent.content.data.cardEventId
156+
: undefined;
161157
let messageCommand = new MessageCommand(
162158
message,
163159
command.id,

packages/host/app/services/matrix-service.ts

+37-19
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ import {
4141
APP_BOXEL_CARDFRAGMENT_MSGTYPE,
4242
APP_BOXEL_COMMAND_MSGTYPE,
4343
APP_BOXEL_COMMAND_RESULT_EVENT_TYPE,
44-
APP_BOXEL_COMMAND_RESULT_MSGTYPE,
44+
APP_BOXEL_COMMAND_RESULT_WITH_NO_OUTPUT_MSGTYPE,
45+
APP_BOXEL_COMMAND_RESULT_WITH_OUTPUT_MSGTYPE,
4546
APP_BOXEL_MESSAGE_MSGTYPE,
4647
APP_BOXEL_REALM_SERVER_EVENT_MSGTYPE,
4748
APP_BOXEL_REALMS_EVENT_TYPE,
@@ -64,8 +65,9 @@ import type * as CardAPI from 'https://cardstack.com/base/card-api';
6465
import type {
6566
CardMessageContent,
6667
CardFragmentContent,
67-
CommandResultContent,
6868
MatrixEvent as DiscreteMatrixEvent,
69+
CommandResultWithNoOutputContent,
70+
CommandResultWithOutputContent,
6971
} from 'https://cardstack.com/base/matrix-event';
7072

7173
import { SkillCard } from 'https://cardstack.com/base/skill-card';
@@ -501,7 +503,11 @@ export default class MatrixService extends Service {
501503
async sendEvent(
502504
roomId: string,
503505
eventType: string,
504-
content: CardMessageContent | CardFragmentContent | CommandResultContent,
506+
content:
507+
| CardMessageContent
508+
| CardFragmentContent
509+
| CommandResultWithNoOutputContent
510+
| CommandResultWithOutputContent,
505511
) {
506512
let roomData = await this.ensureRoomData(roomId);
507513
return roomData.mutex.dispatch(async () => {
@@ -526,17 +532,31 @@ export default class MatrixService extends Service {
526532
if (resultCard) {
527533
[resultCardEventId] = await this.addCardsToRoom([resultCard], roomId);
528534
}
529-
let content: CommandResultContent = {
530-
msgtype: APP_BOXEL_COMMAND_RESULT_MSGTYPE,
531-
'm.relates_to': {
532-
event_id: invokedToolFromEventId,
533-
key: 'applied',
534-
rel_type: 'm.annotation',
535-
},
536-
data: {
537-
cardEventId: resultCardEventId ?? undefined,
538-
},
539-
};
535+
let content:
536+
| CommandResultWithNoOutputContent
537+
| CommandResultWithOutputContent;
538+
if (resultCardEventId === undefined) {
539+
content = {
540+
msgtype: APP_BOXEL_COMMAND_RESULT_WITH_NO_OUTPUT_MSGTYPE,
541+
'm.relates_to': {
542+
event_id: invokedToolFromEventId,
543+
key: 'applied',
544+
rel_type: 'm.annotation',
545+
},
546+
};
547+
} else {
548+
content = {
549+
msgtype: APP_BOXEL_COMMAND_RESULT_WITH_OUTPUT_MSGTYPE,
550+
'm.relates_to': {
551+
event_id: invokedToolFromEventId,
552+
key: 'applied',
553+
rel_type: 'm.annotation',
554+
},
555+
data: {
556+
cardEventId: resultCardEventId,
557+
},
558+
};
559+
}
540560
try {
541561
return await this.sendEvent(
542562
roomId,
@@ -1279,16 +1299,14 @@ export default class MatrixService extends Service {
12791299
} else if (
12801300
roomData &&
12811301
event.type === APP_BOXEL_COMMAND_RESULT_EVENT_TYPE &&
1282-
event.content?.msgtype === APP_BOXEL_COMMAND_RESULT_MSGTYPE
1302+
event.content?.msgtype === APP_BOXEL_COMMAND_RESULT_WITH_OUTPUT_MSGTYPE
12831303
) {
12841304
let data = (
12851305
typeof event.content.data === 'string'
12861306
? JSON.parse(event.content.data)
12871307
: event.content.data
1288-
) as CommandResultContent['data'];
1289-
if (data.cardEventId) {
1290-
this.ensureCardFragmentsLoaded(data.cardEventId, roomData);
1291-
}
1308+
) as CommandResultWithOutputContent['data'];
1309+
this.ensureCardFragmentsLoaded(data.cardEventId, roomData);
12921310
} else if (
12931311
event.type === 'm.room.message' &&
12941312
event.content?.msgtype === APP_BOXEL_REALM_SERVER_EVENT_MSGTYPE

packages/host/tests/integration/components/ai-assistant-panel-test.gts

+3-10
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import {
2020
APP_BOXEL_CARDFRAGMENT_MSGTYPE,
2121
APP_BOXEL_COMMAND_MSGTYPE,
2222
APP_BOXEL_COMMAND_RESULT_EVENT_TYPE,
23-
APP_BOXEL_COMMAND_RESULT_MSGTYPE,
2423
APP_BOXEL_MESSAGE_MSGTYPE,
2524
} from '@cardstack/runtime-common/matrix-constants';
2625

@@ -1982,11 +1981,8 @@ module('Integration | ai-assistant-panel', function (hooks) {
19821981
event_id: '__EVENT_ID__',
19831982
},
19841983
});
1985-
let commandResultEvents = getRoomEvents(roomId).filter(
1986-
(event) =>
1987-
event.type === 'm.room.message' &&
1988-
typeof event.content === 'object' &&
1989-
event.content.msgtype === APP_BOXEL_COMMAND_RESULT_MSGTYPE,
1984+
let commandResultEvents = await getRoomEvents(roomId).filter(
1985+
(event) => event.type === APP_BOXEL_COMMAND_RESULT_EVENT_TYPE,
19901986
);
19911987
assert.equal(
19921988
commandResultEvents.length,
@@ -2005,10 +2001,7 @@ module('Integration | ai-assistant-panel', function (hooks) {
20052001
.exists();
20062002

20072003
commandResultEvents = await getRoomEvents(roomId).filter(
2008-
(event) =>
2009-
event.type === APP_BOXEL_COMMAND_RESULT_EVENT_TYPE &&
2010-
typeof event.content === 'object' &&
2011-
event.content.msgtype === APP_BOXEL_COMMAND_RESULT_MSGTYPE,
2004+
(event) => event.type === APP_BOXEL_COMMAND_RESULT_EVENT_TYPE,
20122005
);
20132006
assert.equal(
20142007
commandResultEvents.length,

packages/matrix/helpers/matrix-constants.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ export const APP_BOXEL_MESSAGE_MSGTYPE = 'app.boxel.message';
33
export const APP_BOXEL_COMMAND_MSGTYPE = 'app.boxel.command';
44
export const APP_BOXEL_CARD_FORMAT = 'app.boxel.card';
55
export const APP_BOXEL_COMMAND_RESULT_EVENT_TYPE = 'app.boxel.commandResult';
6-
export const APP_BOXEL_COMMAND_RESULT_MSGTYPE = 'app.boxel.commandResult';
6+
export const APP_BOXEL_COMMAND_RESULT_WITH_OUTPUT_MSGTYPE =
7+
'app.boxel.commandResultWithOutput';
8+
export const APP_BOXEL_COMMAND_RESULT_WITH_NO_OUTPUT_MSGTYPE =
9+
'app.boxel.commandResultWithNoOutput';
710
export const APP_BOXEL_REALM_SERVER_EVENT_MSGTYPE =
811
'app.boxel.realm-server-event';
912
export const APP_BOXEL_REALMS_EVENT_TYPE = 'app.boxel.realms';

packages/runtime-common/matrix-constants.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ export const APP_BOXEL_MESSAGE_MSGTYPE = 'app.boxel.message';
33
export const APP_BOXEL_COMMAND_MSGTYPE = 'app.boxel.command';
44
export const APP_BOXEL_CARD_FORMAT = 'app.boxel.card';
55
export const APP_BOXEL_COMMAND_RESULT_EVENT_TYPE = 'app.boxel.commandResult';
6-
export const APP_BOXEL_COMMAND_RESULT_MSGTYPE = 'app.boxel.commandResult';
6+
export const APP_BOXEL_COMMAND_RESULT_WITH_OUTPUT_MSGTYPE =
7+
'app.boxel.commandResultWithOutput';
8+
export const APP_BOXEL_COMMAND_RESULT_WITH_NO_OUTPUT_MSGTYPE =
9+
'app.boxel.commandResultWithNoOutput';
710
export const APP_BOXEL_REALM_SERVER_EVENT_MSGTYPE =
811
'app.boxel.realm-server-event';
912
export const APP_BOXEL_ROOM_SKILLS_EVENT_TYPE = 'app.boxel.room.skills';

0 commit comments

Comments
 (0)