@diegoaltoworks/chatter 0.67.0 → 2.0.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 (72) hide show
  1. package/README.md +3 -1
  2. package/dist/channels/gates.d.ts +21 -2
  3. package/dist/channels/gates.d.ts.map +1 -1
  4. package/dist/channels/index.d.ts +5 -3
  5. package/dist/channels/index.d.ts.map +1 -1
  6. package/dist/channels/index.js +31 -13
  7. package/dist/channels/index.mjs +30 -13
  8. package/dist/channels/matrix/channel.d.ts +2 -0
  9. package/dist/channels/matrix/channel.d.ts.map +1 -1
  10. package/dist/channels/matrix/handler.d.ts +9 -0
  11. package/dist/channels/matrix/handler.d.ts.map +1 -1
  12. package/dist/channels/matrix/index.d.ts +1 -1
  13. package/dist/channels/matrix/index.d.ts.map +1 -1
  14. package/dist/channels/matrix/index.js +75 -20
  15. package/dist/channels/matrix/index.mjs +74 -20
  16. package/dist/channels/matrix/updates.d.ts +8 -2
  17. package/dist/channels/matrix/updates.d.ts.map +1 -1
  18. package/dist/channels/pipeline.d.ts +34 -4
  19. package/dist/channels/pipeline.d.ts.map +1 -1
  20. package/dist/channels/polling.d.ts +11 -0
  21. package/dist/channels/polling.d.ts.map +1 -1
  22. package/dist/channels/telegram/channel.d.ts +2 -0
  23. package/dist/channels/telegram/channel.d.ts.map +1 -1
  24. package/dist/channels/telegram/handler.d.ts +9 -0
  25. package/dist/channels/telegram/handler.d.ts.map +1 -1
  26. package/dist/channels/telegram/index.d.ts +1 -1
  27. package/dist/channels/telegram/index.d.ts.map +1 -1
  28. package/dist/channels/telegram/index.js +73 -24
  29. package/dist/channels/telegram/index.mjs +72 -24
  30. package/dist/channels/telegram/updates.d.ts +8 -2
  31. package/dist/channels/telegram/updates.d.ts.map +1 -1
  32. package/dist/channels/telegram/webhook.d.ts +10 -0
  33. package/dist/channels/telegram/webhook.d.ts.map +1 -1
  34. package/dist/channels/whatsapp/channel.d.ts +8 -0
  35. package/dist/channels/whatsapp/channel.d.ts.map +1 -1
  36. package/dist/channels/whatsapp/inbound.d.ts +2 -0
  37. package/dist/channels/whatsapp/inbound.d.ts.map +1 -1
  38. package/dist/channels/whatsapp/index.js +45 -18
  39. package/dist/channels/whatsapp/index.mjs +45 -18
  40. package/dist/core/answer.d.ts +9 -2
  41. package/dist/core/answer.d.ts.map +1 -1
  42. package/dist/core/guardrails.d.ts +11 -1
  43. package/dist/core/guardrails.d.ts.map +1 -1
  44. package/dist/core/llm.d.ts +3 -1
  45. package/dist/core/llm.d.ts.map +1 -1
  46. package/dist/history/index.js +12 -9
  47. package/dist/history/index.mjs +12 -9
  48. package/dist/index.d.ts +1 -1
  49. package/dist/index.d.ts.map +1 -1
  50. package/dist/index.js +50 -27
  51. package/dist/index.mjs +49 -27
  52. package/dist/mcp-server.d.ts.map +1 -1
  53. package/dist/mcp-server.js +16 -11
  54. package/dist/mcp-server.mjs +16 -11
  55. package/dist/personas/index.d.ts +1 -1
  56. package/dist/personas/index.d.ts.map +1 -1
  57. package/dist/personas/index.js +17 -5
  58. package/dist/personas/index.mjs +17 -5
  59. package/dist/personas/resolver.d.ts +35 -8
  60. package/dist/personas/resolver.d.ts.map +1 -1
  61. package/dist/routes/demo.d.ts.map +1 -1
  62. package/dist/routes/openai.d.ts.map +1 -1
  63. package/dist/routes/private.d.ts.map +1 -1
  64. package/dist/routes/public.d.ts.map +1 -1
  65. package/dist/scheduler/index.js +12 -9
  66. package/dist/scheduler/index.mjs +12 -9
  67. package/dist/server.d.ts.map +1 -1
  68. package/dist/server.js +44 -25
  69. package/dist/server.mjs +44 -25
  70. package/dist/types.d.ts +32 -1
  71. package/dist/types.d.ts.map +1 -1
  72. package/package.json +11 -11
