@contractspec/lib.knowledge 2.3.0 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/access/guard.d.ts +3 -0
  2. package/dist/access/guard.js +290 -5
  3. package/dist/access/index.js +290 -5
  4. package/dist/i18n/catalogs/en.d.ts +8 -0
  5. package/dist/i18n/catalogs/en.js +107 -0
  6. package/dist/i18n/catalogs/es.d.ts +6 -0
  7. package/dist/i18n/catalogs/es.js +80 -0
  8. package/dist/i18n/catalogs/fr.d.ts +6 -0
  9. package/dist/i18n/catalogs/fr.js +80 -0
  10. package/dist/i18n/catalogs/index.d.ts +8 -0
  11. package/dist/i18n/catalogs/index.js +263 -0
  12. package/dist/i18n/i18n.test.d.ts +1 -0
  13. package/dist/i18n/index.d.ts +27 -0
  14. package/dist/i18n/index.js +321 -0
  15. package/dist/i18n/keys.d.ts +74 -0
  16. package/dist/i18n/keys.js +33 -0
  17. package/dist/i18n/locale.d.ts +8 -0
  18. package/dist/i18n/locale.js +14 -0
  19. package/dist/i18n/messages.d.ts +14 -0
  20. package/dist/i18n/messages.js +275 -0
  21. package/dist/index.js +316 -23
  22. package/dist/ingestion/gmail-adapter.d.ts +2 -1
  23. package/dist/ingestion/gmail-adapter.js +284 -8
  24. package/dist/ingestion/index.js +284 -8
  25. package/dist/node/access/guard.js +290 -5
  26. package/dist/node/access/index.js +290 -5
  27. package/dist/node/i18n/catalogs/en.js +106 -0
  28. package/dist/node/i18n/catalogs/es.js +79 -0
  29. package/dist/node/i18n/catalogs/fr.js +79 -0
  30. package/dist/node/i18n/catalogs/index.js +262 -0
  31. package/dist/node/i18n/index.js +320 -0
  32. package/dist/node/i18n/keys.js +32 -0
  33. package/dist/node/i18n/locale.js +13 -0
  34. package/dist/node/i18n/messages.js +274 -0
  35. package/dist/node/index.js +316 -23
  36. package/dist/node/ingestion/gmail-adapter.js +284 -8
  37. package/dist/node/ingestion/index.js +284 -8
  38. package/dist/node/query/index.js +282 -10
  39. package/dist/node/query/service.js +282 -10
  40. package/dist/query/index.js +282 -10
  41. package/dist/query/service.d.ts +3 -0
  42. package/dist/query/service.js +282 -10
  43. package/dist/types.d.ts +2 -0
  44. package/package.json +125 -5
