@qearlyao/familiar 0.3.0 → 0.4.0

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.
Files changed (83) hide show
  1. package/HEARTBEAT.md +1 -1
  2. package/README.md +29 -0
  3. package/config.example.toml +2 -0
  4. package/dist/{agent.js → agent/factory.js} +11 -11
  5. package/dist/agent/session-helpers.js +1 -1
  6. package/dist/agent/tools.js +4 -4
  7. package/dist/cli.js +11 -11
  8. package/dist/{config.js → config/index.js} +7 -7
  9. package/dist/config/model-refs.js +1 -1
  10. package/dist/{config-overrides.js → config/overrides.js} +1 -1
  11. package/dist/{config-registry.js → config/registry.js} +2 -2
  12. package/dist/{settings.js → config/settings.js} +2 -2
  13. package/dist/{chat-log.js → conversation/chat-log.js} +1 -1
  14. package/dist/{contact-note.js → conversation/contact-note.js} +1 -1
  15. package/dist/{owner-identity.js → conversation/owner-identity.js} +2 -2
  16. package/dist/discord/channel.js +1 -1
  17. package/dist/discord/commands.js +1 -1
  18. package/dist/{discord.js → discord/daemon.js} +17 -17
  19. package/dist/discord/inbound.js +1 -1
  20. package/dist/discord/send.js +29 -20
  21. package/dist/discord/turn.js +3 -3
  22. package/dist/index.js +12 -12
  23. package/dist/{data-retention.js → lifecycle/data-retention.js} +1 -1
  24. package/dist/{hot-reload.js → lifecycle/hot-reload.js} +2 -2
  25. package/dist/media/attachment-limits.js +3 -0
  26. package/dist/{generated-media.js → media/generated-media.js} +1 -1
  27. package/dist/{image-gen.js → media/image-gen.js} +2 -2
  28. package/dist/{inbound-attachments.js → media/inbound-attachments.js} +47 -43
  29. package/dist/media/media-understanding.js +215 -0
  30. package/dist/memory/lcm/summarizer.js +1 -1
  31. package/dist/{added-models.js → models/added-models.js} +1 -1
  32. package/dist/{persona.js → prompting/persona.js} +1 -1
  33. package/dist/{agent-core.js → runtime/agent-core.js} +1 -1
  34. package/dist/{agent-events.js → runtime/agent-events.js} +1 -1
  35. package/dist/{agent-work-queue.js → runtime/agent-work-queue.js} +2 -2
  36. package/dist/{runtime.js → runtime/conversation-runtime.js} +3 -3
  37. package/dist/{runtime-manager.js → runtime/runtime-manager.js} +2 -2
  38. package/dist/{scheduler-runner.js → runtime/scheduler-runner.js} +1 -1
  39. package/dist/{scheduler.js → runtime/scheduler.js} +3 -3
  40. package/dist/{browser-tools.js → tools/browser-tools.js} +17 -26
  41. package/dist/util/fs.js +2 -1
  42. package/dist/web/agent-routes.js +104 -0
  43. package/dist/web/auth-routes.js +39 -0
  44. package/dist/web/auth.js +124 -30
  45. package/dist/web/config-routes.js +55 -0
  46. package/dist/web/conversation-routes.js +122 -0
  47. package/dist/web/daemon.js +108 -0
  48. package/dist/web/diary-routes.js +88 -0
  49. package/dist/web/errors.js +3 -0
  50. package/dist/web/event-hub.js +3 -3
  51. package/dist/web/messages.js +13 -10
  52. package/dist/web/multipart.js +7 -1
  53. package/dist/web/payloads.js +1 -1
  54. package/dist/web/request-context.js +25 -0
  55. package/dist/web/route-helpers.js +9 -0
  56. package/dist/web/routes.js +37 -0
  57. package/dist/web/runtime-actions.js +231 -0
  58. package/dist/web/session-store.js +161 -0
  59. package/dist/web/static.js +1 -1
  60. package/dist/web/stream.js +12 -3
  61. package/dist/{web-tools.js → web-tools/index.js} +8 -8
  62. package/npm-shrinkwrap.json +79 -2
  63. package/package.json +3 -1
  64. package/web/dist/assets/index-C-k4O5Dz.js +6 -0
  65. package/web/dist/assets/index-Dj-L9nX4.css +2 -0
  66. package/web/dist/assets/markdown-kaIeGxdv.js +14 -0
  67. package/web/dist/assets/react-Bi_azaFt.js +9 -0
  68. package/web/dist/assets/rolldown-runtime-S-ySWqyJ.js +1 -0
  69. package/web/dist/assets/ui-C12-nN_X.js +51 -0
  70. package/web/dist/assets/vendor-D1QXMhXm.js +16 -0
  71. package/web/dist/index.html +7 -2
  72. package/dist/media-understanding.js +0 -120
  73. package/dist/web.js +0 -641
  74. package/web/dist/assets/index-CSkxUQCr.js +0 -63
  75. package/web/dist/assets/index-DllM6RqL.css +0 -2
  76. /package/dist/{ids.js → conversation/ids.js} +0 -0
  77. /package/dist/{control.js → lifecycle/control.js} +0 -0
  78. /package/dist/{service.js → lifecycle/service.js} +0 -0
  79. /package/dist/{image-derivatives.js → media/image-derivatives.js} +0 -0
  80. /package/dist/{tts.js → media/tts.js} +0 -0
  81. /package/dist/{models.js → models/index.js} +0 -0
  82. /package/dist/{skills.js → prompting/skills.js} +0 -0
  83. /package/dist/{silent-marker.js → runtime/silent-marker.js} +0 -0
