openmates 0.12.0-alpha.6 → 0.12.0-alpha.8
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/dist/{chunk-MCVRDNDO.js → chunk-M5PZQEY2.js} +1972 -809
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +55 -1
- package/dist/index.js +7 -1
- package/package.json +1 -1
|
@@ -730,6 +730,18 @@ function writeJsonFile(filePath, data) {
|
|
|
730
730
|
});
|
|
731
731
|
chmodSync(filePath, 384);
|
|
732
732
|
}
|
|
733
|
+
function loadAnonymousId() {
|
|
734
|
+
const filePath = join(getStateDir(), "anonymous.json");
|
|
735
|
+
const data = readJsonFile(filePath);
|
|
736
|
+
return typeof data?.anonymousId === "string" && data.anonymousId.length > 0 ? data.anonymousId : null;
|
|
737
|
+
}
|
|
738
|
+
function saveAnonymousId(anonymousId) {
|
|
739
|
+
const filePath = join(ensureStateDir(), "anonymous.json");
|
|
740
|
+
writeJsonFile(filePath, {
|
|
741
|
+
anonymousId,
|
|
742
|
+
createdAt: Math.floor(Date.now() / 1e3)
|
|
743
|
+
});
|
|
744
|
+
}
|
|
733
745
|
function saveSession(session) {
|
|
734
746
|
const filePath = join(ensureStateDir(), "session.json");
|
|
735
747
|
const result = storeMasterKey(session.masterKeyExportedB64, session.hashedEmail);
|
|
@@ -2759,6 +2771,65 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
2759
2771
|
hasSession() {
|
|
2760
2772
|
return this.session !== null;
|
|
2761
2773
|
}
|
|
2774
|
+
async getAnonymousFreeUsageStatus() {
|
|
2775
|
+
const response = await this.http.get("/v1/anonymous/free-usage/status");
|
|
2776
|
+
if (response.status === 404) {
|
|
2777
|
+
return {
|
|
2778
|
+
active: false,
|
|
2779
|
+
reason: "self_hosted",
|
|
2780
|
+
resetAt: null,
|
|
2781
|
+
cta: "Anonymous free chat is not available on this server."
|
|
2782
|
+
};
|
|
2783
|
+
}
|
|
2784
|
+
if (!response.ok) {
|
|
2785
|
+
const detail = response.data.detail;
|
|
2786
|
+
const message = typeof detail === "object" && detail?.message ? detail.message : typeof detail === "string" ? detail : `Anonymous free usage status failed with HTTP ${response.status}`;
|
|
2787
|
+
throw new Error(message);
|
|
2788
|
+
}
|
|
2789
|
+
return {
|
|
2790
|
+
active: response.data.active === true,
|
|
2791
|
+
reason: response.data.reason ?? null,
|
|
2792
|
+
resetAt: response.data.reset_at ?? null,
|
|
2793
|
+
cta: response.data.cta ?? null
|
|
2794
|
+
};
|
|
2795
|
+
}
|
|
2796
|
+
async sendAnonymousMessage(params) {
|
|
2797
|
+
const availability = await this.getAnonymousFreeUsageStatus();
|
|
2798
|
+
if (!availability.active) {
|
|
2799
|
+
throw new Error(availability.cta ?? "Create an account to keep using OpenMates.");
|
|
2800
|
+
}
|
|
2801
|
+
let anonymousId = loadAnonymousId();
|
|
2802
|
+
if (!anonymousId) {
|
|
2803
|
+
anonymousId = randomUUID2();
|
|
2804
|
+
saveAnonymousId(anonymousId);
|
|
2805
|
+
}
|
|
2806
|
+
const chatId = `anonymous-${randomUUID2()}`;
|
|
2807
|
+
const messageId = `anonymous-message-${randomUUID2()}`;
|
|
2808
|
+
const response = await this.http.post("/v1/anonymous/chat/stream", {
|
|
2809
|
+
anonymous_id: anonymousId,
|
|
2810
|
+
client_chat_id: chatId,
|
|
2811
|
+
client_message_id: messageId,
|
|
2812
|
+
plaintext_message: params.message,
|
|
2813
|
+
message_history: []
|
|
2814
|
+
});
|
|
2815
|
+
if (!response.ok) {
|
|
2816
|
+
const detail = response.data.detail;
|
|
2817
|
+
const message = typeof detail === "object" && detail?.message ? detail.message : typeof detail === "string" ? detail : `Anonymous chat failed with HTTP ${response.status}`;
|
|
2818
|
+
throw new Error(message);
|
|
2819
|
+
}
|
|
2820
|
+
return {
|
|
2821
|
+
status: "completed",
|
|
2822
|
+
chatId: response.data.chatId ?? chatId,
|
|
2823
|
+
messageId: response.data.messageId ?? messageId,
|
|
2824
|
+
assistant: response.data.assistant ?? "",
|
|
2825
|
+
category: response.data.category ?? null,
|
|
2826
|
+
modelName: response.data.modelName ?? null,
|
|
2827
|
+
mateName: null,
|
|
2828
|
+
followUpSuggestions: response.data.followUpSuggestions ?? [],
|
|
2829
|
+
subChatEvents: [],
|
|
2830
|
+
appSettingsMemoryRequests: []
|
|
2831
|
+
};
|
|
2832
|
+
}
|
|
2762
2833
|
async createTurnTokenRefs(params) {
|
|
2763
2834
|
if (params.refs.length === 0) return [];
|
|
2764
2835
|
const response = await this.http.post(
|
|
@@ -2783,6 +2854,35 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
2783
2854
|
};
|
|
2784
2855
|
});
|
|
2785
2856
|
}
|
|
2857
|
+
async cancelConnectedAccountAction(params) {
|
|
2858
|
+
const response = await this.http.post(
|
|
2859
|
+
`/v1/connected-accounts/actions/${encodeURIComponent(params.actionId)}/cancel`,
|
|
2860
|
+
{
|
|
2861
|
+
chat_id: params.chatId,
|
|
2862
|
+
message_id: params.messageId
|
|
2863
|
+
},
|
|
2864
|
+
this.getCliRequestHeaders()
|
|
2865
|
+
);
|
|
2866
|
+
if (!response.ok) {
|
|
2867
|
+
throw new Error(`Failed to cancel connected-account action (HTTP ${response.status})`);
|
|
2868
|
+
}
|
|
2869
|
+
return response.data;
|
|
2870
|
+
}
|
|
2871
|
+
async undoConnectedAccountAction(params) {
|
|
2872
|
+
const response = await this.http.post(
|
|
2873
|
+
`/v1/connected-accounts/actions/${encodeURIComponent(params.actionId)}/undo`,
|
|
2874
|
+
{
|
|
2875
|
+
chat_id: params.chatId,
|
|
2876
|
+
message_id: params.messageId,
|
|
2877
|
+
turn_token_ref: params.turnTokenRef
|
|
2878
|
+
},
|
|
2879
|
+
this.getCliRequestHeaders()
|
|
2880
|
+
);
|
|
2881
|
+
if (!response.ok) {
|
|
2882
|
+
throw new Error(`Failed to undo connected-account action (HTTP ${response.status})`);
|
|
2883
|
+
}
|
|
2884
|
+
return response.data;
|
|
2885
|
+
}
|
|
2786
2886
|
// -------------------------------------------------------------------------
|
|
2787
2887
|
// Auth
|
|
2788
2888
|
// -------------------------------------------------------------------------
|
|
@@ -3553,6 +3653,7 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
3553
3653
|
refs: params.connectedAccountTokenRefInputs
|
|
3554
3654
|
}) : [];
|
|
3555
3655
|
assertNoConnectedAccountSecretLeak(connectedAccountTokenRefs);
|
|
3656
|
+
const piiMappings = params.piiMappings ?? [];
|
|
3556
3657
|
const messagePayload = {
|
|
3557
3658
|
chat_id: chatId,
|
|
3558
3659
|
is_incognito: Boolean(params.incognito),
|
|
@@ -3666,6 +3767,12 @@ var OpenMatesClient = class _OpenMatesClient {
|
|
|
3666
3767
|
last_edited_overall_timestamp: createdAt
|
|
3667
3768
|
}
|
|
3668
3769
|
};
|
|
3770
|
+
if (piiMappings.length > 0) {
|
|
3771
|
+
metadataPayload.encrypted_pii_mappings = await encryptWithAesGcmCombined(
|
|
3772
|
+
JSON.stringify(piiMappings),
|
|
3773
|
+
chatKeyBytes
|
|
3774
|
+
);
|
|
3775
|
+
}
|
|
3669
3776
|
ws.send("encrypted_chat_metadata", metadataPayload);
|
|
3670
3777
|
}
|
|
3671
3778
|
let assistant = "";
|
|
@@ -5574,6 +5681,7 @@ Required: ${schema.required.join(", ")}`
|
|
|
5574
5681
|
const embedKeys = [];
|
|
5575
5682
|
let newChatSuggestions = [];
|
|
5576
5683
|
let totalChatCount = 0;
|
|
5684
|
+
const pendingAIResponses = [];
|
|
5577
5685
|
try {
|
|
5578
5686
|
ws.send("phased_sync_request", {
|
|
5579
5687
|
phase: "all",
|
|
@@ -5613,6 +5721,8 @@ Required: ${schema.required.join(", ")}`
|
|
|
5613
5721
|
}
|
|
5614
5722
|
if (p.embeds) embeds.push(...p.embeds);
|
|
5615
5723
|
if (p.embed_keys) embedKeys.push(...p.embed_keys);
|
|
5724
|
+
} else if (frame.type === "pending_ai_response") {
|
|
5725
|
+
pendingAIResponses.push(frame.payload);
|
|
5616
5726
|
}
|
|
5617
5727
|
}
|
|
5618
5728
|
for (const chat of chats) {
|
|
@@ -5623,8 +5733,9 @@ Required: ${schema.required.join(", ")}`
|
|
|
5623
5733
|
}
|
|
5624
5734
|
}
|
|
5625
5735
|
if (totalChatCount === 0) totalChatCount = chats.length;
|
|
5626
|
-
}
|
|
5736
|
+
} catch (error) {
|
|
5627
5737
|
ws.close();
|
|
5738
|
+
throw error;
|
|
5628
5739
|
}
|
|
5629
5740
|
if (existingCache) {
|
|
5630
5741
|
const cachedById = new Map(
|
|
@@ -5681,6 +5792,11 @@ Required: ${schema.required.join(", ")}`
|
|
|
5681
5792
|
if (totalChatCount > 0 && chats.length > totalChatCount) {
|
|
5682
5793
|
chats.length = totalChatCount;
|
|
5683
5794
|
}
|
|
5795
|
+
try {
|
|
5796
|
+
await this.persistPendingAIResponsesFromSync(ws, chats, pendingAIResponses);
|
|
5797
|
+
} finally {
|
|
5798
|
+
ws.close();
|
|
5799
|
+
}
|
|
5684
5800
|
const cache = {
|
|
5685
5801
|
syncedAt: Date.now(),
|
|
5686
5802
|
totalChatCount,
|
|
@@ -5693,6 +5809,76 @@ Required: ${schema.required.join(", ")}`
|
|
|
5693
5809
|
saveSyncCache(cache);
|
|
5694
5810
|
return cache;
|
|
5695
5811
|
}
|
|
5812
|
+
async persistPendingAIResponsesFromSync(ws, chats, pendingResponses) {
|
|
5813
|
+
if (pendingResponses.length === 0) return;
|
|
5814
|
+
const masterKey = this.getMasterKeyBytes();
|
|
5815
|
+
for (const pending of pendingResponses) {
|
|
5816
|
+
const chatId = pending.chat_id;
|
|
5817
|
+
const messageId = pending.message_id;
|
|
5818
|
+
const content = pending.content;
|
|
5819
|
+
if (!chatId || !messageId || !content) continue;
|
|
5820
|
+
const chat = chats.find((candidate) => String(candidate.details.id ?? "") === chatId);
|
|
5821
|
+
if (!chat) continue;
|
|
5822
|
+
const existingIndex = chat.messages.findIndex((raw) => {
|
|
5823
|
+
try {
|
|
5824
|
+
const message = typeof raw === "string" ? JSON.parse(raw) : raw;
|
|
5825
|
+
return message?.message_id === messageId || message?.id === messageId;
|
|
5826
|
+
} catch {
|
|
5827
|
+
return false;
|
|
5828
|
+
}
|
|
5829
|
+
});
|
|
5830
|
+
if (existingIndex >= 0) {
|
|
5831
|
+
const existingRaw = chat.messages[existingIndex];
|
|
5832
|
+
try {
|
|
5833
|
+
const existing = typeof existingRaw === "string" ? JSON.parse(existingRaw) : existingRaw;
|
|
5834
|
+
if (existing?.role === "assistant" && existing?.encrypted_content) continue;
|
|
5835
|
+
} catch {
|
|
5836
|
+
}
|
|
5837
|
+
}
|
|
5838
|
+
const encryptedChatKey = typeof chat.details.encrypted_chat_key === "string" ? chat.details.encrypted_chat_key : null;
|
|
5839
|
+
if (!encryptedChatKey) continue;
|
|
5840
|
+
const chatKeyBytes = await this.decryptChatKey(encryptedChatKey, masterKey);
|
|
5841
|
+
if (!chatKeyBytes) continue;
|
|
5842
|
+
const completedAt = normalizeUnixSeconds(
|
|
5843
|
+
pending.fired_at,
|
|
5844
|
+
Math.floor(Date.now() / 1e3)
|
|
5845
|
+
);
|
|
5846
|
+
const currentMessagesV = typeof chat.details.messages_v === "number" ? chat.details.messages_v : 0;
|
|
5847
|
+
const nextMessagesV = currentMessagesV + 1;
|
|
5848
|
+
const encryptedMessage = {
|
|
5849
|
+
message_id: messageId,
|
|
5850
|
+
chat_id: chatId,
|
|
5851
|
+
role: "assistant",
|
|
5852
|
+
created_at: completedAt,
|
|
5853
|
+
status: "synced",
|
|
5854
|
+
encrypted_content: await encryptWithAesGcmCombined(content, chatKeyBytes),
|
|
5855
|
+
encrypted_category: pending.category ? await encryptWithAesGcmCombined(pending.category, chatKeyBytes) : void 0,
|
|
5856
|
+
encrypted_model_name: pending.model_name ? await encryptWithAesGcmCombined(pending.model_name, chatKeyBytes) : void 0
|
|
5857
|
+
};
|
|
5858
|
+
const confirmed = ws.waitForMessage(
|
|
5859
|
+
"ai_response_storage_confirmed",
|
|
5860
|
+
(payload) => {
|
|
5861
|
+
const p = payload;
|
|
5862
|
+
return p.message_id === messageId;
|
|
5863
|
+
},
|
|
5864
|
+
2e4
|
|
5865
|
+
);
|
|
5866
|
+
await ws.sendAsync("ai_response_completed", {
|
|
5867
|
+
chat_id: chatId,
|
|
5868
|
+
message: encryptedMessage,
|
|
5869
|
+
versions: {
|
|
5870
|
+
messages_v: nextMessagesV,
|
|
5871
|
+
last_edited_overall_timestamp: completedAt
|
|
5872
|
+
}
|
|
5873
|
+
});
|
|
5874
|
+
await confirmed;
|
|
5875
|
+
const serializedMessage = JSON.stringify(encryptedMessage);
|
|
5876
|
+
if (existingIndex >= 0) chat.messages[existingIndex] = serializedMessage;
|
|
5877
|
+
else chat.messages.push(serializedMessage);
|
|
5878
|
+
chat.details.messages_v = nextMessagesV;
|
|
5879
|
+
chat.details.last_edited_overall_timestamp = completedAt;
|
|
5880
|
+
}
|
|
5881
|
+
}
|
|
5696
5882
|
async prompt(question) {
|
|
5697
5883
|
const rl = createInterface({ input: stdin, output: stdout });
|
|
5698
5884
|
try {
|
|
@@ -5991,6 +6177,31 @@ var SECRET_PATTERNS = [
|
|
|
5991
6177
|
placeholderPrefix: "JWT_TOKEN"
|
|
5992
6178
|
}
|
|
5993
6179
|
];
|
|
6180
|
+
var GENERAL_PII_PATTERNS = [
|
|
6181
|
+
{
|
|
6182
|
+
type: "EMAIL",
|
|
6183
|
+
regex: /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g,
|
|
6184
|
+
label: "Email Address",
|
|
6185
|
+
placeholderPrefix: "EMAIL"
|
|
6186
|
+
},
|
|
6187
|
+
{
|
|
6188
|
+
type: "PHONE",
|
|
6189
|
+
regex: /(?:(?:\+|00)[1-9]\d{0,2}[-.\s/]?(?:\(?\d{1,5}\)?[-.\s/]?){1,4}\d{2,4})|(?:\+?1[-.\s]?)?\(?[2-9]\d{2}\)?[-.\s]?\d{3}[-.\s]?\d{4}|(?:0\d[-.\s/]?(?:\(?\d{1,5}\)?[-.\s/]?){1,4}\d{2,4})/g,
|
|
6190
|
+
label: "Phone Number",
|
|
6191
|
+
placeholderPrefix: "PHONE",
|
|
6192
|
+
validate: (match) => {
|
|
6193
|
+
const trimmed = match.trim();
|
|
6194
|
+
const digits = trimmed.replace(/\D/g, "");
|
|
6195
|
+
if (digits.length < 7 || digits.length > 15) return false;
|
|
6196
|
+
if (/^\d{4}$/.test(trimmed)) return false;
|
|
6197
|
+
if (/^\d{4}[-/.]\d{1,2}[-/.]\d{1,2}$/.test(trimmed)) return false;
|
|
6198
|
+
if (/^\d{1,2}[-/.]\d{1,2}[-/.]\d{4}$/.test(trimmed)) return false;
|
|
6199
|
+
if (/^(?:19|20)\d{6}$/.test(trimmed)) return false;
|
|
6200
|
+
if (/^0\d{1,2}[-/.]\d{1,2}[-/.]\d{1,2}$/.test(trimmed)) return false;
|
|
6201
|
+
return true;
|
|
6202
|
+
}
|
|
6203
|
+
}
|
|
6204
|
+
];
|
|
5994
6205
|
|
|
5995
6206
|
// ../secret-scanner/src/registry.ts
|
|
5996
6207
|
var require3 = createRequire2(import.meta.url);
|
|
@@ -6328,6 +6539,7 @@ var SecretScanner = class {
|
|
|
6328
6539
|
if (!text) return { redacted: text, mappings: [] };
|
|
6329
6540
|
const allMappings = [];
|
|
6330
6541
|
const coveredRanges = [];
|
|
6542
|
+
const urlExclusionZones = getUrlExclusionZones(text);
|
|
6331
6543
|
if (this.options.enableRegistryDetection && this.registry.size > 0) {
|
|
6332
6544
|
const registryMappings = this.registry.findAll(text);
|
|
6333
6545
|
for (const mapping of registryMappings) {
|
|
@@ -6345,13 +6557,14 @@ var SecretScanner = class {
|
|
|
6345
6557
|
}
|
|
6346
6558
|
const typeCounts = {};
|
|
6347
6559
|
if (this.options.enablePatternDetection) {
|
|
6348
|
-
for (const pattern of SECRET_PATTERNS) {
|
|
6560
|
+
for (const pattern of [...SECRET_PATTERNS, ...GENERAL_PII_PATTERNS]) {
|
|
6349
6561
|
pattern.regex.lastIndex = 0;
|
|
6350
6562
|
let match;
|
|
6351
6563
|
while ((match = pattern.regex.exec(text)) !== null) {
|
|
6352
6564
|
const matchText = match[0];
|
|
6353
6565
|
const startIndex = match.index;
|
|
6354
6566
|
const endIndex = startIndex + matchText.length;
|
|
6567
|
+
if (isInsideAnyRange(startIndex, endIndex, urlExclusionZones)) continue;
|
|
6355
6568
|
const overlaps = coveredRanges.some(
|
|
6356
6569
|
(range) => startIndex >= range.start && startIndex < range.end || endIndex > range.start && endIndex <= range.end || startIndex <= range.start && endIndex >= range.end
|
|
6357
6570
|
);
|
|
@@ -6427,6 +6640,18 @@ var SecretScanner = class {
|
|
|
6427
6640
|
function escapeRegExp2(string) {
|
|
6428
6641
|
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
6429
6642
|
}
|
|
6643
|
+
function getUrlExclusionZones(text) {
|
|
6644
|
+
const urlExclusionRegex = /https?:\/\/[^\s]+/g;
|
|
6645
|
+
const zones = [];
|
|
6646
|
+
let match;
|
|
6647
|
+
while ((match = urlExclusionRegex.exec(text)) !== null) {
|
|
6648
|
+
zones.push({ start: match.index, end: match.index + match[0].length });
|
|
6649
|
+
}
|
|
6650
|
+
return zones;
|
|
6651
|
+
}
|
|
6652
|
+
function isInsideAnyRange(startIndex, endIndex, ranges) {
|
|
6653
|
+
return ranges.some((range) => startIndex >= range.start && endIndex <= range.end);
|
|
6654
|
+
}
|
|
6430
6655
|
|
|
6431
6656
|
// src/outputRedactor.ts
|
|
6432
6657
|
var PRIVACY_APP_ID = "privacy";
|
|
@@ -7080,6 +7305,8 @@ var DIRECT_TYPES = /* @__PURE__ */ new Set([
|
|
|
7080
7305
|
"recording",
|
|
7081
7306
|
"mail-email",
|
|
7082
7307
|
"math-plot",
|
|
7308
|
+
"mermaid",
|
|
7309
|
+
"diagrams-mermaid",
|
|
7083
7310
|
"events-event",
|
|
7084
7311
|
"health-appointment",
|
|
7085
7312
|
"shopping-product",
|
|
@@ -7107,6 +7334,8 @@ var DIRECT_TYPE_LABELS = {
|
|
|
7107
7334
|
"recording": "recording",
|
|
7108
7335
|
"mail-email": "email",
|
|
7109
7336
|
"math-plot": "plot",
|
|
7337
|
+
"mermaid": "Mermaid diagram",
|
|
7338
|
+
"diagrams-mermaid": "Mermaid diagram",
|
|
7110
7339
|
"events-event": "event",
|
|
7111
7340
|
"health-appointment": "appointment",
|
|
7112
7341
|
"shopping-product": "product",
|
|
@@ -8063,6 +8292,11 @@ function renderByDirectType(embed, c, ln) {
|
|
|
8063
8292
|
ln("\x1B[2m[mathematical plot]\x1B[0m");
|
|
8064
8293
|
break;
|
|
8065
8294
|
}
|
|
8295
|
+
case "mermaid":
|
|
8296
|
+
case "diagrams-mermaid": {
|
|
8297
|
+
renderMermaidPreview(c, ln);
|
|
8298
|
+
break;
|
|
8299
|
+
}
|
|
8066
8300
|
case "images-image-result": {
|
|
8067
8301
|
const title = str(c.title) ?? "";
|
|
8068
8302
|
const source = str(c.source) ?? str(c.url) ?? "";
|
|
@@ -8228,6 +8462,11 @@ function renderDirectTypeFullscreen(embed, c) {
|
|
|
8228
8462
|
}
|
|
8229
8463
|
break;
|
|
8230
8464
|
}
|
|
8465
|
+
case "mermaid":
|
|
8466
|
+
case "diagrams-mermaid": {
|
|
8467
|
+
renderMermaidFullscreen(c);
|
|
8468
|
+
break;
|
|
8469
|
+
}
|
|
8231
8470
|
default: {
|
|
8232
8471
|
for (const [k, v] of Object.entries(c)) {
|
|
8233
8472
|
if (v === null || v === void 0 || k.startsWith("_")) continue;
|
|
@@ -8246,6 +8485,38 @@ function renderDirectTypeFullscreen(embed, c) {
|
|
|
8246
8485
|
}
|
|
8247
8486
|
}
|
|
8248
8487
|
}
|
|
8488
|
+
function renderMermaidPreview(c, ln) {
|
|
8489
|
+
const title = str(c.title) ?? "Mermaid diagram";
|
|
8490
|
+
const kind = str(c.diagram_kind) ?? "mermaid";
|
|
8491
|
+
const source = str(c.diagram_code) ?? str(c.code) ?? str(c.source) ?? "";
|
|
8492
|
+
ln(`\x1B[1m${title}\x1B[0m`);
|
|
8493
|
+
ln(`\x1B[2mMermaid diagram \xB7 ${kind}\x1B[0m`);
|
|
8494
|
+
if (source) {
|
|
8495
|
+
for (const line of source.split("\n").slice(0, 4)) {
|
|
8496
|
+
if (line.trim()) ln(` ${trunc(line, 80)}`);
|
|
8497
|
+
}
|
|
8498
|
+
if (source.split("\n").length > 4) ln(" ...");
|
|
8499
|
+
}
|
|
8500
|
+
}
|
|
8501
|
+
function renderMermaidFullscreen(c) {
|
|
8502
|
+
const title = str(c.title) ?? "Mermaid diagram";
|
|
8503
|
+
const kind = str(c.diagram_kind) ?? "mermaid";
|
|
8504
|
+
const status = str(c.status);
|
|
8505
|
+
const source = str(c.diagram_code) ?? str(c.code) ?? str(c.source) ?? "";
|
|
8506
|
+
process.stdout.write(`\x1B[1m${title}\x1B[0m
|
|
8507
|
+
`);
|
|
8508
|
+
process.stdout.write(`\x1B[2mMermaid diagram \xB7 ${kind}${status ? ` \xB7 ${status}` : ""}\x1B[0m
|
|
8509
|
+
|
|
8510
|
+
`);
|
|
8511
|
+
if (source) {
|
|
8512
|
+
process.stdout.write("```mermaid\n");
|
|
8513
|
+
process.stdout.write(`${source.trim()}
|
|
8514
|
+
`);
|
|
8515
|
+
process.stdout.write("```\n");
|
|
8516
|
+
} else {
|
|
8517
|
+
process.stdout.write("No Mermaid source available.\n");
|
|
8518
|
+
}
|
|
8519
|
+
}
|
|
8249
8520
|
function resolveResultCount(c) {
|
|
8250
8521
|
if (typeof c.result_count === "number") return c.result_count;
|
|
8251
8522
|
const results = c.results;
|
|
@@ -8293,7 +8564,16 @@ var COMPOSE_OVERRIDE = join3("backend", "core", "docker-compose.override.yml");
|
|
|
8293
8564
|
var DEFAULT_INSTALL_PATH = join3(homedir5(), "openmates");
|
|
8294
8565
|
var REPO_URL = "https://github.com/glowingkitty/OpenMates.git";
|
|
8295
8566
|
var DEV_BRANCH = "dev";
|
|
8567
|
+
var MAIN_BRANCH = "main";
|
|
8296
8568
|
var DEFAULT_IMAGE_REGISTRY = "ghcr.io/glowingkitty";
|
|
8569
|
+
var UPDATE_HEALTH_TIMEOUT_MS = 12e4;
|
|
8570
|
+
var UPDATE_HEALTH_INTERVAL_MS = 5e3;
|
|
8571
|
+
var HEALTH_REQUEST_TIMEOUT_MS = 5e3;
|
|
8572
|
+
var IMAGE_CHANNEL_TAGS = {
|
|
8573
|
+
stable: MAIN_BRANCH,
|
|
8574
|
+
main: MAIN_BRANCH,
|
|
8575
|
+
dev: DEV_BRANCH
|
|
8576
|
+
};
|
|
8297
8577
|
var MINIMAL_ENV_TEMPLATE = `# OpenMates self-host image-mode environment
|
|
8298
8578
|
SECRET__MISTRAL_AI__API_KEY=
|
|
8299
8579
|
SECRET__CEREBRAS__API_KEY=
|
|
@@ -8428,13 +8708,52 @@ function getPackageVersion() {
|
|
|
8428
8708
|
return "";
|
|
8429
8709
|
}
|
|
8430
8710
|
}
|
|
8711
|
+
function getDefaultImageTagForVersion(version) {
|
|
8712
|
+
return version ? `v${version}` : DEV_BRANCH;
|
|
8713
|
+
}
|
|
8431
8714
|
function getDefaultImageTag() {
|
|
8432
8715
|
const version = getPackageVersion();
|
|
8433
|
-
return version
|
|
8716
|
+
return getDefaultImageTagForVersion(version);
|
|
8434
8717
|
}
|
|
8435
8718
|
function defaultTemplateRefForVersion(version) {
|
|
8436
8719
|
return /-(alpha|beta|rc)(\.|\d|$)/.test(version) ? DEV_BRANCH : `v${version}`;
|
|
8437
8720
|
}
|
|
8721
|
+
function templateRefForImageTag(imageTag, packageVersion = "") {
|
|
8722
|
+
const channelTag = IMAGE_CHANNEL_TAGS[imageTag];
|
|
8723
|
+
if (channelTag) return channelTag;
|
|
8724
|
+
if (imageTag.startsWith("v")) return defaultTemplateRefForVersion(imageTag.slice(1));
|
|
8725
|
+
if (!imageTag && packageVersion) return defaultTemplateRefForVersion(packageVersion);
|
|
8726
|
+
return DEV_BRANCH;
|
|
8727
|
+
}
|
|
8728
|
+
function resolveTargetImageTag(flags, currentTag, packageVersion) {
|
|
8729
|
+
const imageTag = flags["image-tag"];
|
|
8730
|
+
const channel = flags.channel;
|
|
8731
|
+
if (imageTag === true) {
|
|
8732
|
+
throw new Error("Provide an image tag value: --image-tag <tag>.");
|
|
8733
|
+
}
|
|
8734
|
+
if (channel === true) {
|
|
8735
|
+
throw new Error("Provide an update channel value: --channel stable, --channel main, or --channel dev.");
|
|
8736
|
+
}
|
|
8737
|
+
if (typeof imageTag === "string" && typeof channel === "string") {
|
|
8738
|
+
throw new Error("Use either --image-tag or --channel, not both.");
|
|
8739
|
+
}
|
|
8740
|
+
if (typeof imageTag === "string") {
|
|
8741
|
+
const trimmed = imageTag.trim();
|
|
8742
|
+
if (!trimmed) throw new Error("--image-tag cannot be empty.");
|
|
8743
|
+
return { tag: trimmed };
|
|
8744
|
+
}
|
|
8745
|
+
if (typeof channel === "string") {
|
|
8746
|
+
const normalized = channel.trim().toLowerCase();
|
|
8747
|
+
const tag = IMAGE_CHANNEL_TAGS[normalized];
|
|
8748
|
+
if (!tag) {
|
|
8749
|
+
throw new Error("Unsupported update channel. Use --channel stable, --channel main, or --channel dev.");
|
|
8750
|
+
}
|
|
8751
|
+
return { tag, channel: tag };
|
|
8752
|
+
}
|
|
8753
|
+
const installedChannel = IMAGE_CHANNEL_TAGS[currentTag];
|
|
8754
|
+
if (installedChannel) return { tag: installedChannel, channel: installedChannel };
|
|
8755
|
+
return { tag: getDefaultImageTagForVersion(packageVersion) };
|
|
8756
|
+
}
|
|
8438
8757
|
function randomHex(bytes) {
|
|
8439
8758
|
return randomBytes2(bytes).toString("hex");
|
|
8440
8759
|
}
|
|
@@ -8475,7 +8794,7 @@ async function fetchText(url) {
|
|
|
8475
8794
|
}
|
|
8476
8795
|
return response.text();
|
|
8477
8796
|
}
|
|
8478
|
-
async function loadSelfHostComposeTemplate(
|
|
8797
|
+
async function loadSelfHostComposeTemplate(templateRef) {
|
|
8479
8798
|
const templateDir = process.env.OPENMATES_SELFHOST_TEMPLATE_DIR;
|
|
8480
8799
|
if (templateDir) {
|
|
8481
8800
|
return readFileSync5(join3(resolve3(templateDir), IMAGE_COMPOSE_FILE), "utf-8");
|
|
@@ -8484,16 +8803,15 @@ async function loadSelfHostComposeTemplate(version) {
|
|
|
8484
8803
|
if (overrideUrl) {
|
|
8485
8804
|
return fetchText(overrideUrl);
|
|
8486
8805
|
}
|
|
8487
|
-
const ref = defaultTemplateRefForVersion(version);
|
|
8488
8806
|
return fetchText(
|
|
8489
|
-
`https://raw.githubusercontent.com/glowingkitty/OpenMates/${
|
|
8807
|
+
`https://raw.githubusercontent.com/glowingkitty/OpenMates/${templateRef}/backend/core/docker-compose.selfhost.yml`
|
|
8490
8808
|
);
|
|
8491
8809
|
}
|
|
8492
8810
|
async function writeImageModeRuntimeFiles(installPath, imageTag) {
|
|
8493
8811
|
const coreDir = join3(installPath, "backend", "core");
|
|
8494
8812
|
const vaultConfigDir = join3(coreDir, "vault", "config");
|
|
8495
8813
|
mkdirSync3(vaultConfigDir, { recursive: true });
|
|
8496
|
-
writeFileSync3(join3(coreDir, "docker-compose.selfhost.yml"), await loadSelfHostComposeTemplate(getPackageVersion()));
|
|
8814
|
+
writeFileSync3(join3(coreDir, "docker-compose.selfhost.yml"), await loadSelfHostComposeTemplate(templateRefForImageTag(imageTag, getPackageVersion())));
|
|
8497
8815
|
writeFileSync3(join3(vaultConfigDir, "vault.hcl"), VAULT_CONFIG_TEMPLATE);
|
|
8498
8816
|
const envPath = join3(installPath, ".env");
|
|
8499
8817
|
let envContent = existsSync5(envPath) ? readFileSync5(envPath, "utf-8") : MINIMAL_ENV_TEMPLATE;
|
|
@@ -8511,6 +8829,48 @@ async function writeImageModeRuntimeFiles(installPath, imageTag) {
|
|
|
8511
8829
|
writeFileSync3(envPath, envContent.endsWith("\n") ? envContent : `${envContent}
|
|
8512
8830
|
`);
|
|
8513
8831
|
}
|
|
8832
|
+
function getImageTagFromEnv(installPath, config) {
|
|
8833
|
+
const envPath = join3(installPath, ".env");
|
|
8834
|
+
if (existsSync5(envPath)) {
|
|
8835
|
+
const envTag = getEnvVar(readFileSync5(envPath, "utf-8"), "OPENMATES_IMAGE_TAG");
|
|
8836
|
+
if (envTag) return envTag;
|
|
8837
|
+
}
|
|
8838
|
+
return config?.imageTag ?? "";
|
|
8839
|
+
}
|
|
8840
|
+
function trailingSlashTrimmed(value) {
|
|
8841
|
+
return value.replace(/\/+$/, "");
|
|
8842
|
+
}
|
|
8843
|
+
function sleep2(ms) {
|
|
8844
|
+
return new Promise((resolveSleep) => setTimeout(resolveSleep, ms));
|
|
8845
|
+
}
|
|
8846
|
+
async function checkUrl(url) {
|
|
8847
|
+
const controller = new AbortController();
|
|
8848
|
+
const timeout = setTimeout(() => controller.abort(), HEALTH_REQUEST_TIMEOUT_MS);
|
|
8849
|
+
try {
|
|
8850
|
+
const response = await fetch(url, { signal: controller.signal });
|
|
8851
|
+
return response.ok;
|
|
8852
|
+
} catch {
|
|
8853
|
+
return false;
|
|
8854
|
+
} finally {
|
|
8855
|
+
clearTimeout(timeout);
|
|
8856
|
+
}
|
|
8857
|
+
}
|
|
8858
|
+
async function waitForServerHealth(installPath) {
|
|
8859
|
+
const envPath = join3(installPath, ".env");
|
|
8860
|
+
const envContent = existsSync5(envPath) ? readFileSync5(envPath, "utf-8") : "";
|
|
8861
|
+
const urls = deriveSelfHostCliUrls(envContent);
|
|
8862
|
+
const apiHealthUrl = `${trailingSlashTrimmed(urls.apiUrl)}/health`;
|
|
8863
|
+
const appUrl = trailingSlashTrimmed(urls.appUrl);
|
|
8864
|
+
const deadline = Date.now() + UPDATE_HEALTH_TIMEOUT_MS;
|
|
8865
|
+
while (Date.now() < deadline) {
|
|
8866
|
+
const [apiOk, appOk] = await Promise.all([checkUrl(apiHealthUrl), checkUrl(appUrl)]);
|
|
8867
|
+
if (apiOk && appOk) return;
|
|
8868
|
+
await sleep2(UPDATE_HEALTH_INTERVAL_MS);
|
|
8869
|
+
}
|
|
8870
|
+
throw new Error(
|
|
8871
|
+
`Updated containers started, but health checks did not pass in time. Check 'openmates server status' and 'openmates server logs --tail 200'. Tried ${apiHealthUrl} and ${appUrl}.`
|
|
8872
|
+
);
|
|
8873
|
+
}
|
|
8514
8874
|
function defaultCloneBranchForVersion(version) {
|
|
8515
8875
|
return /-(alpha|beta|rc)(\.|\d|$)/.test(version) ? DEV_BRANCH : null;
|
|
8516
8876
|
}
|
|
@@ -8796,25 +9156,85 @@ CLI default API: ${cliUrls.apiUrl}`);
|
|
|
8796
9156
|
}
|
|
8797
9157
|
}
|
|
8798
9158
|
async function serverUpdate(flags) {
|
|
8799
|
-
requireDocker();
|
|
8800
9159
|
const installPath = resolveServerPath(flags);
|
|
8801
|
-
|
|
8802
|
-
|
|
9160
|
+
const dryRun = flags["dry-run"] === true;
|
|
9161
|
+
if (!dryRun) ensureGitWorkDirEnv(installPath);
|
|
8803
9162
|
const config = loadConfigForInstallPath(installPath);
|
|
8804
9163
|
const withOverrides = config?.composeProfile === "full";
|
|
8805
9164
|
const installMode = getInstallMode(installPath, config);
|
|
9165
|
+
if (installMode === "source" && (flags["image-tag"] !== void 0 || flags.channel !== void 0)) {
|
|
9166
|
+
throw new Error("--image-tag and --channel only apply to image-mode installs. Source-mode installs update from Git.");
|
|
9167
|
+
}
|
|
9168
|
+
if (!dryRun) requireDocker();
|
|
9169
|
+
console.error("Updating OpenMates...");
|
|
8806
9170
|
if (installMode === "image") {
|
|
9171
|
+
const currentTag = getImageTagFromEnv(installPath, config);
|
|
9172
|
+
const target = resolveTargetImageTag(flags, currentTag, getPackageVersion());
|
|
9173
|
+
const templateRef = templateRefForImageTag(target.tag, getPackageVersion());
|
|
9174
|
+
const plan = {
|
|
9175
|
+
command: "update",
|
|
9176
|
+
path: installPath,
|
|
9177
|
+
mode: "image",
|
|
9178
|
+
currentImageTag: currentTag || null,
|
|
9179
|
+
targetImageTag: target.tag,
|
|
9180
|
+
channel: target.channel ?? null,
|
|
9181
|
+
templateRef,
|
|
9182
|
+
dryRun
|
|
9183
|
+
};
|
|
9184
|
+
if (dryRun) {
|
|
9185
|
+
if (flags.json === true) {
|
|
9186
|
+
printJson({ ...plan, status: "planned" });
|
|
9187
|
+
} else {
|
|
9188
|
+
console.log("Update plan:");
|
|
9189
|
+
console.log(` Mode: image`);
|
|
9190
|
+
console.log(` Current tag: ${currentTag || "unknown"}`);
|
|
9191
|
+
console.log(` Target tag: ${target.tag}`);
|
|
9192
|
+
console.log(` Template ref: ${templateRef}`);
|
|
9193
|
+
console.log(" Commands: refresh compose, docker compose pull, docker compose up -d, health checks");
|
|
9194
|
+
}
|
|
9195
|
+
return;
|
|
9196
|
+
}
|
|
9197
|
+
console.error(`Mode: image`);
|
|
9198
|
+
console.error(`Current image tag: ${currentTag || "unknown"}`);
|
|
9199
|
+
console.error(`Target image tag: ${target.tag}`);
|
|
9200
|
+
console.error(`Refreshing self-host runtime files from ${templateRef}...`);
|
|
9201
|
+
await writeImageModeRuntimeFiles(installPath, target.tag);
|
|
8807
9202
|
const pullArgs = [...composeArgs(installPath, withOverrides, installMode), "pull"];
|
|
8808
|
-
|
|
8809
|
-
|
|
8810
|
-
|
|
9203
|
+
let code2 = 0;
|
|
9204
|
+
if (shouldPullImages()) {
|
|
9205
|
+
console.error("Pulling prebuilt images...");
|
|
9206
|
+
code2 = await runInteractive("docker", pullArgs, installPath);
|
|
9207
|
+
if (code2 !== 0) process.exit(code2);
|
|
9208
|
+
} else {
|
|
9209
|
+
console.error("Skipping image pull because OPENMATES_SKIP_IMAGE_PULL=1.");
|
|
9210
|
+
}
|
|
8811
9211
|
const upArgs2 = [...composeArgs(installPath, withOverrides, installMode), "up", "-d"];
|
|
8812
9212
|
code2 = await runInteractive("docker", upArgs2, installPath);
|
|
8813
9213
|
if (code2 !== 0) process.exit(code2);
|
|
9214
|
+
console.error("Waiting for API and web health checks...");
|
|
9215
|
+
try {
|
|
9216
|
+
await waitForServerHealth(installPath);
|
|
9217
|
+
} catch (error) {
|
|
9218
|
+
await runInteractive("docker", [...composeArgs(installPath, withOverrides, installMode), "ps"], installPath);
|
|
9219
|
+
throw error;
|
|
9220
|
+
}
|
|
9221
|
+
if (config) {
|
|
9222
|
+
saveServerConfig({ ...config, imageTag: target.tag, imageChannel: target.channel });
|
|
9223
|
+
}
|
|
8814
9224
|
if (flags.json === true) {
|
|
8815
|
-
printJson({
|
|
9225
|
+
printJson({ ...plan, status: "success", dryRun: false });
|
|
8816
9226
|
} else {
|
|
8817
|
-
console.log("Server images
|
|
9227
|
+
console.log("Server images updated, containers restarted, and health checks passed.");
|
|
9228
|
+
}
|
|
9229
|
+
return;
|
|
9230
|
+
}
|
|
9231
|
+
if (dryRun) {
|
|
9232
|
+
if (flags.json === true) {
|
|
9233
|
+
printJson({ command: "update", status: "planned", path: installPath, mode: "source", dryRun: true });
|
|
9234
|
+
} else {
|
|
9235
|
+
console.log("Update plan:");
|
|
9236
|
+
console.log(" Mode: source");
|
|
9237
|
+
console.log(" Commands: git pull --ff-only, docker compose build, docker compose up -d, health checks");
|
|
8818
9238
|
}
|
|
8819
9239
|
return;
|
|
8820
9240
|
}
|
|
@@ -8829,7 +9249,7 @@ async function serverUpdate(flags) {
|
|
|
8829
9249
|
try {
|
|
8830
9250
|
const pullOutput = exec("git pull --ff-only", installPath);
|
|
8831
9251
|
console.error(pullOutput || "Already up to date.");
|
|
8832
|
-
} catch
|
|
9252
|
+
} catch {
|
|
8833
9253
|
if (flags.force === true) {
|
|
8834
9254
|
try {
|
|
8835
9255
|
exec("git stash pop", installPath);
|
|
@@ -8853,10 +9273,17 @@ async function serverUpdate(flags) {
|
|
|
8853
9273
|
const upArgs = [...composeArgs(installPath, withOverrides, installMode), "up", "-d"];
|
|
8854
9274
|
code = await runInteractive("docker", upArgs, installPath);
|
|
8855
9275
|
if (code !== 0) process.exit(code);
|
|
9276
|
+
console.error("Waiting for API and web health checks...");
|
|
9277
|
+
try {
|
|
9278
|
+
await waitForServerHealth(installPath);
|
|
9279
|
+
} catch (error) {
|
|
9280
|
+
await runInteractive("docker", [...composeArgs(installPath, withOverrides, installMode), "ps"], installPath);
|
|
9281
|
+
throw error;
|
|
9282
|
+
}
|
|
8856
9283
|
if (flags.json === true) {
|
|
8857
|
-
printJson({ command: "update", status: "success", path: installPath });
|
|
9284
|
+
printJson({ command: "update", status: "success", path: installPath, mode: "source" });
|
|
8858
9285
|
} else {
|
|
8859
|
-
console.log("Server updated and
|
|
9286
|
+
console.log("Server updated, restarted, and health checks passed.");
|
|
8860
9287
|
}
|
|
8861
9288
|
}
|
|
8862
9289
|
async function serverReset(flags) {
|
|
@@ -9038,7 +9465,10 @@ Command Options:
|
|
|
9038
9465
|
--tail <n> Number of lines to show (default: 100)
|
|
9039
9466
|
|
|
9040
9467
|
update:
|
|
9041
|
-
--
|
|
9468
|
+
--dry-run Show update plan without changing files or containers
|
|
9469
|
+
--image-tag <tag> Image mode: update to a specific prebuilt image tag
|
|
9470
|
+
--channel <name> Image mode: update using stable/main or dev channel tags
|
|
9471
|
+
--force Source mode: stash local changes before pulling
|
|
9042
9472
|
|
|
9043
9473
|
reset:
|
|
9044
9474
|
--delete-user-data-only Only delete database and cache (preserve config)
|
|
@@ -9057,6 +9487,9 @@ Examples:
|
|
|
9057
9487
|
openmates server logs --container api --follow
|
|
9058
9488
|
openmates server make-admin user@example.com
|
|
9059
9489
|
openmates server update
|
|
9490
|
+
openmates server update --dry-run
|
|
9491
|
+
openmates server update --image-tag v0.12.0-alpha.1
|
|
9492
|
+
openmates server update --channel dev
|
|
9060
9493
|
openmates server restart --rebuild
|
|
9061
9494
|
`.trim());
|
|
9062
9495
|
}
|
|
@@ -22151,6 +22584,18 @@ var memoryTravelPreferredTransportChat = {
|
|
|
22151
22584
|
"content": "example_chats.memory_travel_preferred_transport.message_1",
|
|
22152
22585
|
"created_at": 1781280405
|
|
22153
22586
|
},
|
|
22587
|
+
{
|
|
22588
|
+
"id": "32b6170b-a0f8-4e21-b48f-581782e5957e-memory-response",
|
|
22589
|
+
"role": "system",
|
|
22590
|
+
"content": '{"type":"app_settings_memories_response","user_message_id":"32b6170b-a0f8-4e21-b48f-581782e5957e","action":"included","categories":[{"appId":"travel","itemType":"preferred_transport_methods","entryCount":2}]}',
|
|
22591
|
+
"created_at": 1781280406
|
|
22592
|
+
},
|
|
22593
|
+
{
|
|
22594
|
+
"id": "32b6170b-a0f8-4e21-b48f-581782e5957e-memory-request",
|
|
22595
|
+
"role": "system",
|
|
22596
|
+
"content": '{"type":"app_settings_memories_request","user_message_id":"32b6170b-a0f8-4e21-b48f-581782e5957e","request_id":"32b6170b-a0f8-4e21-b48f-581782e5957e-memory-request","requested_keys":["travel-preferred_transport_methods"],"categories":[{"appId":"travel","itemType":"preferred_transport_methods","entryCount":0}]}',
|
|
22597
|
+
"created_at": 1781280406
|
|
22598
|
+
},
|
|
22154
22599
|
{
|
|
22155
22600
|
"id": "6fad1476-1f2f-4977-97ca-b9f5ba773f3d",
|
|
22156
22601
|
"role": "assistant",
|
|
@@ -22185,6 +22630,18 @@ var memoryTravelPreferredActivitiesChat = {
|
|
|
22185
22630
|
"content": "example_chats.memory_travel_preferred_activities.message_1",
|
|
22186
22631
|
"created_at": 1781280434
|
|
22187
22632
|
},
|
|
22633
|
+
{
|
|
22634
|
+
"id": "b59b7592-a869-4bda-ab68-e87a1663ed11-memory-response",
|
|
22635
|
+
"role": "system",
|
|
22636
|
+
"content": '{"type":"app_settings_memories_response","user_message_id":"b59b7592-a869-4bda-ab68-e87a1663ed11","action":"included","categories":[{"appId":"travel","itemType":"preferred_activities","entryCount":2}]}',
|
|
22637
|
+
"created_at": 1781280435
|
|
22638
|
+
},
|
|
22639
|
+
{
|
|
22640
|
+
"id": "b59b7592-a869-4bda-ab68-e87a1663ed11-memory-request",
|
|
22641
|
+
"role": "system",
|
|
22642
|
+
"content": '{"type":"app_settings_memories_request","user_message_id":"b59b7592-a869-4bda-ab68-e87a1663ed11","request_id":"b59b7592-a869-4bda-ab68-e87a1663ed11-memory-request","requested_keys":["travel-preferred_activities"],"categories":[{"appId":"travel","itemType":"preferred_activities","entryCount":0}]}',
|
|
22643
|
+
"created_at": 1781280435
|
|
22644
|
+
},
|
|
22188
22645
|
{
|
|
22189
22646
|
"id": "5dfa7876-6b55-47f8-a472-fd542e19589d",
|
|
22190
22647
|
"role": "assistant",
|
|
@@ -22219,6 +22676,18 @@ var memoryTvWatchedMoviesChat = {
|
|
|
22219
22676
|
"content": "example_chats.memory_tv_watched_movies.message_1",
|
|
22220
22677
|
"created_at": 1781280521
|
|
22221
22678
|
},
|
|
22679
|
+
{
|
|
22680
|
+
"id": "7909540d-9830-48b0-a7ea-ec3438cb7ff6-memory-response",
|
|
22681
|
+
"role": "system",
|
|
22682
|
+
"content": '{"type":"app_settings_memories_response","user_message_id":"7909540d-9830-48b0-a7ea-ec3438cb7ff6","action":"included","categories":[{"appId":"tv","itemType":"watched_movies","entryCount":2}]}',
|
|
22683
|
+
"created_at": 1781280522
|
|
22684
|
+
},
|
|
22685
|
+
{
|
|
22686
|
+
"id": "7909540d-9830-48b0-a7ea-ec3438cb7ff6-memory-request",
|
|
22687
|
+
"role": "system",
|
|
22688
|
+
"content": '{"type":"app_settings_memories_request","user_message_id":"7909540d-9830-48b0-a7ea-ec3438cb7ff6","request_id":"7909540d-9830-48b0-a7ea-ec3438cb7ff6-memory-request","requested_keys":["tv-watched_movies"],"categories":[{"appId":"tv","itemType":"watched_movies","entryCount":0}]}',
|
|
22689
|
+
"created_at": 1781280522
|
|
22690
|
+
},
|
|
22222
22691
|
{
|
|
22223
22692
|
"id": "0bc111c3-32e3-4451-aa54-cae5dfcd1d8a",
|
|
22224
22693
|
"role": "assistant",
|
|
@@ -22253,6 +22722,18 @@ var memoryTvWatchedShowsChat = {
|
|
|
22253
22722
|
"content": "example_chats.memory_tv_watched_shows.message_1",
|
|
22254
22723
|
"created_at": 1781280553
|
|
22255
22724
|
},
|
|
22725
|
+
{
|
|
22726
|
+
"id": "e54287c0-54dc-4961-93f1-0caed8596ba7-memory-response",
|
|
22727
|
+
"role": "system",
|
|
22728
|
+
"content": '{"type":"app_settings_memories_response","user_message_id":"e54287c0-54dc-4961-93f1-0caed8596ba7","action":"included","categories":[{"appId":"tv","itemType":"watched_tv_shows","entryCount":2}]}',
|
|
22729
|
+
"created_at": 1781280554
|
|
22730
|
+
},
|
|
22731
|
+
{
|
|
22732
|
+
"id": "e54287c0-54dc-4961-93f1-0caed8596ba7-memory-request",
|
|
22733
|
+
"role": "system",
|
|
22734
|
+
"content": '{"type":"app_settings_memories_request","user_message_id":"e54287c0-54dc-4961-93f1-0caed8596ba7","request_id":"e54287c0-54dc-4961-93f1-0caed8596ba7-memory-request","requested_keys":["tv-watched_tv_shows"],"categories":[{"appId":"tv","itemType":"watched_tv_shows","entryCount":0}]}',
|
|
22735
|
+
"created_at": 1781280554
|
|
22736
|
+
},
|
|
22256
22737
|
{
|
|
22257
22738
|
"id": "7cf4ba64-2134-4341-9a65-ac3ef207349e",
|
|
22258
22739
|
"role": "assistant",
|
|
@@ -22287,6 +22768,18 @@ var memoryTvToWatchListChat = {
|
|
|
22287
22768
|
"content": "example_chats.memory_tv_to_watch_list.message_1",
|
|
22288
22769
|
"created_at": 1781280581
|
|
22289
22770
|
},
|
|
22771
|
+
{
|
|
22772
|
+
"id": "794b4c25-ad08-4088-adb4-5729b5fc89d2-memory-response",
|
|
22773
|
+
"role": "system",
|
|
22774
|
+
"content": '{"type":"app_settings_memories_response","user_message_id":"794b4c25-ad08-4088-adb4-5729b5fc89d2","action":"included","categories":[{"appId":"tv","itemType":"to_watch_list","entryCount":2}]}',
|
|
22775
|
+
"created_at": 1781280582
|
|
22776
|
+
},
|
|
22777
|
+
{
|
|
22778
|
+
"id": "794b4c25-ad08-4088-adb4-5729b5fc89d2-memory-request",
|
|
22779
|
+
"role": "system",
|
|
22780
|
+
"content": '{"type":"app_settings_memories_request","user_message_id":"794b4c25-ad08-4088-adb4-5729b5fc89d2","request_id":"794b4c25-ad08-4088-adb4-5729b5fc89d2-memory-request","requested_keys":["tv-to_watch_list"],"categories":[{"appId":"tv","itemType":"to_watch_list","entryCount":0}]}',
|
|
22781
|
+
"created_at": 1781280582
|
|
22782
|
+
},
|
|
22290
22783
|
{
|
|
22291
22784
|
"id": "4c8d7bb0-2b55-4428-aa22-c9ab6aa8f01e",
|
|
22292
22785
|
"role": "assistant",
|
|
@@ -22321,6 +22814,18 @@ var memoryVideosToWatchListChat = {
|
|
|
22321
22814
|
"content": "example_chats.memory_videos_to_watch_list.message_1",
|
|
22322
22815
|
"created_at": 1781280606
|
|
22323
22816
|
},
|
|
22817
|
+
{
|
|
22818
|
+
"id": "9df4f00f-8468-482f-886c-4c323fdc7ef8-memory-response",
|
|
22819
|
+
"role": "system",
|
|
22820
|
+
"content": '{"type":"app_settings_memories_response","user_message_id":"9df4f00f-8468-482f-886c-4c323fdc7ef8","action":"included","categories":[{"appId":"videos","itemType":"to_watch_list","entryCount":2}]}',
|
|
22821
|
+
"created_at": 1781280607
|
|
22822
|
+
},
|
|
22823
|
+
{
|
|
22824
|
+
"id": "9df4f00f-8468-482f-886c-4c323fdc7ef8-memory-request",
|
|
22825
|
+
"role": "system",
|
|
22826
|
+
"content": '{"type":"app_settings_memories_request","user_message_id":"9df4f00f-8468-482f-886c-4c323fdc7ef8","request_id":"9df4f00f-8468-482f-886c-4c323fdc7ef8-memory-request","requested_keys":["videos-to_watch_list"],"categories":[{"appId":"videos","itemType":"to_watch_list","entryCount":0}]}',
|
|
22827
|
+
"created_at": 1781280607
|
|
22828
|
+
},
|
|
22324
22829
|
{
|
|
22325
22830
|
"id": "16c2890e-358f-419f-a607-98d8ecb447b2",
|
|
22326
22831
|
"role": "assistant",
|
|
@@ -22355,6 +22860,18 @@ var memoryWebBookmarksChat = {
|
|
|
22355
22860
|
"content": "example_chats.memory_web_bookmarks.message_1",
|
|
22356
22861
|
"created_at": 1781280634
|
|
22357
22862
|
},
|
|
22863
|
+
{
|
|
22864
|
+
"id": "9a448c41-309c-49ef-a90d-ae00b16b1e10-memory-response",
|
|
22865
|
+
"role": "system",
|
|
22866
|
+
"content": '{"type":"app_settings_memories_response","user_message_id":"9a448c41-309c-49ef-a90d-ae00b16b1e10","action":"included","categories":[{"appId":"web","itemType":"bookmarks","entryCount":2}]}',
|
|
22867
|
+
"created_at": 1781280635
|
|
22868
|
+
},
|
|
22869
|
+
{
|
|
22870
|
+
"id": "9a448c41-309c-49ef-a90d-ae00b16b1e10-memory-request",
|
|
22871
|
+
"role": "system",
|
|
22872
|
+
"content": '{"type":"app_settings_memories_request","user_message_id":"9a448c41-309c-49ef-a90d-ae00b16b1e10","request_id":"9a448c41-309c-49ef-a90d-ae00b16b1e10-memory-request","requested_keys":["web-bookmarks"],"categories":[{"appId":"web","itemType":"bookmarks","entryCount":0}]}',
|
|
22873
|
+
"created_at": 1781280635
|
|
22874
|
+
},
|
|
22358
22875
|
{
|
|
22359
22876
|
"id": "f3be11d8-e466-4649-8078-ee5f6ad7920e",
|
|
22360
22877
|
"role": "assistant",
|
|
@@ -22389,6 +22906,18 @@ var memoryWebReadLaterChat = {
|
|
|
22389
22906
|
"content": "example_chats.memory_web_read_later.message_1",
|
|
22390
22907
|
"created_at": 1781280720
|
|
22391
22908
|
},
|
|
22909
|
+
{
|
|
22910
|
+
"id": "7365e817-390f-4e70-a020-fa4eef5f637c-memory-response",
|
|
22911
|
+
"role": "system",
|
|
22912
|
+
"content": '{"type":"app_settings_memories_response","user_message_id":"7365e817-390f-4e70-a020-fa4eef5f637c","action":"included","categories":[{"appId":"web","itemType":"read_later","entryCount":2}]}',
|
|
22913
|
+
"created_at": 1781280721
|
|
22914
|
+
},
|
|
22915
|
+
{
|
|
22916
|
+
"id": "7365e817-390f-4e70-a020-fa4eef5f637c-memory-request",
|
|
22917
|
+
"role": "system",
|
|
22918
|
+
"content": '{"type":"app_settings_memories_request","user_message_id":"7365e817-390f-4e70-a020-fa4eef5f637c","request_id":"7365e817-390f-4e70-a020-fa4eef5f637c-memory-request","requested_keys":["web-read_later"],"categories":[{"appId":"web","itemType":"read_later","entryCount":0}]}',
|
|
22919
|
+
"created_at": 1781280721
|
|
22920
|
+
},
|
|
22392
22921
|
{
|
|
22393
22922
|
"id": "b1bdbb77-38e4-4974-ac51-4fff6ea5c6fd",
|
|
22394
22923
|
"role": "assistant",
|
|
@@ -22892,6 +23421,108 @@ var classicCarReverseImageSearchChat = {
|
|
|
22892
23421
|
}
|
|
22893
23422
|
};
|
|
22894
23423
|
|
|
23424
|
+
// ../ui/src/demo_chats/data/example_chats/usb-c-3v3-ldo-pcb-schematic.ts
|
|
23425
|
+
var usbC3v3LdoPcbSchematicChat = {
|
|
23426
|
+
chat_id: "example-usb-c-3v3-ldo",
|
|
23427
|
+
slug: "usb-c-3v3-ldo-pcb-schematic",
|
|
23428
|
+
title: "example_chats.usb_c_3v3_ldo_pcb_schematic.title",
|
|
23429
|
+
summary: "example_chats.usb_c_3v3_ldo_pcb_schematic.summary",
|
|
23430
|
+
icon: "circuit-board",
|
|
23431
|
+
category: "software_development",
|
|
23432
|
+
keywords: ["PCB schematic", "Atopile", "USB-C", "LDO", "3.3V regulator", "electronics design"],
|
|
23433
|
+
follow_up_suggestions: ["example_chats.usb_c_3v3_ldo_pcb_schematic.follow_up_1", "example_chats.usb_c_3v3_ldo_pcb_schematic.follow_up_2", "example_chats.usb_c_3v3_ldo_pcb_schematic.follow_up_3", "example_chats.usb_c_3v3_ldo_pcb_schematic.follow_up_4", "example_chats.usb_c_3v3_ldo_pcb_schematic.follow_up_5", "example_chats.usb_c_3v3_ldo_pcb_schematic.follow_up_6"],
|
|
23434
|
+
messages: [
|
|
23435
|
+
{
|
|
23436
|
+
"id": "verified-pcb-user-1",
|
|
23437
|
+
"role": "user",
|
|
23438
|
+
"content": "example_chats.usb_c_3v3_ldo_pcb_schematic.message_1",
|
|
23439
|
+
"created_at": 1781772935
|
|
23440
|
+
},
|
|
23441
|
+
{
|
|
23442
|
+
"id": "verified-pcb-assistant-1",
|
|
23443
|
+
"role": "assistant",
|
|
23444
|
+
"content": "example_chats.usb_c_3v3_ldo_pcb_schematic.message_2",
|
|
23445
|
+
"created_at": 1781772936,
|
|
23446
|
+
"category": "software_development",
|
|
23447
|
+
"model_name": "Gemini 3.1 Pro"
|
|
23448
|
+
}
|
|
23449
|
+
],
|
|
23450
|
+
embeds: [
|
|
23451
|
+
{
|
|
23452
|
+
"embed_id": "46d63593-516e-49b5-8e7d-600c4fe18836",
|
|
23453
|
+
"type": "pcb_schematic",
|
|
23454
|
+
"content": 'type: pcb_schematic\napp_id: electronics\nskill_id: schematic\nlanguage: atopile\ncode: "import Resistor\\nimport Capacitor\\nimport Diode\\nimport ElectricPower\\n\\n# Modeling the USB-C input behavior\\nmodule USBCInput:\\n power = new ElectricPower\\n signal vbus\\n signal gnd\\n \\n # Associate the interface pins with local power signals\\n power.vcc ~ vbus\\n power.gnd ~ gnd\\n\\n# Modeling the LDO regulator behavior\\nmodule LDO:\\n power_in = new ElectricPower\\n power_out = new ElectricPower\\n \\n # Regulators typically share a common ground reference\\n power_in.gnd ~ power_out.gnd\\n\\n# Top-level application module\\nmodule App:\\n # 1. Instantiate functional blocks\\n usb_input = new USBCInput\\n regulator = new LDO\\n\\n # 2. Establish main power path (5V Input to LDO)\\n usb_input.power ~ regulator.power_in\\n\\n # 3. Input Decoupling Capacitor (5V rail)\\n c_in = new Capacitor\\n c_in.capacitance = 10uF +/- 20%\\n regulator.power_in.vcc ~ c_in.unnamed[0]\\n regulator.power_in.gnd ~ c_in.unnamed[1]\\n\\n # 4. Output Decoupling Capacitor (3.3V rail)\\n c_out = new Capacitor\\n c_out.capacitance = 1uF +/- 20%\\n regulator.power_out.vcc ~ c_out.unnamed[0]\\n regulator.power_out.gnd ~ c_out.unnamed[1]\\n\\n # 5. Power Status Indicator (Modeled as Diode) on the 3.3V Rail\\n status_led = new Diode\\n limit_res = new Resistor\\n limit_res.resistance = 1kohm +/- 5%\\n\\n # Connections: VCC -> Diode Anode -> Diode Cathode -> Resistor -> GND\\n regulator.power_out.vcc ~ status_led.anode\\n status_led.cathode ~ limit_res.unnamed[0]\\n limit_res.unnamed[1] ~ regulator.power_out.gnd\\n"\nfilename: main.ato\ntitle: USB-C 3.3V LDO PCB Schematic\nmodule_name: App\nembed_ref: main.ato-46d635\nstatus: finished\nline_count: 54\ncompile_id: null\ncompile_status: null\nartifact_manifest: null\natopile_version: 0.15.7\natopile_docs_version: 0.15.7\ncompile_logs: null',
|
|
23455
|
+
"parent_embed_id": null,
|
|
23456
|
+
"embed_ids": null
|
|
23457
|
+
}
|
|
23458
|
+
],
|
|
23459
|
+
metadata: {
|
|
23460
|
+
featured: true,
|
|
23461
|
+
order: 103
|
|
23462
|
+
}
|
|
23463
|
+
};
|
|
23464
|
+
|
|
23465
|
+
// ../ui/src/demo_chats/data/example_chats/berlin-morning-bike-forecast.ts
|
|
23466
|
+
var berlinMorningBikeForecastChat = {
|
|
23467
|
+
chat_id: "example-berlin-morning-bike-forecast",
|
|
23468
|
+
slug: "berlin-morning-bike-forecast",
|
|
23469
|
+
title: "example_chats.berlin_morning_bike_forecast.title",
|
|
23470
|
+
summary: "example_chats.berlin_morning_bike_forecast.summary",
|
|
23471
|
+
icon: "bike",
|
|
23472
|
+
category: "general_knowledge",
|
|
23473
|
+
keywords: ["Berlin weather", "Tempelhofer Feld", "bike commute", "weather forecast"],
|
|
23474
|
+
follow_up_suggestions: ["example_chats.berlin_morning_bike_forecast.follow_up_1", "example_chats.berlin_morning_bike_forecast.follow_up_2", "example_chats.berlin_morning_bike_forecast.follow_up_3", "example_chats.berlin_morning_bike_forecast.follow_up_4", "example_chats.berlin_morning_bike_forecast.follow_up_5", "example_chats.berlin_morning_bike_forecast.follow_up_6"],
|
|
23475
|
+
messages: [
|
|
23476
|
+
{
|
|
23477
|
+
"id": "40fe35ff-396a-40aa-ae7e-463067a91833",
|
|
23478
|
+
"role": "user",
|
|
23479
|
+
"content": "example_chats.berlin_morning_bike_forecast.message_1",
|
|
23480
|
+
"created_at": 1781772332
|
|
23481
|
+
},
|
|
23482
|
+
{
|
|
23483
|
+
"id": "560ef618-e8df-4bd2-99fb-68a737cee232",
|
|
23484
|
+
"role": "assistant",
|
|
23485
|
+
"content": "example_chats.berlin_morning_bike_forecast.message_2",
|
|
23486
|
+
"created_at": 1781772346,
|
|
23487
|
+
"user_message_id": "40fe35ff-396a-40aa-ae7e-463067a91833",
|
|
23488
|
+
"response_credits": 25,
|
|
23489
|
+
"category": "general_knowledge",
|
|
23490
|
+
"model_name": "Gemini 3 Flash"
|
|
23491
|
+
}
|
|
23492
|
+
],
|
|
23493
|
+
embeds: [
|
|
23494
|
+
{
|
|
23495
|
+
"embed_id": "324eba33-3622-409c-b401-183f95d6183e",
|
|
23496
|
+
"type": "app_skill_use",
|
|
23497
|
+
"content": 'app_id: weather\nskill_id: forecast\nresult_count: 2\nembed_ids: c72c110f-9513-4ce8-a943-d66888c6bad0|4d855526-af41-47cf-a354-c769f3098c94\nstatus: finished\nembed_id: 324eba33-3622-409c-b401-183f95d6183e\ndays: 2\nlocation: Berlin\nquery: Berlin weather forecast\ndays_requested: 2\nprovider: Deutscher Wetterdienst (DWD) + Open-Meteo\npreview_results[2]{title,source_provider,source_station_name,source_dwd_station_id,source_wmo_station_id,source_observation_type,source_distance_m,source_lat,source_lon,source_height_m,source_first_record,source_last_record,date,provider}:\n Berlin weather 2026-06-18,Deutscher Wetterdienst (DWD),BERLIN-ALEX.,"00399","10389",forecast,805.0,52.52,13.42,37.0,"2026-06-18T08:00:00+02:00","2026-06-28T12:00:00+02:00",2026-06-18,Deutscher Wetterdienst (DWD)\n Berlin weather 2026-06-19,Deutscher Wetterdienst (DWD),BERLIN-ALEX.,"00399","10389",forecast,805.0,52.52,13.42,37.0,"2026-06-18T08:00:00+02:00","2026-06-28T12:00:00+02:00",2026-06-19,Deutscher Wetterdienst (DWD)',
|
|
23498
|
+
"parent_embed_id": null,
|
|
23499
|
+
"embed_ids": [
|
|
23500
|
+
"c72c110f-9513-4ce8-a943-d66888c6bad0",
|
|
23501
|
+
"4d855526-af41-47cf-a354-c769f3098c94"
|
|
23502
|
+
]
|
|
23503
|
+
},
|
|
23504
|
+
{
|
|
23505
|
+
"embed_id": "c72c110f-9513-4ce8-a943-d66888c6bad0",
|
|
23506
|
+
"type": "weather_day",
|
|
23507
|
+
"content": 'type: weather_day\ntitle: Berlin weather 2026-06-18\ndate: 2026-06-18\nlabel: today\nlocation_name: Berlin\ncountry_code: DE\ntimezone: Europe/Berlin\nprovider: Deutscher Wetterdienst (DWD)\ncondition: dry\nicon: partly-cloudy-day\ntemperature_min_c: 20.1\ntemperature_max_c: 28.3\nprecipitation_total_mm: 0\nprecipitation_probability_max_pct: 7\nrain_hours: 0\nwind_speed_max_kmh: 15.8\nwind_gust_speed_max_kmh: 31.5\ncloud_cover_avg_pct: 79\nrelative_humidity_avg_pct: 69.2\nhourly[24]:\n - time: "00:00"\n timestamp: "2026-06-18T00:00:00+02:00"\n condition: dry\n icon: cloudy\n temperature_c: 21.0\n precipitation_mm: 0.0\n precipitation_probability_pct: null\n precipitation_probability_6h_pct: null\n cloud_cover_pct: 100\n relative_humidity_pct: 65\n dew_point_c: 14.1\n pressure_msl_hpa: 1018.9\n visibility_m: 39690\n wind_direction_deg: 290\n wind_speed_kmh: 6.8\n wind_gust_direction_deg: 280\n wind_gust_speed_kmh: 10.4\n sunshine_minutes: 0.0\n solar: 0.0\n source_id: 303712\n fallback_source_ids_sunshine: 6885\n fallback_source_ids_solar: 255262\n fallback_source_ids_dew_point: 6150\n fallback_source_ids_relative_humidity: 6150\n fallback_source_ids_wind_direction: 6150\n fallback_source_ids_precipitation: 6150\n fallback_source_ids_condition: 6150\n fallback_source_ids_temperature: 6150\n fallback_source_ids_wind_speed: 6150\n - time: "01:00"\n timestamp: "2026-06-18T01:00:00+02:00"\n condition: dry\n icon: cloudy\n temperature_c: 21.0\n precipitation_mm: 0.0\n precipitation_probability_pct: null\n precipitation_probability_6h_pct: null\n cloud_cover_pct: 100\n relative_humidity_pct: 65\n dew_point_c: 14.2\n pressure_msl_hpa: 1018.9\n visibility_m: 41770\n wind_direction_deg: 240\n wind_speed_kmh: 6.1\n wind_gust_direction_deg: 230\n wind_gust_speed_kmh: 8.6\n sunshine_minutes: 0.0\n solar: 0.0\n source_id: 303712\n fallback_source_ids_sunshine: 6885\n fallback_source_ids_solar: 255262\n fallback_source_ids_dew_point: 6150\n fallback_source_ids_relative_humidity: 6150\n fallback_source_ids_wind_direction: 6150\n fallback_source_ids_precipitation: 6150\n fallback_source_ids_condition: 6150\n fallback_source_ids_temperature: 6150\n fallback_source_ids_wind_speed: 6150\n - time: "02:00"\n timestamp: "2026-06-18T02:00:00+02:00"\n condition: dry\n icon: cloudy\n temperature_c: 20.5\n precipitation_mm: 0.0\n precipitation_probability_pct: null\n precipitation_probability_6h_pct: null\n cloud_cover_pct: 100\n relative_humidity_pct: 68\n dew_point_c: 14.4\n pressure_msl_hpa: 1019.1\n visibility_m: 42400\n wind_direction_deg: 240\n wind_speed_kmh: 6.1\n wind_gust_direction_deg: null\n wind_gust_speed_kmh: 9.0\n sunshine_minutes: 0.0\n solar: 0.0\n source_id: 6150\n fallback_source_ids_sunshine: 6148\n fallback_source_ids_solar: 6147\n - time: "03:00"\n timestamp: "2026-06-18T03:00:00+02:00"\n condition: dry\n icon: cloudy\n temperature_c: 20.5\n precipitation_mm: 0.0\n precipitation_probability_pct: null\n precipitation_probability_6h_pct: null\n cloud_cover_pct: 100\n relative_humidity_pct: 68\n dew_point_c: 14.5\n pressure_msl_hpa: 1019.1\n visibility_m: 43300\n wind_direction_deg: 240\n wind_speed_kmh: 6.1\n wind_gust_direction_deg: null\n wind_gust_speed_kmh: 7.9\n sunshine_minutes: 0.0\n solar: 0.0\n source_id: 6150\n fallback_source_ids_sunshine: 6148\n fallback_source_ids_solar: 6147\n - time: "04:00"\n timestamp: "2026-06-18T04:00:00+02:00"\n condition: dry\n icon: cloudy\n temperature_c: 20.4\n precipitation_mm: 0.0\n precipitation_probability_pct: null\n precipitation_probability_6h_pct: null\n cloud_cover_pct: 100\n relative_humidity_pct: 70\n dew_point_c: 14.7\n pressure_msl_hpa: 1018.9\n visibility_m: 39100\n wind_direction_deg: 230\n wind_speed_kmh: 6.8\n wind_gust_direction_deg: null\n wind_gust_speed_kmh: 11.9\n sunshine_minutes: 0.0\n solar: 0.0\n source_id: 6150\n fallback_source_ids_sunshine: 6148\n fallback_source_ids_solar: 6147\n - time: "05:00"\n timestamp: "2026-06-18T05:00:00+02:00"\n condition: dry\n icon: cloudy\n temperature_c: 20.1\n precipitation_mm: 0.0\n precipitation_probability_pct: null\n precipitation_probability_6h_pct: null\n cloud_cover_pct: 100\n relative_humidity_pct: 72\n dew_point_c: 15.0\n pressure_msl_hpa: 1018.8\n visibility_m: 14400\n wind_direction_deg: 230\n wind_speed_kmh: 5.0\n wind_gust_direction_deg: null\n wind_gust_speed_kmh: 11.2\n sunshine_minutes: 0.0\n solar: 0.001\n source_id: 6150\n fallback_source_ids_sunshine: 6148\n fallback_source_ids_solar: 6147\n - time: "06:00"\n timestamp: "2026-06-18T06:00:00+02:00"\n condition: dry\n icon: cloudy\n temperature_c: 20.2\n precipitation_mm: 0.0\n precipitation_probability_pct: null\n precipitation_probability_6h_pct: null\n cloud_cover_pct: 100\n relative_humidity_pct: 73\n dew_point_c: 15.3\n pressure_msl_hpa: 1019.0\n visibility_m: 32500\n wind_direction_deg: 240\n wind_speed_kmh: 9.0\n wind_gust_direction_deg: null\n wind_gust_speed_kmh: 15.1\n sunshine_minutes: 0.0\n solar: 0.014\n source_id: 6150\n fallback_source_ids_sunshine: 6148\n fallback_source_ids_solar: 6147\n - time: "07:00"\n timestamp: "2026-06-18T07:00:00+02:00"\n condition: dry\n icon: partly-cloudy-day\n temperature_c: 20.7\n precipitation_mm: 0.0\n precipitation_probability_pct: null\n precipitation_probability_6h_pct: null\n cloud_cover_pct: 75\n relative_humidity_pct: 74\n dew_point_c: 15.8\n pressure_msl_hpa: 1018.9\n visibility_m: 37200\n wind_direction_deg: 230\n wind_speed_kmh: 7.9\n wind_gust_direction_deg: null\n wind_gust_speed_kmh: 15.8\n sunshine_minutes: 6.0\n solar: 0.069\n source_id: 6150\n fallback_source_ids_sunshine: 6148\n fallback_source_ids_solar: 6147\n - time: "08:00"\n timestamp: "2026-06-18T08:00:00+02:00"\n condition: dry\n icon: partly-cloudy-day\n temperature_c: 22.2\n precipitation_mm: 0.0\n precipitation_probability_pct: 7\n precipitation_probability_6h_pct: 10\n cloud_cover_pct: 75\n relative_humidity_pct: 68\n dew_point_c: 16.1\n pressure_msl_hpa: 1019.1\n visibility_m: 54700\n wind_direction_deg: 280\n wind_speed_kmh: 14.0\n wind_gust_direction_deg: null\n wind_gust_speed_kmh: 22.0\n sunshine_minutes: 36.0\n solar: 0.218\n source_id: 6150\n fallback_source_ids_sunshine: 6148\n fallback_source_ids_solar: 6147\n fallback_source_ids_precipitation_probability_6h: 2382\n fallback_source_ids_precipitation_probability: 2382\n - time: "09:00"\n timestamp: "2026-06-18T09:00:00+02:00"\n condition: dry\n icon: cloudy\n temperature_c: 22.4\n precipitation_mm: 0.0\n precipitation_probability_pct: 6\n precipitation_probability_6h_pct: null\n cloud_cover_pct: 100\n relative_humidity_pct: 70\n dew_point_c: 16.6\n pressure_msl_hpa: 1019.6\n visibility_m: 59800\n wind_direction_deg: 280\n wind_speed_kmh: 15.8\n wind_gust_direction_deg: null\n wind_gust_speed_kmh: 27.0\n sunshine_minutes: 12.0\n solar: 0.248\n source_id: 6150\n fallback_source_ids_sunshine: 6148\n fallback_source_ids_solar: 6147\n fallback_source_ids_precipitation_probability: 2382\n - time: "10:00"\n timestamp: "2026-06-18T10:00:00+02:00"\n condition: dry\n icon: cloudy\n temperature_c: 23.2\n precipitation_mm: 0.0\n precipitation_probability_pct: 5\n precipitation_probability_6h_pct: null\n cloud_cover_pct: 100\n relative_humidity_pct: 68\n dew_point_c: 17.0\n pressure_msl_hpa: 1019.7\n visibility_m: 45500\n wind_direction_deg: 280\n wind_speed_kmh: 15.8\n wind_gust_direction_deg: null\n wind_gust_speed_kmh: 27.0\n sunshine_minutes: 42.0\n solar: 0.517\n source_id: 6150\n fallback_source_ids_sunshine: 6148\n fallback_source_ids_solar: 6147\n fallback_source_ids_precipitation_probability: 2382\n - time: "11:00"\n timestamp: "2026-06-18T11:00:00+02:00"\n condition: dry\n icon: cloudy\n temperature_c: 23.7\n precipitation_mm: 0.0\n precipitation_probability_pct: 5\n precipitation_probability_6h_pct: null\n cloud_cover_pct: 82\n relative_humidity_pct: null\n dew_point_c: 16.3\n pressure_msl_hpa: 1019.4\n visibility_m: 33400\n wind_direction_deg: 269\n wind_speed_kmh: 13.0\n wind_gust_direction_deg: null\n wind_gust_speed_kmh: 27.8\n sunshine_minutes: 32.0\n solar: 0.514\n source_id: 2382\n fallback_source_ids: null\n - time: "12:00"\n timestamp: "2026-06-18T12:00:00+02:00"\n condition: dry\n icon: partly-cloudy-day\n temperature_c: 24.6\n precipitation_mm: 0.0\n precipitation_probability_pct: 3\n precipitation_probability_6h_pct: null\n cloud_cover_pct: 78\n relative_humidity_pct: null\n dew_point_c: 16.4\n pressure_msl_hpa: 1019.0\n visibility_m: 32800\n wind_direction_deg: 274\n wind_speed_kmh: 13.0\n wind_gust_direction_deg: null\n wind_gust_speed_kmh: 29.6\n sunshine_minutes: 32.0\n solar: 0.6\n source_id: 2382\n fallback_source_ids: null\n - time: "13:00"\n timestamp: "2026-06-18T13:00:00+02:00"\n condition: dry\n icon: partly-cloudy-day\n temperature_c: 25.4\n precipitation_mm: 0.0\n precipitation_probability_pct: 4\n precipitation_probability_6h_pct: null\n cloud_cover_pct: 74\n relative_humidity_pct: null\n dew_point_c: 16.4\n pressure_msl_hpa: 1018.7\n visibility_m: 31600\n wind_direction_deg: 282\n wind_speed_kmh: 13.0\n wind_gust_direction_deg: null\n wind_gust_speed_kmh: 31.5\n sunshine_minutes: 33.0\n solar: 0.653\n source_id: 2382\n fallback_source_ids: null\n - time: "14:00"\n timestamp: "2026-06-18T14:00:00+02:00"\n condition: dry\n icon: partly-cloudy-day\n temperature_c: 26.4\n precipitation_mm: 0.0\n precipitation_probability_pct: 3\n precipitation_probability_6h_pct: 8\n cloud_cover_pct: 72\n relative_humidity_pct: null\n dew_point_c: 16.0\n pressure_msl_hpa: 1018.4\n visibility_m: 32600\n wind_direction_deg: 283\n wind_speed_kmh: 13.0\n wind_gust_direction_deg: null\n wind_gust_speed_kmh: 31.5\n sunshine_minutes: 34.0\n solar: 0.656\n source_id: 2382\n fallback_source_ids: null\n - time: "15:00"\n timestamp: "2026-06-18T15:00:00+02:00"\n condition: dry\n icon: partly-cloudy-day\n temperature_c: 27.1\n precipitation_mm: 0.0\n precipitation_probability_pct: 4\n precipitation_probability_6h_pct: null\n cloud_cover_pct: 71\n relative_humidity_pct: null\n dew_point_c: 15.9\n pressure_msl_hpa: 1018.1\n visibility_m: 35000\n wind_direction_deg: 288\n wind_speed_kmh: 13.0\n wind_gust_direction_deg: null\n wind_gust_speed_kmh: 31.5\n sunshine_minutes: 34.0\n solar: 0.614\n source_id: 2382\n fallback_source_ids: null\n - time: "16:00"\n timestamp: "2026-06-18T16:00:00+02:00"\n condition: dry\n icon: partly-cloudy-day\n temperature_c: 27.5\n precipitation_mm: 0.0\n precipitation_probability_pct: 3\n precipitation_probability_6h_pct: null\n cloud_cover_pct: 70\n relative_humidity_pct: null\n dew_point_c: 15.9\n pressure_msl_hpa: 1017.8\n visibility_m: 34900\n wind_direction_deg: 291\n wind_speed_kmh: 13.0\n wind_gust_direction_deg: null\n wind_gust_speed_kmh: 31.5\n sunshine_minutes: 37.0\n solar: 0.564\n source_id: 2382\n fallback_source_ids: null\n - time: "17:00"\n timestamp: "2026-06-18T17:00:00+02:00"\n condition: dry\n icon: partly-cloudy-day\n temperature_c: 28.1\n precipitation_mm: 0.0\n precipitation_probability_pct: 1\n precipitation_probability_6h_pct: null\n cloud_cover_pct: 67\n relative_humidity_pct: null\n dew_point_c: 15.7\n pressure_msl_hpa: 1017.6\n visibility_m: 35200\n wind_direction_deg: 294\n wind_speed_kmh: 13.0\n wind_gust_direction_deg: null\n wind_gust_speed_kmh: 31.5\n sunshine_minutes: 42.0\n solar: 0.517\n source_id: 2382\n fallback_source_ids: null\n - time: "18:00"\n timestamp: "2026-06-18T18:00:00+02:00"\n condition: dry\n icon: partly-cloudy-day\n temperature_c: 28.3\n precipitation_mm: 0.0\n precipitation_probability_pct: 1\n precipitation_probability_6h_pct: null\n cloud_cover_pct: 63\n relative_humidity_pct: null\n dew_point_c: 15.6\n pressure_msl_hpa: 1017.3\n visibility_m: 36200\n wind_direction_deg: 299\n wind_speed_kmh: 13.0\n wind_gust_direction_deg: null\n wind_gust_speed_kmh: 29.6\n sunshine_minutes: 43.0\n solar: 0.422\n source_id: 2382\n fallback_source_ids: null\n - time: "19:00"\n timestamp: "2026-06-18T19:00:00+02:00"\n condition: dry\n icon: partly-cloudy-day\n temperature_c: 28.1\n precipitation_mm: 0.0\n precipitation_probability_pct: 1\n precipitation_probability_6h_pct: null\n cloud_cover_pct: 62\n relative_humidity_pct: null\n dew_point_c: 15.7\n pressure_msl_hpa: 1017.3\n visibility_m: 36600\n wind_direction_deg: 304\n wind_speed_kmh: 11.1\n wind_gust_direction_deg: null\n wind_gust_speed_kmh: 25.9\n sunshine_minutes: 37.0\n solar: 0.297\n source_id: 2382\n fallback_source_ids: null\n - time: "20:00"\n timestamp: "2026-06-18T20:00:00+02:00"\n condition: dry\n icon: partly-cloudy-day\n temperature_c: 27.2\n precipitation_mm: 0.0\n precipitation_probability_pct: 1\n precipitation_probability_6h_pct: 2\n cloud_cover_pct: 56\n relative_humidity_pct: null\n dew_point_c: 15.8\n pressure_msl_hpa: 1017.3\n visibility_m: 37300\n wind_direction_deg: 311\n wind_speed_kmh: 11.1\n wind_gust_direction_deg: null\n wind_gust_speed_kmh: 24.1\n sunshine_minutes: 33.0\n solar: 0.172\n source_id: 2382\n fallback_source_ids: null\n - time: "21:00"\n timestamp: "2026-06-18T21:00:00+02:00"\n condition: dry\n icon: partly-cloudy-day\n temperature_c: 25.8\n precipitation_mm: 0.0\n precipitation_probability_pct: 2\n precipitation_probability_6h_pct: null\n cloud_cover_pct: 55\n relative_humidity_pct: null\n dew_point_c: 15.9\n pressure_msl_hpa: 1017.4\n visibility_m: 37100\n wind_direction_deg: 337\n wind_speed_kmh: 9.3\n wind_gust_direction_deg: null\n wind_gust_speed_kmh: 20.4\n sunshine_minutes: 22.0\n solar: 0.058\n source_id: 2382\n fallback_source_ids: null\n - time: "22:00"\n timestamp: "2026-06-18T22:00:00+02:00"\n condition: dry\n icon: partly-cloudy-night\n temperature_c: 24.0\n precipitation_mm: 0.0\n precipitation_probability_pct: 2\n precipitation_probability_6h_pct: null\n cloud_cover_pct: 50\n relative_humidity_pct: null\n dew_point_c: 16.1\n pressure_msl_hpa: 1017.7\n visibility_m: 36100\n wind_direction_deg: 17\n wind_speed_kmh: 9.3\n wind_gust_direction_deg: null\n wind_gust_speed_kmh: 16.7\n sunshine_minutes: 11.0\n solar: 0.006\n source_id: 2382\n fallback_source_ids: null\n - time: "23:00"\n timestamp: "2026-06-18T23:00:00+02:00"\n condition: dry\n icon: partly-cloudy-night\n temperature_c: 22.6\n precipitation_mm: 0.0\n precipitation_probability_pct: 1\n precipitation_probability_6h_pct: null\n cloud_cover_pct: 46\n relative_humidity_pct: null\n dew_point_c: 16.4\n pressure_msl_hpa: 1018.0\n visibility_m: 35700\n wind_direction_deg: 61\n wind_speed_kmh: 9.3\n wind_gust_direction_deg: null\n wind_gust_speed_kmh: 16.7\n sunshine_minutes: 0.0\n solar: 0.0\n source_id: 2382\n fallback_source_ids: null\nsource_provider: Deutscher Wetterdienst (DWD)\nsource_station_name: BERLIN-ALEX.\nsource_dwd_station_id: "00399"\nsource_wmo_station_id: "10389"\nsource_observation_type: forecast\nsource_distance_m: 805.0\nsource_lat: 52.52\nsource_lon: 13.42\nsource_height_m: 37.0\nsource_first_record: "2026-06-18T08:00:00+02:00"\nsource_last_record: "2026-06-28T12:00:00+02:00"\ndata_quality_hourly_rows: 24\ndata_quality_raw_source_count: 7\nembed_ref: berlin-weather-2026-mF7\napp_id: weather\nskill_id: forecast',
|
|
23508
|
+
"parent_embed_id": "324eba33-3622-409c-b401-183f95d6183e",
|
|
23509
|
+
"embed_ids": null
|
|
23510
|
+
},
|
|
23511
|
+
{
|
|
23512
|
+
"embed_id": "4d855526-af41-47cf-a354-c769f3098c94",
|
|
23513
|
+
"type": "weather_day",
|
|
23514
|
+
"content": 'type: weather_day\ntitle: Berlin weather 2026-06-19\ndate: 2026-06-19\nlabel: tomorrow\nlocation_name: Berlin\ncountry_code: DE\ntimezone: Europe/Berlin\nprovider: Deutscher Wetterdienst (DWD)\ncondition: dry\nicon: partly-cloudy-day\ntemperature_min_c: 18.4\ntemperature_max_c: 32.0\nprecipitation_total_mm: 0\nprecipitation_probability_max_pct: 4\nrain_hours: 0\nwind_speed_max_kmh: 11.1\nwind_gust_speed_max_kmh: 25.9\ncloud_cover_avg_pct: 58.7\nrelative_humidity_avg_pct: null\nhourly[24]{time,timestamp,condition,icon,temperature_c,precipitation_mm,precipitation_probability_pct,precipitation_probability_6h_pct,cloud_cover_pct,relative_humidity_pct,dew_point_c,pressure_msl_hpa,visibility_m,wind_direction_deg,wind_speed_kmh,wind_gust_direction_deg,wind_gust_speed_kmh,sunshine_minutes,solar,source_id,fallback_source_ids}:\n "00:00","2026-06-19T00:00:00+02:00",dry,partly-cloudy-night,21.3,0.0,1,null,43,null,16.7,1018.2,37100,91,9.3,null,16.7,0.0,0.0,2382,null\n "01:00","2026-06-19T01:00:00+02:00",dry,partly-cloudy-night,20.6,0.0,1,null,39,null,16.7,1018.4,38100,96,9.3,null,16.7,0.0,0.0,2382,null\n "02:00","2026-06-19T02:00:00+02:00",dry,partly-cloudy-night,19.7,0.0,1,1,42,null,16.8,1018.2,39100,110,9.3,null,16.7,0.0,0.0,2382,null\n "03:00","2026-06-19T03:00:00+02:00",dry,partly-cloudy-night,19.2,0.0,1,null,49,null,16.7,1018.2,37900,113,9.3,null,16.7,0.0,0.0,2382,null\n "04:00","2026-06-19T04:00:00+02:00",dry,partly-cloudy-night,18.9,0.0,1,null,53,null,16.8,1018.1,36200,113,9.3,null,16.7,0.0,0.0,2382,null\n "05:00","2026-06-19T05:00:00+02:00",dry,partly-cloudy-day,18.4,0.0,2,null,58,null,16.9,1018.1,34900,121,9.3,null,16.7,6.0,0.0,2382,null\n "06:00","2026-06-19T06:00:00+02:00",dry,partly-cloudy-day,18.7,0.0,1,null,64,null,17.2,1018.2,31700,127,7.4,null,14.8,15.0,0.022,2382,null\n "07:00","2026-06-19T07:00:00+02:00",dry,partly-cloudy-day,20.0,0.0,1,null,66,null,17.6,1018.3,29900,131,7.4,null,14.8,24.0,0.097,2382,null\n "08:00","2026-06-19T08:00:00+02:00",dry,partly-cloudy-day,21.9,0.0,2,3,68,null,17.8,1018.3,31100,137,7.4,null,13.0,30.0,0.192,2382,null\n "09:00","2026-06-19T09:00:00+02:00",dry,partly-cloudy-day,23.9,0.0,2,null,69,null,17.7,1018.4,30400,145,7.4,null,14.8,36.0,0.311,2382,null\n "10:00","2026-06-19T10:00:00+02:00",dry,partly-cloudy-day,25.7,0.0,3,null,69,null,17.6,1018.3,31600,151,7.4,null,16.7,40.0,0.442,2382,null\n "11:00","2026-06-19T11:00:00+02:00",dry,partly-cloudy-day,28.2,0.0,2,null,68,null,17.3,1018.2,32900,141,7.4,null,18.5,42.0,0.533,2382,null\n "12:00","2026-06-19T12:00:00+02:00",dry,partly-cloudy-day,29.2,0.0,4,null,70,null,17.4,1018.2,32500,155,9.3,null,20.4,40.0,0.608,2382,null\n "13:00","2026-06-19T13:00:00+02:00",dry,partly-cloudy-day,30.2,0.0,3,null,67,null,17.2,1017.9,31600,156,9.3,null,24.1,41.0,0.65,2382,null\n "14:00","2026-06-19T14:00:00+02:00",dry,partly-cloudy-day,31.2,0.0,2,3,65,null,16.9,1017.5,29900,151,9.3,null,24.1,43.0,0.683,2382,null\n "15:00","2026-06-19T15:00:00+02:00",dry,partly-cloudy-day,31.6,0.0,2,null,66,null,17.0,1017.3,29200,153,9.3,null,25.9,44.0,0.658,2382,null\n "16:00","2026-06-19T16:00:00+02:00",dry,partly-cloudy-day,31.8,0.0,3,null,66,null,16.9,1016.8,28800,157,9.3,null,24.1,47.0,0.603,2382,null\n "17:00","2026-06-19T17:00:00+02:00",dry,partly-cloudy-day,32.0,0.0,2,null,64,null,17.1,1016.4,29200,140,9.3,null,24.1,49.0,0.536,2382,null\n "18:00","2026-06-19T18:00:00+02:00",dry,partly-cloudy-day,31.7,0.0,2,null,61,null,17.1,1016.2,31800,142,9.3,null,22.2,47.0,0.431,2382,null\n "19:00","2026-06-19T19:00:00+02:00",dry,partly-cloudy-day,30.8,0.0,2,null,62,null,17.1,1016.2,30700,122,9.3,null,22.2,41.0,0.294,2382,null\n "20:00","2026-06-19T20:00:00+02:00",dry,partly-cloudy-day,29.4,0.0,2,7,57,null,17.3,1016.1,31700,112,9.3,null,22.2,34.0,0.167,2382,null\n "21:00","2026-06-19T21:00:00+02:00",dry,partly-cloudy-day,27.6,0.0,1,null,54,null,17.3,1016.4,31900,108,9.3,null,22.2,23.0,0.058,2382,null\n "22:00","2026-06-19T22:00:00+02:00",dry,partly-cloudy-night,26.1,0.0,4,null,49,null,17.8,1016.9,31700,114,11.1,null,22.2,12.0,0.008,2382,null\n "23:00","2026-06-19T23:00:00+02:00",dry,partly-cloudy-night,24.6,0.0,1,null,40,null,17.9,1017.1,32400,115,11.1,null,22.2,0.0,0.0,2382,null\nsource_provider: Deutscher Wetterdienst (DWD)\nsource_station_name: BERLIN-ALEX.\nsource_dwd_station_id: "00399"\nsource_wmo_station_id: "10389"\nsource_observation_type: forecast\nsource_distance_m: 805.0\nsource_lat: 52.52\nsource_lon: 13.42\nsource_height_m: 37.0\nsource_first_record: "2026-06-18T08:00:00+02:00"\nsource_last_record: "2026-06-28T12:00:00+02:00"\ndata_quality_hourly_rows: 24\ndata_quality_raw_source_count: 7\nembed_ref: berlin-weather-2026-Pa5\napp_id: weather\nskill_id: forecast',
|
|
23515
|
+
"parent_embed_id": "324eba33-3622-409c-b401-183f95d6183e",
|
|
23516
|
+
"embed_ids": null
|
|
23517
|
+
}
|
|
23518
|
+
],
|
|
23519
|
+
metadata: {
|
|
23520
|
+
featured: true,
|
|
23521
|
+
order: 104,
|
|
23522
|
+
app_skill_examples: ["weather.forecast"]
|
|
23523
|
+
}
|
|
23524
|
+
};
|
|
23525
|
+
|
|
22895
23526
|
// ../ui/src/demo_chats/exampleChatData.ts
|
|
22896
23527
|
var ALL_EXAMPLE_CHATS = [
|
|
22897
23528
|
giganticAirplanesChat,
|
|
@@ -22977,7 +23608,9 @@ var ALL_EXAMPLE_CHATS = [
|
|
|
22977
23608
|
berlinCentralStationMapLocationChat,
|
|
22978
23609
|
launchReadinessChecklistDocChat,
|
|
22979
23610
|
rostockHeavyRainRadarChat,
|
|
22980
|
-
classicCarReverseImageSearchChat
|
|
23611
|
+
classicCarReverseImageSearchChat,
|
|
23612
|
+
usbC3v3LdoPcbSchematicChat,
|
|
23613
|
+
berlinMorningBikeForecastChat
|
|
22981
23614
|
].sort((a, b) => a.metadata.order - b.metadata.order);
|
|
22982
23615
|
|
|
22983
23616
|
// ../ui/src/i18n/locales/en.json
|
|
@@ -24607,6 +25240,30 @@ Only output the final Markdown table. Do NOT include explanations, notes, or any
|
|
|
24607
25240
|
ai_generated: {
|
|
24608
25241
|
text: "AI generated"
|
|
24609
25242
|
},
|
|
25243
|
+
authentic: {
|
|
25244
|
+
text: "Authentic"
|
|
25245
|
+
},
|
|
25246
|
+
ai_generated_chance: {
|
|
25247
|
+
text: "{percentage} chance it's AI generated"
|
|
25248
|
+
},
|
|
25249
|
+
via_provider_prefix: {
|
|
25250
|
+
text: "via"
|
|
25251
|
+
},
|
|
25252
|
+
via_provider: {
|
|
25253
|
+
text: "via {provider}"
|
|
25254
|
+
},
|
|
25255
|
+
detection_limitations: {
|
|
25256
|
+
text: "Pixel-based detection is useful for photos and art, including many compressed or re-shared images. It is not definitive; heavy degradation, UI-heavy screenshots, screen or print recaptures, and new generators may need human judgment."
|
|
25257
|
+
},
|
|
25258
|
+
detection_failed_title: {
|
|
25259
|
+
text: "AI detection unavailable"
|
|
25260
|
+
},
|
|
25261
|
+
detection_failed_retry: {
|
|
25262
|
+
text: "AI detection failed temporarily. Try again later."
|
|
25263
|
+
},
|
|
25264
|
+
detection_failed_description: {
|
|
25265
|
+
text: "The image upload succeeded, but Sightengine did not return a usable AI-detection score for this attempt."
|
|
25266
|
+
},
|
|
24610
25267
|
signup_to_upload: {
|
|
24611
25268
|
text: "Signup to upload\u2026"
|
|
24612
25269
|
}
|
|
@@ -25398,6 +26055,12 @@ Only output the final Markdown table. Do NOT include explanations, notes, or any
|
|
|
25398
26055
|
text: "Get coding help, docs & project advice."
|
|
25399
26056
|
}
|
|
25400
26057
|
},
|
|
26058
|
+
diagrams: {
|
|
26059
|
+
text: "Diagrams",
|
|
26060
|
+
description: {
|
|
26061
|
+
text: "Create flowcharts, sequences, timelines & more."
|
|
26062
|
+
}
|
|
26063
|
+
},
|
|
25401
26064
|
docs: {
|
|
25402
26065
|
text: "Docs",
|
|
25403
26066
|
description: {
|
|
@@ -25582,9 +26245,40 @@ Only output the final Markdown table. Do NOT include explanations, notes, or any
|
|
|
25582
26245
|
}
|
|
25583
26246
|
},
|
|
25584
26247
|
chat: {
|
|
26248
|
+
generated_by_cost: {
|
|
26249
|
+
text: "Cost: {credits} credits"
|
|
26250
|
+
},
|
|
25585
26251
|
new_chat: {
|
|
25586
26252
|
text: "New chat"
|
|
25587
26253
|
},
|
|
26254
|
+
request_feature: {
|
|
26255
|
+
text: "Request feature"
|
|
26256
|
+
},
|
|
26257
|
+
request_feature_prefill: {
|
|
26258
|
+
text: "Requested feature:"
|
|
26259
|
+
},
|
|
26260
|
+
assistant_feedback: {
|
|
26261
|
+
rate_label: {
|
|
26262
|
+
text: "Rate assistant response:"
|
|
26263
|
+
},
|
|
26264
|
+
star_label: {
|
|
26265
|
+
text: "Rate {count} out of 5"
|
|
26266
|
+
},
|
|
26267
|
+
submit: {
|
|
26268
|
+
text: "Submit"
|
|
26269
|
+
},
|
|
26270
|
+
thanks: {
|
|
26271
|
+
text: "Thanks for the feedback!"
|
|
26272
|
+
},
|
|
26273
|
+
report_title: {
|
|
26274
|
+
text: "Assistant response quality bad:"
|
|
26275
|
+
}
|
|
26276
|
+
},
|
|
26277
|
+
anonymous_free_usage: {
|
|
26278
|
+
feature_notice: {
|
|
26279
|
+
text: "You are using free anonymous credits. File uploads, memories, chat sync, and some other features require creating an account."
|
|
26280
|
+
}
|
|
26281
|
+
},
|
|
25588
26282
|
interactive_question_failed: {
|
|
25589
26283
|
text: "Failed to display question."
|
|
25590
26284
|
},
|
|
@@ -25651,8 +26345,38 @@ Only output the final Markdown table. Do NOT include explanations, notes, or any
|
|
|
25651
26345
|
approve: {
|
|
25652
26346
|
text: "Approve once"
|
|
25653
26347
|
},
|
|
26348
|
+
approve_selected: {
|
|
26349
|
+
text: "Approve selected"
|
|
26350
|
+
},
|
|
25654
26351
|
loading: {
|
|
25655
26352
|
text: "Approving..."
|
|
26353
|
+
},
|
|
26354
|
+
account: {
|
|
26355
|
+
text: "Account"
|
|
26356
|
+
},
|
|
26357
|
+
request_title: {
|
|
26358
|
+
text: "Requested {action} action"
|
|
26359
|
+
},
|
|
26360
|
+
calendar: {
|
|
26361
|
+
text: "Calendar"
|
|
26362
|
+
},
|
|
26363
|
+
event_title: {
|
|
26364
|
+
text: "Event"
|
|
26365
|
+
},
|
|
26366
|
+
event_id: {
|
|
26367
|
+
text: "Event ID"
|
|
26368
|
+
},
|
|
26369
|
+
start: {
|
|
26370
|
+
text: "Start"
|
|
26371
|
+
},
|
|
26372
|
+
end: {
|
|
26373
|
+
text: "End"
|
|
26374
|
+
},
|
|
26375
|
+
from: {
|
|
26376
|
+
text: "From"
|
|
26377
|
+
},
|
|
26378
|
+
to: {
|
|
26379
|
+
text: "To"
|
|
25656
26380
|
}
|
|
25657
26381
|
},
|
|
25658
26382
|
connected_account_receipts: {
|
|
@@ -25668,6 +26392,9 @@ Only output the final Markdown table. Do NOT include explanations, notes, or any
|
|
|
25668
26392
|
cancelled: {
|
|
25669
26393
|
text: "{action} cancelled."
|
|
25670
26394
|
},
|
|
26395
|
+
pending_cancel: {
|
|
26396
|
+
text: "{action} auto-approved. Press to cancel before it starts."
|
|
26397
|
+
},
|
|
25671
26398
|
failed: {
|
|
25672
26399
|
text: "{action} could not be completed."
|
|
25673
26400
|
},
|
|
@@ -25683,6 +26410,18 @@ Only output the final Markdown table. Do NOT include explanations, notes, or any
|
|
|
25683
26410
|
undo_failed: {
|
|
25684
26411
|
text: "Could not undo this action."
|
|
25685
26412
|
},
|
|
26413
|
+
press_to_cancel: {
|
|
26414
|
+
text: "Press to cancel"
|
|
26415
|
+
},
|
|
26416
|
+
cancelling: {
|
|
26417
|
+
text: "Cancelling..."
|
|
26418
|
+
},
|
|
26419
|
+
cancel_started: {
|
|
26420
|
+
text: "Action cancelled"
|
|
26421
|
+
},
|
|
26422
|
+
cancel_failed: {
|
|
26423
|
+
text: "Could not cancel this action."
|
|
26424
|
+
},
|
|
25686
26425
|
undo_available: {
|
|
25687
26426
|
text: "Undo is available."
|
|
25688
26427
|
},
|
|
@@ -28989,6 +29728,50 @@ Only output the final Markdown table. Do NOT include explanations, notes, or any
|
|
|
28989
29728
|
},
|
|
28990
29729
|
electrical: {
|
|
28991
29730
|
text: "Electrical"
|
|
29731
|
+
},
|
|
29732
|
+
pcb_schematic: {
|
|
29733
|
+
title: {
|
|
29734
|
+
text: "PCB schematic"
|
|
29735
|
+
},
|
|
29736
|
+
prepare_files: {
|
|
29737
|
+
text: "Prepare PCB files"
|
|
29738
|
+
},
|
|
29739
|
+
preparing: {
|
|
29740
|
+
text: "Preparing PCB files"
|
|
29741
|
+
},
|
|
29742
|
+
prepared: {
|
|
29743
|
+
text: "PCB files prepared"
|
|
29744
|
+
},
|
|
29745
|
+
prepare_failed: {
|
|
29746
|
+
text: "PCB file preparation failed"
|
|
29747
|
+
},
|
|
29748
|
+
show_logs: {
|
|
29749
|
+
text: "Show logs"
|
|
29750
|
+
},
|
|
29751
|
+
hide_logs: {
|
|
29752
|
+
text: "Hide logs"
|
|
29753
|
+
},
|
|
29754
|
+
logs: {
|
|
29755
|
+
text: "Compile logs"
|
|
29756
|
+
},
|
|
29757
|
+
no_logs: {
|
|
29758
|
+
text: "No logs available."
|
|
29759
|
+
},
|
|
29760
|
+
artifacts: {
|
|
29761
|
+
text: "Prepared files"
|
|
29762
|
+
},
|
|
29763
|
+
safety_note: {
|
|
29764
|
+
text: "Atopile source is compiled in a sandbox before KiCad and project files are prepared."
|
|
29765
|
+
},
|
|
29766
|
+
copied: {
|
|
29767
|
+
text: "PCB schematic copied"
|
|
29768
|
+
},
|
|
29769
|
+
copy_failed: {
|
|
29770
|
+
text: "Could not copy PCB schematic"
|
|
29771
|
+
},
|
|
29772
|
+
download_failed: {
|
|
29773
|
+
text: "Could not download PCB schematic"
|
|
29774
|
+
}
|
|
28992
29775
|
}
|
|
28993
29776
|
},
|
|
28994
29777
|
math: {
|
|
@@ -28999,6 +29782,11 @@ Only output the final Markdown table. Do NOT include explanations, notes, or any
|
|
|
28999
29782
|
text: "Plot"
|
|
29000
29783
|
}
|
|
29001
29784
|
},
|
|
29785
|
+
diagrams: {
|
|
29786
|
+
mermaid: {
|
|
29787
|
+
text: "Mermaid Diagram"
|
|
29788
|
+
}
|
|
29789
|
+
},
|
|
29002
29790
|
mail: {
|
|
29003
29791
|
email: {
|
|
29004
29792
|
text: "Email Draft"
|
|
@@ -29178,6 +29966,15 @@ Only output the final Markdown table. Do NOT include explanations, notes, or any
|
|
|
29178
29966
|
},
|
|
29179
29967
|
drop_files: {
|
|
29180
29968
|
text: "Drop files to upload"
|
|
29969
|
+
},
|
|
29970
|
+
signup_required_title: {
|
|
29971
|
+
text: "Create an account to upload files"
|
|
29972
|
+
},
|
|
29973
|
+
signup_required_body: {
|
|
29974
|
+
text: "Your message draft will be kept, but files are only uploaded after signup. Remove the file to keep chatting without an account."
|
|
29975
|
+
},
|
|
29976
|
+
remove_pending_file: {
|
|
29977
|
+
text: "Remove file"
|
|
29181
29978
|
}
|
|
29182
29979
|
},
|
|
29183
29980
|
file_size_limits: {
|
|
@@ -29249,6 +30046,9 @@ Only output the final Markdown table. Do NOT include explanations, notes, or any
|
|
|
29249
30046
|
click_to_add_text: {
|
|
29250
30047
|
text: "Click here to add text"
|
|
29251
30048
|
},
|
|
30049
|
+
anonymous_terms_reminder: {
|
|
30050
|
+
text: "By sending a message you accept the terms & privacy policy of OpenMates."
|
|
30051
|
+
},
|
|
29252
30052
|
send: {
|
|
29253
30053
|
text: "Send"
|
|
29254
30054
|
},
|
|
@@ -29620,6 +30420,38 @@ Only output the final Markdown table. Do NOT include explanations, notes, or any
|
|
|
29620
30420
|
text: '```json\n{"type": "app_skill_use", "embed_id": "7fbf38fc-1126-46b3-bdf4-60bfb1e3197f", "app_id": "health", "skill_id": "search_appointments"}\n```\n\nI found several excellent options for an English-speaking [dermatology](wiki:Dermatology) routine skin check (Hautkrebsvorsorge) in [Berlin Mitte](wiki:Mitte_(locality)) with immediate availability.\n\nBased on your request for English-speaking doctors in Mitte, here are the best options for a routine skin cancer screening:\n\n### **Top Recommendations in Berlin Mitte**\n\n```json\n{"type": "sheet", "embed_id": "08e6061c-e224-46d4-8ac7-7318d0279b57"}\n```\n\n\n### **Additional Options (Nearby)**\nIf those do not suit your schedule, there are also appointments available slightly outside of Mitte:\n* **HLCP Haut- und Lasercentrum**: Located at Kurf\xFCrstendamm 96 (Charlottenburg). They speak several languages including English and have slots starting tomorrow, June 9th.\n* **Meike Schr\xF6der**: Located at Clayallee 343 (Zehlendorf). They have slots available on June 9th and June 11th.\n\n**Important Note on Insurance:**\nMost of the immediate appointments found are in private practices or for "self-paying" patients. If you have public health insurance (GKV), please confirm during booking if the screening is covered or if it is a private service (IGeL).\n\nWould you like me to help you [set a reminder](/#settings/apps/reminder) to follow up on this appointment, or should I search for more options further into next week?\n---\n*\u26A0\uFE0F This is educational information only and not medical advice. Consult your doctor for medical decisions.*'
|
|
29621
30421
|
}
|
|
29622
30422
|
},
|
|
30423
|
+
berlin_morning_bike_forecast: {
|
|
30424
|
+
title: {
|
|
30425
|
+
text: "Berlin Morning Bike Forecast"
|
|
30426
|
+
},
|
|
30427
|
+
summary: {
|
|
30428
|
+
text: "A concise Berlin weather forecast that helps decide whether to bike to Tempelhofer Feld."
|
|
30429
|
+
},
|
|
30430
|
+
message_1: {
|
|
30431
|
+
text: "@skill:weather:forecast What's the weather forecast for Berlin tomorrow morning, and should I bike to Tempelhofer Feld? Keep the answer concise."
|
|
30432
|
+
},
|
|
30433
|
+
message_2: {
|
|
30434
|
+
text: '```json\n{"type": "app_skill_use", "embed_id": "324eba33-3622-409c-b401-183f95d6183e", "app_id": "weather", "skill_id": "forecast", "query": "Berlin weather forecast", "provider": "Deutscher Wetterdienst (DWD) + Open-Meteo", "location": "Berlin"}\n```\n\nTomorrow morning in [Berlin](wiki:Berlin) (Friday, June 19, 2026), the weather will be dry and partly cloudy, with temperatures starting around 18\u201320\xB0C and rising quickly toward a high of 32\xB0C.\n\n**Should you bike to [Tempelhofer Feld](wiki:Tempelhofer_Feld)?**\nYes, it\'s a great idea! Conditions are ideal for biking:\n* **Dry:** No rain expected (4% probability).\n* **Calm:** Wind speeds are light (approx. 11 km/h).\n* **Morning Window:** Biking in the morning is highly recommended to avoid the afternoon heat peak of 32\xB0C, especially since the airfield offers very little shade.\n\n**What\'s your plan for the rest of the day?**\n* Would you like to find a [beer garden](wiki:Beer_garden) or cafe near Tempelhof to cool off afterward?\n* Do you need a more detailed [hourly forecast](/#settings/apps/weather) to time your ride perfectly?'
|
|
30435
|
+
},
|
|
30436
|
+
follow_up_1: {
|
|
30437
|
+
text: "Search for bike-friendly cafes near Tempelhofer Feld"
|
|
30438
|
+
},
|
|
30439
|
+
follow_up_2: {
|
|
30440
|
+
text: "What are some must-see spots at Tempelhofer Feld?"
|
|
30441
|
+
},
|
|
30442
|
+
follow_up_3: {
|
|
30443
|
+
text: "Find the hourly weather forecast for Berlin tomorrow"
|
|
30444
|
+
},
|
|
30445
|
+
follow_up_4: {
|
|
30446
|
+
text: "How should I prepare for biking in Berlin's summer heat?"
|
|
30447
|
+
},
|
|
30448
|
+
follow_up_5: {
|
|
30449
|
+
text: "Show me the best cycling routes to Tempelhofer Feld"
|
|
30450
|
+
},
|
|
30451
|
+
follow_up_6: {
|
|
30452
|
+
text: "Are there any events happening at Tempelhofer Feld tomorrow?"
|
|
30453
|
+
}
|
|
30454
|
+
},
|
|
29623
30455
|
berlin_weather_bike_commute: {
|
|
29624
30456
|
title: {
|
|
29625
30457
|
text: "Berlin Weather and Bike Commute Advice"
|
|
@@ -31021,6 +31853,38 @@ As of mid-2026, the severe supply shocks from the 2024\u20132025 avian flu have
|
|
|
31021
31853
|
sub_chat_3_message_1: {
|
|
31022
31854
|
text: "Research market concentration in the US egg producer industry (e.g., Cal-Maine Foods' profit margins, price-fixing lawsuits) and the role of retailer/grocery store markup margins in keeping egg prices high post-avian flu. Distinguish confirmed data from inferred conclusions."
|
|
31023
31855
|
}
|
|
31856
|
+
},
|
|
31857
|
+
usb_c_3v3_ldo_pcb_schematic: {
|
|
31858
|
+
title: {
|
|
31859
|
+
text: "USB-C 3.3V LDO PCB Schematic"
|
|
31860
|
+
},
|
|
31861
|
+
summary: {
|
|
31862
|
+
text: "Design and prepare a compilable Atopile PCB schematic for a USB-C powered 3.3V regulator circuit."
|
|
31863
|
+
},
|
|
31864
|
+
message_1: {
|
|
31865
|
+
text: "Create a simple compilable Atopile 0.15.7 PCB schematic for a 5V USB-C input feeding a 3.3V LDO rail with a power indicator diode and input/output decoupling capacitors."
|
|
31866
|
+
},
|
|
31867
|
+
message_2: {
|
|
31868
|
+
text: 'Here is a compact Atopile 0.15.7 starting point for a USB-C powered 3.3V regulator stage. It keeps the schematic self-contained, uses `module App` as the build entry, models the USB-C input and LDO as simple power-interface modules, and adds 10uF/1uF decoupling plus a diode indicator on the regulated rail.\n\n```json\n{"type": "pcb_schematic", "embed_id": "46d63593-516e-49b5-8e7d-600c4fe18836"}\n```\n\nThe source is ready for the PCB preparation flow: OpenMates can send it to the sandboxed Atopile compiler, show the build status, and expose the generated source bundle when the build succeeds.'
|
|
31869
|
+
},
|
|
31870
|
+
follow_up_1: {
|
|
31871
|
+
text: "Add voltage constraints to the 5V and 3.3V rails"
|
|
31872
|
+
},
|
|
31873
|
+
follow_up_2: {
|
|
31874
|
+
text: "Choose manufacturer parts for the LDO and diode"
|
|
31875
|
+
},
|
|
31876
|
+
follow_up_3: {
|
|
31877
|
+
text: "Add USB-C connector details and CC resistors"
|
|
31878
|
+
},
|
|
31879
|
+
follow_up_4: {
|
|
31880
|
+
text: "Add an enable pin to the regulator"
|
|
31881
|
+
},
|
|
31882
|
+
follow_up_5: {
|
|
31883
|
+
text: "Prepare PCB files from this schematic"
|
|
31884
|
+
},
|
|
31885
|
+
follow_up_6: {
|
|
31886
|
+
text: "Explain the decoupling capacitor choices"
|
|
31887
|
+
}
|
|
31024
31888
|
}
|
|
31025
31889
|
},
|
|
31026
31890
|
focus_modes: {
|
|
@@ -32176,1014 +33040,1014 @@ As of mid-2026, the severe supply shocks from the 2024\u20132025 avian flu have
|
|
|
32176
33040
|
text: "Groq Privacy Policy"
|
|
32177
33041
|
}
|
|
32178
33042
|
},
|
|
32179
|
-
|
|
32180
|
-
|
|
32181
|
-
|
|
32182
|
-
|
|
32183
|
-
|
|
32184
|
-
|
|
32185
|
-
|
|
32186
|
-
|
|
32187
|
-
|
|
32188
|
-
|
|
32189
|
-
|
|
32190
|
-
|
|
32191
|
-
|
|
32192
|
-
|
|
32193
|
-
|
|
32194
|
-
|
|
32195
|
-
|
|
33043
|
+
providers: {
|
|
33044
|
+
payments: {
|
|
33045
|
+
revolut_business: {
|
|
33046
|
+
heading: {
|
|
33047
|
+
text: "3.15 Revolut Business (SEPA Bank Transfer)"
|
|
33048
|
+
},
|
|
33049
|
+
description: {
|
|
33050
|
+
text: "We use Revolut Bank UAB (Lithuania, EU) as our receiving bank for SEPA bank transfers. When you choose to pay via bank transfer, we display our Revolut IBAN to you. Revolut notifies us via a cryptographically signed webhook when a transfer arrives. We do not send your personal data to Revolut \u2014 only the transaction reference, amount, and Revolut's own transaction ID are processed."
|
|
33051
|
+
},
|
|
33052
|
+
privacy_policy_link: {
|
|
33053
|
+
text: "Revolut Privacy Policy"
|
|
33054
|
+
}
|
|
33055
|
+
},
|
|
33056
|
+
heading: {
|
|
33057
|
+
text: "Payment providers (only when you purchase credits)"
|
|
33058
|
+
},
|
|
33059
|
+
description: {
|
|
33060
|
+
text: "These providers are only used when you purchase credits, subscribe to monthly auto top-up, or receive a refund. If you never make a purchase, none of your data is shared with them."
|
|
33061
|
+
},
|
|
33062
|
+
stripe: {
|
|
33063
|
+
heading: {
|
|
33064
|
+
text: "Stripe (primary payment processor)"
|
|
33065
|
+
},
|
|
33066
|
+
description: {
|
|
33067
|
+
text: "Stripe processes one-time credit purchases and monthly auto top-up subscriptions. We send Stripe your email address, a tokenized payment method (we never see your card number), billing address and VAT information when required for invoicing, and transaction metadata."
|
|
33068
|
+
}
|
|
33069
|
+
}
|
|
32196
33070
|
},
|
|
32197
|
-
privacy_policy_link: {
|
|
32198
|
-
text: "Flightradar24 Privacy Policy"
|
|
32199
|
-
}
|
|
32200
|
-
},
|
|
32201
|
-
security_measures: {
|
|
32202
33071
|
heading: {
|
|
32203
|
-
text: "
|
|
33072
|
+
text: "When each provider is used"
|
|
32204
33073
|
},
|
|
32205
33074
|
intro: {
|
|
32206
|
-
text: "
|
|
33075
|
+
text: "Most of the providers below are only reached when you use a specific feature \u2014 for example, image generation providers are only used when you invoke an image skill. The groups below tell you exactly which providers are involved for each feature, so you can opt in or out of data sharing by choosing which features to use."
|
|
32207
33076
|
},
|
|
32208
|
-
|
|
32209
|
-
|
|
32210
|
-
text: "
|
|
33077
|
+
always_active: {
|
|
33078
|
+
heading: {
|
|
33079
|
+
text: "Always active (used for every user)"
|
|
32211
33080
|
},
|
|
32212
|
-
|
|
32213
|
-
text: "
|
|
33081
|
+
description: {
|
|
33082
|
+
text: "These providers are used for every user, regardless of which features you use. Using OpenMates at all means your data passes through them."
|
|
32214
33083
|
},
|
|
32215
|
-
|
|
32216
|
-
|
|
33084
|
+
vercel: {
|
|
33085
|
+
heading: {
|
|
33086
|
+
text: "Vercel (frontend hosting)"
|
|
33087
|
+
},
|
|
33088
|
+
description: {
|
|
33089
|
+
text: "We use Vercel to host the static frontend of our website and web app. Vercel receives page requests and serves static assets."
|
|
33090
|
+
}
|
|
32217
33091
|
},
|
|
32218
|
-
|
|
32219
|
-
|
|
33092
|
+
hetzner: {
|
|
33093
|
+
heading: {
|
|
33094
|
+
text: "Hetzner (backend, database, storage, observability)"
|
|
33095
|
+
},
|
|
33096
|
+
description: {
|
|
33097
|
+
text: "We host all of our backend services \u2014 API servers, Directus CMS, Postgres database, Redis caches, S3-compatible object storage, and our self-hosted OpenObserve observability platform \u2014 on Hetzner infrastructure in the EU. OpenObserve runs on our own Hetzner servers and is not a third-party service; traces never leave the EU."
|
|
33098
|
+
}
|
|
33099
|
+
},
|
|
33100
|
+
brevo: {
|
|
33101
|
+
heading: {
|
|
33102
|
+
text: "Brevo (transactional email)"
|
|
33103
|
+
},
|
|
33104
|
+
description: {
|
|
33105
|
+
text: "We use Brevo to send transactional email \u2014 account verification, password reset links, payment receipts, and credit notes \u2014 and, if you opt in, the OpenMates newsletter. If you are an existing OpenMates customer (i.e. you have completed a purchase), we may also send you occasional product announcements and service updates about OpenMates' own similar services under the existing-customer exception (\xA77 Abs. 3 UWG / ePrivacy Directive Art. 13(2)); you can object in Settings \u2192 Newsletter or via the one-click unsubscribe link in any such email. Brevo sees recipient email addresses and the content of the emails we send you."
|
|
33106
|
+
}
|
|
33107
|
+
},
|
|
33108
|
+
ip_api: {
|
|
33109
|
+
heading: {
|
|
33110
|
+
text: "IP-API (IP geolocation)"
|
|
33111
|
+
},
|
|
33112
|
+
description: {
|
|
33113
|
+
text: "We use IP-API to resolve IP addresses to approximate geolocation for abuse prevention, rate limiting, and regional compliance. The resolved country and city are used transiently and are not stored against your account."
|
|
33114
|
+
}
|
|
33115
|
+
},
|
|
33116
|
+
sightengine: {
|
|
33117
|
+
heading: {
|
|
33118
|
+
text: "Sightengine (image and video moderation)"
|
|
33119
|
+
},
|
|
33120
|
+
description: {
|
|
33121
|
+
text: "Every image and video you upload is scanned by Sightengine for safety moderation before it is stored. Sightengine sees the image or video content."
|
|
33122
|
+
}
|
|
33123
|
+
},
|
|
33124
|
+
api_video: {
|
|
33125
|
+
heading: {
|
|
33126
|
+
text: "api.video (product video hosting)"
|
|
33127
|
+
},
|
|
33128
|
+
description: {
|
|
33129
|
+
text: "We use api.video (EU company, France) to host and stream product demo and feature walkthrough videos shown on our website and web app. When you watch a video, your IP address and basic viewing data are processed by api.video. No user account identifiers are shared."
|
|
33130
|
+
}
|
|
32220
33131
|
}
|
|
32221
|
-
}
|
|
32222
|
-
},
|
|
32223
|
-
data_categories: {
|
|
32224
|
-
heading: {
|
|
32225
|
-
text: "5. Data Categories We Collect and Process"
|
|
32226
|
-
},
|
|
32227
|
-
intro: {
|
|
32228
|
-
text: "We collect and process the following categories of data to operate and secure our services:"
|
|
32229
|
-
},
|
|
32230
|
-
account: {
|
|
32231
|
-
text: "Account: Email address, username/display name, profile image, locale, and security settings (e.g., 2FA enabled). Email and username are encrypted with your key before storage. We also keep a separate server-side Vault-encrypted copy of your verified email address for mandatory account lifecycle notices, such as account verification, security alerts, and deletion reminders. Passwords are stored as salted hashes."
|
|
32232
|
-
},
|
|
32233
|
-
usage: {
|
|
32234
|
-
text: "Usage: Server logs, event timestamps, feature usage, error logs, and device recognition identifiers (hashed). IP addresses may be temporarily processed for security and rate limiting."
|
|
32235
|
-
},
|
|
32236
|
-
content: {
|
|
32237
|
-
text: "Content: Chat messages, prompts, attachments, and uploaded images/videos necessary to deliver the service (subject to moderation where applicable)."
|
|
32238
|
-
},
|
|
32239
|
-
payments: {
|
|
32240
|
-
text: "Payments: Payment method tokens, transaction IDs, billing address and VAT information as required for invoicing (processed primarily by Stripe). We do not store full card numbers."
|
|
32241
|
-
},
|
|
32242
|
-
newsletter: {
|
|
32243
|
-
text: "Newsletter: If you choose to subscribe to our newsletter, we store your email address in encrypted form. Newsletter subscription is optional and separate from account creation \u2014 creating an account does not automatically subscribe you to the newsletter. If you are an OpenMates customer (i.e. you have completed a purchase), we may additionally send you occasional product announcements and service updates about OpenMates' own similar services under the existing-customer exception (\xA77 Abs. 3 UWG / ePrivacy Directive Art. 13(2)). You can object to this at any time in Settings \u2192 Newsletter or via the one-click unsubscribe link in any such email, without affecting your account."
|
|
32244
|
-
},
|
|
32245
|
-
stability_logs: {
|
|
32246
|
-
text: "Stability Logs: To diagnose technical errors, anonymized browser console logs (error messages, component status, connection events) are collected from authenticated users and stored for up to 48 hours. If an error occurs, surrounding context is retained for up to 14 days. No message content, chat titles, names, email addresses, or other personally identifiable information is included. A random session identifier (generated per browser tab, not linked to your account) is used for log correlation. You can disable this at any time in Settings > Privacy > Stability Logs."
|
|
32247
|
-
}
|
|
32248
|
-
},
|
|
32249
|
-
data_retention: {
|
|
32250
|
-
heading: {
|
|
32251
|
-
text: "6. Data Retention"
|
|
32252
|
-
},
|
|
32253
|
-
account: {
|
|
32254
|
-
text: "Account data: Retained until account deletion and completion of related legal obligations."
|
|
32255
|
-
},
|
|
32256
|
-
usage_and_logs: {
|
|
32257
|
-
text: "Usage and logs: Operational logs retained up to 12 months; IPs tied to failed logins up to 30 days for security."
|
|
32258
|
-
},
|
|
32259
|
-
device_fingerprints: {
|
|
32260
|
-
text: "Device fingerprints: Retained until account deletion or manual removal from account settings (when available)."
|
|
32261
|
-
},
|
|
32262
|
-
content: {
|
|
32263
|
-
text: "Content: Retained until the user deletes the content or closes the account, subject to backup cycles."
|
|
32264
|
-
},
|
|
32265
|
-
payments_and_invoices: {
|
|
32266
|
-
text: "Payments and invoices: Retained up to 10 years where required by tax/commercial law."
|
|
32267
|
-
},
|
|
32268
|
-
compliance_logs: {
|
|
32269
|
-
text: "**Compliance logs:** audit logs retained 2 years (BSI \xA734 BDSG); financial compliance logs retained 10 years (AO \xA7147 and HGB \xA7257)"
|
|
32270
|
-
},
|
|
32271
|
-
observability_traces: {
|
|
32272
|
-
text: "**Observability traces:** up to 30 days; user IDs in traces use a hash that rotates every 24 hours, so older traces cannot be joined to a specific user"
|
|
32273
|
-
},
|
|
32274
|
-
user_data_backups: {
|
|
32275
|
-
text: "**User data backups:** 60 days via S3 lifecycle; backups contain only ciphertext encrypted with your own key"
|
|
32276
|
-
}
|
|
32277
|
-
},
|
|
32278
|
-
legal_basis: {
|
|
32279
|
-
heading: {
|
|
32280
|
-
text: "7. Legal Basis for Processing (GDPR)"
|
|
32281
|
-
},
|
|
32282
|
-
contract: {
|
|
32283
|
-
text: "Contract: Processing necessary to perform our contract with you or to take steps at your request prior to entering into a contract (e.g., account creation, delivering web app features)."
|
|
32284
|
-
},
|
|
32285
|
-
consent: {
|
|
32286
|
-
text: "Consent: Processing based on your consent, where applicable (e.g., optional communications, certain analytics or marketing where used). You can withdraw consent at any time."
|
|
32287
|
-
},
|
|
32288
|
-
legitimate_interests: {
|
|
32289
|
-
text: "Legitimate interests: Processing necessary for our legitimate interests, such as service security, fraud prevention, abuse detection, and improving the service, provided these interests are not overridden by your rights and interests."
|
|
32290
33132
|
},
|
|
32291
|
-
|
|
32292
|
-
|
|
32293
|
-
|
|
32294
|
-
legal_obligation: {
|
|
32295
|
-
text: "Legal obligation: Processing necessary to comply with legal obligations (e.g., tax and accounting requirements for invoices and transaction records)."
|
|
32296
|
-
}
|
|
32297
|
-
},
|
|
32298
|
-
legal_rights: {
|
|
32299
|
-
heading: {
|
|
32300
|
-
text: "8. Your Privacy Rights"
|
|
32301
|
-
},
|
|
32302
|
-
intro: {
|
|
32303
|
-
text: "Depending on your jurisdiction, you may have certain rights over your personal data. The following summarizes key rights under GDPR and CCPA/CPRA and how to exercise them:"
|
|
32304
|
-
},
|
|
32305
|
-
gdpr: {
|
|
32306
|
-
subheading: {
|
|
32307
|
-
text: "GDPR (EU/EEA)"
|
|
33133
|
+
ai_models: {
|
|
33134
|
+
heading: {
|
|
33135
|
+
text: "AI model providers (only when you use a model that routes through them)"
|
|
32308
33136
|
},
|
|
32309
|
-
|
|
32310
|
-
text: "
|
|
33137
|
+
description: {
|
|
33138
|
+
text: 'OpenMates routes AI requests to different providers depending on which model you select. Before any message is sent to a provider in this group, your browser replaces real names, emails, and physical addresses with placeholders (see the "PII placeholder substitution" measure above). You can always see which provider a model uses in the model details panel in AI settings.'
|
|
32311
33139
|
},
|
|
32312
|
-
|
|
32313
|
-
|
|
33140
|
+
mistral: {
|
|
33141
|
+
heading: {
|
|
33142
|
+
text: "Mistral (EU direct)"
|
|
33143
|
+
},
|
|
33144
|
+
description: {
|
|
33145
|
+
text: "Mistral hosts its own family of models in the EU. Only used when you select a Mistral model. Mistral receives your chat messages with PII placeholders already substituted client-side."
|
|
33146
|
+
}
|
|
32314
33147
|
},
|
|
32315
|
-
|
|
32316
|
-
|
|
33148
|
+
aws_bedrock: {
|
|
33149
|
+
heading: {
|
|
33150
|
+
text: "AWS Bedrock (EU region, primary Claude path)"
|
|
33151
|
+
},
|
|
33152
|
+
description: {
|
|
33153
|
+
text: "AWS Bedrock hosts Anthropic Claude models in its Frankfurt EU region. This is the primary path for Claude requests. AWS receives your chat messages with PII placeholders already substituted client-side."
|
|
33154
|
+
}
|
|
32317
33155
|
},
|
|
32318
|
-
|
|
32319
|
-
|
|
33156
|
+
anthropic: {
|
|
33157
|
+
heading: {
|
|
33158
|
+
text: "Anthropic (US, fallback Claude path)"
|
|
33159
|
+
},
|
|
33160
|
+
description: {
|
|
33161
|
+
text: "Anthropic's direct API is used as a fallback path for Claude models when AWS Bedrock is unreachable. Anthropic is based in the US and receives your chat messages with PII placeholders already substituted client-side."
|
|
33162
|
+
}
|
|
32320
33163
|
},
|
|
32321
|
-
|
|
32322
|
-
|
|
33164
|
+
openai: {
|
|
33165
|
+
heading: {
|
|
33166
|
+
text: "OpenAI (US)"
|
|
33167
|
+
},
|
|
33168
|
+
description: {
|
|
33169
|
+
text: "OpenAI processes requests for GPT-family models when you select one. OpenAI receives your chat messages with PII placeholders already substituted client-side."
|
|
33170
|
+
}
|
|
32323
33171
|
},
|
|
32324
|
-
|
|
32325
|
-
|
|
33172
|
+
openrouter: {
|
|
33173
|
+
heading: {
|
|
33174
|
+
text: "OpenRouter (US, routing aggregator)"
|
|
33175
|
+
},
|
|
33176
|
+
description: {
|
|
33177
|
+
text: "OpenRouter is used as a routing aggregator for several third-party models, including Cerebras-hosted models. OpenRouter sees your chat messages with PII placeholders substituted client-side and forwards them to the underlying model host."
|
|
33178
|
+
}
|
|
32326
33179
|
},
|
|
32327
|
-
|
|
32328
|
-
|
|
33180
|
+
cerebras: {
|
|
33181
|
+
heading: {
|
|
33182
|
+
text: "Cerebras (US, via OpenRouter)"
|
|
33183
|
+
},
|
|
33184
|
+
description: {
|
|
33185
|
+
text: "Cerebras hosts some of the high-speed models routed through OpenRouter. Only used when you select a Cerebras-hosted model."
|
|
33186
|
+
}
|
|
32329
33187
|
},
|
|
32330
|
-
|
|
32331
|
-
|
|
33188
|
+
google_gemini: {
|
|
33189
|
+
heading: {
|
|
33190
|
+
text: "Google Gemini (US)"
|
|
33191
|
+
},
|
|
33192
|
+
description: {
|
|
33193
|
+
text: "Google Gemini processes requests for Gemini-family models when you select one. Google receives your chat messages with PII placeholders already substituted client-side."
|
|
33194
|
+
}
|
|
32332
33195
|
},
|
|
32333
|
-
|
|
32334
|
-
|
|
33196
|
+
google_vertex_maas: {
|
|
33197
|
+
heading: {
|
|
33198
|
+
text: "Google Vertex AI Model-as-a-Service (US)"
|
|
33199
|
+
},
|
|
33200
|
+
description: {
|
|
33201
|
+
text: "Google Vertex AI Model-as-a-Service hosts third-party open models (such as DeepSeek) on Google infrastructure via an OpenAI-compatible endpoint. This is a separate service from Google Gemini: the model weights are third-party, but the inference runs on Google's servers. Only used when you select a MaaS-hosted model."
|
|
33202
|
+
}
|
|
33203
|
+
},
|
|
33204
|
+
together: {
|
|
33205
|
+
heading: {
|
|
33206
|
+
text: "Together AI (US)"
|
|
33207
|
+
},
|
|
33208
|
+
description: {
|
|
33209
|
+
text: "Together AI hosts third-party models such as Kimi K2.6 on an OpenAI-compatible endpoint. Only used when you select a Together-hosted model. Together receives your chat messages with PII placeholders already substituted client-side."
|
|
33210
|
+
}
|
|
33211
|
+
},
|
|
33212
|
+
groq: {
|
|
33213
|
+
heading: {
|
|
33214
|
+
text: "Groq (US, content sanitization)"
|
|
33215
|
+
},
|
|
33216
|
+
description: {
|
|
33217
|
+
text: "Groq is used server-side to sanitize external content (search results and scraped web pages) before it is included in AI responses. Groq sees the external content we fetched on your behalf, not your chat messages, and never learns which user requested each piece of content."
|
|
33218
|
+
}
|
|
33219
|
+
},
|
|
33220
|
+
alibaba: {
|
|
33221
|
+
heading: {
|
|
33222
|
+
text: "Alibaba Cloud (Qwen models)"
|
|
33223
|
+
},
|
|
33224
|
+
description: {
|
|
33225
|
+
text: "Alibaba Cloud hosts Qwen-family models. Alibaba receives your chat messages with PII placeholders already substituted client-side only when you select an Alibaba-hosted model."
|
|
33226
|
+
}
|
|
33227
|
+
},
|
|
33228
|
+
deepseek: {
|
|
33229
|
+
heading: {
|
|
33230
|
+
text: "DeepSeek (reasoning models)"
|
|
33231
|
+
},
|
|
33232
|
+
description: {
|
|
33233
|
+
text: "DeepSeek provides DeepSeek-family reasoning models. DeepSeek receives your chat messages with PII placeholders already substituted client-side only when you select a DeepSeek model."
|
|
33234
|
+
}
|
|
33235
|
+
},
|
|
33236
|
+
moonshot: {
|
|
33237
|
+
heading: {
|
|
33238
|
+
text: "Moonshot AI / Kimi (language models)"
|
|
33239
|
+
},
|
|
33240
|
+
description: {
|
|
33241
|
+
text: "Moonshot AI provides Kimi-family models. Moonshot receives your chat messages with PII placeholders already substituted client-side only when you select a Moonshot-hosted Kimi model."
|
|
33242
|
+
}
|
|
33243
|
+
},
|
|
33244
|
+
zai: {
|
|
33245
|
+
heading: {
|
|
33246
|
+
text: "Z.ai (GLM models)"
|
|
33247
|
+
},
|
|
33248
|
+
description: {
|
|
33249
|
+
text: "Z.ai provides GLM-family models. Z.ai receives your chat messages with PII placeholders already substituted client-side only when you select a Z.ai model."
|
|
33250
|
+
}
|
|
32335
33251
|
}
|
|
32336
33252
|
},
|
|
32337
|
-
|
|
32338
|
-
|
|
32339
|
-
text: "
|
|
33253
|
+
image_generation: {
|
|
33254
|
+
heading: {
|
|
33255
|
+
text: "Image generation and editing (only when you use an image skill)"
|
|
32340
33256
|
},
|
|
32341
|
-
|
|
32342
|
-
text: "
|
|
33257
|
+
description: {
|
|
33258
|
+
text: "These providers are only used when you invoke an image generation or editing skill. If you never use the images app, none of your data is shared with them."
|
|
32343
33259
|
},
|
|
32344
|
-
|
|
32345
|
-
|
|
33260
|
+
fal: {
|
|
33261
|
+
heading: {
|
|
33262
|
+
text: "FAL (Flux models)"
|
|
33263
|
+
},
|
|
33264
|
+
description: {
|
|
33265
|
+
text: "FAL hosts Flux image generation models used by the images.generate_draft skill. FAL receives your image generation prompts and, when editing, the source images you supply."
|
|
33266
|
+
}
|
|
32346
33267
|
},
|
|
32347
|
-
|
|
32348
|
-
|
|
33268
|
+
recraft: {
|
|
33269
|
+
heading: {
|
|
33270
|
+
text: "Recraft (vector and raster image generation)"
|
|
33271
|
+
},
|
|
33272
|
+
description: {
|
|
33273
|
+
text: "Recraft powers the images.generate and images.vectorize skills, producing vector and raster images and converting bitmaps to SVG. Recraft receives your prompts, style preferences, and \u2014 when vectorizing \u2014 your source images."
|
|
33274
|
+
}
|
|
32349
33275
|
},
|
|
32350
|
-
|
|
32351
|
-
|
|
33276
|
+
bfl: {
|
|
33277
|
+
heading: {
|
|
33278
|
+
text: "Black Forest Labs (FLUX image models)"
|
|
33279
|
+
},
|
|
33280
|
+
description: {
|
|
33281
|
+
text: "Black Forest Labs powers draft image generation via FLUX models. BFL receives your image generation prompts and, when editing, the source images you provide."
|
|
33282
|
+
}
|
|
33283
|
+
}
|
|
33284
|
+
},
|
|
33285
|
+
music_generation: {
|
|
33286
|
+
heading: {
|
|
33287
|
+
text: "Music generation (only when you use a music skill)"
|
|
32352
33288
|
},
|
|
32353
|
-
|
|
32354
|
-
text: "
|
|
33289
|
+
description: {
|
|
33290
|
+
text: "These providers are only used when you invoke a music generation skill. If you never use the music app, none of your data is shared with them."
|
|
32355
33291
|
},
|
|
32356
|
-
|
|
32357
|
-
|
|
33292
|
+
google_vertex_ai: {
|
|
33293
|
+
heading: {
|
|
33294
|
+
text: "Google Vertex AI (Lyria music generation)"
|
|
33295
|
+
},
|
|
33296
|
+
description: {
|
|
33297
|
+
text: "Google Vertex AI hosts the Lyria models used by the music.generate skill. Google receives your music generation prompts, optional lyrics, style preferences, and generation settings only when you ask OpenMates to generate music or background music."
|
|
33298
|
+
}
|
|
32358
33299
|
}
|
|
32359
|
-
}
|
|
32360
|
-
},
|
|
32361
|
-
discord_integration: {
|
|
32362
|
-
heading: {
|
|
32363
|
-
text: "9. Discord Integration"
|
|
32364
33300
|
},
|
|
32365
|
-
|
|
32366
|
-
|
|
32367
|
-
|
|
32368
|
-
|
|
32369
|
-
|
|
33301
|
+
code_execution: {
|
|
33302
|
+
heading: {
|
|
33303
|
+
text: "Code and developer tools (only when you use code skills)"
|
|
33304
|
+
},
|
|
33305
|
+
description: {
|
|
33306
|
+
text: "These providers are only used when you invoke a code app skill, such as searching public repositories, running code in an isolated sandbox, creating a generated application that auto-starts a short screenshot preview, or explicitly starting a generated application live preview. If you never use the code app, none of your data is shared with them."
|
|
33307
|
+
},
|
|
33308
|
+
github: {
|
|
33309
|
+
heading: {
|
|
33310
|
+
text: "GitHub (public repository search)"
|
|
33311
|
+
},
|
|
33312
|
+
description: {
|
|
33313
|
+
text: "GitHub powers the code app's repository search skill. GitHub receives public repository search queries without an OpenMates user identifier."
|
|
33314
|
+
}
|
|
33315
|
+
},
|
|
33316
|
+
context7: {
|
|
33317
|
+
heading: {
|
|
33318
|
+
text: "Context7 (programming documentation lookup)"
|
|
33319
|
+
},
|
|
33320
|
+
description: {
|
|
33321
|
+
text: "Context7 powers the code app's documentation lookup skill. Context7 receives library names and documentation questions without an OpenMates user identifier."
|
|
33322
|
+
}
|
|
33323
|
+
},
|
|
33324
|
+
e2b: {
|
|
33325
|
+
heading: {
|
|
33326
|
+
text: "E2B (isolated code sandbox)"
|
|
33327
|
+
},
|
|
33328
|
+
description: {
|
|
33329
|
+
text: "E2B runs selected code embeds, generated application previews, and Remotion video renders inside isolated sandboxes. E2B receives the code or generated application/Remotion files and assets needed to run or render the result, runtime inputs, preview/render logs, and execution output needed to display the sandbox result. This sharing happens when you explicitly run code, create a generated application that auto-starts a short screenshot preview, start/resume an application live preview, or when a Remotion video render auto-starts after the assistant closes an explicit remotion code fence."
|
|
33330
|
+
}
|
|
33331
|
+
}
|
|
32370
33332
|
},
|
|
32371
|
-
|
|
32372
|
-
text: "Discord Privacy Policy"
|
|
32373
|
-
}
|
|
32374
|
-
},
|
|
32375
|
-
contact: {
|
|
32376
|
-
heading: {
|
|
32377
|
-
text: "10. Contact Information"
|
|
32378
|
-
},
|
|
32379
|
-
questions: {
|
|
32380
|
-
text: "For questions about data protection:"
|
|
32381
|
-
},
|
|
32382
|
-
email: {
|
|
32383
|
-
text: "Email"
|
|
32384
|
-
},
|
|
32385
|
-
postal: {
|
|
32386
|
-
text: "Postal address: See Legal Notice (Imprint) for the current registered address."
|
|
32387
|
-
},
|
|
32388
|
-
controller: {
|
|
32389
|
-
text: "Controller: OpenMates (see Imprint for legal representative details)."
|
|
32390
|
-
}
|
|
32391
|
-
},
|
|
32392
|
-
aggregate_analytics: {
|
|
32393
|
-
heading: {
|
|
32394
|
-
text: "11. Anonymous Aggregate Analytics"
|
|
32395
|
-
},
|
|
32396
|
-
overview: {
|
|
32397
|
-
text: "We collect anonymous, aggregate statistics about how our website and web application are used. This data is genuinely anonymous \u2014 it cannot be linked to any individual user, and no cookies or tracking identifiers are set. No consent banner is required because no personal data is collected."
|
|
32398
|
-
},
|
|
32399
|
-
what_we_collect: {
|
|
32400
|
-
text: "What we collect (all aggregate, never individual):"
|
|
32401
|
-
},
|
|
32402
|
-
items: {
|
|
32403
|
-
text: "Daily page load counts; approximate unique visit counts (probabilistic HyperLogLog, ~0.81% error); country distribution (GeoIP lookup \u2014 IP discarded immediately, never stored); device class (mobile/tablet/desktop); browser family and major version; OS family; referrer domain (domain only, never full URL); screen size class; session duration distribution (bucketed)"
|
|
32404
|
-
},
|
|
32405
|
-
no_pii: {
|
|
32406
|
-
text: "IP addresses are used transiently for GeoIP lookup only and are never written to disk or any database. User-Agent strings are parsed to metadata only (browser name, OS) \u2014 the raw string is never stored. All data is stored as daily aggregate counters, not as individual records."
|
|
32407
|
-
}
|
|
32408
|
-
},
|
|
32409
|
-
follow_up_1: {
|
|
32410
|
-
text: "How is my chat content encrypted?"
|
|
32411
|
-
},
|
|
32412
|
-
follow_up_2: {
|
|
32413
|
-
text: "How does client-side encryption work?"
|
|
32414
|
-
},
|
|
32415
|
-
follow_up_3: {
|
|
32416
|
-
text: "What if I lose my encryption key?"
|
|
32417
|
-
},
|
|
32418
|
-
follow_up_4: {
|
|
32419
|
-
text: "Do AI providers see my messages?"
|
|
32420
|
-
},
|
|
32421
|
-
follow_up_5: {
|
|
32422
|
-
text: "How do I delete my account?"
|
|
32423
|
-
},
|
|
32424
|
-
follow_up_6: {
|
|
32425
|
-
text: "What data do you share with third parties?"
|
|
32426
|
-
},
|
|
32427
|
-
provider_link_label: {
|
|
32428
|
-
text: "Privacy policy"
|
|
32429
|
-
},
|
|
32430
|
-
overview: {
|
|
32431
|
-
heading: {
|
|
32432
|
-
text: "Overview"
|
|
32433
|
-
},
|
|
32434
|
-
summary: {
|
|
32435
|
-
text: "OpenMates is designed so that most of your data is encrypted on your device before it reaches our servers. This is **not** end-to-end encryption: our servers briefly decrypt your content in memory to run AI responses, render invoices, and deliver reminders. But we never write decrypted content to disk, logs, or traces, and when you delete your account we destroy the encryption key that protects your data \u2014 cryptographically shredding every encrypted field we still hold."
|
|
32436
|
-
}
|
|
32437
|
-
},
|
|
32438
|
-
protection: {
|
|
32439
|
-
heading: {
|
|
32440
|
-
text: "How we protect your data"
|
|
32441
|
-
},
|
|
32442
|
-
intro: {
|
|
32443
|
-
text: "We rely on six technical measures to protect your data. Each is verifiable in our open-source code and maps to a specific GDPR obligation."
|
|
32444
|
-
},
|
|
32445
|
-
client_side_encryption: {
|
|
32446
|
-
heading: {
|
|
32447
|
-
text: "Client-side encryption of your content"
|
|
32448
|
-
},
|
|
32449
|
-
description: {
|
|
32450
|
-
text: "Your chat content, titles, summaries, tags, per-chat keys, app settings and memories, reminders, and sensitive profile fields (encrypted username, auto-top-up email, 2FA secrets) are encrypted in your browser before being sent to our servers. The browser uses a key derived from your login credential; our servers reject writes that contain anything but ciphertext. This is *not* end-to-end encryption \u2014 our servers can decrypt your content in memory when they need to (for AI responses, invoice rendering, reminder delivery). The difference from end-to-end encryption is that decryption happens transiently in process memory and the plaintext is never persisted to disk, logs, or traces."
|
|
32451
|
-
}
|
|
32452
|
-
},
|
|
32453
|
-
pii_placeholder_substitution: {
|
|
32454
|
-
heading: {
|
|
32455
|
-
text: "PII placeholder substitution before AI calls"
|
|
32456
|
-
},
|
|
32457
|
-
description: {
|
|
32458
|
-
text: "Before any message is sent to a third-party AI model, your browser scans your message for real names, email addresses, and physical addresses that you have saved in your privacy settings (or that match common patterns) and replaces any matches with neutral placeholders (for example [USER_0]). The mapping back to your real values is stored encrypted with your key and is never decrypted on our servers. **Important limitation:** this substitution only protects personal data that our client-side detector actually recognizes \u2014 typically the values you have explicitly saved in your privacy settings and values highlighted in the message field before you send. Anything the detector does not recognize (typos of your own name, uncommon address formats, other people's personal data, sensitive topics that are not formally PII) will be transmitted as you typed it. Please treat every message as you would treat a note passed to a third-party AI provider and be cautious with personal data."
|
|
32459
|
-
}
|
|
32460
|
-
},
|
|
32461
|
-
encrypted_at_rest: {
|
|
32462
|
-
heading: {
|
|
32463
|
-
text: "Encrypted at rest, decrypted only in memory"
|
|
32464
|
-
},
|
|
32465
|
-
description: {
|
|
32466
|
-
text: "Our PostgreSQL database, Redis caches, S3 object storage, and backup snapshots hold only ciphertext for any field containing user content. When our servers need to read your content to run an AI response, render an invoice, or deliver a reminder, decryption happens transiently in process memory and is discarded immediately after. Key material is released on demand by HashiCorp Vault and never stored in raw form outside it."
|
|
32467
|
-
}
|
|
32468
|
-
},
|
|
32469
|
-
hashed_identifiers: {
|
|
32470
|
-
heading: {
|
|
32471
|
-
text: "Hashed identifiers throughout"
|
|
32472
|
-
},
|
|
32473
|
-
description: {
|
|
32474
|
-
text: "Your user ID is stored in most database tables as a SHA-256 hash rather than as a direct reference. Login uses a zero-knowledge password verification flow: we verify that you know your password without ever learning it. API-key device records anonymize the IP address to its first two octets before encryption, so we can recognize a device without storing a precise location."
|
|
32475
|
-
}
|
|
32476
|
-
},
|
|
32477
|
-
cryptographic_erasure: {
|
|
32478
|
-
heading: {
|
|
32479
|
-
text: "Cryptographically shredded on deletion"
|
|
32480
|
-
},
|
|
32481
|
-
description: {
|
|
32482
|
-
text: "When you delete your account we destroy your HashiCorp Vault Transit key in addition to removing your rows from our database. Any encrypted field still sitting in a backup, cache, or running process becomes mathematically unrecoverable ciphertext at that moment \u2014 cryptographic erasure on top of row-level deletion."
|
|
32483
|
-
}
|
|
32484
|
-
},
|
|
32485
|
-
observability_without_tracking: {
|
|
32486
|
-
heading: {
|
|
32487
|
-
text: "Observability without tracking"
|
|
32488
|
-
},
|
|
32489
|
-
description: {
|
|
32490
|
-
text: "Our internal tracing pipeline (OpenTelemetry) replaces your user ID with a hash that rotates every 24 hours, strips authentication headers and cookies from every span, and redacts chat content before any trace leaves the backend. Our web analytics use a server-side beacon with no cookies and no stored individual identifiers. We do not use Google Analytics, Plausible, PostHog, or any third-party analytics platform."
|
|
32491
|
-
}
|
|
32492
|
-
}
|
|
32493
|
-
},
|
|
32494
|
-
promises: {
|
|
32495
|
-
intro: {
|
|
32496
|
-
text: "Below are the privacy promises we make to every user. Each one is backed by code in our open-source repository and verified by automated tests. When the enforcing code changes, a registry-linked check reminds us to rerun those tests before release."
|
|
32497
|
-
},
|
|
32498
|
-
client_side_chat_encryption: {
|
|
32499
|
-
heading: {
|
|
32500
|
-
text: "Client-side chat encryption"
|
|
32501
|
-
},
|
|
32502
|
-
description: {
|
|
32503
|
-
text: "Your chats, settings, and memories are encrypted on your device with AES-256-GCM before they leave your browser. The server stores only ciphertext on disk, in caches, and in backups. This is not end-to-end encryption: when a server-side task needs to read content (to run an AI response, render an invoice, or deliver a reminder), it decrypts in memory via HashiCorp Vault and discards the plaintext immediately after use."
|
|
32504
|
-
}
|
|
32505
|
-
},
|
|
32506
|
-
email_encryption_at_rest: {
|
|
32507
|
-
heading: {
|
|
32508
|
-
text: "Email addresses encrypted at rest"
|
|
32509
|
-
},
|
|
32510
|
-
description: {
|
|
32511
|
-
text: "Your email address is stored only as ciphertext. The server decrypts it transiently in memory during login, billing, and notification delivery, and never writes plaintext to disk or logs."
|
|
32512
|
-
}
|
|
32513
|
-
},
|
|
32514
|
-
no_third_party_tracking: {
|
|
32515
|
-
heading: {
|
|
32516
|
-
text: "No third-party tracking cookies or analytics"
|
|
32517
|
-
},
|
|
32518
|
-
description: {
|
|
32519
|
-
text: "We do not use Google Analytics, Plausible, PostHog, Mixpanel, Amplitude, Segment, or any other third-party analytics platform. Our telemetry is server-side, cookie-free, and contains no individually stored identifiers. A pre-edit check in our repository blocks the introduction of analytics SDKs."
|
|
32520
|
-
}
|
|
32521
|
-
},
|
|
32522
|
-
pii_placeholder_substitution: {
|
|
32523
|
-
heading: {
|
|
32524
|
-
text: "Personal information never reaches AI providers"
|
|
32525
|
-
},
|
|
32526
|
-
description: {
|
|
32527
|
-
text: "Before any message is sent to an AI provider, your browser detects 32 categories of personal data \u2014 emails, phone numbers, credit card numbers, API keys, and more \u2014 and replaces them with placeholders like `[EMAIL_com]`. The substitution map is encrypted with your chat key so only your devices can restore the original values."
|
|
32528
|
-
}
|
|
32529
|
-
},
|
|
32530
|
-
telemetry_privacy_filter: {
|
|
32531
|
-
heading: {
|
|
32532
|
-
text: "Telemetry stripped of sensitive data"
|
|
32533
|
-
},
|
|
32534
|
-
description: {
|
|
32535
|
-
text: "Our internal tracing pipeline (OpenTelemetry) strips authentication headers, cookies, and database statements from every span, and pseudonymises your user ID with a salt that rotates every 24 hours. Regular users never have their raw identifiers in telemetry."
|
|
32536
|
-
}
|
|
32537
|
-
},
|
|
32538
|
-
cryptographic_erasure: {
|
|
32539
|
-
heading: {
|
|
32540
|
-
text: "Deleted accounts are cryptographically erased"
|
|
32541
|
-
},
|
|
32542
|
-
description: {
|
|
32543
|
-
text: "When you delete your account, the first step destroys your encryption keys. Any residual ciphertext in backups, caches, or audit logs is rendered permanently unreadable \u2014 no key, no content."
|
|
32544
|
-
}
|
|
32545
|
-
},
|
|
32546
|
-
argon2_password_hashing: {
|
|
32547
|
-
heading: {
|
|
32548
|
-
text: "Passwords stored as Argon2 hashes"
|
|
32549
|
-
},
|
|
32550
|
-
description: {
|
|
32551
|
-
text: "We store password and backup-code verifiers as Argon2 hashes. Plaintext passwords are never persisted to disk, never written to logs, and never recoverable \u2014 even by us."
|
|
32552
|
-
}
|
|
32553
|
-
},
|
|
32554
|
-
payment_data_minimization: {
|
|
32555
|
-
heading: {
|
|
32556
|
-
text: "No card numbers ever touch our servers"
|
|
32557
|
-
},
|
|
32558
|
-
description: {
|
|
32559
|
-
text: "Payments flow directly from your browser to Stripe. We store only provider tokens and customer identifiers. We never see, receive, or persist full card numbers, security codes, or bank account details."
|
|
32560
|
-
}
|
|
32561
|
-
},
|
|
32562
|
-
logging_redaction: {
|
|
32563
|
-
heading: {
|
|
32564
|
-
text: "Logs are scrubbed of sensitive data"
|
|
32565
|
-
},
|
|
32566
|
-
description: {
|
|
32567
|
-
text: "Every log line passes through a redaction filter that strips email addresses, IP addresses, bearer tokens, and password values. Compliance logs preserve pseudonymous user IDs but remove all other personal data."
|
|
32568
|
-
}
|
|
32569
|
-
},
|
|
32570
|
-
prompt_injection_defense: {
|
|
32571
|
-
heading: {
|
|
32572
|
-
text: "Defense against prompt injection"
|
|
32573
|
-
},
|
|
32574
|
-
description: {
|
|
32575
|
-
text: "Two layers protect your conversations from malicious content in web pages, files, and URLs you reference. Invisible Unicode characters are stripped first, then a dedicated safety model detects semantic injection attempts and blocks or replaces high-risk content before it reaches your assistant."
|
|
32576
|
-
}
|
|
32577
|
-
},
|
|
32578
|
-
no_training_on_user_data: {
|
|
32579
|
-
heading: {
|
|
32580
|
-
text: "Your conversations are not used to train AI models"
|
|
32581
|
-
},
|
|
32582
|
-
description: {
|
|
32583
|
-
text: "All AI chat providers we use (Anthropic, OpenAI, Mistral, Google Gemini, Google Vertex AI, Together AI, Groq, Cerebras, and OpenRouter) have explicit no-training clauses in their terms \u2014 your conversations are never used to fine-tune, evaluate, or otherwise train AI models. For image generation, Recraft has a training opt-out which we have activated. fal.ai (used for draft image generation) may use anonymized, aggregated derivatives of prompts for model improvement under their standard API terms; no full opt-out is available outside an enterprise contract. A per-provider audit with verbatim policy quotes and verification dates is published in our open-source repository."
|
|
32584
|
-
}
|
|
32585
|
-
},
|
|
32586
|
-
no_external_resources: {
|
|
32587
|
-
heading: {
|
|
32588
|
-
text: "The web app never loads external images or scripts"
|
|
32589
|
-
},
|
|
32590
|
-
description: {
|
|
32591
|
-
text: "Every external image, favicon, or preview shown in the app is fetched through our own preview.openmates.org proxy \u2014 the originating website never sees your IP address. The only third-party script the web app loads is the Stripe payment SDK needed to process a purchase, and only when you explicitly open the payment flow. There is no ad network, tag manager, CDN font loader, or third-party analytics library."
|
|
32592
|
-
}
|
|
32593
|
-
},
|
|
32594
|
-
cli_no_credential_prompts: {
|
|
32595
|
-
heading: {
|
|
32596
|
-
text: "The command-line tool never asks for your password"
|
|
32597
|
-
},
|
|
32598
|
-
description: {
|
|
32599
|
-
text: "The OpenMates CLI never prompts you for your email, password, 2FA code, or any other login credential. Authentication uses a browser-based pair-auth handshake: the CLI shows a QR code and URL, you approve the session in your logged-in web browser, and you type a short 6-character binding PIN back into the CLI to confirm. Your credentials never pass through the terminal."
|
|
32600
|
-
}
|
|
32601
|
-
},
|
|
32602
|
-
open_source_transparency: {
|
|
33333
|
+
web_and_search: {
|
|
32603
33334
|
heading: {
|
|
32604
|
-
text: "
|
|
33335
|
+
text: "Web, search, and content retrieval (only when you use a web or search skill)"
|
|
32605
33336
|
},
|
|
32606
33337
|
description: {
|
|
32607
|
-
text: "
|
|
32608
|
-
}
|
|
32609
|
-
}
|
|
32610
|
-
},
|
|
32611
|
-
providers: {
|
|
32612
|
-
heading: {
|
|
32613
|
-
text: "When each provider is used"
|
|
32614
|
-
},
|
|
32615
|
-
intro: {
|
|
32616
|
-
text: "Most of the providers below are only reached when you use a specific feature \u2014 for example, image generation providers are only used when you invoke an image skill. The groups below tell you exactly which providers are involved for each feature, so you can opt in or out of data sharing by choosing which features to use."
|
|
32617
|
-
},
|
|
32618
|
-
always_active: {
|
|
32619
|
-
heading: {
|
|
32620
|
-
text: "Always active (used for every user)"
|
|
33338
|
+
text: "These providers are only used when you invoke a web, videos, news, or web-read skill. They see the content of what you search for or the URLs you ask to read, but no user identifier \u2014 they do not know which OpenMates user made each request."
|
|
32621
33339
|
},
|
|
32622
|
-
|
|
32623
|
-
|
|
33340
|
+
brave: {
|
|
33341
|
+
heading: {
|
|
33342
|
+
text: "Brave Search (web, videos, news)"
|
|
33343
|
+
},
|
|
33344
|
+
description: {
|
|
33345
|
+
text: "Brave Search powers the web, videos, and news search skills. Brave sees the text of your search queries and your country and language preferences for localized results, but no user identifier."
|
|
33346
|
+
}
|
|
32624
33347
|
},
|
|
32625
|
-
|
|
33348
|
+
firecrawl: {
|
|
32626
33349
|
heading: {
|
|
32627
|
-
text: "
|
|
33350
|
+
text: "Firecrawl (web page scraping)"
|
|
32628
33351
|
},
|
|
32629
33352
|
description: {
|
|
32630
|
-
text: "
|
|
33353
|
+
text: "Firecrawl powers the web read skill, scraping and extracting content from web pages you ask about. Firecrawl sees the URL you requested and the full content of the scraped page."
|
|
32631
33354
|
}
|
|
32632
33355
|
},
|
|
32633
|
-
|
|
33356
|
+
webshare: {
|
|
32634
33357
|
heading: {
|
|
32635
|
-
text: "
|
|
33358
|
+
text: "Webshare (rotating proxy)"
|
|
32636
33359
|
},
|
|
32637
33360
|
description: {
|
|
32638
|
-
text: "
|
|
33361
|
+
text: "Webshare provides a rotating proxy for requests to services that would otherwise block us. We route YouTube transcript requests (for the videos get-transcript skill) and doctor appointment searches (for the health search-appointments skill \u2014 see Group H) through Webshare. Webshare sees the URLs we route through it."
|
|
32639
33362
|
}
|
|
32640
33363
|
},
|
|
32641
|
-
|
|
33364
|
+
google_maps: {
|
|
32642
33365
|
heading: {
|
|
32643
|
-
text: "
|
|
33366
|
+
text: "Google Maps Platform (places search)"
|
|
32644
33367
|
},
|
|
32645
33368
|
description: {
|
|
32646
|
-
text: "
|
|
33369
|
+
text: "Google Maps powers the places search skill. Google sees the text of your place queries and an approximate location for localized results, but no user identifier."
|
|
32647
33370
|
}
|
|
32648
33371
|
},
|
|
32649
|
-
|
|
33372
|
+
youtube: {
|
|
32650
33373
|
heading: {
|
|
32651
|
-
text: "
|
|
33374
|
+
text: "YouTube (video transcripts)"
|
|
32652
33375
|
},
|
|
32653
33376
|
description: {
|
|
32654
|
-
text: "
|
|
33377
|
+
text: "YouTube is queried when you ask OpenMates to retrieve a transcript for a YouTube video. YouTube receives video URLs and requested transcript language preferences without an OpenMates user identifier."
|
|
32655
33378
|
}
|
|
33379
|
+
}
|
|
33380
|
+
},
|
|
33381
|
+
travel: {
|
|
33382
|
+
heading: {
|
|
33383
|
+
text: "Travel (only when you use a travel skill)"
|
|
32656
33384
|
},
|
|
32657
|
-
|
|
33385
|
+
description: {
|
|
33386
|
+
text: "These providers are only used when you invoke a travel skill \u2014 flight search, hotel search, or flight tracking. If you never use the travel app, none of your data is shared with them."
|
|
33387
|
+
},
|
|
33388
|
+
serpapi: {
|
|
32658
33389
|
heading: {
|
|
32659
|
-
text: "
|
|
33390
|
+
text: "SerpAPI (flight and hotel search)"
|
|
32660
33391
|
},
|
|
32661
33392
|
description: {
|
|
32662
|
-
text: "
|
|
33393
|
+
text: "SerpAPI powers flight and hotel search via its Google Flights and Google Hotels endpoints. SerpAPI receives your search queries \u2014 origin, destination, dates, and preferences \u2014 without a user identifier."
|
|
32663
33394
|
}
|
|
32664
33395
|
},
|
|
32665
|
-
|
|
33396
|
+
flightradar24: {
|
|
32666
33397
|
heading: {
|
|
32667
|
-
text: "
|
|
33398
|
+
text: "Flightradar24 (flight tracking)"
|
|
32668
33399
|
},
|
|
32669
33400
|
description: {
|
|
32670
|
-
text: "
|
|
33401
|
+
text: "Flightradar24 provides real flight track data when you ask about a specific flight number. It receives IATA flight numbers and departure dates without a user identifier."
|
|
32671
33402
|
}
|
|
32672
|
-
}
|
|
32673
|
-
},
|
|
32674
|
-
payments: {
|
|
32675
|
-
heading: {
|
|
32676
|
-
text: "Payment providers (only when you purchase credits)"
|
|
32677
33403
|
},
|
|
32678
|
-
|
|
32679
|
-
|
|
33404
|
+
deutsche_bahn: {
|
|
33405
|
+
heading: {
|
|
33406
|
+
text: "Deutsche Bahn (train connections)"
|
|
33407
|
+
},
|
|
33408
|
+
description: {
|
|
33409
|
+
text: "Deutsche Bahn provides train connection results when you search for rail routes. Deutsche Bahn receives origin, destination, and date search details without an OpenMates user identifier."
|
|
33410
|
+
}
|
|
32680
33411
|
},
|
|
32681
|
-
|
|
33412
|
+
flix: {
|
|
32682
33413
|
heading: {
|
|
32683
|
-
text: "
|
|
33414
|
+
text: "FlixBus / FlixTrain (bus and train connections)"
|
|
32684
33415
|
},
|
|
32685
33416
|
description: {
|
|
32686
|
-
text: "
|
|
33417
|
+
text: "Flix provides intercity bus and rail connection results when you search for FlixBus or FlixTrain routes. Flix receives origin, destination, and date search details without an OpenMates user identifier."
|
|
32687
33418
|
}
|
|
32688
33419
|
}
|
|
32689
33420
|
},
|
|
32690
|
-
|
|
33421
|
+
events: {
|
|
32691
33422
|
heading: {
|
|
32692
|
-
text: "
|
|
33423
|
+
text: "Events (only when you use the events skill)"
|
|
32693
33424
|
},
|
|
32694
33425
|
description: {
|
|
32695
|
-
text:
|
|
33426
|
+
text: "These providers are only used when you invoke the events search skill. The specific set of providers called depends on the city and event type you search for."
|
|
32696
33427
|
},
|
|
32697
|
-
|
|
33428
|
+
meetup: {
|
|
32698
33429
|
heading: {
|
|
32699
|
-
text: "
|
|
33430
|
+
text: "Meetup"
|
|
32700
33431
|
},
|
|
32701
33432
|
description: {
|
|
32702
|
-
text: "
|
|
33433
|
+
text: "Meetup powers event search for community events. Meetup receives your search queries \u2014 city, category, dates \u2014 without a user identifier."
|
|
32703
33434
|
}
|
|
32704
33435
|
},
|
|
32705
|
-
|
|
33436
|
+
luma: {
|
|
32706
33437
|
heading: {
|
|
32707
|
-
text: "
|
|
33438
|
+
text: "Luma"
|
|
32708
33439
|
},
|
|
32709
33440
|
description: {
|
|
32710
|
-
text: "
|
|
33441
|
+
text: "Luma powers event search for tech, creative, and community events. Luma receives your search queries without a user identifier."
|
|
32711
33442
|
}
|
|
32712
33443
|
},
|
|
32713
|
-
|
|
33444
|
+
resident_advisor: {
|
|
32714
33445
|
heading: {
|
|
32715
|
-
text: "
|
|
33446
|
+
text: "Resident Advisor"
|
|
32716
33447
|
},
|
|
32717
33448
|
description: {
|
|
32718
|
-
text: "
|
|
33449
|
+
text: "Resident Advisor powers event search for electronic music events. RA receives your search queries without a user identifier."
|
|
32719
33450
|
}
|
|
33451
|
+
}
|
|
33452
|
+
},
|
|
33453
|
+
health: {
|
|
33454
|
+
heading: {
|
|
33455
|
+
text: "Health appointment search (only when you use the health skill)"
|
|
32720
33456
|
},
|
|
32721
|
-
|
|
33457
|
+
description: {
|
|
33458
|
+
text: `These upstream services are reached through the Webshare rotating proxy (see Group E) only when you invoke the health app's appointment search skill. The queries you send \u2014 for example "dermatologist in Berlin next Tuesday" \u2014 are transmitted without user identifiers, but the content of the query itself can imply a health concern. By invoking the skill you consent to this processing under GDPR Art. 9(2)(a) (explicit consent for special-category data).`
|
|
33459
|
+
},
|
|
33460
|
+
doctolib: {
|
|
32722
33461
|
heading: {
|
|
32723
|
-
text: "
|
|
33462
|
+
text: "Doctolib (EU)"
|
|
32724
33463
|
},
|
|
32725
33464
|
description: {
|
|
32726
|
-
text: "
|
|
33465
|
+
text: "Doctolib lists doctors and specialists across supported EU regions. We query Doctolib for available appointments matching your criteria, routed through the Webshare proxy, without a user identifier."
|
|
32727
33466
|
}
|
|
32728
33467
|
},
|
|
32729
|
-
|
|
33468
|
+
jameda: {
|
|
32730
33469
|
heading: {
|
|
32731
|
-
text: "
|
|
33470
|
+
text: "Jameda (Germany)"
|
|
32732
33471
|
},
|
|
32733
33472
|
description: {
|
|
32734
|
-
text: "
|
|
33473
|
+
text: "Jameda lists doctors and specialists across Germany. We query Jameda for available appointments matching your criteria, routed through the Webshare proxy, without a user identifier."
|
|
32735
33474
|
}
|
|
33475
|
+
}
|
|
33476
|
+
},
|
|
33477
|
+
shopping: {
|
|
33478
|
+
heading: {
|
|
33479
|
+
text: "Shopping (only when you use the shopping skill)"
|
|
32736
33480
|
},
|
|
32737
|
-
|
|
33481
|
+
description: {
|
|
33482
|
+
text: "These providers are only used when you invoke the shopping search skill for live product and price lookups."
|
|
33483
|
+
},
|
|
33484
|
+
rewe: {
|
|
32738
33485
|
heading: {
|
|
32739
|
-
text: "
|
|
33486
|
+
text: "REWE (German grocery)"
|
|
32740
33487
|
},
|
|
32741
33488
|
description: {
|
|
32742
|
-
text: "
|
|
33489
|
+
text: "REWE's product catalogue is queried for live product and price data when you search German grocery items. REWE receives your search queries without a user identifier."
|
|
32743
33490
|
}
|
|
32744
33491
|
},
|
|
32745
|
-
|
|
33492
|
+
amazon: {
|
|
32746
33493
|
heading: {
|
|
32747
|
-
text: "
|
|
33494
|
+
text: "Amazon"
|
|
32748
33495
|
},
|
|
32749
33496
|
description: {
|
|
32750
|
-
text: "
|
|
33497
|
+
text: "Amazon's product catalogue is queried for live product and price data. Amazon receives your search queries without a user identifier."
|
|
32751
33498
|
}
|
|
33499
|
+
}
|
|
33500
|
+
},
|
|
33501
|
+
nutrition: {
|
|
33502
|
+
heading: {
|
|
33503
|
+
text: "Nutrition (only when you use the recipe search skill)"
|
|
32752
33504
|
},
|
|
32753
|
-
|
|
33505
|
+
description: {
|
|
33506
|
+
text: "These providers are only used when you invoke the nutrition recipe search skill. They receive recipe search queries and selected dietary filters without a user identifier."
|
|
33507
|
+
},
|
|
33508
|
+
edamam: {
|
|
32754
33509
|
heading: {
|
|
32755
|
-
text: "
|
|
33510
|
+
text: "Edamam"
|
|
32756
33511
|
},
|
|
32757
33512
|
description: {
|
|
32758
|
-
text: "
|
|
33513
|
+
text: "Edamam's Recipe Search API is queried when you search for recipes. Edamam receives the recipe query and selected dietary or nutrition filters without a user identifier."
|
|
32759
33514
|
}
|
|
33515
|
+
}
|
|
33516
|
+
},
|
|
33517
|
+
electronics: {
|
|
33518
|
+
heading: {
|
|
33519
|
+
text: "Electronics (only when you use the electronics skill)"
|
|
32760
33520
|
},
|
|
32761
|
-
|
|
33521
|
+
description: {
|
|
33522
|
+
text: "These providers are only used when you invoke the electronics component search skill for live component and reference-design lookups."
|
|
33523
|
+
},
|
|
33524
|
+
ti_webench: {
|
|
32762
33525
|
heading: {
|
|
32763
|
-
text: "
|
|
33526
|
+
text: "Texas Instruments WEBENCH (US)"
|
|
32764
33527
|
},
|
|
32765
33528
|
description: {
|
|
32766
|
-
text: "
|
|
33529
|
+
text: "TI WEBENCH is queried for power converter component candidates and reference design summaries. Texas Instruments receives the electrical requirements you search for, such as input voltage, output voltage, output current, temperature, isolation, and optimization goal, without a user identifier."
|
|
32767
33530
|
}
|
|
33531
|
+
}
|
|
33532
|
+
},
|
|
33533
|
+
mail: {
|
|
33534
|
+
heading: {
|
|
33535
|
+
text: "Mail (only when you use mail skills)"
|
|
32768
33536
|
},
|
|
32769
|
-
|
|
33537
|
+
description: {
|
|
33538
|
+
text: "This provider is only used when you invoke mail skills against a connected mailbox. If you never use the mail app, none of your data is shared with it."
|
|
33539
|
+
},
|
|
33540
|
+
protonmail: {
|
|
32770
33541
|
heading: {
|
|
32771
|
-
text: "
|
|
33542
|
+
text: "Proton Mail (mail search)"
|
|
32772
33543
|
},
|
|
32773
33544
|
description: {
|
|
32774
|
-
text: "
|
|
33545
|
+
text: "Proton Mail is queried when you search a connected Proton Mail mailbox. Proton receives the mail search query and mailbox data needed to return matching results."
|
|
32775
33546
|
}
|
|
33547
|
+
}
|
|
33548
|
+
},
|
|
33549
|
+
home: {
|
|
33550
|
+
heading: {
|
|
33551
|
+
text: "Home and housing (only when you use housing search)"
|
|
32776
33552
|
},
|
|
32777
|
-
|
|
33553
|
+
description: {
|
|
33554
|
+
text: "These providers are only used when you invoke the home app's housing search skill. If you never search for housing, none of your data is shared with them."
|
|
33555
|
+
},
|
|
33556
|
+
immoscout24: {
|
|
32778
33557
|
heading: {
|
|
32779
|
-
text: "
|
|
33558
|
+
text: "ImmoScout24 (German housing search)"
|
|
32780
33559
|
},
|
|
32781
33560
|
description: {
|
|
32782
|
-
text: "
|
|
33561
|
+
text: "ImmoScout24 is queried when you search German housing listings. ImmoScout24 receives housing search queries, locations, filters, and listing pages requested without an OpenMates user identifier."
|
|
32783
33562
|
}
|
|
32784
33563
|
},
|
|
32785
|
-
|
|
33564
|
+
kleinanzeigen: {
|
|
32786
33565
|
heading: {
|
|
32787
|
-
text: "
|
|
33566
|
+
text: "Kleinanzeigen (German classified housing search)"
|
|
32788
33567
|
},
|
|
32789
33568
|
description: {
|
|
32790
|
-
text: "
|
|
33569
|
+
text: "Kleinanzeigen is queried when you search German classified housing listings. Kleinanzeigen receives housing search queries, locations, filters, and listing pages requested without an OpenMates user identifier."
|
|
32791
33570
|
}
|
|
32792
33571
|
},
|
|
32793
|
-
|
|
33572
|
+
wg_gesucht: {
|
|
32794
33573
|
heading: {
|
|
32795
|
-
text: "
|
|
33574
|
+
text: "WG-Gesucht (German shared-apartment search)"
|
|
32796
33575
|
},
|
|
32797
33576
|
description: {
|
|
32798
|
-
text: "
|
|
33577
|
+
text: "WG-Gesucht is queried when you search German shared-apartment or rental listings. WG-Gesucht receives housing search queries, locations, filters, and listing pages requested without an OpenMates user identifier."
|
|
32799
33578
|
}
|
|
33579
|
+
}
|
|
33580
|
+
},
|
|
33581
|
+
community: {
|
|
33582
|
+
heading: {
|
|
33583
|
+
text: "Community and developer channels (only if you choose to use them)"
|
|
32800
33584
|
},
|
|
32801
|
-
|
|
33585
|
+
description: {
|
|
33586
|
+
text: "These providers are only involved if you choose to join our community or developer channels. You can use OpenMates without ever touching them."
|
|
33587
|
+
},
|
|
33588
|
+
discord: {
|
|
32802
33589
|
heading: {
|
|
32803
|
-
text: "
|
|
33590
|
+
text: "Discord (community server)"
|
|
32804
33591
|
},
|
|
32805
33592
|
description: {
|
|
32806
|
-
text: "
|
|
33593
|
+
text: "If you join the OpenMates community Discord server, Discord will see anything you post on its platform, plus your Discord username and whatever data Discord itself collects per its own privacy policy. We have no control over what Discord does with that data."
|
|
33594
|
+
},
|
|
33595
|
+
admin_access: {
|
|
33596
|
+
text: "OpenMates administrators with Discord server admin rights can read all messages posted in the OpenMates Discord server. Do not share sensitive information on Discord."
|
|
32807
33597
|
}
|
|
32808
33598
|
}
|
|
32809
33599
|
},
|
|
32810
|
-
|
|
33600
|
+
social_media: {
|
|
32811
33601
|
heading: {
|
|
32812
|
-
text: "
|
|
33602
|
+
text: "Social media (only when you use social media skills)"
|
|
32813
33603
|
},
|
|
32814
33604
|
description: {
|
|
32815
|
-
text: "These providers are only used when you invoke
|
|
33605
|
+
text: "These providers are only used when you invoke the social media app's get-posts or search skills. They receive public profile, page, or search queries without an OpenMates user identifier."
|
|
32816
33606
|
},
|
|
32817
|
-
|
|
33607
|
+
reddit: {
|
|
32818
33608
|
heading: {
|
|
32819
|
-
text: "
|
|
33609
|
+
text: "Reddit (public posts and discussions)"
|
|
32820
33610
|
},
|
|
32821
33611
|
description: {
|
|
32822
|
-
text: "
|
|
33612
|
+
text: "Reddit is queried when you fetch or search public Reddit posts. Reddit receives subreddit names, public post URLs, and search queries without an OpenMates user identifier."
|
|
32823
33613
|
}
|
|
32824
33614
|
},
|
|
32825
|
-
|
|
33615
|
+
bluesky: {
|
|
32826
33616
|
heading: {
|
|
32827
|
-
text: "
|
|
33617
|
+
text: "Bluesky (public posts and profile feeds)"
|
|
32828
33618
|
},
|
|
32829
33619
|
description: {
|
|
32830
|
-
text: "
|
|
33620
|
+
text: "Bluesky is queried when you fetch or search public Bluesky posts. Bluesky receives handles and search queries without an OpenMates user identifier."
|
|
33621
|
+
}
|
|
33622
|
+
},
|
|
33623
|
+
mastodon: {
|
|
33624
|
+
heading: {
|
|
33625
|
+
text: "Mastodon (public profile posts)"
|
|
33626
|
+
},
|
|
33627
|
+
description: {
|
|
33628
|
+
text: "Mastodon is queried when you fetch public Mastodon profile posts. The relevant Mastodon server receives profile identifiers or public profile URLs without an OpenMates user identifier."
|
|
32831
33629
|
}
|
|
33630
|
+
}
|
|
33631
|
+
}
|
|
33632
|
+
},
|
|
33633
|
+
flightradar24: {
|
|
33634
|
+
heading: {
|
|
33635
|
+
text: "3.16 Flightradar24"
|
|
33636
|
+
},
|
|
33637
|
+
description: {
|
|
33638
|
+
text: 'When you request real flight track data using the "Get flight track" skill, we query Flightradar24 to retrieve historical GPS track data for completed flights. Flightradar24 receives the flight number and departure date. No personal data is shared.'
|
|
33639
|
+
},
|
|
33640
|
+
privacy_policy_link: {
|
|
33641
|
+
text: "Flightradar24 Privacy Policy"
|
|
33642
|
+
}
|
|
33643
|
+
},
|
|
33644
|
+
security_measures: {
|
|
33645
|
+
heading: {
|
|
33646
|
+
text: "4. Security Measures"
|
|
33647
|
+
},
|
|
33648
|
+
intro: {
|
|
33649
|
+
text: "We implement various security measures to protect your account and data in the web application:"
|
|
33650
|
+
},
|
|
33651
|
+
device_fingerprinting: {
|
|
33652
|
+
subheading: {
|
|
33653
|
+
text: "Device Fingerprinting"
|
|
33654
|
+
},
|
|
33655
|
+
purpose: {
|
|
33656
|
+
text: "To enhance account security, prevent fraudulent access, and distinguish between new and recognized devices during login and session validation, we collect User-Agent strings (browser, OS, device type information) and IP addresses (used temporarily to derive approximate geo-location like country/city, then discarded from the device record)."
|
|
33657
|
+
},
|
|
33658
|
+
storage: {
|
|
33659
|
+
text: "A unique, stable hash is generated based on a combination of the collected signals. This hash is the only value stored in the device record. Browser/OS type and derived country code may be used transiently for risk assessment and notifications but are not stored with the device record. Raw IP addresses are NOT stored as part of the device record itself."
|
|
33660
|
+
},
|
|
33661
|
+
ip_logging: {
|
|
33662
|
+
text: "For security monitoring and abuse prevention, IP addresses associated with failed login attempts (invalid password or 2FA code) may be logged with limited retention. In addition, certain security/compliance events (e.g., successful backup-code logins) may be logged with IP for audit purposes. IP addresses are also used for rate limiting login attempts."
|
|
33663
|
+
}
|
|
33664
|
+
}
|
|
33665
|
+
},
|
|
33666
|
+
data_categories: {
|
|
33667
|
+
heading: {
|
|
33668
|
+
text: "5. Data Categories We Collect and Process"
|
|
33669
|
+
},
|
|
33670
|
+
intro: {
|
|
33671
|
+
text: "We collect and process the following categories of data to operate and secure our services:"
|
|
33672
|
+
},
|
|
33673
|
+
account: {
|
|
33674
|
+
text: "Account: Email address, username/display name, profile image, locale, and security settings (e.g., 2FA enabled). Email and username are encrypted with your key before storage. We also keep a separate server-side Vault-encrypted copy of your verified email address for mandatory account lifecycle notices, such as account verification, security alerts, and deletion reminders. Passwords are stored as salted hashes."
|
|
33675
|
+
},
|
|
33676
|
+
usage: {
|
|
33677
|
+
text: "Usage: Server logs, event timestamps, feature usage, error logs, and device recognition identifiers (hashed). IP addresses may be temporarily processed for security and rate limiting."
|
|
33678
|
+
},
|
|
33679
|
+
content: {
|
|
33680
|
+
text: "Content: Chat messages, prompts, attachments, and uploaded images/videos necessary to deliver the service (subject to moderation where applicable)."
|
|
33681
|
+
},
|
|
33682
|
+
payments: {
|
|
33683
|
+
text: "Payments: Payment method tokens, transaction IDs, billing address and VAT information as required for invoicing (processed primarily by Stripe). We do not store full card numbers."
|
|
33684
|
+
},
|
|
33685
|
+
newsletter: {
|
|
33686
|
+
text: "Newsletter: If you choose to subscribe to our newsletter, we store your email address in encrypted form. Newsletter subscription is optional and separate from account creation \u2014 creating an account does not automatically subscribe you to the newsletter. If you are an OpenMates customer (i.e. you have completed a purchase), we may additionally send you occasional product announcements and service updates about OpenMates' own similar services under the existing-customer exception (\xA77 Abs. 3 UWG / ePrivacy Directive Art. 13(2)). You can object to this at any time in Settings \u2192 Newsletter or via the one-click unsubscribe link in any such email, without affecting your account."
|
|
33687
|
+
},
|
|
33688
|
+
stability_logs: {
|
|
33689
|
+
text: "Stability Logs: To diagnose technical errors, anonymized browser console logs (error messages, component status, connection events) are collected from authenticated users and stored for up to 48 hours. If an error occurs, surrounding context is retained for up to 14 days. No message content, chat titles, names, email addresses, or other personally identifiable information is included. A random session identifier (generated per browser tab, not linked to your account) is used for log correlation. You can disable this at any time in Settings > Privacy > Stability Logs."
|
|
33690
|
+
}
|
|
33691
|
+
},
|
|
33692
|
+
data_retention: {
|
|
33693
|
+
heading: {
|
|
33694
|
+
text: "6. Data Retention"
|
|
33695
|
+
},
|
|
33696
|
+
account: {
|
|
33697
|
+
text: "Account data: Retained until account deletion and completion of related legal obligations."
|
|
33698
|
+
},
|
|
33699
|
+
usage_and_logs: {
|
|
33700
|
+
text: "Usage and logs: Operational logs retained up to 12 months; IPs tied to failed logins up to 30 days for security."
|
|
33701
|
+
},
|
|
33702
|
+
device_fingerprints: {
|
|
33703
|
+
text: "Device fingerprints: Retained until account deletion or manual removal from account settings (when available)."
|
|
33704
|
+
},
|
|
33705
|
+
content: {
|
|
33706
|
+
text: "Content: Retained until the user deletes the content or closes the account, subject to backup cycles."
|
|
33707
|
+
},
|
|
33708
|
+
payments_and_invoices: {
|
|
33709
|
+
text: "Payments and invoices: Retained up to 10 years where required by tax/commercial law."
|
|
33710
|
+
},
|
|
33711
|
+
compliance_logs: {
|
|
33712
|
+
text: "**Compliance logs:** audit logs retained 2 years (BSI \xA734 BDSG); financial compliance logs retained 10 years (AO \xA7147 and HGB \xA7257)"
|
|
33713
|
+
},
|
|
33714
|
+
observability_traces: {
|
|
33715
|
+
text: "**Observability traces:** up to 30 days; user IDs in traces use a hash that rotates every 24 hours, so older traces cannot be joined to a specific user"
|
|
33716
|
+
},
|
|
33717
|
+
user_data_backups: {
|
|
33718
|
+
text: "**User data backups:** 60 days via S3 lifecycle; backups contain only ciphertext encrypted with your own key"
|
|
33719
|
+
}
|
|
33720
|
+
},
|
|
33721
|
+
legal_basis: {
|
|
33722
|
+
heading: {
|
|
33723
|
+
text: "7. Legal Basis for Processing (GDPR)"
|
|
33724
|
+
},
|
|
33725
|
+
contract: {
|
|
33726
|
+
text: "Contract: Processing necessary to perform our contract with you or to take steps at your request prior to entering into a contract (e.g., account creation, delivering web app features)."
|
|
33727
|
+
},
|
|
33728
|
+
consent: {
|
|
33729
|
+
text: "Consent: Processing based on your consent, where applicable (e.g., optional communications, certain analytics or marketing where used). You can withdraw consent at any time."
|
|
33730
|
+
},
|
|
33731
|
+
legitimate_interests: {
|
|
33732
|
+
text: "Legitimate interests: Processing necessary for our legitimate interests, such as service security, fraud prevention, abuse detection, and improving the service, provided these interests are not overridden by your rights and interests."
|
|
33733
|
+
},
|
|
33734
|
+
direct_marketing: {
|
|
33735
|
+
text: "Direct marketing to existing customers: For existing OpenMates customers (users who have completed a purchase), we may rely on Art. 6(1)(f) GDPR (legitimate interests) in combination with \xA77 Abs. 3 UWG and ePrivacy Directive Art. 13(2) to send occasional product announcements, service updates, and marketing about OpenMates' own similar services. Each such email includes a clearly visible one-click unsubscribe link, and you can also manage these preferences at any time in Settings \u2192 Newsletter. You can object to this processing at any time without giving reasons (Art. 21 GDPR)."
|
|
33736
|
+
},
|
|
33737
|
+
legal_obligation: {
|
|
33738
|
+
text: "Legal obligation: Processing necessary to comply with legal obligations (e.g., tax and accounting requirements for invoices and transaction records)."
|
|
33739
|
+
}
|
|
33740
|
+
},
|
|
33741
|
+
legal_rights: {
|
|
33742
|
+
heading: {
|
|
33743
|
+
text: "8. Your Privacy Rights"
|
|
33744
|
+
},
|
|
33745
|
+
intro: {
|
|
33746
|
+
text: "Depending on your jurisdiction, you may have certain rights over your personal data. The following summarizes key rights under GDPR and CCPA/CPRA and how to exercise them:"
|
|
33747
|
+
},
|
|
33748
|
+
gdpr: {
|
|
33749
|
+
subheading: {
|
|
33750
|
+
text: "GDPR (EU/EEA)"
|
|
33751
|
+
},
|
|
33752
|
+
access: {
|
|
33753
|
+
text: "Right of access"
|
|
33754
|
+
},
|
|
33755
|
+
rectification: {
|
|
33756
|
+
text: "Right to rectification"
|
|
33757
|
+
},
|
|
33758
|
+
erasure: {
|
|
33759
|
+
text: "Right to erasure ('right to be forgotten')"
|
|
33760
|
+
},
|
|
33761
|
+
restriction: {
|
|
33762
|
+
text: "Right to restriction of processing"
|
|
33763
|
+
},
|
|
33764
|
+
portability: {
|
|
33765
|
+
text: "Right to data portability"
|
|
33766
|
+
},
|
|
33767
|
+
objection: {
|
|
33768
|
+
text: "Right to object"
|
|
33769
|
+
},
|
|
33770
|
+
withdraw_consent: {
|
|
33771
|
+
text: "Right to withdraw consent"
|
|
33772
|
+
},
|
|
33773
|
+
exercise: {
|
|
33774
|
+
text: "To exercise your GDPR rights, contact us at contact@openmates.org or via the Imprint. We may need to verify your identity before fulfilling your request."
|
|
33775
|
+
},
|
|
33776
|
+
manual_note: {
|
|
33777
|
+
text: "The following rights are available on request (please contact us by email at contact@openmates.org and we will handle them manually):"
|
|
33778
|
+
}
|
|
33779
|
+
},
|
|
33780
|
+
ccpa_cpra: {
|
|
33781
|
+
subheading: {
|
|
33782
|
+
text: "CCPA/CPRA (California)"
|
|
33783
|
+
},
|
|
33784
|
+
right_to_know: {
|
|
33785
|
+
text: "Right to know/access"
|
|
33786
|
+
},
|
|
33787
|
+
right_to_delete: {
|
|
33788
|
+
text: "Right to delete"
|
|
33789
|
+
},
|
|
33790
|
+
right_to_correct: {
|
|
33791
|
+
text: "Right to correct"
|
|
33792
|
+
},
|
|
33793
|
+
right_to_opt_out_of_sale_or_sharing: {
|
|
33794
|
+
text: "Right to opt-out of sale or sharing"
|
|
33795
|
+
},
|
|
33796
|
+
right_to_non_discrimination: {
|
|
33797
|
+
text: "Right to non-discrimination"
|
|
33798
|
+
},
|
|
33799
|
+
exercise: {
|
|
33800
|
+
text: "California residents can exercise CCPA/CPRA rights by contacting us at contact@openmates.org. We do not sell personal information."
|
|
33801
|
+
}
|
|
33802
|
+
}
|
|
33803
|
+
},
|
|
33804
|
+
discord_integration: {
|
|
33805
|
+
heading: {
|
|
33806
|
+
text: "9. Discord Integration"
|
|
33807
|
+
},
|
|
33808
|
+
description: {
|
|
33809
|
+
text: "If you choose to join our Discord community through links provided on this website, please note that Discord will collect and process your data according to their privacy policy. This includes account information, usage data, communication content, and other information as outlined in Discord's privacy policy."
|
|
33810
|
+
},
|
|
33811
|
+
admin_access: {
|
|
33812
|
+
text: "As Discord server administrators, we have access to all public communications, member lists, and other information shared within our Discord community. However, this information is hosted and primarily processed by Discord. We do not extract, store, or process this data outside of Discord's platform. For more information about how Discord handles your data, please review Discord's Privacy Policy."
|
|
33813
|
+
},
|
|
33814
|
+
privacy_policy_link: {
|
|
33815
|
+
text: "Discord Privacy Policy"
|
|
33816
|
+
}
|
|
33817
|
+
},
|
|
33818
|
+
contact: {
|
|
33819
|
+
heading: {
|
|
33820
|
+
text: "10. Contact Information"
|
|
33821
|
+
},
|
|
33822
|
+
questions: {
|
|
33823
|
+
text: "For questions about data protection:"
|
|
33824
|
+
},
|
|
33825
|
+
email: {
|
|
33826
|
+
text: "Email"
|
|
33827
|
+
},
|
|
33828
|
+
postal: {
|
|
33829
|
+
text: "Postal address: See Legal Notice (Imprint) for the current registered address."
|
|
33830
|
+
},
|
|
33831
|
+
controller: {
|
|
33832
|
+
text: "Controller: OpenMates (see Imprint for legal representative details)."
|
|
33833
|
+
}
|
|
33834
|
+
},
|
|
33835
|
+
aggregate_analytics: {
|
|
33836
|
+
heading: {
|
|
33837
|
+
text: "11. Anonymous Aggregate Analytics"
|
|
33838
|
+
},
|
|
33839
|
+
overview: {
|
|
33840
|
+
text: "We collect anonymous, aggregate statistics about how our website and web application are used. This data is genuinely anonymous \u2014 it cannot be linked to any individual user, and no cookies or tracking identifiers are set. No consent banner is required because no personal data is collected."
|
|
33841
|
+
},
|
|
33842
|
+
what_we_collect: {
|
|
33843
|
+
text: "What we collect (all aggregate, never individual):"
|
|
33844
|
+
},
|
|
33845
|
+
items: {
|
|
33846
|
+
text: "Daily page load counts; approximate unique visit counts (probabilistic HyperLogLog, ~0.81% error); country distribution (GeoIP lookup \u2014 IP discarded immediately, never stored); device class (mobile/tablet/desktop); browser family and major version; OS family; referrer domain (domain only, never full URL); screen size class; session duration distribution (bucketed)"
|
|
33847
|
+
},
|
|
33848
|
+
no_pii: {
|
|
33849
|
+
text: "IP addresses are used transiently for GeoIP lookup only and are never written to disk or any database. User-Agent strings are parsed to metadata only (browser name, OS) \u2014 the raw string is never stored. All data is stored as daily aggregate counters, not as individual records."
|
|
33850
|
+
}
|
|
33851
|
+
},
|
|
33852
|
+
follow_up_1: {
|
|
33853
|
+
text: "How is my chat content encrypted?"
|
|
33854
|
+
},
|
|
33855
|
+
follow_up_2: {
|
|
33856
|
+
text: "How does client-side encryption work?"
|
|
33857
|
+
},
|
|
33858
|
+
follow_up_3: {
|
|
33859
|
+
text: "What if I lose my encryption key?"
|
|
33860
|
+
},
|
|
33861
|
+
follow_up_4: {
|
|
33862
|
+
text: "Do AI providers see my messages?"
|
|
33863
|
+
},
|
|
33864
|
+
follow_up_5: {
|
|
33865
|
+
text: "How do I delete my account?"
|
|
33866
|
+
},
|
|
33867
|
+
follow_up_6: {
|
|
33868
|
+
text: "What data do you share with third parties?"
|
|
33869
|
+
},
|
|
33870
|
+
provider_link_label: {
|
|
33871
|
+
text: "Privacy policy"
|
|
33872
|
+
},
|
|
33873
|
+
overview: {
|
|
33874
|
+
heading: {
|
|
33875
|
+
text: "Overview"
|
|
33876
|
+
},
|
|
33877
|
+
summary: {
|
|
33878
|
+
text: "OpenMates is designed so that most of your data is encrypted on your device before it reaches our servers. This is **not** end-to-end encryption: our servers briefly decrypt your content in memory to run AI responses, render invoices, and deliver reminders. But we never write decrypted content to disk, logs, or traces, and when you delete your account we destroy the encryption key that protects your data \u2014 cryptographically shredding every encrypted field we still hold."
|
|
33879
|
+
}
|
|
33880
|
+
},
|
|
33881
|
+
protection: {
|
|
33882
|
+
heading: {
|
|
33883
|
+
text: "How we protect your data"
|
|
33884
|
+
},
|
|
33885
|
+
intro: {
|
|
33886
|
+
text: "We rely on six technical measures to protect your data. Each is verifiable in our open-source code and maps to a specific GDPR obligation."
|
|
33887
|
+
},
|
|
33888
|
+
client_side_encryption: {
|
|
33889
|
+
heading: {
|
|
33890
|
+
text: "Client-side encryption of your content"
|
|
32832
33891
|
},
|
|
32833
|
-
|
|
32834
|
-
|
|
32835
|
-
text: "Black Forest Labs (FLUX image models)"
|
|
32836
|
-
},
|
|
32837
|
-
description: {
|
|
32838
|
-
text: "Black Forest Labs powers draft image generation via FLUX models. BFL receives your image generation prompts and, when editing, the source images you provide."
|
|
32839
|
-
}
|
|
33892
|
+
description: {
|
|
33893
|
+
text: "Your chat content, titles, summaries, tags, per-chat keys, app settings and memories, reminders, and sensitive profile fields (encrypted username, auto-top-up email, 2FA secrets) are encrypted in your browser before being sent to our servers. The browser uses a key derived from your login credential; our servers reject writes that contain anything but ciphertext. This is *not* end-to-end encryption \u2014 our servers can decrypt your content in memory when they need to (for AI responses, invoice rendering, reminder delivery). The difference from end-to-end encryption is that decryption happens transiently in process memory and the plaintext is never persisted to disk, logs, or traces."
|
|
32840
33894
|
}
|
|
32841
33895
|
},
|
|
32842
|
-
|
|
33896
|
+
pii_placeholder_substitution: {
|
|
32843
33897
|
heading: {
|
|
32844
|
-
text: "
|
|
33898
|
+
text: "PII placeholder substitution before AI calls"
|
|
32845
33899
|
},
|
|
32846
33900
|
description: {
|
|
32847
|
-
text: "
|
|
32848
|
-
},
|
|
32849
|
-
google_vertex_ai: {
|
|
32850
|
-
heading: {
|
|
32851
|
-
text: "Google Vertex AI (Lyria music generation)"
|
|
32852
|
-
},
|
|
32853
|
-
description: {
|
|
32854
|
-
text: "Google Vertex AI hosts the Lyria models used by the music.generate skill. Google receives your music generation prompts, optional lyrics, style preferences, and generation settings only when you ask OpenMates to generate music or background music."
|
|
32855
|
-
}
|
|
33901
|
+
text: "Before any message is sent to a third-party AI model, your browser scans your message for real names, email addresses, and physical addresses that you have saved in your privacy settings (or that match common patterns) and replaces any matches with neutral placeholders (for example [USER_0]). The mapping back to your real values is stored encrypted with your key and is never decrypted on our servers. **Important limitation:** this substitution only protects personal data that our client-side detector actually recognizes \u2014 typically the values you have explicitly saved in your privacy settings and values highlighted in the message field before you send. Anything the detector does not recognize (typos of your own name, uncommon address formats, other people's personal data, sensitive topics that are not formally PII) will be transmitted as you typed it. Please treat every message as you would treat a note passed to a third-party AI provider and be cautious with personal data."
|
|
32856
33902
|
}
|
|
32857
33903
|
},
|
|
32858
|
-
|
|
33904
|
+
encrypted_at_rest: {
|
|
32859
33905
|
heading: {
|
|
32860
|
-
text: "
|
|
33906
|
+
text: "Encrypted at rest, decrypted only in memory"
|
|
32861
33907
|
},
|
|
32862
33908
|
description: {
|
|
32863
|
-
text: "
|
|
32864
|
-
},
|
|
32865
|
-
github: {
|
|
32866
|
-
heading: {
|
|
32867
|
-
text: "GitHub (public repository search)"
|
|
32868
|
-
},
|
|
32869
|
-
description: {
|
|
32870
|
-
text: "GitHub powers the code app's repository search skill. GitHub receives public repository search queries without an OpenMates user identifier."
|
|
32871
|
-
}
|
|
32872
|
-
},
|
|
32873
|
-
context7: {
|
|
32874
|
-
heading: {
|
|
32875
|
-
text: "Context7 (programming documentation lookup)"
|
|
32876
|
-
},
|
|
32877
|
-
description: {
|
|
32878
|
-
text: "Context7 powers the code app's documentation lookup skill. Context7 receives library names and documentation questions without an OpenMates user identifier."
|
|
32879
|
-
}
|
|
32880
|
-
},
|
|
32881
|
-
e2b: {
|
|
32882
|
-
heading: {
|
|
32883
|
-
text: "E2B (isolated code sandbox)"
|
|
32884
|
-
},
|
|
32885
|
-
description: {
|
|
32886
|
-
text: "E2B runs selected code embeds, generated application previews, and Remotion video renders inside isolated sandboxes. E2B receives the code or generated application/Remotion files and assets needed to run or render the result, runtime inputs, preview/render logs, and execution output needed to display the sandbox result. This sharing happens when you explicitly run code, create a generated application that auto-starts a short screenshot preview, start/resume an application live preview, or when a Remotion video render auto-starts after the assistant closes an explicit remotion code fence."
|
|
32887
|
-
}
|
|
33909
|
+
text: "Our PostgreSQL database, Redis caches, S3 object storage, and backup snapshots hold only ciphertext for any field containing user content. When our servers need to read your content to run an AI response, render an invoice, or deliver a reminder, decryption happens transiently in process memory and is discarded immediately after. Key material is released on demand by HashiCorp Vault and never stored in raw form outside it."
|
|
32888
33910
|
}
|
|
32889
33911
|
},
|
|
32890
|
-
|
|
33912
|
+
hashed_identifiers: {
|
|
32891
33913
|
heading: {
|
|
32892
|
-
text: "
|
|
33914
|
+
text: "Hashed identifiers throughout"
|
|
32893
33915
|
},
|
|
32894
33916
|
description: {
|
|
32895
|
-
text: "
|
|
32896
|
-
},
|
|
32897
|
-
brave: {
|
|
32898
|
-
heading: {
|
|
32899
|
-
text: "Brave Search (web, videos, news)"
|
|
32900
|
-
},
|
|
32901
|
-
description: {
|
|
32902
|
-
text: "Brave Search powers the web, videos, and news search skills. Brave sees the text of your search queries and your country and language preferences for localized results, but no user identifier."
|
|
32903
|
-
}
|
|
32904
|
-
},
|
|
32905
|
-
firecrawl: {
|
|
32906
|
-
heading: {
|
|
32907
|
-
text: "Firecrawl (web page scraping)"
|
|
32908
|
-
},
|
|
32909
|
-
description: {
|
|
32910
|
-
text: "Firecrawl powers the web read skill, scraping and extracting content from web pages you ask about. Firecrawl sees the URL you requested and the full content of the scraped page."
|
|
32911
|
-
}
|
|
32912
|
-
},
|
|
32913
|
-
webshare: {
|
|
32914
|
-
heading: {
|
|
32915
|
-
text: "Webshare (rotating proxy)"
|
|
32916
|
-
},
|
|
32917
|
-
description: {
|
|
32918
|
-
text: "Webshare provides a rotating proxy for requests to services that would otherwise block us. We route YouTube transcript requests (for the videos get-transcript skill) and doctor appointment searches (for the health search-appointments skill \u2014 see Group H) through Webshare. Webshare sees the URLs we route through it."
|
|
32919
|
-
}
|
|
32920
|
-
},
|
|
32921
|
-
google_maps: {
|
|
32922
|
-
heading: {
|
|
32923
|
-
text: "Google Maps Platform (places search)"
|
|
32924
|
-
},
|
|
32925
|
-
description: {
|
|
32926
|
-
text: "Google Maps powers the places search skill. Google sees the text of your place queries and an approximate location for localized results, but no user identifier."
|
|
32927
|
-
}
|
|
32928
|
-
},
|
|
32929
|
-
youtube: {
|
|
32930
|
-
heading: {
|
|
32931
|
-
text: "YouTube (video transcripts)"
|
|
32932
|
-
},
|
|
32933
|
-
description: {
|
|
32934
|
-
text: "YouTube is queried when you ask OpenMates to retrieve a transcript for a YouTube video. YouTube receives video URLs and requested transcript language preferences without an OpenMates user identifier."
|
|
32935
|
-
}
|
|
33917
|
+
text: "Your user ID is stored in most database tables as a SHA-256 hash rather than as a direct reference. Login uses a zero-knowledge password verification flow: we verify that you know your password without ever learning it. API-key device records anonymize the IP address to its first two octets before encryption, so we can recognize a device without storing a precise location."
|
|
32936
33918
|
}
|
|
32937
33919
|
},
|
|
32938
|
-
|
|
33920
|
+
cryptographic_erasure: {
|
|
32939
33921
|
heading: {
|
|
32940
|
-
text: "
|
|
33922
|
+
text: "Cryptographically shredded on deletion"
|
|
32941
33923
|
},
|
|
32942
33924
|
description: {
|
|
32943
|
-
text: "
|
|
32944
|
-
},
|
|
32945
|
-
serpapi: {
|
|
32946
|
-
heading: {
|
|
32947
|
-
text: "SerpAPI (flight and hotel search)"
|
|
32948
|
-
},
|
|
32949
|
-
description: {
|
|
32950
|
-
text: "SerpAPI powers flight and hotel search via its Google Flights and Google Hotels endpoints. SerpAPI receives your search queries \u2014 origin, destination, dates, and preferences \u2014 without a user identifier."
|
|
32951
|
-
}
|
|
32952
|
-
},
|
|
32953
|
-
flightradar24: {
|
|
32954
|
-
heading: {
|
|
32955
|
-
text: "Flightradar24 (flight tracking)"
|
|
32956
|
-
},
|
|
32957
|
-
description: {
|
|
32958
|
-
text: "Flightradar24 provides real flight track data when you ask about a specific flight number. It receives IATA flight numbers and departure dates without a user identifier."
|
|
32959
|
-
}
|
|
32960
|
-
},
|
|
32961
|
-
deutsche_bahn: {
|
|
32962
|
-
heading: {
|
|
32963
|
-
text: "Deutsche Bahn (train connections)"
|
|
32964
|
-
},
|
|
32965
|
-
description: {
|
|
32966
|
-
text: "Deutsche Bahn provides train connection results when you search for rail routes. Deutsche Bahn receives origin, destination, and date search details without an OpenMates user identifier."
|
|
32967
|
-
}
|
|
32968
|
-
},
|
|
32969
|
-
flix: {
|
|
32970
|
-
heading: {
|
|
32971
|
-
text: "FlixBus / FlixTrain (bus and train connections)"
|
|
32972
|
-
},
|
|
32973
|
-
description: {
|
|
32974
|
-
text: "Flix provides intercity bus and rail connection results when you search for FlixBus or FlixTrain routes. Flix receives origin, destination, and date search details without an OpenMates user identifier."
|
|
32975
|
-
}
|
|
33925
|
+
text: "When you delete your account we destroy your HashiCorp Vault Transit key in addition to removing your rows from our database. Any encrypted field still sitting in a backup, cache, or running process becomes mathematically unrecoverable ciphertext at that moment \u2014 cryptographic erasure on top of row-level deletion."
|
|
32976
33926
|
}
|
|
32977
33927
|
},
|
|
32978
|
-
|
|
33928
|
+
observability_without_tracking: {
|
|
32979
33929
|
heading: {
|
|
32980
|
-
text: "
|
|
33930
|
+
text: "Observability without tracking"
|
|
32981
33931
|
},
|
|
32982
33932
|
description: {
|
|
32983
|
-
text: "
|
|
32984
|
-
},
|
|
32985
|
-
meetup: {
|
|
32986
|
-
heading: {
|
|
32987
|
-
text: "Meetup"
|
|
32988
|
-
},
|
|
32989
|
-
description: {
|
|
32990
|
-
text: "Meetup powers event search for community events. Meetup receives your search queries \u2014 city, category, dates \u2014 without a user identifier."
|
|
32991
|
-
}
|
|
32992
|
-
},
|
|
32993
|
-
luma: {
|
|
32994
|
-
heading: {
|
|
32995
|
-
text: "Luma"
|
|
32996
|
-
},
|
|
32997
|
-
description: {
|
|
32998
|
-
text: "Luma powers event search for tech, creative, and community events. Luma receives your search queries without a user identifier."
|
|
32999
|
-
}
|
|
33000
|
-
},
|
|
33001
|
-
resident_advisor: {
|
|
33002
|
-
heading: {
|
|
33003
|
-
text: "Resident Advisor"
|
|
33004
|
-
},
|
|
33005
|
-
description: {
|
|
33006
|
-
text: "Resident Advisor powers event search for electronic music events. RA receives your search queries without a user identifier."
|
|
33007
|
-
}
|
|
33933
|
+
text: "Our internal tracing pipeline (OpenTelemetry) replaces your user ID with a hash that rotates every 24 hours, strips authentication headers and cookies from every span, and redacts chat content before any trace leaves the backend. Our web analytics use a server-side beacon with no cookies and no stored individual identifiers. We do not use Google Analytics, Plausible, PostHog, or any third-party analytics platform."
|
|
33008
33934
|
}
|
|
33935
|
+
}
|
|
33936
|
+
},
|
|
33937
|
+
promises: {
|
|
33938
|
+
intro: {
|
|
33939
|
+
text: "Below are the privacy promises we make to every user. Each one is backed by code in our open-source repository and verified by automated tests. When the enforcing code changes, a registry-linked check reminds us to rerun those tests before release."
|
|
33009
33940
|
},
|
|
33010
|
-
|
|
33941
|
+
client_side_chat_encryption: {
|
|
33011
33942
|
heading: {
|
|
33012
|
-
text: "
|
|
33943
|
+
text: "Client-side chat encryption"
|
|
33013
33944
|
},
|
|
33014
33945
|
description: {
|
|
33015
|
-
text:
|
|
33016
|
-
},
|
|
33017
|
-
doctolib: {
|
|
33018
|
-
heading: {
|
|
33019
|
-
text: "Doctolib (EU)"
|
|
33020
|
-
},
|
|
33021
|
-
description: {
|
|
33022
|
-
text: "Doctolib lists doctors and specialists across supported EU regions. We query Doctolib for available appointments matching your criteria, routed through the Webshare proxy, without a user identifier."
|
|
33023
|
-
}
|
|
33024
|
-
},
|
|
33025
|
-
jameda: {
|
|
33026
|
-
heading: {
|
|
33027
|
-
text: "Jameda (Germany)"
|
|
33028
|
-
},
|
|
33029
|
-
description: {
|
|
33030
|
-
text: "Jameda lists doctors and specialists across Germany. We query Jameda for available appointments matching your criteria, routed through the Webshare proxy, without a user identifier."
|
|
33031
|
-
}
|
|
33946
|
+
text: "Your chats, settings, and memories are encrypted on your device with AES-256-GCM before they leave your browser. The server stores only ciphertext on disk, in caches, and in backups. This is not end-to-end encryption: when a server-side task needs to read content (to run an AI response, render an invoice, or deliver a reminder), it decrypts in memory via HashiCorp Vault and discards the plaintext immediately after use."
|
|
33032
33947
|
}
|
|
33033
33948
|
},
|
|
33034
|
-
|
|
33949
|
+
email_encryption_at_rest: {
|
|
33035
33950
|
heading: {
|
|
33036
|
-
text: "
|
|
33951
|
+
text: "Email addresses encrypted at rest"
|
|
33037
33952
|
},
|
|
33038
33953
|
description: {
|
|
33039
|
-
text: "
|
|
33040
|
-
},
|
|
33041
|
-
rewe: {
|
|
33042
|
-
heading: {
|
|
33043
|
-
text: "REWE (German grocery)"
|
|
33044
|
-
},
|
|
33045
|
-
description: {
|
|
33046
|
-
text: "REWE's product catalogue is queried for live product and price data when you search German grocery items. REWE receives your search queries without a user identifier."
|
|
33047
|
-
}
|
|
33048
|
-
},
|
|
33049
|
-
amazon: {
|
|
33050
|
-
heading: {
|
|
33051
|
-
text: "Amazon"
|
|
33052
|
-
},
|
|
33053
|
-
description: {
|
|
33054
|
-
text: "Amazon's product catalogue is queried for live product and price data. Amazon receives your search queries without a user identifier."
|
|
33055
|
-
}
|
|
33954
|
+
text: "Your email address is stored only as ciphertext. The server decrypts it transiently in memory during login, billing, and notification delivery, and never writes plaintext to disk or logs."
|
|
33056
33955
|
}
|
|
33057
33956
|
},
|
|
33058
|
-
|
|
33957
|
+
no_third_party_tracking: {
|
|
33059
33958
|
heading: {
|
|
33060
|
-
text: "
|
|
33959
|
+
text: "No third-party tracking cookies or analytics"
|
|
33061
33960
|
},
|
|
33062
33961
|
description: {
|
|
33063
|
-
text: "
|
|
33064
|
-
},
|
|
33065
|
-
edamam: {
|
|
33066
|
-
heading: {
|
|
33067
|
-
text: "Edamam"
|
|
33068
|
-
},
|
|
33069
|
-
description: {
|
|
33070
|
-
text: "Edamam's Recipe Search API is queried when you search for recipes. Edamam receives the recipe query and selected dietary or nutrition filters without a user identifier."
|
|
33071
|
-
}
|
|
33962
|
+
text: "We do not use Google Analytics, Plausible, PostHog, Mixpanel, Amplitude, Segment, or any other third-party analytics platform. Our telemetry is server-side, cookie-free, and contains no individually stored identifiers. A pre-edit check in our repository blocks the introduction of analytics SDKs."
|
|
33072
33963
|
}
|
|
33073
33964
|
},
|
|
33074
|
-
|
|
33965
|
+
pii_placeholder_substitution: {
|
|
33075
33966
|
heading: {
|
|
33076
|
-
text: "
|
|
33967
|
+
text: "Personal information never reaches AI providers"
|
|
33077
33968
|
},
|
|
33078
33969
|
description: {
|
|
33079
|
-
text: "
|
|
33080
|
-
},
|
|
33081
|
-
ti_webench: {
|
|
33082
|
-
heading: {
|
|
33083
|
-
text: "Texas Instruments WEBENCH (US)"
|
|
33084
|
-
},
|
|
33085
|
-
description: {
|
|
33086
|
-
text: "TI WEBENCH is queried for power converter component candidates and reference design summaries. Texas Instruments receives the electrical requirements you search for, such as input voltage, output voltage, output current, temperature, isolation, and optimization goal, without a user identifier."
|
|
33087
|
-
}
|
|
33970
|
+
text: "Before any message is sent to an AI provider, your browser detects 32 categories of personal data \u2014 emails, phone numbers, credit card numbers, API keys, and more \u2014 and replaces them with placeholders like `[EMAIL_com]`. The substitution map is encrypted with your chat key so only your devices can restore the original values."
|
|
33088
33971
|
}
|
|
33089
33972
|
},
|
|
33090
|
-
|
|
33973
|
+
telemetry_privacy_filter: {
|
|
33091
33974
|
heading: {
|
|
33092
|
-
text: "
|
|
33975
|
+
text: "Telemetry stripped of sensitive data"
|
|
33093
33976
|
},
|
|
33094
33977
|
description: {
|
|
33095
|
-
text: "
|
|
33096
|
-
},
|
|
33097
|
-
protonmail: {
|
|
33098
|
-
heading: {
|
|
33099
|
-
text: "Proton Mail (mail search)"
|
|
33100
|
-
},
|
|
33101
|
-
description: {
|
|
33102
|
-
text: "Proton Mail is queried when you search a connected Proton Mail mailbox. Proton receives the mail search query and mailbox data needed to return matching results."
|
|
33103
|
-
}
|
|
33978
|
+
text: "Our internal tracing pipeline (OpenTelemetry) strips authentication headers, cookies, and database statements from every span, and pseudonymises your user ID with a salt that rotates every 24 hours. Regular users never have their raw identifiers in telemetry."
|
|
33104
33979
|
}
|
|
33105
33980
|
},
|
|
33106
|
-
|
|
33981
|
+
cryptographic_erasure: {
|
|
33107
33982
|
heading: {
|
|
33108
|
-
text: "
|
|
33983
|
+
text: "Deleted accounts are cryptographically erased"
|
|
33109
33984
|
},
|
|
33110
33985
|
description: {
|
|
33111
|
-
text: "
|
|
33112
|
-
}
|
|
33113
|
-
|
|
33114
|
-
|
|
33115
|
-
|
|
33116
|
-
|
|
33117
|
-
description: {
|
|
33118
|
-
text: "ImmoScout24 is queried when you search German housing listings. ImmoScout24 receives housing search queries, locations, filters, and listing pages requested without an OpenMates user identifier."
|
|
33119
|
-
}
|
|
33986
|
+
text: "When you delete your account, the first step destroys your encryption keys. Any residual ciphertext in backups, caches, or audit logs is rendered permanently unreadable \u2014 no key, no content."
|
|
33987
|
+
}
|
|
33988
|
+
},
|
|
33989
|
+
argon2_password_hashing: {
|
|
33990
|
+
heading: {
|
|
33991
|
+
text: "Passwords stored as Argon2 hashes"
|
|
33120
33992
|
},
|
|
33121
|
-
|
|
33122
|
-
|
|
33123
|
-
|
|
33124
|
-
|
|
33125
|
-
|
|
33126
|
-
|
|
33127
|
-
|
|
33993
|
+
description: {
|
|
33994
|
+
text: "We store password and backup-code verifiers as Argon2 hashes. Plaintext passwords are never persisted to disk, never written to logs, and never recoverable \u2014 even by us."
|
|
33995
|
+
}
|
|
33996
|
+
},
|
|
33997
|
+
payment_data_minimization: {
|
|
33998
|
+
heading: {
|
|
33999
|
+
text: "No card numbers ever touch our servers"
|
|
33128
34000
|
},
|
|
33129
|
-
|
|
33130
|
-
|
|
33131
|
-
text: "WG-Gesucht (German shared-apartment search)"
|
|
33132
|
-
},
|
|
33133
|
-
description: {
|
|
33134
|
-
text: "WG-Gesucht is queried when you search German shared-apartment or rental listings. WG-Gesucht receives housing search queries, locations, filters, and listing pages requested without an OpenMates user identifier."
|
|
33135
|
-
}
|
|
34001
|
+
description: {
|
|
34002
|
+
text: "Payments flow directly from your browser to Stripe. We store only provider tokens and customer identifiers. We never see, receive, or persist full card numbers, security codes, or bank account details."
|
|
33136
34003
|
}
|
|
33137
34004
|
},
|
|
33138
|
-
|
|
34005
|
+
logging_redaction: {
|
|
33139
34006
|
heading: {
|
|
33140
|
-
text: "
|
|
34007
|
+
text: "Logs are scrubbed of sensitive data"
|
|
33141
34008
|
},
|
|
33142
34009
|
description: {
|
|
33143
|
-
text: "
|
|
34010
|
+
text: "Every log line passes through a redaction filter that strips email addresses, IP addresses, bearer tokens, and password values. Compliance logs preserve pseudonymous user IDs but remove all other personal data."
|
|
34011
|
+
}
|
|
34012
|
+
},
|
|
34013
|
+
prompt_injection_defense: {
|
|
34014
|
+
heading: {
|
|
34015
|
+
text: "Defense against prompt injection"
|
|
33144
34016
|
},
|
|
33145
|
-
|
|
33146
|
-
|
|
33147
|
-
text: "Discord (community server)"
|
|
33148
|
-
},
|
|
33149
|
-
description: {
|
|
33150
|
-
text: "If you join the OpenMates community Discord server, Discord will see anything you post on its platform, plus your Discord username and whatever data Discord itself collects per its own privacy policy. We have no control over what Discord does with that data."
|
|
33151
|
-
},
|
|
33152
|
-
admin_access: {
|
|
33153
|
-
text: "OpenMates administrators with Discord server admin rights can read all messages posted in the OpenMates Discord server. Do not share sensitive information on Discord."
|
|
33154
|
-
}
|
|
34017
|
+
description: {
|
|
34018
|
+
text: "Two layers protect your conversations from malicious content in web pages, files, and URLs you reference. Invisible Unicode characters are stripped first, then a dedicated safety model detects semantic injection attempts and blocks or replaces high-risk content before it reaches your assistant."
|
|
33155
34019
|
}
|
|
33156
34020
|
},
|
|
33157
|
-
|
|
34021
|
+
no_training_on_user_data: {
|
|
33158
34022
|
heading: {
|
|
33159
|
-
text: "
|
|
34023
|
+
text: "Your conversations are not used to train AI models"
|
|
33160
34024
|
},
|
|
33161
34025
|
description: {
|
|
33162
|
-
text: "
|
|
34026
|
+
text: "All AI chat providers we use (Anthropic, OpenAI, Mistral, Google Gemini, Google Vertex AI, Together AI, Groq, Cerebras, and OpenRouter) have explicit no-training clauses in their terms \u2014 your conversations are never used to fine-tune, evaluate, or otherwise train AI models. For image generation, Recraft has a training opt-out which we have activated. fal.ai (used for draft image generation) may use anonymized, aggregated derivatives of prompts for model improvement under their standard API terms; no full opt-out is available outside an enterprise contract. A per-provider audit with verbatim policy quotes and verification dates is published in our open-source repository."
|
|
34027
|
+
}
|
|
34028
|
+
},
|
|
34029
|
+
no_external_resources: {
|
|
34030
|
+
heading: {
|
|
34031
|
+
text: "The web app never loads external images or scripts"
|
|
33163
34032
|
},
|
|
33164
|
-
|
|
33165
|
-
|
|
33166
|
-
|
|
33167
|
-
|
|
33168
|
-
|
|
33169
|
-
|
|
33170
|
-
|
|
34033
|
+
description: {
|
|
34034
|
+
text: "Every external image, favicon, or preview shown in the app is fetched through our own preview.openmates.org proxy \u2014 the originating website never sees your IP address. The only third-party script the web app loads is the Stripe payment SDK needed to process a purchase, and only when you explicitly open the payment flow. There is no ad network, tag manager, CDN font loader, or third-party analytics library."
|
|
34035
|
+
}
|
|
34036
|
+
},
|
|
34037
|
+
cli_no_credential_prompts: {
|
|
34038
|
+
heading: {
|
|
34039
|
+
text: "The command-line tool never asks for your password"
|
|
33171
34040
|
},
|
|
33172
|
-
|
|
33173
|
-
|
|
33174
|
-
|
|
33175
|
-
|
|
33176
|
-
|
|
33177
|
-
|
|
33178
|
-
|
|
34041
|
+
description: {
|
|
34042
|
+
text: "The OpenMates CLI never prompts you for your email, password, 2FA code, or any other login credential. Authentication uses a browser-based pair-auth handshake: the CLI shows a QR code and URL, you approve the session in your logged-in web browser, and you type a short 6-character binding PIN back into the CLI to confirm. Your credentials never pass through the terminal."
|
|
34043
|
+
}
|
|
34044
|
+
},
|
|
34045
|
+
open_source_transparency: {
|
|
34046
|
+
heading: {
|
|
34047
|
+
text: "Every claim on this page is independently auditable"
|
|
33179
34048
|
},
|
|
33180
|
-
|
|
33181
|
-
|
|
33182
|
-
text: "Mastodon (public profile posts)"
|
|
33183
|
-
},
|
|
33184
|
-
description: {
|
|
33185
|
-
text: "Mastodon is queried when you fetch public Mastodon profile posts. The relevant Mastodon server receives profile identifiers or public profile URLs without an OpenMates user identifier."
|
|
33186
|
-
}
|
|
34049
|
+
description: {
|
|
34050
|
+
text: "OpenMates is open source. Every promise above is backed by code you can read, tests you can run, and architecture documents you can audit in our public repository. Our privacy-promises registry links each claim to the exact files that enforce it."
|
|
33187
34051
|
}
|
|
33188
34052
|
}
|
|
33189
34053
|
},
|
|
@@ -35439,6 +36303,33 @@ As of mid-2026, the severe supply shocks from the 2024\u20132025 avian flu have
|
|
|
35439
36303
|
connect_button: {
|
|
35440
36304
|
text: "Connect Google Calendar"
|
|
35441
36305
|
},
|
|
36306
|
+
manage_account: {
|
|
36307
|
+
text: "Manage {label}"
|
|
36308
|
+
},
|
|
36309
|
+
capability_read: {
|
|
36310
|
+
text: "Read events"
|
|
36311
|
+
},
|
|
36312
|
+
capability_read_description: {
|
|
36313
|
+
text: "Let OpenMates request permission before reading calendar events."
|
|
36314
|
+
},
|
|
36315
|
+
capability_write: {
|
|
36316
|
+
text: "Write events"
|
|
36317
|
+
},
|
|
36318
|
+
capability_write_description: {
|
|
36319
|
+
text: "Let OpenMates request permission before creating or updating events."
|
|
36320
|
+
},
|
|
36321
|
+
capability_delete: {
|
|
36322
|
+
text: "Delete events"
|
|
36323
|
+
},
|
|
36324
|
+
capability_delete_description: {
|
|
36325
|
+
text: "Let OpenMates request permission before deleting events."
|
|
36326
|
+
},
|
|
36327
|
+
oauth_summary: {
|
|
36328
|
+
text: "OAuth request: {capabilities}"
|
|
36329
|
+
},
|
|
36330
|
+
no_capabilities_selected: {
|
|
36331
|
+
text: "No access selected"
|
|
36332
|
+
},
|
|
35442
36333
|
finalizing: {
|
|
35443
36334
|
text: "Encrypting and saving your connected account..."
|
|
35444
36335
|
},
|
|
@@ -36966,7 +37857,10 @@ As of mid-2026, the severe supply shocks from the 2024\u20132025 avian flu have
|
|
|
36966
37857
|
}
|
|
36967
37858
|
},
|
|
36968
37859
|
apps: {
|
|
36969
|
-
text: "Apps"
|
|
37860
|
+
text: "Apps",
|
|
37861
|
+
all: {
|
|
37862
|
+
text: "Apps"
|
|
37863
|
+
}
|
|
36970
37864
|
},
|
|
36971
37865
|
mates: {
|
|
36972
37866
|
description: {
|
|
@@ -37444,6 +38338,71 @@ As of mid-2026, the severe supply shocks from the 2024\u20132025 avian flu have
|
|
|
37444
38338
|
text: "OpenMates will auto replace sensitive data with placeholders, before sending them to the OpenMates server and any external provider, to protect your privacy. Replaced data are visually highlighted. You can always click on a replaced data entry to overwrite the replacement, or turn off the functionality completely here."
|
|
37445
38339
|
}
|
|
37446
38340
|
},
|
|
38341
|
+
connected_accounts: {
|
|
38342
|
+
title: {
|
|
38343
|
+
text: "Connected accounts"
|
|
38344
|
+
},
|
|
38345
|
+
subtitle: {
|
|
38346
|
+
text: "Apps | Providers"
|
|
38347
|
+
},
|
|
38348
|
+
description: {
|
|
38349
|
+
text: "Connected accounts are stored as encrypted browser-managed records. OpenMates only receives encrypted account data and opaque hashes."
|
|
38350
|
+
},
|
|
38351
|
+
loading: {
|
|
38352
|
+
text: "Loading connected accounts..."
|
|
38353
|
+
},
|
|
38354
|
+
empty: {
|
|
38355
|
+
text: "No connected accounts yet."
|
|
38356
|
+
},
|
|
38357
|
+
accounts: {
|
|
38358
|
+
text: "Accounts"
|
|
38359
|
+
},
|
|
38360
|
+
details: {
|
|
38361
|
+
text: "Account details"
|
|
38362
|
+
},
|
|
38363
|
+
account_label: {
|
|
38364
|
+
text: "Account label"
|
|
38365
|
+
},
|
|
38366
|
+
provider: {
|
|
38367
|
+
text: "Provider"
|
|
38368
|
+
},
|
|
38369
|
+
provider_google_calendar: {
|
|
38370
|
+
text: "Google Calendar"
|
|
38371
|
+
},
|
|
38372
|
+
app: {
|
|
38373
|
+
text: "App"
|
|
38374
|
+
},
|
|
38375
|
+
capabilities: {
|
|
38376
|
+
text: "Capabilities"
|
|
38377
|
+
},
|
|
38378
|
+
runtime_modes: {
|
|
38379
|
+
text: "Runtime modes"
|
|
38380
|
+
},
|
|
38381
|
+
none: {
|
|
38382
|
+
text: "None"
|
|
38383
|
+
},
|
|
38384
|
+
add_calendar_write: {
|
|
38385
|
+
text: "Add Calendar write access"
|
|
38386
|
+
},
|
|
38387
|
+
finalizing: {
|
|
38388
|
+
text: "Encrypting and saving connected account update..."
|
|
38389
|
+
},
|
|
38390
|
+
updated_success: {
|
|
38391
|
+
text: "Connected account updated. New access was encrypted before saving."
|
|
38392
|
+
},
|
|
38393
|
+
sign_in_required: {
|
|
38394
|
+
text: "Sign in to manage connected accounts."
|
|
38395
|
+
},
|
|
38396
|
+
load_error: {
|
|
38397
|
+
text: "Could not load connected accounts."
|
|
38398
|
+
},
|
|
38399
|
+
update_error: {
|
|
38400
|
+
text: "Could not start connected account update."
|
|
38401
|
+
},
|
|
38402
|
+
finalize_error: {
|
|
38403
|
+
text: "Could not finish updating connected account."
|
|
38404
|
+
}
|
|
38405
|
+
},
|
|
37447
38406
|
nearby_by_default: {
|
|
37448
38407
|
text: "Nearby by default"
|
|
37449
38408
|
},
|
|
@@ -37787,7 +38746,10 @@ As of mid-2026, the severe supply shocks from the 2024\u20132025 avian flu have
|
|
|
37787
38746
|
text: "Community Suggestions"
|
|
37788
38747
|
},
|
|
37789
38748
|
stats: {
|
|
37790
|
-
text: "Stats"
|
|
38749
|
+
text: "Stats",
|
|
38750
|
+
subtitle: {
|
|
38751
|
+
text: "View global server usage and growth metrics"
|
|
38752
|
+
}
|
|
37791
38753
|
},
|
|
37792
38754
|
gift_cards: {
|
|
37793
38755
|
text: "Gift Cards",
|
|
@@ -37938,6 +38900,89 @@ As of mid-2026, the severe supply shocks from the 2024\u20132025 avian flu have
|
|
|
37938
38900
|
free_testing_credits: {
|
|
37939
38901
|
text: "Free testing credits"
|
|
37940
38902
|
},
|
|
38903
|
+
anonymous_free_usage_budget: {
|
|
38904
|
+
title: {
|
|
38905
|
+
text: "Anonymous free usage"
|
|
38906
|
+
},
|
|
38907
|
+
subtitle: {
|
|
38908
|
+
text: "Configure logged-out free chat budget caps"
|
|
38909
|
+
},
|
|
38910
|
+
loading: {
|
|
38911
|
+
text: "Loading anonymous free usage budget..."
|
|
38912
|
+
},
|
|
38913
|
+
load_error: {
|
|
38914
|
+
text: "Failed to load anonymous free usage budget."
|
|
38915
|
+
},
|
|
38916
|
+
save_success: {
|
|
38917
|
+
text: "Anonymous free usage budget saved."
|
|
38918
|
+
},
|
|
38919
|
+
save_error: {
|
|
38920
|
+
text: "Failed to save anonymous free usage budget."
|
|
38921
|
+
},
|
|
38922
|
+
status_active: {
|
|
38923
|
+
text: "Anonymous free usage is active for new logged-out users."
|
|
38924
|
+
},
|
|
38925
|
+
status_daily_exhausted: {
|
|
38926
|
+
text: "The anonymous free usage daily cap is exhausted until the next reset."
|
|
38927
|
+
},
|
|
38928
|
+
status_weekly_exhausted: {
|
|
38929
|
+
text: "The anonymous free usage weekly cap is exhausted."
|
|
38930
|
+
},
|
|
38931
|
+
status_disabled: {
|
|
38932
|
+
text: "Anonymous free usage is currently inactive."
|
|
38933
|
+
},
|
|
38934
|
+
daily_cap: {
|
|
38935
|
+
text: "Daily cap"
|
|
38936
|
+
},
|
|
38937
|
+
weekly_cap: {
|
|
38938
|
+
text: "Weekly cap"
|
|
38939
|
+
},
|
|
38940
|
+
daily_used: {
|
|
38941
|
+
text: "Daily used"
|
|
38942
|
+
},
|
|
38943
|
+
weekly_used: {
|
|
38944
|
+
text: "Weekly used"
|
|
38945
|
+
},
|
|
38946
|
+
daily_remaining: {
|
|
38947
|
+
text: "Daily remaining"
|
|
38948
|
+
},
|
|
38949
|
+
weekly_remaining: {
|
|
38950
|
+
text: "Weekly remaining"
|
|
38951
|
+
},
|
|
38952
|
+
reset_at: {
|
|
38953
|
+
text: "Daily reset"
|
|
38954
|
+
},
|
|
38955
|
+
updated_at: {
|
|
38956
|
+
text: "Updated"
|
|
38957
|
+
},
|
|
38958
|
+
never: {
|
|
38959
|
+
text: "Never"
|
|
38960
|
+
},
|
|
38961
|
+
enabled: {
|
|
38962
|
+
text: "Enable anonymous free usage for logged-out users"
|
|
38963
|
+
},
|
|
38964
|
+
monthly_budget: {
|
|
38965
|
+
text: "Monthly budget credits"
|
|
38966
|
+
},
|
|
38967
|
+
daily_percent: {
|
|
38968
|
+
text: "Daily hard cap percent"
|
|
38969
|
+
},
|
|
38970
|
+
weekly_percent: {
|
|
38971
|
+
text: "Weekly cap percent"
|
|
38972
|
+
},
|
|
38973
|
+
per_identity_cap: {
|
|
38974
|
+
text: "Per-identity daily cap credits"
|
|
38975
|
+
},
|
|
38976
|
+
derived_daily_cap: {
|
|
38977
|
+
text: "Derived daily cap"
|
|
38978
|
+
},
|
|
38979
|
+
derived_weekly_cap: {
|
|
38980
|
+
text: "Derived weekly cap"
|
|
38981
|
+
},
|
|
38982
|
+
validation_percent: {
|
|
38983
|
+
text: "Percent values must be between 0 and 100."
|
|
38984
|
+
}
|
|
38985
|
+
},
|
|
37941
38986
|
tests: {
|
|
37942
38987
|
text: "Tests",
|
|
37943
38988
|
subtitle: {
|
|
@@ -40433,6 +41478,28 @@ function toWaitingForUserResult(params) {
|
|
|
40433
41478
|
};
|
|
40434
41479
|
}
|
|
40435
41480
|
|
|
41481
|
+
// src/feedback.ts
|
|
41482
|
+
var ASSISTANT_FEEDBACK_THANKS = "Thanks for the feedback!";
|
|
41483
|
+
var ASSISTANT_FEEDBACK_REPORT_TITLE = "Assistant response quality bad:";
|
|
41484
|
+
function buildAssistantFeedbackDecision(rating) {
|
|
41485
|
+
if (!Number.isInteger(rating) || rating < 1 || rating > 5) {
|
|
41486
|
+
throw new Error("Rating must be an integer from 1 to 5.");
|
|
41487
|
+
}
|
|
41488
|
+
if (rating <= 3) {
|
|
41489
|
+
return {
|
|
41490
|
+
rating,
|
|
41491
|
+
action: "report_issue",
|
|
41492
|
+
message: ASSISTANT_FEEDBACK_THANKS,
|
|
41493
|
+
reportTitle: ASSISTANT_FEEDBACK_REPORT_TITLE
|
|
41494
|
+
};
|
|
41495
|
+
}
|
|
41496
|
+
return {
|
|
41497
|
+
rating,
|
|
41498
|
+
action: "thanks",
|
|
41499
|
+
message: ASSISTANT_FEEDBACK_THANKS
|
|
41500
|
+
};
|
|
41501
|
+
}
|
|
41502
|
+
|
|
40436
41503
|
// src/cli.ts
|
|
40437
41504
|
async function main() {
|
|
40438
41505
|
const parsed = parseArgs(process.argv.slice(2));
|
|
@@ -40441,11 +41508,13 @@ async function main() {
|
|
|
40441
41508
|
apiUrl: typeof parsed.flags["api-url"] === "string" ? parsed.flags["api-url"] : void 0
|
|
40442
41509
|
});
|
|
40443
41510
|
const redactor = new OutputRedactor();
|
|
40444
|
-
|
|
41511
|
+
const piiDetectionEnabled = parsed.flags["no-pii-detection"] !== true;
|
|
41512
|
+
if (piiDetectionEnabled && shouldInitializeRedactor(command, subcommand)) {
|
|
40445
41513
|
try {
|
|
40446
|
-
const memories = await client.listMemories();
|
|
41514
|
+
const memories = client.hasSession() ? await client.listMemories() : [];
|
|
40447
41515
|
redactor.initializeFromMemories(memories);
|
|
40448
41516
|
} catch {
|
|
41517
|
+
redactor.initializeFromMemories([]);
|
|
40449
41518
|
}
|
|
40450
41519
|
}
|
|
40451
41520
|
if (!command || command === "help") {
|
|
@@ -40493,6 +41562,10 @@ async function main() {
|
|
|
40493
41562
|
printServerHelp();
|
|
40494
41563
|
return;
|
|
40495
41564
|
}
|
|
41565
|
+
if (command === "feedback") {
|
|
41566
|
+
printFeedbackHelp();
|
|
41567
|
+
return;
|
|
41568
|
+
}
|
|
40496
41569
|
if (command === "docs") {
|
|
40497
41570
|
printDocsHelp();
|
|
40498
41571
|
return;
|
|
@@ -40563,6 +41636,10 @@ async function main() {
|
|
|
40563
41636
|
await handleNewChatSuggestions(client, parsed.flags);
|
|
40564
41637
|
return;
|
|
40565
41638
|
}
|
|
41639
|
+
if (command === "feedback") {
|
|
41640
|
+
handleFeedback(subcommand, rest, parsed.flags);
|
|
41641
|
+
return;
|
|
41642
|
+
}
|
|
40566
41643
|
throw new Error(`Unknown command '${command}'. Run 'openmates help'.`);
|
|
40567
41644
|
}
|
|
40568
41645
|
function shouldInitializeRedactor(command, subcommand) {
|
|
@@ -40618,7 +41695,8 @@ async function handleChats(client, subcommand, rest, flags, redactor) {
|
|
|
40618
41695
|
incognito: false,
|
|
40619
41696
|
json: flags.json === true,
|
|
40620
41697
|
autoApproveSubChats: flags["auto-approve"] === true,
|
|
40621
|
-
autoApproveMemories: flags["auto-approve-memories"] === true
|
|
41698
|
+
autoApproveMemories: flags["auto-approve-memories"] === true,
|
|
41699
|
+
piiDetection: flags["no-pii-detection"] !== true
|
|
40622
41700
|
},
|
|
40623
41701
|
redactor
|
|
40624
41702
|
);
|
|
@@ -40691,7 +41769,8 @@ Run 'openmates chats show ` + chatId + "' to check if suggestions have been save
|
|
|
40691
41769
|
incognito: flags.incognito === true,
|
|
40692
41770
|
json: flags.json === true,
|
|
40693
41771
|
autoApproveSubChats: flags["auto-approve"] === true,
|
|
40694
|
-
autoApproveMemories: flags["auto-approve-memories"] === true
|
|
41772
|
+
autoApproveMemories: flags["auto-approve-memories"] === true,
|
|
41773
|
+
piiDetection: flags["no-pii-detection"] !== true
|
|
40695
41774
|
},
|
|
40696
41775
|
redactor
|
|
40697
41776
|
);
|
|
@@ -40711,7 +41790,8 @@ Run 'openmates chats show ` + chatId + "' to check if suggestions have been save
|
|
|
40711
41790
|
incognito: true,
|
|
40712
41791
|
json: flags.json === true,
|
|
40713
41792
|
autoApproveSubChats: flags["auto-approve"] === true,
|
|
40714
|
-
autoApproveMemories: flags["auto-approve-memories"] === true
|
|
41793
|
+
autoApproveMemories: flags["auto-approve-memories"] === true,
|
|
41794
|
+
piiDetection: flags["no-pii-detection"] !== true
|
|
40715
41795
|
},
|
|
40716
41796
|
redactor
|
|
40717
41797
|
);
|
|
@@ -43013,6 +44093,29 @@ Usage: openmates settings memories <list|types|create|update|delete>`
|
|
|
43013
44093
|
);
|
|
43014
44094
|
process.exit(1);
|
|
43015
44095
|
}
|
|
44096
|
+
function handleFeedback(subcommand, _rest, flags) {
|
|
44097
|
+
if (!subcommand || subcommand === "help" || flags.help === true) {
|
|
44098
|
+
printFeedbackHelp();
|
|
44099
|
+
return;
|
|
44100
|
+
}
|
|
44101
|
+
if (subcommand !== "assistant-response") {
|
|
44102
|
+
throw new Error(`Unknown feedback command '${subcommand}'. Run 'openmates feedback --help'.`);
|
|
44103
|
+
}
|
|
44104
|
+
const rawRating = flags.rating;
|
|
44105
|
+
if (typeof rawRating !== "string") {
|
|
44106
|
+
throw new Error("Missing --rating <1-5>.");
|
|
44107
|
+
}
|
|
44108
|
+
const decision = buildAssistantFeedbackDecision(Number(rawRating));
|
|
44109
|
+
if (flags.json === true) {
|
|
44110
|
+
printJson2(decision);
|
|
44111
|
+
return;
|
|
44112
|
+
}
|
|
44113
|
+
console.log(decision.message);
|
|
44114
|
+
if (decision.action === "report_issue") {
|
|
44115
|
+
console.log(`Report issue title: ${decision.reportTitle}`);
|
|
44116
|
+
console.log("Open the report issue form and include the affected assistant response.");
|
|
44117
|
+
}
|
|
44118
|
+
}
|
|
43016
44119
|
function parseArgs(argv) {
|
|
43017
44120
|
const positionals = [];
|
|
43018
44121
|
const flags = {};
|
|
@@ -43301,6 +44404,20 @@ async function sendMessageStreaming(client, params, redactor) {
|
|
|
43301
44404
|
process.exit(1);
|
|
43302
44405
|
}
|
|
43303
44406
|
if (parsed.filePaths.length > 0) {
|
|
44407
|
+
if (!client.hasSession()) {
|
|
44408
|
+
clearTyping();
|
|
44409
|
+
const result2 = {
|
|
44410
|
+
status: "signup_required",
|
|
44411
|
+
reason: "file_upload_requires_signup",
|
|
44412
|
+
signup_required: true,
|
|
44413
|
+
message: "File uploads require signup. Your message text can be kept as a draft, but files must be attached after creating an account."
|
|
44414
|
+
};
|
|
44415
|
+
if (!params.json) {
|
|
44416
|
+
process.stderr.write(`${result2.message}
|
|
44417
|
+
`);
|
|
44418
|
+
}
|
|
44419
|
+
return result2;
|
|
44420
|
+
}
|
|
43304
44421
|
const fileResult = processFiles(parsed.filePaths, redactor ?? null);
|
|
43305
44422
|
for (const b of fileResult.blocked) {
|
|
43306
44423
|
clearTyping();
|
|
@@ -43439,6 +44556,29 @@ async function sendMessageStreaming(client, params, redactor) {
|
|
|
43439
44556
|
} catch {
|
|
43440
44557
|
}
|
|
43441
44558
|
}
|
|
44559
|
+
const piiResult = params.piiDetection !== false && redactor?.isInitialized ? redactor.redactWithMappings(finalMessage) : { redacted: finalMessage, mappings: [] };
|
|
44560
|
+
finalMessage = piiResult.redacted;
|
|
44561
|
+
if (!client.hasSession()) {
|
|
44562
|
+
let result2;
|
|
44563
|
+
try {
|
|
44564
|
+
result2 = await client.sendAnonymousMessage({ message: finalMessage });
|
|
44565
|
+
} finally {
|
|
44566
|
+
clearTyping();
|
|
44567
|
+
}
|
|
44568
|
+
if (!params.json) {
|
|
44569
|
+
const mateBlock = ansiMateBlock(result2.category, result2.mateName);
|
|
44570
|
+
const modelSuffix = result2.modelName ? ` \x1B[2m${result2.modelName}\x1B[0m` : "";
|
|
44571
|
+
process.stdout.write(`${SEP}
|
|
44572
|
+
`);
|
|
44573
|
+
process.stdout.write(`${mateBlock}${modelSuffix}
|
|
44574
|
+
`);
|
|
44575
|
+
process.stdout.write(`${SEP}
|
|
44576
|
+
`);
|
|
44577
|
+
process.stdout.write(`${result2.assistant}
|
|
44578
|
+
`);
|
|
44579
|
+
}
|
|
44580
|
+
return result2;
|
|
44581
|
+
}
|
|
43442
44582
|
const urlResult = prepareUrlEmbeds(finalMessage);
|
|
43443
44583
|
finalMessage = urlResult.message;
|
|
43444
44584
|
preparedEmbeds.push(...urlResult.embeds);
|
|
@@ -43451,7 +44591,12 @@ async function sendMessageStreaming(client, params, redactor) {
|
|
|
43451
44591
|
onSubChatApprovalRequest,
|
|
43452
44592
|
autoApproveSubChats: params.autoApproveSubChats,
|
|
43453
44593
|
autoApproveMemories: params.autoApproveMemories,
|
|
43454
|
-
preparedEmbeds: preparedEmbeds.length > 0 ? preparedEmbeds : void 0
|
|
44594
|
+
preparedEmbeds: preparedEmbeds.length > 0 ? preparedEmbeds : void 0,
|
|
44595
|
+
piiMappings: piiResult.mappings.map((mapping) => ({
|
|
44596
|
+
placeholder: mapping.placeholder,
|
|
44597
|
+
original: mapping.original,
|
|
44598
|
+
type: mapping.type
|
|
44599
|
+
}))
|
|
43455
44600
|
});
|
|
43456
44601
|
clearTyping();
|
|
43457
44602
|
if (result.status === "waiting_for_user") {
|
|
@@ -44754,6 +45899,7 @@ Commands:
|
|
|
44754
45899
|
openmates settings [--help] Predefined settings commands
|
|
44755
45900
|
openmates inspirations [--lang <code>] [--json] Daily inspirations
|
|
44756
45901
|
openmates newchatsuggestions [--limit <n>] [--json] Personalized new chat suggestions
|
|
45902
|
+
openmates feedback [--help] Assistant response feedback helpers
|
|
44757
45903
|
openmates server [--help] Server management (install, start, stop, ...)
|
|
44758
45904
|
openmates docs [--help] Browse, search, and download documentation
|
|
44759
45905
|
openmates e2e provision-auth-accounts Provision local E2E auth-account artifacts
|
|
@@ -44764,6 +45910,18 @@ Flags:
|
|
|
44764
45910
|
--api-key <key> Optional API key override (or set OPENMATES_API_KEY)
|
|
44765
45911
|
--help Show contextual help for any command`);
|
|
44766
45912
|
}
|
|
45913
|
+
function printFeedbackHelp() {
|
|
45914
|
+
console.log(`Feedback commands:
|
|
45915
|
+
openmates feedback assistant-response --rating <1-5> [--json]
|
|
45916
|
+
|
|
45917
|
+
Mirrors the web chat assistant-response feedback decision:
|
|
45918
|
+
4-5 stars Thank the user only
|
|
45919
|
+
1-3 stars Thank the user and prompt a report issue with the standard prefill
|
|
45920
|
+
|
|
45921
|
+
Options:
|
|
45922
|
+
--rating <1-5> Required star rating
|
|
45923
|
+
--json Output the decision contract as JSON`);
|
|
45924
|
+
}
|
|
44767
45925
|
function printSignupHelp() {
|
|
44768
45926
|
console.log(`Signup command:
|
|
44769
45927
|
openmates signup --email <email> --username <name> --invite-code <code>
|
|
@@ -44804,13 +45962,13 @@ function printChatsHelp() {
|
|
|
44804
45962
|
openmates chats show <chat-id> [--raw] [--json]
|
|
44805
45963
|
openmates chats open [<n|example-id|slug>] [--json]
|
|
44806
45964
|
openmates chats search <query> [--json]
|
|
44807
|
-
openmates chats new <message> [--json] [--auto-approve] [--auto-approve-memories]
|
|
44808
|
-
openmates chats send [--chat <id>] [--incognito] <message> [--json] [--auto-approve] [--auto-approve-memories]
|
|
45965
|
+
openmates chats new <message> [--json] [--auto-approve] [--auto-approve-memories] [--no-pii-detection]
|
|
45966
|
+
openmates chats send [--chat <id>] [--incognito] <message> [--json] [--auto-approve] [--auto-approve-memories] [--no-pii-detection]
|
|
44809
45967
|
openmates chats send --chat <id> --followup <n> [--json] [--auto-approve] [--auto-approve-memories]
|
|
44810
45968
|
openmates chats download <chat-id> [--output <path>] [--zip] [--json]
|
|
44811
45969
|
openmates chats delete <id1> [id2] [id3] ... [--yes]
|
|
44812
45970
|
openmates chats share [<chat-id>] [--expires <seconds>] [--password <pwd>] [--json]
|
|
44813
|
-
openmates chats incognito <message> [--json]
|
|
45971
|
+
openmates chats incognito <message> [--json] [--no-pii-detection]
|
|
44814
45972
|
openmates chats incognito-history [--json] Deprecated: incognito stores no history
|
|
44815
45973
|
openmates chats incognito-clear Deprecated: incognito stores no history
|
|
44816
45974
|
|
|
@@ -44839,8 +45997,10 @@ Options for 'new', 'send', and 'incognito':
|
|
|
44839
45997
|
--auto-approve Automatically approve server-requested sub-chat batches.
|
|
44840
45998
|
Without this, the CLI prompts in the terminal like the web app.
|
|
44841
45999
|
--auto-approve-memories Explicitly approve server-requested memory categories.
|
|
44842
|
-
|
|
44843
|
-
|
|
46000
|
+
Memories are never approved by default.
|
|
46001
|
+
Use only for trusted non-interactive runs.
|
|
46002
|
+
--no-pii-detection Send the message exactly as typed. By default, the CLI
|
|
46003
|
+
replaces detected PII with placeholders before send.
|
|
44844
46004
|
|
|
44845
46005
|
Options for 'download':
|
|
44846
46006
|
--output <path> Target directory (default: current directory)
|
|
@@ -45172,6 +46332,9 @@ export {
|
|
|
45172
46332
|
deriveAppUrl,
|
|
45173
46333
|
OpenMatesClient,
|
|
45174
46334
|
defaultCloneBranchForVersion,
|
|
46335
|
+
ASSISTANT_FEEDBACK_THANKS,
|
|
46336
|
+
ASSISTANT_FEEDBACK_REPORT_TITLE,
|
|
46337
|
+
buildAssistantFeedbackDecision,
|
|
45175
46338
|
serializeToYaml,
|
|
45176
46339
|
getExtForLang
|
|
45177
46340
|
};
|