@johpaz/hive-sdk 0.0.18 → 0.1.3
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/bun.lock +291 -1
- package/docs/HIVE-HARNESS.md +113 -0
- package/package.json +36 -2
- package/packages/cli/package.json +1 -1
- package/packages/core/package.json +13 -2
- package/packages/core/src/ace/Tracer.ts +1 -1
- package/packages/core/src/agent/AgentRunner.ts +12 -0
- package/packages/core/src/agent/ContextCompiler.ts +4 -4
- package/packages/core/src/agent/ConversationStore.ts +30 -20
- package/packages/core/src/agent/selectors/PlaybookSelector.ts +50 -76
- package/packages/core/src/agent/selectors/SkillSelector.ts +106 -262
- package/packages/core/src/agent/selectors/ToolSelector.ts +53 -89
- package/packages/core/src/auth/auth.ts +36 -23
- package/packages/core/src/harness/boot-id.ts +20 -0
- package/packages/core/src/harness/collections.ts +98 -0
- package/packages/core/src/harness/db-helpers.ts +87 -0
- package/packages/core/src/harness/durable-queue.ts +337 -0
- package/packages/core/src/harness/goal-verifier.ts +141 -0
- package/packages/core/src/harness/harness.test.ts +236 -0
- package/packages/core/src/harness/index.ts +34 -0
- package/packages/core/src/harness/job-store.ts +399 -0
- package/packages/core/src/harness/proof-packet.ts +69 -0
- package/packages/core/src/harness/reconcile.ts +149 -0
- package/packages/core/src/harness/run-epoch.ts +32 -0
- package/packages/core/src/harness/run-store.ts +334 -0
- package/packages/core/src/index.ts +6 -0
- package/packages/core/src/memory/Scratchpad.test.ts +23 -21
- package/packages/core/src/memory/Scratchpad.ts +41 -24
- package/packages/core/src/storage/HiveDBStorage.ts +64 -0
- package/packages/core/src/storage/SQLiteStorage.ts +7 -0
- package/packages/core/src/storage/hiveSeed.ts +308 -0
- package/packages/core/src/storage/hiveStorage.test.ts +38 -0
- package/packages/core/src/storage/index.ts +10 -0
- package/packages/core/src/storage/seed.ts +5 -1
- package/packages/core/src/storage/usage.ts +106 -167
- package/packages/core/src/tool-runtime/tool-runtime.test.ts +11 -3
- package/packages/core/src/tools/agents/get-available-models.ts +52 -56
- package/packages/core/src/tools/agents/index.ts +77 -60
- package/packages/core/src/tools/core/index.ts +106 -291
- package/packages/core/src/tools/meeting/index.ts +83 -93
- package/packages/core/src/utils/toon.ts +4 -4
|
@@ -2,20 +2,33 @@
|
|
|
2
2
|
* Meeting Tools - 4 tools para transcripción de reuniones
|
|
3
3
|
*
|
|
4
4
|
* @category meeting
|
|
5
|
-
*
|
|
6
|
-
* - meeting_start — Inicia una sesión de transcripción
|
|
7
|
-
* - meeting_add_segment — Transcribe un chunk de audio y lo agrega a la sesión
|
|
8
|
-
* - meeting_stop — Detiene la sesión de transcripción
|
|
9
|
-
* - meeting_report — Obtiene el transcript completo para que el agente genere el reporte
|
|
10
5
|
*/
|
|
11
6
|
|
|
12
7
|
import type { Tool } from "../types.ts";
|
|
13
|
-
import {
|
|
8
|
+
import { getHiveDB } from "../../storage/HiveDBStorage.ts";
|
|
14
9
|
import { voiceService, type AudioInput } from "../../voice/index.ts";
|
|
15
10
|
import { logger } from "../../utils/logger.ts";
|
|
16
11
|
|
|
17
12
|
const log = logger.child("meeting");
|
|
18
13
|
|
|
14
|
+
interface MeetingSession {
|
|
15
|
+
id: string;
|
|
16
|
+
title: string;
|
|
17
|
+
status: string;
|
|
18
|
+
sttModel: string;
|
|
19
|
+
startedAt: number;
|
|
20
|
+
stoppedAt?: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface MeetingSegment {
|
|
24
|
+
sessionId: string;
|
|
25
|
+
seq: number;
|
|
26
|
+
speaker?: string;
|
|
27
|
+
text: string;
|
|
28
|
+
durationMs?: number;
|
|
29
|
+
createdAt: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
19
32
|
// ─── meeting_start ───────────────────────────────────────────────────────────
|
|
20
33
|
|
|
21
34
|
export const meetingStartTool: Tool = {
|
|
@@ -43,30 +56,27 @@ export const meetingStartTool: Tool = {
|
|
|
43
56
|
(params.stt_model as string) || "whisper-large-v3-turbo";
|
|
44
57
|
|
|
45
58
|
try {
|
|
46
|
-
const db =
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
title: string;
|
|
56
|
-
status: string;
|
|
57
|
-
stt_model: string;
|
|
58
|
-
started_at: number;
|
|
59
|
+
const db = await getHiveDB();
|
|
60
|
+
const sessionsCol = db.collection<MeetingSession>("meeting_sessions");
|
|
61
|
+
const id = crypto.randomUUID();
|
|
62
|
+
const session: MeetingSession = {
|
|
63
|
+
id,
|
|
64
|
+
title,
|
|
65
|
+
status: "active",
|
|
66
|
+
sttModel,
|
|
67
|
+
startedAt: Math.floor(Date.now() / 1000),
|
|
59
68
|
};
|
|
69
|
+
await sessionsCol.put(id, session);
|
|
60
70
|
|
|
61
|
-
log.info(`Meeting session started: ${
|
|
71
|
+
log.info(`Meeting session started: ${id} — "${title}"`);
|
|
62
72
|
|
|
63
73
|
return {
|
|
64
74
|
ok: true,
|
|
65
|
-
session_id:
|
|
66
|
-
title
|
|
67
|
-
status:
|
|
68
|
-
stt_model:
|
|
69
|
-
message: `✅ Sesión de reunión iniciada. ID: ${
|
|
75
|
+
session_id: id,
|
|
76
|
+
title,
|
|
77
|
+
status: session.status,
|
|
78
|
+
stt_model: sttModel,
|
|
79
|
+
message: `✅ Sesión de reunión iniciada. ID: ${id}\nTítulo: "${title}"\nModelo STT: ${sttModel}`,
|
|
70
80
|
};
|
|
71
81
|
} catch (error) {
|
|
72
82
|
log.error(`meeting_start error: ${(error as Error).message}`);
|
|
@@ -106,21 +116,19 @@ export const meetingAddSegmentTool: Tool = {
|
|
|
106
116
|
execute: async (params) => {
|
|
107
117
|
const sessionId = params.session_id as string;
|
|
108
118
|
const audioBase64 = params.audio_base64 as string;
|
|
109
|
-
const speaker = (params.speaker as string) ||
|
|
119
|
+
const speaker = (params.speaker as string) || undefined;
|
|
110
120
|
const mimeType = (params.mime_type as string) || "audio/webm";
|
|
111
121
|
|
|
112
122
|
try {
|
|
113
|
-
const db =
|
|
123
|
+
const db = await getHiveDB();
|
|
124
|
+
const sessionsCol = db.collection<MeetingSession>("meeting_sessions");
|
|
125
|
+
const segmentsCol = db.collection<MeetingSegment>("meeting_segments");
|
|
114
126
|
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
`SELECT id, stt_model, status FROM meeting_sessions WHERE id = ?`
|
|
118
|
-
)
|
|
119
|
-
.get(sessionId) as { id: string; stt_model: string; status: string } | undefined;
|
|
120
|
-
|
|
121
|
-
if (!session) {
|
|
127
|
+
const sessionEntry = await sessionsCol.get(sessionId);
|
|
128
|
+
if (!sessionEntry) {
|
|
122
129
|
return { ok: false, error: `Sesión ${sessionId} no encontrada.` };
|
|
123
130
|
}
|
|
131
|
+
const session = sessionEntry.doc;
|
|
124
132
|
if (session.status !== "active") {
|
|
125
133
|
return {
|
|
126
134
|
ok: false,
|
|
@@ -136,7 +144,7 @@ export const meetingAddSegmentTool: Tool = {
|
|
|
136
144
|
|
|
137
145
|
let transcription: string;
|
|
138
146
|
try {
|
|
139
|
-
transcription = await voiceService.transcribe(audioInput, session.
|
|
147
|
+
transcription = await voiceService.transcribe(audioInput, session.sttModel);
|
|
140
148
|
} catch (transcribeError) {
|
|
141
149
|
return {
|
|
142
150
|
ok: false,
|
|
@@ -144,18 +152,19 @@ export const meetingAddSegmentTool: Tool = {
|
|
|
144
152
|
};
|
|
145
153
|
}
|
|
146
154
|
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
)
|
|
151
|
-
.get(sessionId) as { next_seq: number };
|
|
152
|
-
|
|
153
|
-
const seq = seqResult.next_seq;
|
|
155
|
+
const entries = await segmentsCol.scan();
|
|
156
|
+
const seq = entries
|
|
157
|
+
.filter(e => e.doc.sessionId === sessionId)
|
|
158
|
+
.reduce((max, e) => Math.max(max, e.doc.seq), -1) + 1;
|
|
154
159
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
160
|
+
const segmentId = `${sessionId}:${seq}`;
|
|
161
|
+
await segmentsCol.put(segmentId, {
|
|
162
|
+
sessionId,
|
|
163
|
+
seq,
|
|
164
|
+
speaker,
|
|
165
|
+
text: transcription,
|
|
166
|
+
createdAt: Math.floor(Date.now() / 1000),
|
|
167
|
+
});
|
|
159
168
|
|
|
160
169
|
log.info(`Segment ${seq} added to session ${sessionId}: "${transcription.substring(0, 60)}..."`);
|
|
161
170
|
|
|
@@ -196,15 +205,15 @@ export const meetingStopTool: Tool = {
|
|
|
196
205
|
const sessionId = params.session_id as string;
|
|
197
206
|
|
|
198
207
|
try {
|
|
199
|
-
const db =
|
|
200
|
-
|
|
201
|
-
const
|
|
202
|
-
.query(`SELECT id, title, status FROM meeting_sessions WHERE id = ?`)
|
|
203
|
-
.get(sessionId) as { id: string; title: string; status: string } | undefined;
|
|
208
|
+
const db = await getHiveDB();
|
|
209
|
+
const sessionsCol = db.collection<MeetingSession>("meeting_sessions");
|
|
210
|
+
const segmentsCol = db.collection<MeetingSegment>("meeting_segments");
|
|
204
211
|
|
|
205
|
-
|
|
212
|
+
const sessionEntry = await sessionsCol.get(sessionId);
|
|
213
|
+
if (!sessionEntry) {
|
|
206
214
|
return { ok: false, error: `Sesión ${sessionId} no encontrada.` };
|
|
207
215
|
}
|
|
216
|
+
const session = sessionEntry.doc;
|
|
208
217
|
if (session.status === "stopped" || session.status === "report_ready") {
|
|
209
218
|
return {
|
|
210
219
|
ok: true,
|
|
@@ -213,22 +222,20 @@ export const meetingStopTool: Tool = {
|
|
|
213
222
|
};
|
|
214
223
|
}
|
|
215
224
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
).run(sessionId);
|
|
225
|
+
const stoppedAt = Math.floor(Date.now() / 1000);
|
|
226
|
+
await sessionsCol.put(sessionId, { ...session, status: "stopped", stoppedAt });
|
|
219
227
|
|
|
220
|
-
const
|
|
221
|
-
|
|
222
|
-
.get(sessionId) as { count: number };
|
|
228
|
+
const entries = await segmentsCol.scan();
|
|
229
|
+
const count = entries.filter(e => e.doc.sessionId === sessionId).length;
|
|
223
230
|
|
|
224
|
-
log.info(`Meeting session stopped: ${sessionId} — ${
|
|
231
|
+
log.info(`Meeting session stopped: ${sessionId} — ${count} segments`);
|
|
225
232
|
|
|
226
233
|
return {
|
|
227
234
|
ok: true,
|
|
228
235
|
session_id: sessionId,
|
|
229
236
|
title: session.title,
|
|
230
|
-
segment_count:
|
|
231
|
-
message: `⏹️ Sesión "${session.title}" detenida.\n${
|
|
237
|
+
segment_count: count,
|
|
238
|
+
message: `⏹️ Sesión "${session.title}" detenida.\n${count} segmentos transcritos.\n\nPuedes pedir el reporte con: "Genera el reporte de la reunión ${sessionId}"`,
|
|
232
239
|
};
|
|
233
240
|
} catch (error) {
|
|
234
241
|
log.error(`meeting_stop error: ${(error as Error).message}`);
|
|
@@ -257,38 +264,21 @@ export const meetingReportTool: Tool = {
|
|
|
257
264
|
const sessionId = params.session_id as string;
|
|
258
265
|
|
|
259
266
|
try {
|
|
260
|
-
const db =
|
|
261
|
-
|
|
262
|
-
const
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
)
|
|
267
|
-
.get(sessionId) as {
|
|
268
|
-
id: string;
|
|
269
|
-
title: string;
|
|
270
|
-
status: string;
|
|
271
|
-
stt_model: string;
|
|
272
|
-
started_at: number;
|
|
273
|
-
stopped_at: number | null;
|
|
274
|
-
} | undefined;
|
|
275
|
-
|
|
276
|
-
if (!session) {
|
|
267
|
+
const db = await getHiveDB();
|
|
268
|
+
const sessionsCol = db.collection<MeetingSession>("meeting_sessions");
|
|
269
|
+
const segmentsCol = db.collection<MeetingSegment>("meeting_segments");
|
|
270
|
+
|
|
271
|
+
const sessionEntry = await sessionsCol.get(sessionId);
|
|
272
|
+
if (!sessionEntry) {
|
|
277
273
|
return { ok: false, error: `Sesión ${sessionId} no encontrada.` };
|
|
278
274
|
}
|
|
275
|
+
const session = sessionEntry.doc;
|
|
279
276
|
|
|
280
|
-
const
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
)
|
|
286
|
-
.all(sessionId) as {
|
|
287
|
-
seq: number;
|
|
288
|
-
speaker: string | null;
|
|
289
|
-
text: string;
|
|
290
|
-
created_at: number;
|
|
291
|
-
}[];
|
|
277
|
+
const entries = await segmentsCol.scan();
|
|
278
|
+
const segments = entries
|
|
279
|
+
.filter(e => e.doc.sessionId === sessionId)
|
|
280
|
+
.map(e => e.doc)
|
|
281
|
+
.sort((a, b) => a.seq - b.seq);
|
|
292
282
|
|
|
293
283
|
if (segments.length === 0) {
|
|
294
284
|
return {
|
|
@@ -301,9 +291,9 @@ export const meetingReportTool: Tool = {
|
|
|
301
291
|
.map((s) => (s.speaker ? `[${s.speaker}]: ${s.text}` : s.text))
|
|
302
292
|
.join("\n");
|
|
303
293
|
|
|
304
|
-
const durationSec = session.
|
|
305
|
-
? session.
|
|
306
|
-
: Math.floor(Date.now() / 1000) - session.
|
|
294
|
+
const durationSec = session.stoppedAt
|
|
295
|
+
? session.stoppedAt - session.startedAt
|
|
296
|
+
: Math.floor(Date.now() / 1000) - session.startedAt;
|
|
307
297
|
const durationMin = Math.floor(durationSec / 60);
|
|
308
298
|
const durationSecRem = durationSec % 60;
|
|
309
299
|
|
|
@@ -113,7 +113,7 @@ export function formatToolResult(data: any, model?: string): string {
|
|
|
113
113
|
const result = stringify(data, model)
|
|
114
114
|
|
|
115
115
|
if (result.tokensSaved > 0 && model) {
|
|
116
|
-
recordToonSavings({
|
|
116
|
+
void recordToonSavings({
|
|
117
117
|
jsonBytes: result.jsonBytes,
|
|
118
118
|
toonBytes: result.toonBytes,
|
|
119
119
|
savedBytes: result.savedBytes,
|
|
@@ -135,7 +135,7 @@ export function formatMCPResponse(data: any, model?: string): string {
|
|
|
135
135
|
const result = stringify(data, model)
|
|
136
136
|
|
|
137
137
|
if (result.tokensSaved > 0 && model) {
|
|
138
|
-
recordToonSavings({
|
|
138
|
+
void recordToonSavings({
|
|
139
139
|
jsonBytes: result.jsonBytes,
|
|
140
140
|
toonBytes: result.toonBytes,
|
|
141
141
|
savedBytes: result.savedBytes,
|
|
@@ -157,7 +157,7 @@ export function formatSkillOutput(data: any, model?: string): string {
|
|
|
157
157
|
const result = stringify(data, model)
|
|
158
158
|
|
|
159
159
|
if (result.tokensSaved > 0 && model) {
|
|
160
|
-
recordToonSavings({
|
|
160
|
+
void recordToonSavings({
|
|
161
161
|
jsonBytes: result.jsonBytes,
|
|
162
162
|
toonBytes: result.toonBytes,
|
|
163
163
|
savedBytes: result.savedBytes,
|
|
@@ -179,7 +179,7 @@ export function formatContext(data: any, model?: string): string {
|
|
|
179
179
|
const result = stringify(data, model)
|
|
180
180
|
|
|
181
181
|
if (result.tokensSaved > 0 && model) {
|
|
182
|
-
recordToonSavings({
|
|
182
|
+
void recordToonSavings({
|
|
183
183
|
jsonBytes: result.jsonBytes,
|
|
184
184
|
toonBytes: result.toonBytes,
|
|
185
185
|
savedBytes: result.savedBytes,
|