@@ -1,14 +1,286 @@
1
+ // src/i18n/catalogs/en.ts
2
+ import { defineTranslation } from "@contractspec/lib.contracts-spec/translations";
3
+ var enMessages = defineTranslation({
4
+ meta: {
5
+ key: "knowledge.messages",
6
+ version: "1.0.0",
7
+ domain: "knowledge",
8
+ description: "All user-facing and LLM-facing strings for the knowledge package",
9
+ owners: ["platform"],
10
+ stability: "experimental"
11
+ },
12
+ locale: "en",
13
+ fallback: "en",
14
+ messages: {
15
+ "access.notBound": {
16
+ value: 'Knowledge space "{spaceKey}" is not bound in the resolved app config.',
17
+ description: "Denial reason when a knowledge space is not bound",
18
+ placeholders: [{ name: "spaceKey", type: "string" }]
19
+ },
20
+ "access.readOnly": {
21
+ value: 'Knowledge space "{spaceKey}" is category "{category}" and is read-only.',
22
+ description: "Denial reason when write is attempted on a read-only space",
23
+ placeholders: [
24
+ { name: "spaceKey", type: "string" },
25
+ { name: "category", type: "string" }
26
+ ]
27
+ },
28
+ "access.workflowUnauthorized": {
29
+ value: 'Workflow "{workflowName}" is not authorized to access knowledge space "{spaceKey}".',
30
+ description: "Denial reason when a workflow lacks space access",
31
+ placeholders: [
32
+ { name: "workflowName", type: "string" },
33
+ { name: "spaceKey", type: "string" }
34
+ ]
35
+ },
36
+ "access.agentUnauthorized": {
37
+ value: 'Agent "{agentName}" is not authorized to access knowledge space "{spaceKey}".',
38
+ description: "Denial reason when an agent lacks space access",
39
+ placeholders: [
40
+ { name: "agentName", type: "string" },
41
+ { name: "spaceKey", type: "string" }
42
+ ]
43
+ },
44
+ "access.ephemeralWarning": {
45
+ value: 'Knowledge space "{spaceKey}" is ephemeral; results may be transient.',
46
+ description: "Warning for ephemeral knowledge spaces",
47
+ placeholders: [{ name: "spaceKey", type: "string" }]
48
+ },
49
+ "query.systemPrompt": {
50
+ value: "You are a knowledge assistant that answers questions using the provided context. Cite relevant sources if possible.",
51
+ description: "Default LLM system prompt for knowledge queries"
52
+ },
53
+ "query.userMessage": {
54
+ value: `Question:
55
+ {question}
56
+
57
+ Context:
58
+ {context}`,
59
+ description: "User message template combining question and context",
60
+ placeholders: [
61
+ { name: "question", type: "string" },
62
+ { name: "context", type: "string" }
63
+ ]
64
+ },
65
+ "query.noResults": {
66
+ value: "No relevant documents found.",
67
+ description: "Displayed when vector search returns zero results"
68
+ },
69
+ "query.sourceLabel": {
70
+ value: "Source {index} (score: {score}):",
71
+ description: "Label prefix for each source in the context block",
72
+ placeholders: [
73
+ { name: "index", type: "number" },
74
+ { name: "score", type: "string" }
75
+ ]
76
+ },
77
+ "ingestion.gmail.subject": {
78
+ value: "Subject: {subject}",
79
+ description: "Gmail thread subject label",
80
+ placeholders: [{ name: "subject", type: "string" }]
81
+ },
82
+ "ingestion.gmail.snippet": {
83
+ value: "Snippet: {snippet}",
84
+ description: "Gmail thread snippet label",
85
+ placeholders: [{ name: "snippet", type: "string" }]
86
+ },
87
+ "ingestion.gmail.from": {
88
+ value: "From: {from}",
89
+ description: "Gmail message sender label",
90
+ placeholders: [{ name: "from", type: "string" }]
91
+ },
92
+ "ingestion.gmail.to": {
93
+ value: "To: {to}",
94
+ description: "Gmail message recipients label",
95
+ placeholders: [{ name: "to", type: "string" }]
96
+ },
97
+ "ingestion.gmail.date": {
98
+ value: "Date: {date}",
99
+ description: "Gmail message date label",
100
+ placeholders: [{ name: "date", type: "string" }]
101
+ }
102
+ }
103
+ });
104
+
105
+ // src/i18n/catalogs/fr.ts
106
+ import { defineTranslation as defineTranslation2 } from "@contractspec/lib.contracts-spec/translations";
107
+ var frMessages = defineTranslation2({
108
+ meta: {
109
+ key: "knowledge.messages",
110
+ version: "1.0.0",
111
+ domain: "knowledge",
112
+ description: "All user-facing and LLM-facing strings for the knowledge package (French)",
113
+ owners: ["platform"],
114
+ stability: "experimental"
115
+ },
116
+ locale: "fr",
117
+ fallback: "en",
118
+ messages: {
119
+ "access.notBound": {
120
+ value: `L'espace de connaissances "{spaceKey}" n'est pas lié dans la configuration de l'application.`,
121
+ description: "Denial reason when a knowledge space is not bound"
122
+ },
123
+ "access.readOnly": {
124
+ value: `L'espace de connaissances "{spaceKey}" est de catégorie "{category}" et est en lecture seule.`,
125
+ description: "Denial reason when write is attempted on a read-only space"
126
+ },
127
+ "access.workflowUnauthorized": {
128
+ value: `Le workflow "{workflowName}" n'est pas autorisé à accéder à l'espace de connaissances "{spaceKey}".`,
129
+ description: "Denial reason when a workflow lacks space access"
130
+ },
131
+ "access.agentUnauthorized": {
132
+ value: `L'agent "{agentName}" n'est pas autorisé à accéder à l'espace de connaissances "{spaceKey}".`,
133
+ description: "Denial reason when an agent lacks space access"
134
+ },
135
+ "access.ephemeralWarning": {
136
+ value: `L'espace de connaissances "{spaceKey}" est éphémère ; les résultats peuvent être transitoires.`,
137
+ description: "Warning for ephemeral knowledge spaces"
138
+ },
139
+ "query.systemPrompt": {
140
+ value: "Vous êtes un assistant de connaissances qui répond aux questions en utilisant le contexte fourni. Citez les sources pertinentes si possible.",
141
+ description: "Default LLM system prompt for knowledge queries"
142
+ },
143
+ "query.userMessage": {
144
+ value: `Question :
145
+ {question}
146
+
147
+ Contexte :
148
+ {context}`,
149
+ description: "User message template combining question and context"
150
+ },
151
+ "query.noResults": {
152
+ value: "Aucun document pertinent trouvé.",
153
+ description: "Displayed when vector search returns zero results"
154
+ },
155
+ "query.sourceLabel": {
156
+ value: "Source {index} (score : {score}) :",
157
+ description: "Label prefix for each source in the context block"
158
+ },
159
+ "ingestion.gmail.subject": {
160
+ value: "Objet : {subject}",
161
+ description: "Gmail thread subject label"
162
+ },
163
+ "ingestion.gmail.snippet": {
164
+ value: "Extrait : {snippet}",
165
+ description: "Gmail thread snippet label"
166
+ },
167
+ "ingestion.gmail.from": {
168
+ value: "De : {from}",
169
+ description: "Gmail message sender label"
170
+ },
171
+ "ingestion.gmail.to": {
172
+ value: "À : {to}",
173
+ description: "Gmail message recipients label"
174
+ },
175
+ "ingestion.gmail.date": {
176
+ value: "Date : {date}",
177
+ description: "Gmail message date label"
178
+ }
179
+ }
180
+ });
181
+
182
+ // src/i18n/catalogs/es.ts
183
+ import { defineTranslation as defineTranslation3 } from "@contractspec/lib.contracts-spec/translations";
184
+ var esMessages = defineTranslation3({
185
+ meta: {
186
+ key: "knowledge.messages",
187
+ version: "1.0.0",
188
+ domain: "knowledge",
189
+ description: "All user-facing and LLM-facing strings for the knowledge package (Spanish)",
190
+ owners: ["platform"],
191
+ stability: "experimental"
192
+ },
193
+ locale: "es",
194
+ fallback: "en",
195
+ messages: {
196
+ "access.notBound": {
197
+ value: 'El espacio de conocimiento "{spaceKey}" no está vinculado en la configuración de la aplicación.',
198
+ description: "Denial reason when a knowledge space is not bound"
199
+ },
200
+ "access.readOnly": {
201
+ value: 'El espacio de conocimiento "{spaceKey}" es de categoría "{category}" y es de solo lectura.',
202
+ description: "Denial reason when write is attempted on a read-only space"
203
+ },
204
+ "access.workflowUnauthorized": {
205
+ value: 'El flujo de trabajo "{workflowName}" no está autorizado para acceder al espacio de conocimiento "{spaceKey}".',
206
+ description: "Denial reason when a workflow lacks space access"
207
+ },
208
+ "access.agentUnauthorized": {
209
+ value: 'El agente "{agentName}" no está autorizado para acceder al espacio de conocimiento "{spaceKey}".',
210
+ description: "Denial reason when an agent lacks space access"
211
+ },
212
+ "access.ephemeralWarning": {
213
+ value: 'El espacio de conocimiento "{spaceKey}" es efímero; los resultados pueden ser transitorios.',
214
+ description: "Warning for ephemeral knowledge spaces"
215
+ },
216
+ "query.systemPrompt": {
217
+ value: "Eres un asistente de conocimiento que responde preguntas utilizando el contexto proporcionado. Cita las fuentes relevantes si es posible.",
218
+ description: "Default LLM system prompt for knowledge queries"
219
+ },
220
+ "query.userMessage": {
221
+ value: `Pregunta:
222
+ {question}
223
+
224
+ Contexto:
225
+ {context}`,
226
+ description: "User message template combining question and context"
227
+ },
228
+ "query.noResults": {
229
+ value: "No se encontraron documentos relevantes.",
230
+ description: "Displayed when vector search returns zero results"
231
+ },
232
+ "query.sourceLabel": {
233
+ value: "Fuente {index} (puntuación: {score}):",
234
+ description: "Label prefix for each source in the context block"
235
+ },
236
+ "ingestion.gmail.subject": {
237
+ value: "Asunto: {subject}",
238
+ description: "Gmail thread subject label"
239
+ },
240
+ "ingestion.gmail.snippet": {
241
+ value: "Extracto: {snippet}",
242
+ description: "Gmail thread snippet label"
243
+ },
244
+ "ingestion.gmail.from": {
245
+ value: "De: {from}",
246
+ description: "Gmail message sender label"
247
+ },
248
+ "ingestion.gmail.to": {
249
+ value: "Para: {to}",
250
+ description: "Gmail message recipients label"
251
+ },
252
+ "ingestion.gmail.date": {
253
+ value: "Fecha: {date}",
254
+ description: "Gmail message date label"
255
+ }
256
+ }
257
+ });
258
+
259
+ // src/i18n/messages.ts
260
+ import {
261
+ createI18nFactory
262
+ } from "@contractspec/lib.contracts-spec/translations";
263
+ var factory = createI18nFactory({
264
+ specKey: "knowledge.messages",
265
+ catalogs: [enMessages, frMessages, esMessages]
266
+ });
267
+ var createKnowledgeI18n = factory.create;
268
+ var getDefaultI18n = factory.getDefault;
269
+ var resetI18nRegistry = factory.resetRegistry;
270
+
1
271
  // src/ingestion/gmail-adapter.ts
