@dropout-ai/runtime 0.4.3 → 0.4.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -115,6 +115,61 @@ bootstrap();
115
115
  | `apiKey` | `string` | Your Secret Key (`dp_live_...`). | ✅ |
116
116
  | `debug` | `boolean` | Set `true` to see "🟢 Online" logs and errors in console. | No |
117
117
  | `privacy` | `string` | `full` (capture text) or `mask` (metadata only). Default: `full`. | No |
118
+ | `sessionId` | `string` | Optional initial session ID. If not provided, one is generated. | No |
119
+ | `captureEndpoint` | `string` | Custom capture endpoint (for testing). Default: production URL. | No |
120
+
121
+ ---
122
+
123
+ ## 🔑 Session Management
124
+
125
+ **Important:** A **session** represents a single conversation thread. By default, Dropout generates one session ID when initialized and uses it for all interactions. For proper analytics, you should call `startNewSession()` whenever the user starts a new conversation.
126
+
127
+ ### When to Call `startNewSession()`
128
+
129
+ Call this method when:
130
+ - User clicks "New Chat" button
131
+ - User switches to a different conversation thread
132
+ - User switches AI models (if you want to treat that as a new conversation)
133
+ - Page refreshes and you want to start a fresh session
134
+
135
+ ### Example Usage
136
+
137
+ ```javascript
138
+ const Dropout = require('@dropout-ai/runtime');
139
+
140
+ // Initialize once at app startup
141
+ const dropout = Dropout.init({
142
+ apiKey: process.env.DROPOUT_API_KEY,
143
+ projectId: process.env.DROPOUT_PROJECT_ID
144
+ });
145
+
146
+ // Later, when user clicks "New Chat":
147
+ app.post('/api/new-chat', (req, res) => {
148
+ const newSessionId = dropout.startNewSession();
149
+ console.log(`Started new session: ${newSessionId}`);
150
+ res.json({ sessionId: newSessionId });
151
+ });
152
+
153
+ // Or with a custom session ID:
154
+ dropout.startNewSession('user_123_chat_456');
155
+ ```
156
+
157
+ ### TypeScript Example
158
+
159
+ ```typescript
160
+ import Dropout from '@dropout-ai/runtime';
161
+
162
+ const dropout = Dropout.init({
163
+ apiKey: process.env.DROPOUT_API_KEY!,
164
+ projectId: process.env.DROPOUT_PROJECT_ID!
165
+ });
166
+
167
+ // Access current session
168
+ console.log(dropout.currentSessionId);
169
+
170
+ // Start new session
171
+ const newSessionId = dropout.startNewSession();
172
+ ```
118
173
 
119
174
  ---
120
175
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dropout-ai/runtime",
3
- "version": "0.4.3",
3
+ "version": "0.4.5",
4
4
  "description": "Invisible Node.js runtime for understanding behavioral impact of AI interactions.",
5
5
  "main": "src/index.js",
6
6
  "types": "src/index.d.ts",
package/src/index.d.ts CHANGED
@@ -5,6 +5,8 @@ export interface DropoutConfig {
5
5
  apiKey: string;
6
6
  debug?: boolean;
7
7
  privacy?: 'full' | 'mask';
8
+ sessionId?: string;
9
+ captureEndpoint?: string;
8
10
  }
9
11
 
