osborn 0.9.30 → 0.9.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/skills/user-context/SKILL.md +8 -3
- package/dist/index.js +12 -0
- package/dist/recall-client.d.ts +26 -11
- package/dist/recall-client.js +32 -19
- package/package.json +1 -1
|
@@ -17,12 +17,17 @@ Do not batch — capture as they happen.
|
|
|
17
17
|
</what-to-do>
|
|
18
18
|
|
|
19
19
|
<trigger-phrases>
|
|
20
|
-
|
|
20
|
+
This skill is a META operation — about building a model of the USER THEMSELVES,
|
|
21
|
+
not about any subject matter in the current conversation. The trigger phrases below
|
|
22
|
+
are intentionally specific so they cannot be confused with domain requests in any session.
|
|
23
|
+
|
|
24
|
+
- "update user context"
|
|
21
25
|
- "learn my language"
|
|
22
|
-
- "
|
|
26
|
+
- "start context interview"
|
|
27
|
+
- "grill me on my language"
|
|
23
28
|
- "learn how I talk"
|
|
24
29
|
- "standardise my language"
|
|
25
|
-
- "update
|
|
30
|
+
- "update my context"
|
|
26
31
|
</trigger-phrases>
|
|
27
32
|
|
|
28
33
|
<supporting-info>
|
package/dist/index.js
CHANGED
|
@@ -1111,6 +1111,7 @@ async function main() {
|
|
|
1111
1111
|
// Empty string = anonymous / unauthenticated; uploads fall back to a
|
|
1112
1112
|
// session-only path (no user prefix).
|
|
1113
1113
|
let currentUserId = '';
|
|
1114
|
+
let activeMeetingBotId = null; // Recall.ai bot ID if in a meeting
|
|
1114
1115
|
// Track the active resume session ID across scopes (ParticipantConnected + DataReceived)
|
|
1115
1116
|
// Updated by resume_session, session_selected, continue_session, switch_session handlers
|
|
1116
1117
|
let currentResumeSessionId;
|
|
@@ -3099,6 +3100,15 @@ async function main() {
|
|
|
3099
3100
|
currentLLM = null;
|
|
3100
3101
|
clearFastBrainSession();
|
|
3101
3102
|
clearPipelineFastBrainSession();
|
|
3103
|
+
// Auto-leave any active meeting bot when user disconnects from the room
|
|
3104
|
+
if (activeMeetingBotId) {
|
|
3105
|
+
const recallDisconnect = getRecallClient();
|
|
3106
|
+
if (recallDisconnect) {
|
|
3107
|
+
console.log(`🤝 Auto-leaving meeting (bot ${activeMeetingBotId}) — user disconnected from room`);
|
|
3108
|
+
recallDisconnect.leaveMeeting(activeMeetingBotId).catch(() => { });
|
|
3109
|
+
activeMeetingBotId = null;
|
|
3110
|
+
}
|
|
3111
|
+
}
|
|
3102
3112
|
console.log('⏳ Waiting for new user...\n');
|
|
3103
3113
|
});
|
|
3104
3114
|
room.on(RoomEvent.DataReceived, async (payload, participant, kind, topic) => {
|
|
@@ -3678,6 +3688,7 @@ async function main() {
|
|
|
3678
3688
|
const botId = await recallJoin.joinMeeting(meetingUrl, webhookBase);
|
|
3679
3689
|
const sessionId = currentLLM?.sessionId || currentResumeSessionId || 'default';
|
|
3680
3690
|
recallJoin.registerBot(botId, sessionId);
|
|
3691
|
+
activeMeetingBotId = botId;
|
|
3681
3692
|
await sendToFrontend({ type: 'meeting_joined', botId, message: 'Osborn has joined the meeting' });
|
|
3682
3693
|
}
|
|
3683
3694
|
catch (err) {
|
|
@@ -3693,6 +3704,7 @@ async function main() {
|
|
|
3693
3704
|
if (recallLeave && botId) {
|
|
3694
3705
|
try {
|
|
3695
3706
|
await recallLeave.leaveMeeting(botId);
|
|
3707
|
+
activeMeetingBotId = null;
|
|
3696
3708
|
await sendToFrontend({ type: 'meeting_left', botId });
|
|
3697
3709
|
}
|
|
3698
3710
|
catch (err) {
|
package/dist/recall-client.d.ts
CHANGED
|
@@ -4,18 +4,33 @@ export interface RecallBot {
|
|
|
4
4
|
meeting_url: string;
|
|
5
5
|
status: string;
|
|
6
6
|
}
|
|
7
|
-
export interface TranscriptWord {
|
|
8
|
-
text: string;
|
|
9
|
-
start_time: number;
|
|
10
|
-
end_time: number;
|
|
11
|
-
}
|
|
12
7
|
export interface TranscriptPayload {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
8
|
+
event: string;
|
|
9
|
+
data: {
|
|
10
|
+
data: {
|
|
11
|
+
words: Array<{
|
|
12
|
+
text: string;
|
|
13
|
+
start_timestamp?: {
|
|
14
|
+
relative?: number;
|
|
15
|
+
};
|
|
16
|
+
end_timestamp?: {
|
|
17
|
+
relative?: number;
|
|
18
|
+
};
|
|
19
|
+
}>;
|
|
20
|
+
language_code?: string;
|
|
21
|
+
participant?: {
|
|
22
|
+
id: number;
|
|
23
|
+
name: string;
|
|
24
|
+
is_host?: boolean;
|
|
25
|
+
platform?: string;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
bot?: {
|
|
29
|
+
id: string;
|
|
30
|
+
};
|
|
31
|
+
recording?: {
|
|
32
|
+
id: string;
|
|
33
|
+
};
|
|
19
34
|
};
|
|
20
35
|
}
|
|
21
36
|
export declare class RecallClient extends EventEmitter {
|
package/dist/recall-client.js
CHANGED
|
@@ -9,6 +9,17 @@ export class RecallClient extends EventEmitter {
|
|
|
9
9
|
this.#apiKey = apiKey;
|
|
10
10
|
}
|
|
11
11
|
async joinMeeting(meetingUrl, webhookBaseUrl, botName = 'Osborn') {
|
|
12
|
+
// Authoritative structure per https://docs.recall.ai/reference/bot_create
|
|
13
|
+
// and https://docs.recall.ai/docs/real-time-transcription:
|
|
14
|
+
//
|
|
15
|
+
// recording_config.transcript.provider — transcription provider config
|
|
16
|
+
// recording_config.realtime_endpoints — webhook/websocket delivery
|
|
17
|
+
//
|
|
18
|
+
// IMPORTANT:
|
|
19
|
+
// - Field is `realtime_endpoints` (NOT `real_time_endpoints`)
|
|
20
|
+
// - `url` and `events` are flat on the endpoint object (NOT nested under `config`)
|
|
21
|
+
// - `transcription_options` does NOT exist — use `transcript.provider`
|
|
22
|
+
// - Both transcript.provider AND realtime_endpoints must be set, or no events delivered
|
|
12
23
|
const res = await fetch(`${RECALL_BASE_URL}/bot`, {
|
|
13
24
|
method: 'POST',
|
|
14
25
|
headers: {
|
|
@@ -19,24 +30,26 @@ export class RecallClient extends EventEmitter {
|
|
|
19
30
|
meeting_url: meetingUrl,
|
|
20
31
|
bot_name: botName,
|
|
21
32
|
recording_config: {
|
|
22
|
-
|
|
23
|
-
|
|
33
|
+
transcript: {
|
|
34
|
+
provider: {
|
|
35
|
+
// recallai_streaming is built-in — no external API key needed,
|
|
36
|
+
// low-latency, works across all meeting platforms.
|
|
37
|
+
recallai_streaming: {
|
|
38
|
+
mode: 'prioritize_low_latency',
|
|
39
|
+
language_code: 'en',
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
},
|
|
24
43
|
realtime_endpoints: [{
|
|
25
44
|
type: 'webhook',
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
events: ['transcript.data'],
|
|
29
|
-
},
|
|
45
|
+
url: `${webhookBaseUrl}/webhook/recall`,
|
|
46
|
+
events: ['transcript.data'],
|
|
30
47
|
}],
|
|
31
|
-
transcription_options: {
|
|
32
|
-
provider: 'assembly_ai',
|
|
33
|
-
mode: 'prioritize_low_latency',
|
|
34
|
-
},
|
|
35
48
|
},
|
|
36
49
|
output_media: {
|
|
37
50
|
camera: {
|
|
38
|
-
//
|
|
39
|
-
//
|
|
51
|
+
// `kind` (not `type`) — confirmed from prior debugging.
|
|
52
|
+
// Output webpage plays TTS audio so meeting participants can hear the agent.
|
|
40
53
|
kind: 'webpage',
|
|
41
54
|
config: {
|
|
42
55
|
url: `${webhookBaseUrl}/meeting-output`,
|
|
@@ -69,16 +82,16 @@ export class RecallClient extends EventEmitter {
|
|
|
69
82
|
return bot.status_changes?.at(-1)?.code ?? 'unknown';
|
|
70
83
|
}
|
|
71
84
|
handleWebhook(payload) {
|
|
72
|
-
|
|
85
|
+
// Only process final transcripts (transcript.data), skip partials
|
|
86
|
+
if (payload.event !== 'transcript.data')
|
|
73
87
|
return;
|
|
74
|
-
const
|
|
88
|
+
const words = payload.data?.data?.words ?? [];
|
|
89
|
+
const text = words.map(w => w.text).join(' ').trim();
|
|
75
90
|
if (!text)
|
|
76
91
|
return;
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
text,
|
|
81
|
-
});
|
|
92
|
+
const speaker = payload.data?.data?.participant?.name ?? 'Unknown';
|
|
93
|
+
const botId = payload.data?.bot?.id ?? 'unknown';
|
|
94
|
+
this.emit('transcript', { botId, speaker, text });
|
|
82
95
|
}
|
|
83
96
|
registerBot(botId, sessionId) {
|
|
84
97
|
this.#activeBots.set(botId, sessionId);
|