2
272
  class GmailIngestionAdapter {
3
273
  gmail;
4
274
  processor;
5
275
  embeddings;
6
276
  indexer;
7
- constructor(gmail, processor, embeddings, indexer) {
277
+ i18n;
278
+ constructor(gmail, processor, embeddings, indexer, locale) {
8
279
  this.gmail = gmail;
9
280
  this.processor = processor;
10
281
  this.embeddings = embeddings;
11
282
  this.indexer = indexer;
283
+ this.i18n = locale ? createKnowledgeI18n(locale) : getDefaultI18n();
12
284
  }
13
285
  async syncThreads(query) {
14
286
  const threads = await this.gmail.listThreads(query);
@@ -23,7 +295,7 @@ class GmailIngestionAdapter {
23
295
  await this.indexer.upsert(fragments, embeddings);
24
296
  }
25
297
  toRawDocument(thread) {
26
- const content = composeThreadText(thread);
298
+ const content = composeThreadText(thread, this.i18n);
27
299
  return {
28
300
  id: thread.id,
29
301
  mimeType: "text/plain",
@@ -36,18 +308,22 @@ class GmailIngestionAdapter {
36
308
  };
37
309
  }
38
310
  }
39
- function composeThreadText(thread) {
311
+ function composeThreadText(thread, i18n) {
40
312
  const header = [
41
- `Subject: ${thread.subject ?? ""}`,
42
- `Snippet: ${thread.snippet ?? ""}`
313
+ i18n.t("ingestion.gmail.subject", { subject: thread.subject ?? "" }),
314
+ i18n.t("ingestion.gmail.snippet", { snippet: thread.snippet ?? "" })
43
315
  ];
44
316
  const messageTexts = thread.messages.map((message) => {
45
317
  const parts = [
46
- `From: ${formatAddress(message.from)}`,
47
- `To: ${message.to.map(formatAddress).join(", ")}`
318
+ i18n.t("ingestion.gmail.from", { from: formatAddress(message.from) }),
319
+ i18n.t("ingestion.gmail.to", {
320
+ to: message.to.map(formatAddress).join(", ")
321
+ })
48
322
  ];
49
323
  if (message.sentAt) {
50
- parts.push(`Date: ${message.sentAt.toISOString()}`);
324
+ parts.push(i18n.t("ingestion.gmail.date", {
325
+ date: message.sentAt.toISOString()
326
+ }));
51
327
  }
52
328
  const body = message.textBody ?? stripHtml(message.htmlBody ?? "");
53
329
  return `${parts.join(`
@@ -1,3 +1,273 @@
1
+ // src/i18n/catalogs/en.ts
2
+ import { defineTranslation } from "@contractspec/lib.contracts-spec/translations";
3
+ var enMessages = defineTranslation({
4
+ meta: {
5
+ key: "knowledge.messages",
6
+ version: "1.0.0",
7
+ domain: "knowledge",
8
+ description: "All user-facing and LLM-facing strings for the knowledge package",
9
+ owners: ["platform"],
10
+ stability: "experimental"
11
+ },
12
+ locale: "en",
13
+ fallback: "en",
14
+ messages: {
15
+ "access.notBound": {
16
+ value: 'Knowledge space "{spaceKey}" is not bound in the resolved app config.',
17
+ description: "Denial reason when a knowledge space is not bound",
18
+ placeholders: [{ name: "spaceKey", type: "string" }]
19
+ },
20
+ "access.readOnly": {
21
+ value: 'Knowledge space "{spaceKey}" is category "{category}" and is read-only.',
22
+ description: "Denial reason when write is attempted on a read-only space",
23
+ placeholders: [
24
+ { name: "spaceKey", type: "string" },
25
+ { name: "category", type: "string" }
26
+ ]
27
+ },
28
+ "access.workflowUnauthorized": {
29
+ value: 'Workflow "{workflowName}" is not authorized to access knowledge space "{spaceKey}".',
30
+ description: "Denial reason when a workflow lacks space access",
31
+ placeholders: [
32
+ { name: "workflowName", type: "string" },
33
+ { name: "spaceKey", type: "string" }
34
+ ]
35
+ },
36
+ "access.agentUnauthorized": {
37
+ value: 'Agent "{agentName}" is not authorized to access knowledge space "{spaceKey}".',
38
+ description: "Denial reason when an agent lacks space access",
39
+ placeholders: [
40
+ { name: "agentName", type: "string" },
41
+ { name: "spaceKey", type: "string" }
42
+ ]
43
+ },
44
+ "access.ephemeralWarning": {
45
+ value: 'Knowledge space "{spaceKey}" is ephemeral; results may be transient.',
46
+ description: "Warning for ephemeral knowledge spaces",
47
+ placeholders: [{ name: "spaceKey", type: "string" }]
48
+ },
49
+ "query.systemPrompt": {
50
+ value: "You are a knowledge assistant that answers questions using the provided context. Cite relevant sources if possible.",
51
+ description: "Default LLM system prompt for knowledge queries"
52
+ },
53
+ "query.userMessage": {
54
+ value: `Question:
55
+ {question}
56
+
57
+ Context:
58
+ {context}`,
59
+ description: "User message template combining question and context",
60
+ placeholders: [
61
+ { name: "question", type: "string" },
62
+ { name: "context", type: "string" }
63
+ ]
64
+ },
65
+ "query.noResults": {
66
+ value: "No relevant documents found.",
67
+ description: "Displayed when vector search returns zero results"
68
+ },
69
+ "query.sourceLabel": {
70
+ value: "Source {index} (score: {score}):",
71
+ description: "Label prefix for each source in the context block",
72
+ placeholders: [
73
+ { name: "index", type: "number" },
74
+ { name: "score", type: "string" }
75
+ ]
76
+ },
77
+ "ingestion.gmail.subject": {
78
+ value: "Subject: {subject}",
79
+ description: "Gmail thread subject label",
80
+ placeholders: [{ name: "subject", type: "string" }]
81
+ },
82
+ "ingestion.gmail.snippet": {
83
+ value: "Snippet: {snippet}",
84
+ description: "Gmail thread snippet label",
85
+ placeholders: [{ name: "snippet", type: "string" }]
86
+ },
87
+ "ingestion.gmail.from": {
88
+ value: "From: {from}",
89
+ description: "Gmail message sender label",
90
+ placeholders: [{ name: "from", type: "string" }]
91
+ },
92
+ "ingestion.gmail.to": {
93
+ value: "To: {to}",
94
+ description: "Gmail message recipients label",
95
+ placeholders: [{ name: "to", type: "string" }]
96
+ },
97
+ "ingestion.gmail.date": {
98
+ value: "Date: {date}",
99
+ description: "Gmail message date label",
100
+ placeholders: [{ name: "date", type: "string" }]
101
+ }
102
+ }
103
+ });
104
+
105
+ // src/i18n/catalogs/fr.ts
106
+ import { defineTranslation as defineTranslation2 } from "@contractspec/lib.contracts-spec/translations";
107
+ var frMessages = defineTranslation2({
108
+ meta: {
109
+ key: "knowledge.messages",
110
+ version: "1.0.0",
111
+ domain: "knowledge",
112
+ description: "All user-facing and LLM-facing strings for the knowledge package (French)",
113
+ owners: ["platform"],
114
+ stability: "experimental"
115
+ },
116
+ locale: "fr",
117
+ fallback: "en",
118
+ messages: {
119
+ "access.notBound": {
120
+ value: `L'espace de connaissances "{spaceKey}" n'est pas lié dans la configuration de l'application.`,
121
+ description: "Denial reason when a knowledge space is not bound"
122
+ },
123
+ "access.readOnly": {
124
+ value: `L'espace de connaissances "{spaceKey}" est de catégorie "{category}" et est en lecture seule.`,
125
+ description: "Denial reason when write is attempted on a read-only space"
126
+ },
127
+ "access.workflowUnauthorized": {
128
+ value: `Le workflow "{workflowName}" n'est pas autorisé à accéder à l'espace de connaissances "{spaceKey}".`,
129
+ description: "Denial reason when a workflow lacks space access"
130
+ },
131
+ "access.agentUnauthorized": {
132
+ value: `L'agent "{agentName}" n'est pas autorisé à accéder à l'espace de connaissances "{spaceKey}".`,
133
+ description: "Denial reason when an agent lacks space access"
134
+ },
135
+ "access.ephemeralWarning": {
136
+ value: `L'espace de connaissances "{spaceKey}" est éphémère ; les résultats peuvent être transitoires.`,
137
+ description: "Warning for ephemeral knowledge spaces"
138
+ },
139
+ "query.systemPrompt": {
140
+ value: "Vous êtes un assistant de connaissances qui répond aux questions en utilisant le contexte fourni. Citez les sources pertinentes si possible.",
141
+ description: "Default LLM system prompt for knowledge queries"
142
+ },
143
+ "query.userMessage": {
144
+ value: `Question :
145
+ {question}
146
+
147
+ Contexte :
148
+ {context}`,
149
+ description: "User message template combining question and context"
150
+ },
151
+ "query.noResults": {
152
+ value: "Aucun document pertinent trouvé.",
153
+ description: "Displayed when vector search returns zero results"
154
+ },
155
+ "query.sourceLabel": {
156
+ value: "Source {index} (score : {score}) :",
157
+ description: "Label prefix for each source in the context block"
158
+ },
159
+ "ingestion.gmail.subject": {
160
+ value: "Objet : {subject}",
161
+ description: "Gmail thread subject label"
162
+ },
163
+ "ingestion.gmail.snippet": {
164
+ value: "Extrait : {snippet}",
165
+ description: "Gmail thread snippet label"
166
+ },
167
+ "ingestion.gmail.from": {
168
+ value: "De : {from}",
169
+ description: "Gmail message sender label"
170
+ },
171
+ "ingestion.gmail.to": {
172
+ value: "À : {to}",
173
+ description: "Gmail message recipients label"
174
+ },
175
+ "ingestion.gmail.date": {
176
+ value: "Date : {date}",
177
+ description: "Gmail message date label"
178
+ }
179
+ }
180
+ });
181
+
182
+ // src/i18n/catalogs/es.ts
183
+ import { defineTranslation as defineTranslation3 } from "@contractspec/lib.contracts-spec/translations";
184
+ var esMessages = defineTranslation3({
185
+ meta: {
186
+ key: "knowledge.messages",
187
+ version: "1.0.0",
188
+ domain: "knowledge",
189
+ description: "All user-facing and LLM-facing strings for the knowledge package (Spanish)",
190
+ owners: ["platform"],
191
+ stability: "experimental"
192
+ },
193
+ locale: "es",
194
+ fallback: "en",
195
+ messages: {
196
+ "access.notBound": {
197
+ value: 'El espacio de conocimiento "{spaceKey}" no está vinculado en la configuración de la aplicación.',
198
+ description: "Denial reason when a knowledge space is not bound"
199
+ },
200
+ "access.readOnly": {
201
+ value: 'El espacio de conocimiento "{spaceKey}" es de categoría "{category}" y es de solo lectura.',
202
+ description: "Denial reason when write is attempted on a read-only space"
203
+ },
204
+ "access.workflowUnauthorized": {
205
+ value: 'El flujo de trabajo "{workflowName}" no está autorizado para acceder al espacio de conocimiento "{spaceKey}".',
206
+ description: "Denial reason when a workflow lacks space access"
207
+ },
208
+ "access.agentUnauthorized": {
209
+ value: 'El agente "{agentName}" no está autorizado para acceder al espacio de conocimiento "{spaceKey}".',
210
+ description: "Denial reason when an agent lacks space access"
211
+ },
212
+ "access.ephemeralWarning": {
213
+ value: 'El espacio de conocimiento "{spaceKey}" es efímero; los resultados pueden ser transitorios.',
214
+ description: "Warning for ephemeral knowledge spaces"
215
+ },
216
+ "query.systemPrompt": {
217
+ value: "Eres un asistente de conocimiento que responde preguntas utilizando el contexto proporcionado. Cita las fuentes relevantes si es posible.",
218
+ description: "Default LLM system prompt for knowledge queries"
219
+ },
220
+ "query.userMessage": {
221
+ value: `Pregunta:
222
+ {question}
223
+
224
+ Contexto:
225
+ {context}`,
226
+ description: "User message template combining question and context"
227
+ },
228
+ "query.noResults": {
229
+ value: "No se encontraron documentos relevantes.",
230
+ description: "Displayed when vector search returns zero results"
231
+ },
232
+ "query.sourceLabel": {
233
+ value: "Fuente {index} (puntuación: {score}):",
234
+ description: "Label prefix for each source in the context block"
235
+ },
236
+ "ingestion.gmail.subject": {
237
+ value: "Asunto: {subject}",
238
+ description: "Gmail thread subject label"
239
+ },
240
+ "ingestion.gmail.snippet": {
241
+ value: "Extracto: {snippet}",
242
+ description: "Gmail thread snippet label"
243
+ },
244
+ "ingestion.gmail.from": {
245
+ value: "De: {from}",
246
+ description: "Gmail message sender label"
247
+ },
248
+ "ingestion.gmail.to": {
249
+ value: "Para: {to}",
250
+ description: "Gmail message recipients label"
251
+ },
252
+ "ingestion.gmail.date": {
253
+ value: "Fecha: {date}",
254
+ description: "Gmail message date label"
255
+ }
256
+ }
257
+ });
258
+
259
+ // src/i18n/messages.ts
260
+ import {
261
+ createI18nFactory
262
+ } from "@contractspec/lib.contracts-spec/translations";
263
+ var factory = createI18nFactory({
264
+ specKey: "knowledge.messages",
265
+ catalogs: [enMessages, frMessages, esMessages]
266
+ });
267
+ var createKnowledgeI18n = factory.create;
268
+ var getDefaultI18n = factory.getDefault;
269
+ var resetI18nRegistry = factory.resetRegistry;
270
+
1
271
  // src/ingestion/document-processor.ts
2
272
  import { Buffer as Buffer2 } from "node:buffer";
3
273
 
@@ -120,11 +390,13 @@ class GmailIngestionAdapter {
120
390
  processor;
121
391
  embeddings;
122
392
  indexer;
123
- constructor(gmail, processor, embeddings, indexer) {
393
+ i18n;
394
+ constructor(gmail, processor, embeddings, indexer, locale) {
124
395
  this.gmail = gmail;
125
396
  this.processor = processor;
126
397
  this.embeddings = embeddings;
127
398
  this.indexer = indexer;
399
+ this.i18n = locale ? createKnowledgeI18n(locale) : getDefaultI18n();
128
400
  }
129
401
  async syncThreads(query) {
130
402
  const threads = await this.gmail.listThreads(query);
@@ -139,7 +411,7 @@ class GmailIngestionAdapter {
139
411
  await this.indexer.upsert(fragments, embeddings);
140
412
  }
141
413
  toRawDocument(thread) {
142
- const content = composeThreadText(thread);
414
+ const content = composeThreadText(thread, this.i18n);
143
415
  return {
144
416
  id: thread.id,
145
417
  mimeType: "text/plain",
@@ -152,18 +424,22 @@ class GmailIngestionAdapter {
152
424
  };
153
425
  }
154
426
  }
155
- function composeThreadText(thread) {
427
+ function composeThreadText(thread, i18n) {
156
428
  const header = [
157
- `Subject: ${thread.subject ?? ""}`,
158
- `Snippet: ${thread.snippet ?? ""}`
429
+ i18n.t("ingestion.gmail.subject", { subject: thread.subject ?? "" }),
430
+ i18n.t("ingestion.gmail.snippet", { snippet: thread.snippet ?? "" })
159
431
  ];
160
432
  const messageTexts = thread.messages.map((message) => {
161
433
  const parts = [
162
- `From: ${formatAddress(message.from)}`,
163
- `To: ${message.to.map(formatAddress).join(", ")}`
434
+ i18n.t("ingestion.gmail.from", { from: formatAddress(message.from) }),
435
+ i18n.t("ingestion.gmail.to", {
436
+ to: message.to.map(formatAddress).join(", ")
437
+ })
164
438
  ];
165
439
  if (message.sentAt) {
166
- parts.push(`Date: ${message.sentAt.toISOString()}`);
440
+ parts.push(i18n.t("ingestion.gmail.date", {
441
+ date: message.sentAt.toISOString()
442
+ }));
167
443
  }
168
444
  const body = message.textBody ?? stripHtml(message.htmlBody ?? "");
169
445
  return `${parts.join(`