@@ -96,7 +96,19 @@ function createPersonaResolver(options = {}) {
96
96
  return void 0;
97
97
  }
98
98
  }
99
- function rollPersonaId(contactId) {
99
+ function endpointPersonaId(endpointId) {
100
+ if (!endpointId) return null;
101
+ try {
102
+ return loadRegistry().endpoints?.[endpointId]?.persona ?? null;
103
+ } catch {
104
+ return null;
105
+ }
106
+ }
107
+ function rollPersonaId(key) {
108
+ const { contactId, endpointId } = typeof key === "string" ? { contactId: key } : key;
109
+ const boundToEndpoint = endpointPersonaId(endpointId);
110
+ if (boundToEndpoint) return boundToEndpoint;
111
+ if (contactId === void 0) return null;
100
112
  try {
101
113
  const reg = loadRegistry();
102
114
  const nowMs = now();
@@ -104,8 +116,8 @@ function createPersonaResolver(options = {}) {
104
116
  if (existing && existing.expiresAt > nowMs) {
105
117
  return existing.personaId;
106
118
  }
107
- for (const [key, value] of window) {
108
- if (value.expiresAt <= nowMs) window.delete(key);
119
+ for (const [held, value] of window) {
120
+ if (value.expiresAt <= nowMs) window.delete(held);
109
121
  }
110
122
  const contact = reg.contacts?.[contactId];
111
123
  let personaId = null;
@@ -153,8 +165,8 @@ function createPersonaResolver(options = {}) {
153
165
  return null;
154
166
  }
155
167
  }
156
- function resolvePersonaLayer(contactId) {
157
- return personaLayerFor(rollPersonaId(contactId)) ?? personaLayerFor(defaultPersonaId());
168
+ function resolvePersonaLayer(key) {
169
+ return personaLayerFor(rollPersonaId(key)) ?? personaLayerFor(defaultPersonaId());
158
170
  }
159
171
  return {
160
172
  rollPersonaId,
@@ -20,6 +20,14 @@ export interface PersonaContact {
20
20
  /** Overrides `personaProbability` for this contact when set. */
21
21
  probability?: number;
22
22
  }
23
+ /**
24
+ * A persona bound to one of the bot's own endpoints - which of its numbers,
25
+ * tokens or rooms the message arrived on, rather than who sent it.
26
+ */
27
+ export interface PersonaEndpoint {
28
+ /** Persona id this endpoint always answers as. */
29
+ persona: string;
30
+ }
23
31
  export interface PersonaRegistry {
24
32
  /** Persona id used when a contact is unmapped or its roll doesn't land. */
25
33
  defaultPersona: string;
@@ -28,8 +36,23 @@ export interface PersonaRegistry {
28
36
  /** Minutes a roll is held before the next message re-rolls it. Default 10. */
29
37
  personaWindowMinutes?: number;
30
38
  contacts?: Record<string, PersonaContact>;
39
+ /**
40
+ * Personas keyed on the endpoint that received the message
41
+ * (`ChannelMessage.endpointId`), for a bot whose voice is decided by which
42
+ * of its own identities was reached rather than by who reached it.
43
+ */
44
+ endpoints?: Record<string, PersonaEndpoint>;
31
45
  personas: Record<string, PersonaDefinition>;
32
46
  }
47
+ /**
48
+ * Who a persona is resolved for. A bare string is the contact id, which is
49
+ * every call that predates endpoint keying; the object form adds the endpoint
50
+ * the message arrived on, and either half may be omitted.
51
+ */
52
+ export type PersonaKey = string | {
53
+ contactId?: string;
54
+ endpointId?: string;
55
+ };
33
56
  export interface PersonaResolverOptions {
34
57
  /** Inline registry — takes priority over `registryPath` when both are set. */
35
58
  registry?: PersonaRegistry;
@@ -48,17 +71,21 @@ export interface PersonaResolverOptions {
48
71
  }
49
72
  export interface PersonaResolver {
50
73
  /**
51
- * The persona id in effect for a contact right now: rolled at
52
- * `personaProbability` once per window, held for `personaWindowMinutes`.
53
- * `null` means "use the default persona" (unmapped contact or a lost roll).
74
+ * The persona id in effect right now. A key naming an endpoint that the
75
+ * registry binds resolves to that persona deterministically - no roll, no
76
+ * window - because an endpoint-bound voice is a property of the identity
77
+ * reached, not something that varies over a conversation. Otherwise the
78
+ * contact is rolled at `personaProbability` once per window and held for
79
+ * `personaWindowMinutes`. `null` means "use the default persona" (no key,
80
+ * an unmapped contact, or a lost roll).
54
81
  */
55
- rollPersonaId(contactId: string): string | null;
82
+ rollPersonaId(key: PersonaKey): string | null;
56
83
  /**
57
- * The interpolated prompt layer for the contact's currently-held persona,
58
- * falling back to the default persona's layer. Feeds `prepareChat`'s
59
- * `personaLayer` directly.
84
+ * The interpolated prompt layer for the persona {@link rollPersonaId}
85
+ * yields, falling back to the default persona's layer. Feeds
86
+ * `prepareChat`'s `personaLayer` directly.
60
87
  */
61
- resolvePersonaLayer(contactId: string): string | null;
88
+ resolvePersonaLayer(key: PersonaKey): string | null;
62
89
  /** Deterministic layer for an explicit persona id (no dice roll), or null. */
63
90
  personaLayerFor(personaId: string | null): string | null;
64
91
  /** Registry contact record, or undefined. */
@@ -1 +1 @@
1
- {"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../../src/personas/resolver.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAAuB,KAAK,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAElE,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,wEAAwE;IACxE,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0CAA0C;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,gEAAgE;IAChE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,2EAA2E;IAC3E,cAAc,EAAE,MAAM,CAAC;IACvB,mFAAmF;IACnF,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,8EAA8E;IAC9E,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAC1C,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;CAC7C;AAED,MAAM,WAAW,sBAAsB;IACrC,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,4DAA4D;IAC5D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,0HAA0H;IAC1H,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8DAA8D;IAC9D,MAAM,CAAC,EAAE,MAAM,MAAM,CAAC;IACtB,sDAAsD;IACtD,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,wEAAwE;IACxE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,6FAA6F;IAC7F,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAChD;;;;OAIG;IACH,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IACtD,8EAA8E;IAC9E,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAAC;IACzD,6CAA6C;IAC7C,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAAC;IAC1D,wEAAwE;IACxE,gBAAgB,IAAI,MAAM,GAAG,IAAI,CAAC;CACnC;AAYD,wBAAgB,qBAAqB,CAAC,OAAO,GAAE,sBAA2B,GAAG,eAAe,CAgI3F"}
1
+ {"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../../src/personas/resolver.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAAuB,KAAK,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAElE,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,wEAAwE;IACxE,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0CAA0C;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,gEAAgE;IAChE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;GAGG;AACH,MAAM,WAAW,eAAe;IAC9B,kDAAkD;IAClD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,2EAA2E;IAC3E,cAAc,EAAE,MAAM,CAAC;IACvB,mFAAmF;IACnF,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,8EAA8E;IAC9E,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAC1C;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;IAC5C,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;CAC7C;AAED;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9E,MAAM,WAAW,sBAAsB;IACrC,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,4DAA4D;IAC5D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,0HAA0H;IAC1H,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8DAA8D;IAC9D,MAAM,CAAC,EAAE,MAAM,MAAM,CAAC;IACtB,sDAAsD;IACtD,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,wEAAwE;IACxE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,6FAA6F;IAC7F,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B;;;;;;;;OAQG;IACH,aAAa,CAAC,GAAG,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAAC;IAC9C;;;;OAIG;IACH,mBAAmB,CAAC,GAAG,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAAC;IACpD,8EAA8E;IAC9E,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAAC;IACzD,6CAA6C;IAC7C,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAAC;IAC1D,wEAAwE;IACxE,gBAAgB,IAAI,MAAM,GAAG,IAAI,CAAC;CACnC;AAYD,wBAAgB,qBAAqB,CAAC,OAAO,GAAE,sBAA2B,GAAG,eAAe,CAmJ3F"}
@@ -1 +1 @@
1
- {"version":3,"file":"demo.d.ts","sourceRoot":"","sources":["../../src/routes/demo.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAa5B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AA6FnD,wBAAgB,UAAU,CAAC,IAAI,EAAE,kBAAkB,8EAmIlD"}
1
+ {"version":3,"file":"demo.d.ts","sourceRoot":"","sources":["../../src/routes/demo.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAa5B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AA6FnD,wBAAgB,UAAU,CAAC,IAAI,EAAE,kBAAkB,8EAqIlD"}
@@ -1 +1 @@
1
- {"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../src/routes/openai.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAW5B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAsDnD,wBAAgB,YAAY,CAAC,IAAI,EAAE,kBAAkB,8EAoLpD"}
1
+ {"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../src/routes/openai.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAW5B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAsDnD,wBAAgB,YAAY,CAAC,IAAI,EAAE,kBAAkB,8EAsLpD"}
@@ -1 +1 @@
1
- {"version":3,"file":"private.d.ts","sourceRoot":"","sources":["../../src/routes/private.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAS5B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AASnD,wBAAgB,aAAa,CAAC,IAAI,EAAE,kBAAkB,8EAuFrD"}
1
+ {"version":3,"file":"private.d.ts","sourceRoot":"","sources":["../../src/routes/private.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAS5B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AASnD,wBAAgB,aAAa,CAAC,IAAI,EAAE,kBAAkB,8EAyFrD"}
@@ -1 +1 @@
1
- {"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../src/routes/public.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAW5B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AASnD,wBAAgB,YAAY,CAAC,IAAI,EAAE,kBAAkB,8EAsFpD"}
1
+ {"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../../src/routes/public.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAW5B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AASnD,wBAAgB,YAAY,CAAC,IAAI,EAAE,kBAAkB,8EA+FpD"}
@@ -116,9 +116,10 @@ function detectLeakage(text) {
116
116
  function scrubOutput(text) {
117
117
  return text.replace(/sk-[A-Za-z0-9]{15,}/g, "[REDACTED]").replace(/AIza[0-9A-Za-z_-]{30,}/g, "[REDACTED]");
118
118
  }
119
- function guardOutput(text) {
119
+ var DEFAULT_REFUSAL = "Sorry, I can't share internal instructions. How else can I help?";
120
+ function guardOutput(text, refusal) {
120
121
  if (detectLeakage(text)) {
121
- return "Sorry, I can't share internal instructions. How else can I help?";
122
+ return typeof refusal === "string" && refusal.trim() ? refusal : DEFAULT_REFUSAL;
122
123
  }
123
124
  return scrubOutput(text);
124
125
  }
@@ -130,7 +131,8 @@ async function completeOnce({
130
131
  system,
131
132
  messages,
132
133
  temperature = 0.2,
133
- model = DEFAULT_MODEL
134
+ model = DEFAULT_MODEL,
135
+ refusal
134
136
  }) {
135
137
  const res = await client.chat.completions.create({
136
138
  model,
@@ -139,19 +141,19 @@ async function completeOnce({
139
141
  });
140
142
  const text = res.choices[0]?.message?.content ?? "";
141
143
  return {
142
- content: guardOutput(text),
144
+ content: guardOutput(text, refusal),
143
145
  usage: res.usage || { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 }
144
146
  };
145
147
  }
146
148
 
147
149
  // src/core/answer.ts
148
150
  var noUsage = () => ({ prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 });
149
- async function runAnswerFn(answerFn, { system, messages, mode, sender, conversationId }) {
151
+ async function runAnswerFn(answerFn, { system, messages, mode, sender, conversationId }, refusal) {
150
152
  const result = await answerFn({ system, messages, mode, sender, conversationId });
151
153
  const raw = typeof result === "string" ? result : result?.content;
152
154
  const usage = typeof result === "string" ? void 0 : result?.usage;
153
155
  return {
154
- content: guardOutput(typeof raw === "string" ? raw : ""),
156
+ content: guardOutput(typeof raw === "string" ? raw : "", refusal),
155
157
  usage: usage ?? noUsage()
156
158
  };
157
159
  }
@@ -164,12 +166,13 @@ async function answerOnce({
164
166
  sender,
165
167
  conversationId,
166
168
  temperature,
167
- model
169
+ model,
170
+ refusal
168
171
  }) {
169
172
  if (answerFn) {
170
- return runAnswerFn(answerFn, { system, messages, mode, sender, conversationId });
173
+ return runAnswerFn(answerFn, { system, messages, mode, sender, conversationId }, refusal);
171
174
  }
172
- return completeOnce({ client, system, messages, temperature, model });
175
+ return completeOnce({ client, system, messages, temperature, model, refusal });
173
176
  }
174
177
 
175
178
  // src/core/logger.ts
@@ -75,9 +75,10 @@ function detectLeakage(text) {
75
75
  function scrubOutput(text) {
76
76
  return text.replace(/sk-[A-Za-z0-9]{15,}/g, "[REDACTED]").replace(/AIza[0-9A-Za-z_-]{30,}/g, "[REDACTED]");
77
77
  }
78
- function guardOutput(text) {
78
+ var DEFAULT_REFUSAL = "Sorry, I can't share internal instructions. How else can I help?";
79
+ function guardOutput(text, refusal) {
79
80
  if (detectLeakage(text)) {
80
- return "Sorry, I can't share internal instructions. How else can I help?";
81
+ return typeof refusal === "string" && refusal.trim() ? refusal : DEFAULT_REFUSAL;
81
82
  }
82
83
  return scrubOutput(text);
83
84
  }
@@ -89,7 +90,8 @@ async function completeOnce({
89
90
  system,
90
91
  messages,
91
92
  temperature = 0.2,
92
- model = DEFAULT_MODEL
93
+ model = DEFAULT_MODEL,
94
+ refusal
93
95
  }) {
94
96
  const res = await client.chat.completions.create({
95
97
  model,
@@ -98,19 +100,19 @@ async function completeOnce({
98
100
  });
99
101
  const text = res.choices[0]?.message?.content ?? "";
100
102
  return {
101
- content: guardOutput(text),
103
+ content: guardOutput(text, refusal),
102
104
  usage: res.usage || { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 }
103
105
  };
104
106
  }
105
107
 
106
108
  // src/core/answer.ts
107
109
  var noUsage = () => ({ prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 });
108
- async function runAnswerFn(answerFn, { system, messages, mode, sender, conversationId }) {
110
+ async function runAnswerFn(answerFn, { system, messages, mode, sender, conversationId }, refusal) {
109
111
  const result = await answerFn({ system, messages, mode, sender, conversationId });
110
112
  const raw = typeof result === "string" ? result : result?.content;
111
113
  const usage = typeof result === "string" ? void 0 : result?.usage;
112
114
  return {
113
- content: guardOutput(typeof raw === "string" ? raw : ""),
115
+ content: guardOutput(typeof raw === "string" ? raw : "", refusal),
114
116
  usage: usage ?? noUsage()
115
117
  };
116
118
  }
@@ -123,12 +125,13 @@ async function answerOnce({
123
125
  sender,
124
126
  conversationId,
125
127
  temperature,
126
- model
128
+ model,
129
+ refusal
127
130
  }) {
128
131
  if (answerFn) {
129
- return runAnswerFn(answerFn, { system, messages, mode, sender, conversationId });
132
+ return runAnswerFn(answerFn, { system, messages, mode, sender, conversationId }, refusal);
130
133
  }
131
- return completeOnce({ client, system, messages, temperature, model });
134
+ return completeOnce({ client, system, messages, temperature, model, refusal });
132
135
  }
133
136
 
134
137
  // src/core/logger.ts
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAiB5B,OAAO,KAAK,EAAE,aAAa,EAAsB,MAAM,SAAS,CAAC;AAEjE;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG;IAAE,YAAY,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAC;AAEtE;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,UAAU,CAAC,CA+P7E"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAiB5B,OAAO,KAAK,EAAE,aAAa,EAAsB,MAAM,SAAS,CAAC;AAEjE;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG;IAAE,YAAY,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAC;AAEtE;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,UAAU,CAAC,CAqQ7E"}
package/dist/server.js CHANGED
@@ -672,9 +672,10 @@ function detectLeakage(text) {
672
672
  function scrubOutput(text) {
673
673
  return text.replace(/sk-[A-Za-z0-9]{15,}/g, "[REDACTED]").replace(/AIza[0-9A-Za-z_-]{30,}/g, "[REDACTED]");
674
674
  }
675
- function guardOutput(text) {
675
+ var DEFAULT_REFUSAL = "Sorry, I can't share internal instructions. How else can I help?";
676
+ function guardOutput(text, refusal) {
676
677
  if (detectLeakage(text)) {
677
- return "Sorry, I can't share internal instructions. How else can I help?";
678
+ return typeof refusal === "string" && refusal.trim() ? refusal : DEFAULT_REFUSAL;
678
679
  }
679
680
  return scrubOutput(text);
680
681
  }
@@ -686,7 +687,8 @@ async function completeOnce({
686
687
  system,
687
688
  messages,
688
689
  temperature = 0.2,
689
- model = DEFAULT_MODEL
690
+ model = DEFAULT_MODEL,
691
+ refusal
690
692
  }) {
691
693
  const res = await client.chat.completions.create({
692
694
  model,
@@ -695,7 +697,7 @@ async function completeOnce({
695
697
  });
696
698
  const text = res.choices[0]?.message?.content ?? "";
697
699
  return {
698
- content: guardOutput(text),
700
+ content: guardOutput(text, refusal),
699
701
  usage: res.usage || { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 }
700
702
  };
701
703
  }
@@ -720,12 +722,12 @@ async function* completeStream({
720
722
 
721
723
  // src/core/answer.ts
722
724
  var noUsage = () => ({ prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 });
723
- async function runAnswerFn(answerFn, { system, messages, mode, sender, conversationId }) {
725
+ async function runAnswerFn(answerFn, { system, messages, mode, sender, conversationId }, refusal) {
724
726
  const result = await answerFn({ system, messages, mode, sender, conversationId });
725
727
  const raw = typeof result === "string" ? result : result?.content;
726
728
  const usage = typeof result === "string" ? void 0 : result?.usage;
727
729
  return {
728
- content: guardOutput(typeof raw === "string" ? raw : ""),
730
+ content: guardOutput(typeof raw === "string" ? raw : "", refusal),
729
731
  usage: usage ?? noUsage()
730
732
  };
731
733
  }
@@ -738,12 +740,13 @@ async function answerOnce({
738
740
  sender,
739
741
  conversationId,
740
742
  temperature,
741
- model
743
+ model,
744
+ refusal
742
745
  }) {
743
746
  if (answerFn) {
744
- return runAnswerFn(answerFn, { system, messages, mode, sender, conversationId });
747
+ return runAnswerFn(answerFn, { system, messages, mode, sender, conversationId }, refusal);
745
748
  }
746
- return completeOnce({ client, system, messages, temperature, model });
749
+ return completeOnce({ client, system, messages, temperature, model, refusal });
747
750
  }
748
751
  async function applyTransformReply(transformReply, input, logger) {
749
752
  if (!transformReply) return input.text;
@@ -767,16 +770,15 @@ async function* answerStream({
767
770
  sender,
768
771
  conversationId,
769
772
  temperature,
770
- model
773
+ model,
774
+ refusal
771
775
  }) {
772
776
  if (answerFn) {
773
- const { content } = await runAnswerFn(answerFn, {
774
- system,
775
- messages,
776
- mode,
777
- sender,
778
- conversationId
779
- });
777
+ const { content } = await runAnswerFn(
778
+ answerFn,
779
+ { system, messages, mode, sender, conversationId },
780
+ refusal
781
+ );
780
782
  if (content) yield content;
781
783
  return;
782
784
  }
@@ -1425,7 +1427,8 @@ function demoRoutes(deps) {
1425
1427
  system,
1426
1428
  messages,
1427
1429
  mode: "public",
1428
- model
1430
+ model,
1431
+ refusal: config.refusal
1429
1432
  })) {
1430
1433
  await s.write(`data: ${JSON.stringify({ delta })}
1431
1434
 
@@ -1440,7 +1443,8 @@ function demoRoutes(deps) {
1440
1443
  system,
1441
1444
  messages,
1442
1445
  mode: "public",
1443
- model
1446
+ model,
1447
+ refusal: config.refusal
1444
1448
  });
1445
1449
  const reply = await applyTransformReply(
1446
1450
  config.transformReply,
@@ -1662,7 +1666,8 @@ function openaiRoutes(deps) {
1662
1666
  sender,
1663
1667
  conversationId,
1664
1668
  temperature,
1665
- model: serverModel
1669
+ model: serverModel,
1670
+ refusal: config.refusal
1666
1671
  })) {
1667
1672
  await s.write(chunk2({ content: delta }, null));
1668
1673
  }
@@ -1679,7 +1684,8 @@ function openaiRoutes(deps) {
1679
1684
  sender,
1680
1685
  conversationId,
1681
1686
  temperature,
1682
- model: serverModel
1687
+ model: serverModel,
1688
+ refusal: config.refusal
1683
1689
  });
1684
1690
  const reply = await applyTransformReply(
1685
1691
  config.transformReply,
@@ -1770,7 +1776,8 @@ function privateRoutes(deps) {
1770
1776
  messages,
1771
1777
  mode: "private",
1772
1778
  sender,
1773
- model
1779
+ model,
1780
+ refusal: config.refusal
1774
1781
  })) {
1775
1782
  await s.write(`data: ${JSON.stringify({ delta })}
1776
1783
 
@@ -1786,7 +1793,8 @@ function privateRoutes(deps) {
1786
1793
  messages,
1787
1794
  mode: "private",
1788
1795
  sender,
1789
- model
1796
+ model,
1797
+ refusal: config.refusal
1790
1798
  });
1791
1799
  const reply = await applyTransformReply(
1792
1800
  config.transformReply,
@@ -1953,7 +1961,8 @@ function publicRoutes(deps) {
1953
1961
  system,
1954
1962
  messages,
1955
1963
  mode: "public",
1956
- model
1964
+ model,
1965
+ refusal: config.refusal
1957
1966
  })) {
1958
1967
  await s.write(`data: ${JSON.stringify({ delta })}
1959
1968
 
@@ -1962,7 +1971,15 @@ function publicRoutes(deps) {
1962
1971
  await s.write("event: end\ndata: {}\n\n");
1963
1972
  });
1964
1973
  }
1965
- const out = await answerOnce({ answerFn, client, system, messages, mode: "public", model });
1974
+ const out = await answerOnce({
1975
+ answerFn,
1976
+ client,
1977
+ system,
1978
+ messages,
1979
+ mode: "public",
1980
+ model,
1981
+ refusal: config.refusal
1982
+ });
1966
1983
  const reply = await applyTransformReply(
1967
1984
  config.transformReply,
1968
1985
  { channel: "widget-public", text: out.content },
@@ -2101,6 +2118,7 @@ async function createServer(config) {
2101
2118
  app.get("/demo/react", serveStatic({ path: `${publicDir}/react-demo.html` }));
2102
2119
  }
2103
2120
  const senders = createSenderRegistry(logger);
2121
+ const identities = /* @__PURE__ */ new Map();
2104
2122
  const deps = {
2105
2123
  client,
2106
2124
  store,
@@ -2112,6 +2130,7 @@ async function createServer(config) {
2112
2130
  prompts,
2113
2131
  apiKeyManager,
2114
2132
  senders,
2133
+ identities,
2115
2134
  logger
2116
2135
  };
2117
2136
  if (enablePublic) {
package/dist/server.mjs CHANGED
@@ -644,9 +644,10 @@ function detectLeakage(text) {
644
644
  function scrubOutput(text) {
645
645
  return text.replace(/sk-[A-Za-z0-9]{15,}/g, "[REDACTED]").replace(/AIza[0-9A-Za-z_-]{30,}/g, "[REDACTED]");
646
646
  }
647
- function guardOutput(text) {
647
+ var DEFAULT_REFUSAL = "Sorry, I can't share internal instructions. How else can I help?";
648
+ function guardOutput(text, refusal) {
648
649
  if (detectLeakage(text)) {
649
- return "Sorry, I can't share internal instructions. How else can I help?";
650
+ return typeof refusal === "string" && refusal.trim() ? refusal : DEFAULT_REFUSAL;
650
651
  }
651
652
  return scrubOutput(text);
652
653
  }
@@ -658,7 +659,8 @@ async function completeOnce({
658
659
  system,
659
660
  messages,
660
661
  temperature = 0.2,
661
- model = DEFAULT_MODEL
662
+ model = DEFAULT_MODEL,
663
+ refusal
662
664
  }) {
663
665
  const res = await client.chat.completions.create({
664
666
  model,
@@ -667,7 +669,7 @@ async function completeOnce({
667
669
  });
668
670
  const text = res.choices[0]?.message?.content ?? "";
669
671
  return {
670
- content: guardOutput(text),
672
+ content: guardOutput(text, refusal),
671
673
  usage: res.usage || { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 }
672
674
  };
673
675
  }
@@ -692,12 +694,12 @@ async function* completeStream({
692
694
 
693
695
  // src/core/answer.ts
694
696
  var noUsage = () => ({ prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 });
695
- async function runAnswerFn(answerFn, { system, messages, mode, sender, conversationId }) {
697
+ async function runAnswerFn(answerFn, { system, messages, mode, sender, conversationId }, refusal) {
696
698
  const result = await answerFn({ system, messages, mode, sender, conversationId });
697
699
  const raw = typeof result === "string" ? result : result?.content;
698
700
  const usage = typeof result === "string" ? void 0 : result?.usage;
699
701
  return {
700
- content: guardOutput(typeof raw === "string" ? raw : ""),
702
+ content: guardOutput(typeof raw === "string" ? raw : "", refusal),
701
703
  usage: usage ?? noUsage()
702
704
  };
703
705
  }
@@ -710,12 +712,13 @@ async function answerOnce({
710
712
  sender,
711
713
  conversationId,
712
714
  temperature,
713
- model
715
+ model,
716
+ refusal
714
717
  }) {
715
718
  if (answerFn) {
716
- return runAnswerFn(answerFn, { system, messages, mode, sender, conversationId });
719
+ return runAnswerFn(answerFn, { system, messages, mode, sender, conversationId }, refusal);
717
720
  }
718
- return completeOnce({ client, system, messages, temperature, model });
721
+ return completeOnce({ client, system, messages, temperature, model, refusal });
719
722
  }
720
723
  async function applyTransformReply(transformReply, input, logger) {
721
724
  if (!transformReply) return input.text;
@@ -739,16 +742,15 @@ async function* answerStream({
739
742
  sender,
740
743
  conversationId,
741
744
  temperature,
742
- model
745
+ model,
746
+ refusal
743
747
  }) {
744
748
  if (answerFn) {
745
- const { content } = await runAnswerFn(answerFn, {
746
- system,
747
- messages,
748
- mode,
749
- sender,
750
- conversationId
751
- });
749
+ const { content } = await runAnswerFn(
750
+ answerFn,
751
+ { system, messages, mode, sender, conversationId },
752
+ refusal
753
+ );
752
754
  if (content) yield content;
753
755
  return;
754
756
  }
@@ -1396,7 +1398,8 @@ function demoRoutes(deps) {
1396
1398
  system,
1397
1399
  messages,
1398
1400
  mode: "public",
1399
- model
1401
+ model,
1402
+ refusal: config.refusal
1400
1403
  })) {
1401
1404
  await s.write(`data: ${JSON.stringify({ delta })}
1402
1405
 
@@ -1411,7 +1414,8 @@ function demoRoutes(deps) {
1411
1414
  system,
1412
1415
  messages,
1413
1416
  mode: "public",
1414
- model
1417
+ model,
1418
+ refusal: config.refusal
1415
1419
  });
1416
1420
  const reply = await applyTransformReply(
1417
1421
  config.transformReply,
@@ -1633,7 +1637,8 @@ function openaiRoutes(deps) {
1633
1637
  sender,
1634
1638
  conversationId,
1635
1639
  temperature,
1636
- model: serverModel
1640
+ model: serverModel,
1641
+ refusal: config.refusal
1637
1642
  })) {
1638
1643
  await s.write(chunk2({ content: delta }, null));
1639
1644
  }
@@ -1650,7 +1655,8 @@ function openaiRoutes(deps) {
1650
1655
  sender,
1651
1656
  conversationId,
1652
1657
  temperature,
1653
- model: serverModel
1658
+ model: serverModel,
1659
+ refusal: config.refusal
1654
1660
  });
1655
1661
  const reply = await applyTransformReply(
1656
1662
  config.transformReply,
@@ -1741,7 +1747,8 @@ function privateRoutes(deps) {
1741
1747
  messages,
1742
1748
  mode: "private",
1743
1749
  sender,
1744
- model
1750
+ model,
1751
+ refusal: config.refusal
1745
1752
  })) {
1746
1753
  await s.write(`data: ${JSON.stringify({ delta })}
1747
1754
 
@@ -1757,7 +1764,8 @@ function privateRoutes(deps) {
1757
1764
  messages,
1758
1765
  mode: "private",
1759
1766
  sender,
1760
- model
1767
+ model,
1768
+ refusal: config.refusal
1761
1769
  });
1762
1770
  const reply = await applyTransformReply(
1763
1771
  config.transformReply,
@@ -1924,7 +1932,8 @@ function publicRoutes(deps) {
1924
1932
  system,
1925
1933
  messages,
1926
1934
  mode: "public",
1927
- model
1935
+ model,
1936
+ refusal: config.refusal
1928
1937
  })) {
1929
1938
  await s.write(`data: ${JSON.stringify({ delta })}
1930
1939
 
@@ -1933,7 +1942,15 @@ function publicRoutes(deps) {
1933
1942
  await s.write("event: end\ndata: {}\n\n");
1934
1943
  });
1935
1944
  }
1936
- const out = await answerOnce({ answerFn, client, system, messages, mode: "public", model });
1945
+ const out = await answerOnce({
1946
+ answerFn,
1947
+ client,
1948
+ system,
1949
+ messages,
1950
+ mode: "public",
1951
+ model,
1952
+ refusal: config.refusal
1953
+ });
1937
1954
  const reply = await applyTransformReply(
1938
1955
  config.transformReply,
1939
1956
  { channel: "widget-public", text: out.content },
@@ -2072,6 +2089,7 @@ async function createServer(config) {
2072
2089
  app.get("/demo/react", serveStatic({ path: `${publicDir}/react-demo.html` }));
2073
2090
  }
2074
2091
  const senders = createSenderRegistry(logger);
2092
+ const identities = /* @__PURE__ */ new Map();
2075
2093
  const deps = {
2076
2094
  client,
2077
2095
  store,
@@ -2083,6 +2101,7 @@ async function createServer(config) {
2083
2101
  prompts,
2084
2102
  apiKeyManager,
2085
2103
  senders,
2104
+ identities,
2086
2105
  logger
2087
2106
  };
2088
2107
  if (enablePublic) {