@@ -1,120 +0,0 @@
1
- import { readFile } from "node:fs/promises";
2
- import { createPartFromBase64, createUserContent, GoogleGenAI } from "@google/genai";
3
- import { parseModelRef, resolveModel } from "./models.js";
4
- const GEMINI_API_VERSION_PATTERN = /\/(v1(?:beta|alpha)?|v\d+beta\d*)\/?$/;
5
- function normalizeDerivedText(text) {
6
- return text.trim().replace(/\n{3,}/g, "\n\n");
7
- }
8
- function labelForAttachment(kind) {
9
- if (kind === "audio")
10
- return "transcription";
11
- if (kind === "video")
12
- return "summary";
13
- return "text";
14
- }
15
- function geminiHttpOptions(config) {
16
- const ref = parseModelRef(`google/${config.mediaUnderstanding.video.model}`);
17
- const model = ref ? resolveModel(ref, config) : undefined;
18
- const baseUrl = model?.baseUrl;
19
- if (!baseUrl)
20
- return { timeout: 60_000 };
21
- const match = baseUrl.match(GEMINI_API_VERSION_PATTERN);
22
- if (!match)
23
- return { baseUrl, timeout: 60_000 };
24
- return {
25
- baseUrl: baseUrl.slice(0, match.index),
26
- apiVersion: match[1],
27
- timeout: 60_000,
28
- };
29
- }
30
- async function transcribeAudioAttachment(config, attachment) {
31
- if (!attachment.localPath || !attachment.mimeType?.startsWith("audio/"))
32
- return undefined;
33
- const apiKey = process.env[config.mediaUnderstanding.audio.apiKeyEnv];
34
- if (!apiKey) {
35
- console.warn(`media understanding skipped: ${config.mediaUnderstanding.audio.apiKeyEnv} is not set`);
36
- return undefined;
37
- }
38
- const form = new FormData();
39
- form.set("model", config.mediaUnderstanding.audio.model);
40
- form.set("file", new Blob([await readFile(attachment.localPath)], { type: attachment.mimeType }), attachment.name);
41
- const response = await fetch("https://api.groq.com/openai/v1/audio/transcriptions", {
42
- method: "POST",
43
- headers: {
44
- Authorization: `Bearer ${apiKey}`,
45
- },
46
- body: form,
47
- signal: AbortSignal.timeout(30_000),
48
- });
49
- if (!response.ok)
50
- throw new Error(`Groq transcription failed: HTTP ${response.status}`);
51
- const parsed = (await response.json());
52
- const text = parsed.text?.trim();
53
- if (!text)
54
- return undefined;
55
- return {
56
- provider: "groq",
57
- model: config.mediaUnderstanding.audio.model,
58
- text: normalizeDerivedText(text),
59
- label: labelForAttachment(attachment.kind),
60
- };
61
- }
62
- async function summarizeVideoAttachment(config, attachment) {
63
- if (!attachment.localPath || !attachment.mimeType?.startsWith("video/"))
64
- return undefined;
65
- const apiKey = process.env[config.mediaUnderstanding.video.apiKeyEnv];
66
- if (!apiKey) {
67
- console.warn(`media understanding skipped: ${config.mediaUnderstanding.video.apiKeyEnv} is not set`);
68
- return undefined;
69
- }
70
- const ai = new GoogleGenAI({ apiKey, httpOptions: geminiHttpOptions(config) });
71
- const video = await readFile(attachment.localPath);
72
- const response = await ai.models.generateContent({
73
- model: config.mediaUnderstanding.video.model,
74
- contents: createUserContent([
75
- {
76
- text: "Provide a concise description of this video, including any spoken content if present, and summarize the key visible events.",
77
- },
78
- createPartFromBase64(video.toString("base64"), attachment.mimeType),
79
- ]),
80
- });
81
- const text = response.text?.trim();
82
- if (!text)
83
- return undefined;
84
- return {
85
- provider: "google",
86
- model: config.mediaUnderstanding.video.model,
87
- text: normalizeDerivedText(text),
88
- label: labelForAttachment(attachment.kind),
89
- };
90
- }
91
- export async function deriveInboundAttachmentText(config, attachments) {
92
- const next = [];
93
- for (const attachment of attachments) {
94
- if (attachment.derived?.text || !attachment.localPath) {
95
- next.push(attachment);
96
- continue;
97
- }
98
- try {
99
- if (attachment.mimeType?.startsWith("audio/")) {
100
- const text = await transcribeAudioAttachment(config, attachment);
101
- if (text) {
102
- next.push({ ...attachment, derived: { ...(attachment.derived ?? {}), text } });
103
- continue;
104
- }
105
- }
106
- if (attachment.mimeType?.startsWith("video/")) {
107
- const text = await summarizeVideoAttachment(config, attachment);
108
- if (text) {
109
- next.push({ ...attachment, derived: { ...(attachment.derived ?? {}), text } });
110
- continue;
111
- }
112
- }
113
- }
114
- catch (error) {
115
- console.error("media understanding failed", error);
116
- }
117
- next.push(attachment);
118
- }
119
- return next;
120
- }