10
12
  export default class Dropout {
@@ -28,11 +30,24 @@ export default class Dropout {
28
30
  */
29
31
  privacy: 'full' | 'mask';
30
32
 
33
+ /**
34
+ * Current session ID for tracking conversation threads.
35
+ */
36
+ currentSessionId: string;
37
+
31
38
  /**
32
39
  * Universal Initialization Method (Idempotent).
33
40
  * Safe to call multiple times; will only initialize once.
34
41
  */
35
42
  static init(config: DropoutConfig): Dropout;
36
43
 
44
+ /**
45
+ * Start a new session (e.g., when user clicks "New Chat").
46
+ * Resets turn counter and generates a fresh session ID.
47
+ * @param customSessionId - Optional custom session ID, otherwise auto-generates
48
+ * @returns The new session ID
49
+ */
50
+ startNewSession(customSessionId?: string): string;
51
+
37
52
  constructor(config: DropoutConfig);
38
53
  }
package/src/index.js CHANGED
@@ -69,7 +69,7 @@ class Dropout {
69
69
  this.apiKey = config.apiKey;
70
70
  this.debug = config.debug || false;
71
71
  this.privacy = config.privacy || 'full';
72
- this.captureEndpoint = SUPABASE_FUNCTION_URL;
72
+ this.captureEndpoint = config.captureEndpoint || SUPABASE_FUNCTION_URL;
73
73
  this.maxOutputBytes = 32768;
74
74
 
75
75
  // State
@@ -86,10 +86,10 @@ class Dropout {
86
86
  }
87
87
  global.__dropout_initialized__ = true;
88
88
 
89
- // Identity
90
- if (!global.__dropout_session_id__) {
91
- global.__dropout_session_id__ = this.generateSessionId();
92
- }
89
+ // ✅ Session Identity (Instance-Level, Not Global)
90
+ // Accept session_id from config OR generate a default
91
+ // Host app controls session lifecycle via startNewSession()
92
+ this.currentSessionId = config.sessionId || this.generateSessionId();
93
93
 
94
94
  // ✅ VISUAL SUCCESS INDICATOR
95
95
  if (this.debug) {
@@ -103,9 +103,6 @@ class Dropout {
103
103
  // 4. Start Network Interceptor
104
104
  this.patchNetwork();
105
105
 
106
- // 5. SEND STARTUP PING
107
- //this.sendStartupSignal();
108
-
109
106
  // Lock the instance
110
107
  Dropout.instance = this;
111
108
 
@@ -128,33 +125,23 @@ class Dropout {
128
125
  }
129
126
  }
130
127
 
131
- // --- HEARTBEAT ---
132
- sendStartupSignal() {
133
- const payload = JSON.stringify({
134
- project_id: this.projectId,
135
- session_id: 'system_boot_' + Date.now(),
136
- turn_role: 'system',
137
- turn_index: 0,
138
- content: 'Dropout SDK Initialized',
139
- metadata_flags: {
140
- type: 'system_boot',
141
- environment: process.env.NODE_ENV || 'development',
142
- runtime: 'node'
143
- },
144
- received_at: new Date().toISOString()
145
- });
146
-
147
- const req = https.request(this.captureEndpoint, {
148
- method: 'POST',
149
- headers: {
150
- 'Content-Type': 'application/json',
151
- 'x-dropout-key': this.apiKey
152
- }
153
- });
154
-
155
- req.on('error', (e) => this.log("❌ Startup Signal Failed", e.message));
156
- req.write(payload);
157
- req.end();
128
+ /**
129
+ * 🔑 START NEW SESSION
130
+ * Call this when user clicks "New Chat" or starts a new conversation thread.
131
+ * Resets turn counter and generates a fresh session_id.
132
+ * @param {string} [customSessionId] - Optional custom session ID, otherwise auto-generates
133
+ * @returns {string} The new session ID
134
+ */
135
+ startNewSession(customSessionId) {
136
+ this.currentSessionId = customSessionId || this.generateSessionId();
137
+ this.turnIndex = 0; // Reset turn counter for new session
138
+ this.lastTurnConfirmed = true;
139
+ this.lastPromptHash = null;
140
+ this.lastResponseHash = null;
141
+ if (this.debug) {
142
+ console.log(`[Dropout] 🔄 New Session Started: ${this.currentSessionId}`);
143
+ }
144
+ return this.currentSessionId;
158
145
  }
159
146
 
160
147
  hash(text) {
@@ -208,6 +195,8 @@ class Dropout {
208
195
  session_id: payload.session_id,
209
196
  turn_index: payload.turn_index,
210
197
  turn_role: payload.turn_role,
198
+ // ✅ DIRECTION LOGIC APPLIED HERE
199
+ direction: payload.turn_role === 'user' ? 'user_to_ai' : 'ai_to_user',
211
200
  provider: payload.provider,
212
201
  model: payload.model,
213
202
  latency_ms: payload.latency_ms || null,
@@ -219,7 +208,10 @@ class Dropout {
219
208
 
220
209
  this.log(`🚀 Sending Capture [${payload.turn_role}]`);
221
210
 
222
- const req = https.request(this.captureEndpoint, {
211
+ const isHttp = this.captureEndpoint.startsWith('http:');
212
+ const requestModule = isHttp ? http : https;
213
+
214
+ const req = requestModule.request(this.captureEndpoint, {
223
215
  method: 'POST',
224
216
  headers: {
225
217
  'Content-Type': 'application/json',
@@ -270,7 +262,7 @@ class Dropout {
270
262
  const pHash = _this.hash(bodyStr);
271
263
 
272
264
  _this.emit({
273
- session_id: global.__dropout_session_id__,
265
+ session_id: _this.currentSessionId,
274
266
  turn_index: activeTurn,
275
267
  turn_role: 'user',
276
268
  provider,
@@ -295,7 +287,7 @@ class Dropout {
295
287
  const oHash = _this.hash(oText);
296
288
 
297
289
  _this.emit({
298
- session_id: global.__dropout_session_id__,
290
+ session_id: _this.currentSessionId,
299
291
  turn_index: activeTurn,
300
292
  turn_role: 'assistant',
301
293
  latency_ms: latency,
@@ -381,7 +373,7 @@ class Dropout {
381
373
  clientRequest._dropout_model = model;
382
374
 
383
375
  _this.emit({
384
- session_id: global.__dropout_session_id__,
376
+ session_id: _this.currentSessionId,
385
377
  turn_index: activeTurn,
386
378
  turn_role: 'user',
387
379
  provider,
@@ -408,7 +400,7 @@ class Dropout {
408
400
  const oHash = _this.hash(resBody);
409
401
 
410
402
  _this.emit({
411
- session_id: global.__dropout_session_id__,
403
+ session_id: _this.currentSessionId,
412
404
  turn_index: clientRequest._dropout_turn || 0,
413
405
  turn_role: 'assistant',
414
406
  latency_ms: latency,
@@ -444,4 +436,6 @@ if (typeof process !== 'undefined' && process.env.DROPOUT_PROJECT_ID && process.
444
436
  });
445
437
  }
446
438
 
439
+ // 🛡️ DUAL EXPORT FIX (Crucial for TypeScript + Require compatibility)
440
+ Dropout.default = Dropout;
447
441
  module.exports = Dropout;