@qearlyao/familiar 0.2.2 → 0.2.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/README.md +1 -12
- package/dist/browser-tools.js +3 -0
- package/dist/discord.js +70 -17
- package/dist/inbound-attachments.js +11 -1
- package/dist/memory/index/chunk-indexer.js +23 -6
- package/dist/memory/lcm/context-transformer.js +5 -5
- package/dist/memory/operator.js +1 -27
- package/dist/persona.js +1 -1
- package/dist/runtime.js +10 -0
- package/dist/service.js +15 -13
- package/dist/silent-marker.js +64 -0
- package/dist/web-static.js +36 -1
- package/dist/web.js +77 -19
- package/npm-shrinkwrap.json +5139 -0
- package/package.json +5 -4
- package/web/dist/assets/{index-BPZQbZh5.js → index-C-w9fjBf.js} +1 -1
- package/web/dist/index.html +1 -1
package/dist/web.js
CHANGED
|
@@ -12,6 +12,7 @@ import { publicAttachmentPath } from "./generated-media.js";
|
|
|
12
12
|
import { materializeInboundAttachments } from "./inbound-attachments.js";
|
|
13
13
|
import { PROVIDER_DEFAULTS, parseModelRef, supportedThinkingLevels } from "./models.js";
|
|
14
14
|
import { loadPersona, parsePersonaName } from "./persona.js";
|
|
15
|
+
import { consumeSilentDelta, createSilentFilterState, finalizeSilentFilter, parseAgentReply } from "./silent-marker.js";
|
|
15
16
|
import { createAuth, sessionCookie, verifyTotp } from "./web-auth.js";
|
|
16
17
|
import { acceptWebSocket, decodeFrames, encodeFrame, replayEvents } from "./web-events.js";
|
|
17
18
|
import { isObject, readJsonBody, sendJson, sendText } from "./web-http.js";
|
|
@@ -304,6 +305,7 @@ function webMessageFromRecord(config, record, assistantName) {
|
|
|
304
305
|
attachments: webAttachments(config, record.attachments),
|
|
305
306
|
thinking: record.thinking,
|
|
306
307
|
thinkingMs: record.thinkingMs,
|
|
308
|
+
silent: record.silent || undefined,
|
|
307
309
|
ts: toUnixMs(record.ts),
|
|
308
310
|
};
|
|
309
311
|
}
|
|
@@ -364,6 +366,8 @@ export async function startWebDaemon(config, familiarAgent, discordDaemon, optio
|
|
|
364
366
|
const eventsByChannel = new Map();
|
|
365
367
|
const runtimeSubscriptions = new Map();
|
|
366
368
|
const locallyStreamedOutboundIds = new Set();
|
|
369
|
+
const silentFilters = new Map();
|
|
370
|
+
const pendingMessageStarts = new Map();
|
|
367
371
|
const publish = (event) => {
|
|
368
372
|
const fullEvent = { ...event, eventId: eventId(), ts: event.ts ?? Date.now() };
|
|
369
373
|
const events = eventsByChannel.get(fullEvent.channelKey ?? "") ?? [];
|
|
@@ -390,30 +394,74 @@ export async function startWebDaemon(config, familiarAgent, discordDaemon, optio
|
|
|
390
394
|
const publishStoredAgentEvent = (channelKey, messageIdValue, storedEvent, ts) => {
|
|
391
395
|
if (storedEvent.type === "message_start" && storedEvent.role === "assistant") {
|
|
392
396
|
locallyStreamedOutboundIds.add(messageIdValue);
|
|
397
|
+
silentFilters.set(messageIdValue, createSilentFilterState());
|
|
398
|
+
pendingMessageStarts.set(messageIdValue, ts);
|
|
399
|
+
}
|
|
400
|
+
const startedSilentMessage = () => {
|
|
401
|
+
if (!pendingMessageStarts.has(messageIdValue))
|
|
402
|
+
return false;
|
|
403
|
+
const startTs = pendingMessageStarts.get(messageIdValue);
|
|
404
|
+
pendingMessageStarts.delete(messageIdValue);
|
|
393
405
|
publish({
|
|
394
406
|
type: "message_started",
|
|
395
407
|
channelKey,
|
|
396
408
|
messageId: messageIdValue,
|
|
397
409
|
role: "assistant",
|
|
398
410
|
who: personaName,
|
|
399
|
-
ts,
|
|
411
|
+
ts: startTs,
|
|
400
412
|
});
|
|
401
|
-
|
|
413
|
+
return true;
|
|
414
|
+
};
|
|
402
415
|
if (storedEvent.type === "message_update") {
|
|
403
416
|
const assistantEvent = storedEvent.assistantMessageEvent;
|
|
404
417
|
if (assistantEvent.type === "thinking_delta") {
|
|
418
|
+
startedSilentMessage();
|
|
405
419
|
publishDelta(channelKey, messageIdValue, "thinking", assistantEvent.delta, ts);
|
|
406
420
|
}
|
|
407
421
|
if (assistantEvent.type === "text_delta") {
|
|
408
|
-
|
|
422
|
+
const filter = silentFilters.get(messageIdValue);
|
|
423
|
+
if (!filter) {
|
|
424
|
+
startedSilentMessage();
|
|
425
|
+
publishDelta(channelKey, messageIdValue, "text", assistantEvent.delta, ts);
|
|
426
|
+
}
|
|
427
|
+
else {
|
|
428
|
+
const result = consumeSilentDelta(filter, assistantEvent.delta);
|
|
429
|
+
if (result.kind === "emit" && result.text) {
|
|
430
|
+
startedSilentMessage();
|
|
431
|
+
publishDelta(channelKey, messageIdValue, "text", result.text, ts);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
409
434
|
}
|
|
410
435
|
}
|
|
436
|
+
if (storedEvent.type === "tool_execution_start") {
|
|
437
|
+
startedSilentMessage();
|
|
438
|
+
}
|
|
411
439
|
if (storedEvent.type === "message_end" && storedEvent.role === "assistant") {
|
|
440
|
+
const filter = silentFilters.get(messageIdValue);
|
|
441
|
+
let silent = false;
|
|
442
|
+
if (filter) {
|
|
443
|
+
const final = finalizeSilentFilter(filter);
|
|
444
|
+
silent = final.silent;
|
|
445
|
+
if (!silent) {
|
|
446
|
+
startedSilentMessage();
|
|
447
|
+
if (final.flush) {
|
|
448
|
+
publishDelta(channelKey, messageIdValue, "text", final.flush, ts);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
else {
|
|
452
|
+
pendingMessageStarts.delete(messageIdValue);
|
|
453
|
+
}
|
|
454
|
+
silentFilters.delete(messageIdValue);
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
startedSilentMessage();
|
|
458
|
+
}
|
|
412
459
|
publish({
|
|
413
460
|
type: "message_completed",
|
|
414
461
|
channelKey,
|
|
415
462
|
messageId: messageIdValue,
|
|
416
463
|
usage: storedEvent.usage,
|
|
464
|
+
silent: silent || undefined,
|
|
417
465
|
ts,
|
|
418
466
|
});
|
|
419
467
|
}
|
|
@@ -450,24 +498,27 @@ export async function startWebDaemon(config, familiarAgent, discordDaemon, optio
|
|
|
450
498
|
messageId: outboundId,
|
|
451
499
|
thinkingMs: record.thinkingMs,
|
|
452
500
|
attachments: webAttachments(config, record.attachments),
|
|
501
|
+
silent: record.silent || undefined,
|
|
453
502
|
ts: toUnixMs(record.ts),
|
|
454
503
|
};
|
|
455
504
|
if (locallyStreamedOutboundIds.delete(outboundId)) {
|
|
456
505
|
publish(completion);
|
|
457
506
|
return;
|
|
458
507
|
}
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
508
|
+
if (!record.silent) {
|
|
509
|
+
publish({
|
|
510
|
+
type: "message_started",
|
|
511
|
+
channelKey: runtime.channelKey,
|
|
512
|
+
messageId: outboundId,
|
|
513
|
+
role: "assistant",
|
|
514
|
+
who: personaName,
|
|
515
|
+
ts: toUnixMs(record.ts),
|
|
516
|
+
});
|
|
517
|
+
if (record.thinking)
|
|
518
|
+
publishDelta(runtime.channelKey, outboundId, "thinking", record.thinking, toUnixMs(record.ts));
|
|
519
|
+
if (record.text)
|
|
520
|
+
publishDelta(runtime.channelKey, outboundId, "text", record.text, toUnixMs(record.ts));
|
|
521
|
+
}
|
|
471
522
|
publish(completion);
|
|
472
523
|
}
|
|
473
524
|
});
|
|
@@ -564,7 +615,9 @@ export async function startWebDaemon(config, familiarAgent, discordDaemon, optio
|
|
|
564
615
|
finally {
|
|
565
616
|
await recorder.flush();
|
|
566
617
|
}
|
|
567
|
-
|
|
618
|
+
const parsed = parseAgentReply(reply.text);
|
|
619
|
+
const finalText = parsed.silent ? "" : reply.text;
|
|
620
|
+
if (!started && !parsed.silent) {
|
|
568
621
|
publish({
|
|
569
622
|
type: "message_started",
|
|
570
623
|
channelKey: runtime.channelKey,
|
|
@@ -572,7 +625,9 @@ export async function startWebDaemon(config, familiarAgent, discordDaemon, optio
|
|
|
572
625
|
role: "assistant",
|
|
573
626
|
who: personaName,
|
|
574
627
|
});
|
|
575
|
-
|
|
628
|
+
if (finalText) {
|
|
629
|
+
publishDelta(runtime.channelKey, assistantMessageId, "text", finalText);
|
|
630
|
+
}
|
|
576
631
|
}
|
|
577
632
|
const thinkingMs = thinkingDurationMs(summary);
|
|
578
633
|
publish({
|
|
@@ -581,14 +636,16 @@ export async function startWebDaemon(config, familiarAgent, discordDaemon, optio
|
|
|
581
636
|
messageId: assistantMessageId,
|
|
582
637
|
thinkingMs,
|
|
583
638
|
attachments: webAttachments(config, reply.attachments),
|
|
639
|
+
silent: parsed.silent || undefined,
|
|
584
640
|
});
|
|
585
641
|
locallyStreamedOutboundIds.add(assistantMessageId);
|
|
586
642
|
return {
|
|
587
|
-
text:
|
|
643
|
+
text: finalText,
|
|
588
644
|
messageId: assistantMessageId,
|
|
589
645
|
thinking: summary.thinking,
|
|
590
646
|
thinkingMs,
|
|
591
647
|
attachments: reply.attachments,
|
|
648
|
+
silent: parsed.silent,
|
|
592
649
|
};
|
|
593
650
|
};
|
|
594
651
|
const drainJobs = async (runtime) => {
|
|
@@ -611,6 +668,7 @@ export async function startWebDaemon(config, familiarAgent, discordDaemon, optio
|
|
|
611
668
|
attachments: reply.attachments,
|
|
612
669
|
thinking: reply.thinking,
|
|
613
670
|
thinkingMs: reply.thinkingMs,
|
|
671
|
+
silent: reply.silent,
|
|
614
672
|
replyToMessageId: dispatch.triggerMessageId,
|
|
615
673
|
});
|
|
616
674
|
}
|
|
@@ -672,7 +730,7 @@ export async function startWebDaemon(config, familiarAgent, discordDaemon, optio
|
|
|
672
730
|
}
|
|
673
731
|
try {
|
|
674
732
|
if (request.method === "GET" && url.pathname.startsWith("/api/web/attachments/")) {
|
|
675
|
-
return serveAttachment(config, response, url.pathname);
|
|
733
|
+
return serveAttachment(config, response, url.pathname, request.headers.range);
|
|
676
734
|
}
|
|
677
735
|
if (request.method === "GET" && url.pathname === "/api/web/auth/mode") {
|
|
678
736
|
sendJson(response, 200, { mode: config.web.authMode, personaName });
|