@pellux/goodvibes-sdk 0.33.36 → 0.33.38

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 (75) hide show
  1. package/dist/client-auth/control-plane-auth-snapshot.d.ts +23 -0
  2. package/dist/client-auth/control-plane-auth-snapshot.d.ts.map +1 -1
  3. package/dist/client-auth/oauth-types.d.ts +18 -0
  4. package/dist/client-auth/oauth-types.d.ts.map +1 -1
  5. package/dist/client-auth/types.d.ts +26 -0
  6. package/dist/client-auth/types.d.ts.map +1 -1
  7. package/dist/client.d.ts +22 -0
  8. package/dist/client.d.ts.map +1 -1
  9. package/dist/contracts/artifacts/operator-contract.json +84 -10
  10. package/dist/events/knowledge.d.ts +31 -0
  11. package/dist/events/knowledge.d.ts.map +1 -1
  12. package/dist/events/tasks.d.ts +58 -0
  13. package/dist/events/tasks.d.ts.map +1 -1
  14. package/dist/events/transport.d.ts +60 -0
  15. package/dist/events/transport.d.ts.map +1 -1
  16. package/dist/platform/agents/wrfc-config.d.ts +6 -0
  17. package/dist/platform/agents/wrfc-config.d.ts.map +1 -1
  18. package/dist/platform/agents/wrfc-config.js +6 -0
  19. package/dist/platform/agents/wrfc-controller.d.ts +74 -0
  20. package/dist/platform/agents/wrfc-controller.d.ts.map +1 -1
  21. package/dist/platform/agents/wrfc-controller.js +368 -9
  22. package/dist/platform/agents/wrfc-reporting.d.ts +60 -0
  23. package/dist/platform/agents/wrfc-reporting.d.ts.map +1 -1
  24. package/dist/platform/agents/wrfc-reporting.js +131 -7
  25. package/dist/platform/agents/wrfc-types.d.ts +12 -0
  26. package/dist/platform/agents/wrfc-types.d.ts.map +1 -1
  27. package/dist/platform/config/manager.d.ts +11 -0
  28. package/dist/platform/config/manager.d.ts.map +1 -1
  29. package/dist/platform/config/manager.js +17 -1
  30. package/dist/platform/config/schema-domain-core.d.ts +2 -0
  31. package/dist/platform/config/schema-domain-core.d.ts.map +1 -1
  32. package/dist/platform/config/schema-domain-core.js +17 -0
  33. package/dist/platform/config/schema-shared.d.ts +6 -0
  34. package/dist/platform/config/schema-shared.d.ts.map +1 -1
  35. package/dist/platform/config/schema-types.d.ts +9 -2
  36. package/dist/platform/config/schema-types.d.ts.map +1 -1
  37. package/dist/platform/control-plane/method-catalog-control-automation.d.ts.map +1 -1
  38. package/dist/platform/control-plane/method-catalog-control-automation.js +10 -4
  39. package/dist/platform/control-plane/method-catalog-knowledge.js +4 -4
  40. package/dist/platform/control-plane/method-catalog-runtime.d.ts.map +1 -1
  41. package/dist/platform/control-plane/method-catalog-runtime.js +13 -0
  42. package/dist/platform/control-plane/routes/automation.js +2 -2
  43. package/dist/platform/control-plane/routes/operator.d.ts.map +1 -1
  44. package/dist/platform/control-plane/routes/operator.js +2 -0
  45. package/dist/platform/control-plane/routes/sessions.js +1 -1
  46. package/dist/platform/daemon/http/openai-compatible-routes.d.ts.map +1 -1
  47. package/dist/platform/daemon/http/openai-compatible-routes.js +4 -12
  48. package/dist/platform/daemon/http/router.d.ts.map +1 -1
  49. package/dist/platform/daemon/http/router.js +15 -51
  50. package/dist/platform/daemon/http-listener.d.ts +17 -0
  51. package/dist/platform/daemon/http-listener.d.ts.map +1 -1
  52. package/dist/platform/daemon/http-listener.js +186 -7
  53. package/dist/platform/knowledge/knowledge-api.d.ts +1 -1
  54. package/dist/platform/knowledge/knowledge-api.d.ts.map +1 -1
  55. package/dist/platform/knowledge/knowledge-api.js +1 -1
  56. package/dist/platform/runtime/store/helpers/reducers/lifecycle.d.ts.map +1 -1
  57. package/dist/platform/runtime/store/helpers/reducers/lifecycle.js +4 -0
  58. package/dist/platform/runtime/store/helpers/reducers/sync.d.ts.map +1 -1
  59. package/dist/platform/runtime/store/helpers/reducers/sync.js +4 -0
  60. package/dist/platform/security/http-auth.d.ts.map +1 -1
  61. package/dist/platform/security/http-auth.js +11 -4
  62. package/dist/platform/security/user-auth.d.ts +98 -2
  63. package/dist/platform/security/user-auth.d.ts.map +1 -1
  64. package/dist/platform/security/user-auth.js +195 -18
  65. package/dist/platform/sessions/manager.d.ts +19 -2
  66. package/dist/platform/sessions/manager.d.ts.map +1 -1
  67. package/dist/platform/sessions/manager.js +43 -6
  68. package/dist/platform/state/memory-registry.d.ts +1 -1
  69. package/dist/platform/state/memory-registry.d.ts.map +1 -1
  70. package/dist/platform/state/memory-registry.js +2 -2
  71. package/dist/platform/state/memory-store.d.ts +1 -1
  72. package/dist/platform/state/memory-store.d.ts.map +1 -1
  73. package/dist/platform/state/memory-store.js +2 -2
  74. package/dist/platform/version.js +1 -1
  75. package/package.json +9 -9
@@ -1,17 +1,25 @@
1
1
  import { createHash, randomBytes, scryptSync, timingSafeEqual } from 'crypto';
2
- import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
2
+ import { chmodSync, closeSync, existsSync, fsyncSync, mkdirSync, openSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
3
3
  import { dirname, join } from 'node:path';
4
4
  import { logger } from '../utils/logger.js';
5
5
  const DEFAULT_SESSION_TTL_MS = 3_600_000;
6
6
  const DEFAULT_MAX_SESSIONS = 1000;
7
+ const DEFAULT_MAX_ACCOUNT_LOCKS = 10_000;
7
8
  const SCRYPT_KEY_LENGTH = 64;
9
+ /** Default scrypt cost parameters matching Node.js defaults (N=16384, r=8, p=1). */
10
+ const DEFAULT_SCRYPT_PARAMS = { N: 16384, r: 8, p: 1 };
8
11
  function toBase64(value) {
9
12
  return value.toString('base64');
10
13
  }
11
- function hashPassword(password, salt) {
14
+ /**
15
+ * Hash format: base64(salt):N:r:p:base64(derived)
16
+ * The cost parameters are stored in the hash so old hashes can be verified
17
+ * even after the default params change.
18
+ */
19
+ function hashPassword(password, params = DEFAULT_SCRYPT_PARAMS, salt) {
12
20
  const actualSalt = salt ?? randomBytes(16);
13
- const derived = scryptSync(password, actualSalt, SCRYPT_KEY_LENGTH);
14
- return `${toBase64(actualSalt)}:${toBase64(derived)}`;
21
+ const derived = scryptSync(password, actualSalt, SCRYPT_KEY_LENGTH, { N: params.N, r: params.r, p: params.p });
22
+ return `${toBase64(actualSalt)}:${params.N}:${params.r}:${params.p}:${toBase64(derived)}`;
15
23
  }
16
24
  /**
17
25
  * Generate a cryptographically random one-time password.
@@ -21,15 +29,43 @@ function generateInitialPassword() {
21
29
  return randomBytes(16).toString('hex');
22
30
  }
23
31
  function verifyPassword(password, passwordHash) {
24
- const [saltEncoded, hashEncoded] = passwordHash.split(':');
32
+ const parts = passwordHash.split(':');
33
+ // Legacy format: salt:hash (2 parts) — uses DEFAULT_SCRYPT_PARAMS for backward compat.
34
+ // New format: salt:N:r:p:hash (5 parts)
35
+ let saltEncoded;
36
+ let hashEncoded;
37
+ let params;
38
+ if (parts.length === 2) {
39
+ // Legacy hash: no cost params stored — use defaults.
40
+ [saltEncoded, hashEncoded] = parts;
41
+ params = DEFAULT_SCRYPT_PARAMS;
42
+ }
43
+ else if (parts.length === 5) {
44
+ const N = Number(parts[1]);
45
+ const r = Number(parts[2]);
46
+ const p = Number(parts[3]);
47
+ if (!Number.isInteger(N) || N < 1 || !Number.isInteger(r) || r < 1 || !Number.isInteger(p) || p < 1)
48
+ return false;
49
+ saltEncoded = parts[0];
50
+ hashEncoded = parts[4];
51
+ params = { N, r, p };
52
+ }
53
+ else {
54
+ return false;
55
+ }
25
56
  if (!saltEncoded || !hashEncoded)
26
57
  return false;
27
- const salt = Buffer.from(saltEncoded, 'base64');
28
- const expected = Buffer.from(hashEncoded, 'base64');
29
- const actual = scryptSync(password, salt, SCRYPT_KEY_LENGTH);
30
- if (actual.length !== expected.length)
58
+ try {
59
+ const salt = Buffer.from(saltEncoded, 'base64');
60
+ const expected = Buffer.from(hashEncoded, 'base64');
61
+ const actual = scryptSync(password, salt, SCRYPT_KEY_LENGTH, { N: params.N, r: params.r, p: params.p });
62
+ if (actual.length !== expected.length)
63
+ return false;
64
+ return timingSafeEqual(actual, expected);
65
+ }
66
+ catch {
31
67
  return false;
32
- return timingSafeEqual(actual, expected);
68
+ }
33
69
  }
34
70
  function fingerprintToken(token) {
35
71
  return createHash('sha256').update(token).digest('hex').slice(0, 16);
@@ -59,7 +95,8 @@ function readBootstrapUsers(filePath) {
59
95
  * some Linux fs drivers).
60
96
  */
61
97
  function atomicWriteSecretFile(filePath, content) {
62
- mkdirSync(dirname(filePath), { recursive: true });
98
+ const dir = dirname(filePath);
99
+ mkdirSync(dir, { recursive: true });
63
100
  const tmpPath = filePath + '.tmp';
64
101
  writeFileSync(tmpPath, content, { encoding: 'utf-8', mode: 0o600 });
65
102
  try {
@@ -68,7 +105,23 @@ function atomicWriteSecretFile(filePath, content) {
68
105
  catch (error) {
69
106
  logger.warn('User auth secret temp chmod failed', { path: tmpPath, error: String(error) });
70
107
  }
108
+ // fsync the file data before rename so the content survives power loss.
109
+ const fd = openSync(tmpPath, 'r+');
110
+ try {
111
+ fsyncSync(fd);
112
+ }
113
+ finally {
114
+ closeSync(fd);
115
+ }
71
116
  renameSync(tmpPath, filePath);
117
+ // fsync the parent directory so the directory entry (rename) is durable.
118
+ const dirFd = openSync(dir, 'r');
119
+ try {
120
+ fsyncSync(dirFd);
121
+ }
122
+ finally {
123
+ closeSync(dirFd);
124
+ }
72
125
  try {
73
126
  chmodSync(filePath, 0o600);
74
127
  }
@@ -158,11 +211,17 @@ function detectBootstrapCredentialDrift(users, credentialPath, userStorePath) {
158
211
  export class UserAuthManager {
159
212
  users = new Map();
160
213
  sessions = new Map();
214
+ /** Per-username failure counters for account-level lockout (independent of IP throttling). */
215
+ accountLocks = new Map();
161
216
  sessionTtlMs;
162
217
  maxSessions;
218
+ maxAccountLocks;
163
219
  userStorePath;
164
220
  bootstrapCredentialPath;
165
221
  persistUsers;
222
+ scryptParams;
223
+ /** Injectable clock — defaults to Date.now for production. */
224
+ nowFn;
166
225
  constructor(config) {
167
226
  if (!config.bootstrapFilePath)
168
227
  throw new Error('UserAuthManager requires an explicit bootstrapFilePath.');
@@ -170,9 +229,12 @@ export class UserAuthManager {
170
229
  throw new Error('UserAuthManager requires an explicit bootstrapCredentialPath.');
171
230
  this.sessionTtlMs = config.sessionTtlMs ?? DEFAULT_SESSION_TTL_MS;
172
231
  this.maxSessions = config.maxSessions ?? DEFAULT_MAX_SESSIONS;
232
+ this.maxAccountLocks = config.maxAccountLocks ?? DEFAULT_MAX_ACCOUNT_LOCKS;
173
233
  this.userStorePath = config.bootstrapFilePath;
174
234
  this.bootstrapCredentialPath = config.bootstrapCredentialPath;
175
235
  this.persistUsers = config.users === undefined;
236
+ this.scryptParams = config.scryptParams ?? DEFAULT_SCRYPT_PARAMS;
237
+ this.nowFn = config.nowFn ?? (() => Date.now());
176
238
  const seedUsers = config.users ?? loadOrBootstrapUsers(this.userStorePath, this.bootstrapCredentialPath);
177
239
  for (const user of seedUsers) {
178
240
  this.users.set(user.username, user);
@@ -184,14 +246,129 @@ export class UserAuthManager {
184
246
  detectBootstrapCredentialDrift(seedUsers, this.bootstrapCredentialPath, this.userStorePath);
185
247
  }
186
248
  }
187
- static hashPassword(password) {
188
- return hashPassword(password);
249
+ static hashPassword(password, params) {
250
+ return hashPassword(password, params ?? DEFAULT_SCRYPT_PARAMS);
189
251
  }
252
+ /**
253
+ * Authenticate username/password with per-account lockout.
254
+ * - Does NOT leak whether a username exists (same generic error path for unknown users).
255
+ * - Records failures against the username bucket (not IP) with escalating backoff.
256
+ * - Returns lockedUntilMs when the account is temporarily locked.
257
+ * - Returns usedBootstrapCredential=true when the matched credential originated from
258
+ * the bootstrap credential file, so callers can defer retirement until a non-bootstrap
259
+ * login succeeds.
260
+ */
190
261
  authenticate(username, password) {
262
+ const now = this.nowFn();
263
+ const lockState = this.accountLocks.get(username);
264
+ // Check account lock (before touching user store — avoids user-existence side-channel).
265
+ if (lockState && lockState.lockedUntil > now) {
266
+ // Still locked — record the attempt but keep the lock window intact.
267
+ lockState.failures++;
268
+ return { ok: false, lockedUntilMs: lockState.lockedUntil };
269
+ }
191
270
  const user = this.users.get(username);
192
- if (!user)
193
- return null;
194
- return verifyPassword(password, user.passwordHash) ? user : null;
271
+ // Always verify something with constant-time cost so unknown usernames
272
+ // have the same timing profile as known usernames with wrong password.
273
+ const hashToCheck = user?.passwordHash ?? 'dGVzdHNhbHQ=:16384:8:1:dGVzdGhhc2h0ZXN0aGFzaHRlc3RoYXNodGVzdGhhc2g=';
274
+ const passwordOk = user !== undefined && verifyPassword(password, hashToCheck);
275
+ if (!passwordOk) {
276
+ this._recordLoginFailure(username, now);
277
+ // Check if this failure just triggered a lock.
278
+ const newLock = this.accountLocks.get(username);
279
+ const lockedUntilMs = newLock && newLock.lockedUntil > now ? newLock.lockedUntil : undefined;
280
+ return lockedUntilMs !== undefined ? { ok: false, lockedUntilMs } : { ok: false };
281
+ }
282
+ // Determine whether this login used the bootstrap credential.
283
+ // Compare against the credential file contents without leaking timing info
284
+ // (we already confirmed the password is correct at this point).
285
+ const bootstrapCred = readBootstrapCredentialFile(this.bootstrapCredentialPath);
286
+ const usedBootstrapCredential = bootstrapCred !== null
287
+ && bootstrapCred.username === username
288
+ && bootstrapCred.password === password;
289
+ // Success — clear failure count.
290
+ this.accountLocks.delete(username);
291
+ return { ok: true, user, usedBootstrapCredential };
292
+ }
293
+ /**
294
+ * Record a login failure for the given username and apply escalating backoff.
295
+ *
296
+ * Thresholds are anchored ABOVE the per-IP login budget (default 5/min) so
297
+ * the first account lock cannot fire before the IP limiter has already
298
+ * throttled further attempts. This preserves the 401-then-429-by-IP contract
299
+ * for in-budget attempts.
300
+ *
301
+ * 1-5 failures: no lock (IP budget exhaustion fires at attempt 6+)
302
+ * 6-9 failures: 30-second lock
303
+ * 10-19 failures: 5-minute lock
304
+ * 20+ failures: 30-minute lock
305
+ *
306
+ * Note: failures also increment during an active lock window (when the account
307
+ * is already locked and another attempt arrives). This means a lock can expire
308
+ * with a failure count already in a higher tier, causing the next lock after
309
+ * expiry to jump directly to that tier. This is intentional: repeated attempts
310
+ * during a lock are themselves failures and escalate the penalty schedule.
311
+ */
312
+ _recordLoginFailure(username, now) {
313
+ const existing = this.accountLocks.get(username);
314
+ if (!existing) {
315
+ // New entry — evict if at cap before inserting.
316
+ this._evictAccountLockIfNeeded(now);
317
+ }
318
+ const state = existing ?? { failures: 0, lockedUntil: 0 };
319
+ state.failures++;
320
+ const f = state.failures;
321
+ let lockDurationMs = 0;
322
+ if (f >= 20)
323
+ lockDurationMs = 30 * 60_000;
324
+ else if (f >= 10)
325
+ lockDurationMs = 5 * 60_000;
326
+ else if (f >= 6)
327
+ lockDurationMs = 30_000;
328
+ state.lockedUntil = lockDurationMs > 0 ? now + lockDurationMs : 0;
329
+ this.accountLocks.set(username, state);
330
+ }
331
+ /**
332
+ * Evict account lock entries when the map is at capacity.
333
+ * Prefers entries with expired locks + no recent failures (stale entries).
334
+ * Falls back to the entry with the oldest/soonest-expired lock.
335
+ * Preserves no-username-enumeration: eviction policy is time-based, not
336
+ * user-existence-based.
337
+ */
338
+ _evictAccountLockIfNeeded(now) {
339
+ if (this.accountLocks.size < this.maxAccountLocks)
340
+ return;
341
+ // First sweep: remove all fully stale entries (lock expired AND low failure count).
342
+ for (const [key, state] of this.accountLocks.entries()) {
343
+ if (state.lockedUntil <= now && state.failures < 6) {
344
+ this.accountLocks.delete(key);
345
+ if (this.accountLocks.size < this.maxAccountLocks)
346
+ return;
347
+ }
348
+ }
349
+ // Second sweep: remove the entry with the smallest lockedUntil (most expired).
350
+ let oldestKey;
351
+ let oldestUntil = Infinity;
352
+ for (const [key, state] of this.accountLocks.entries()) {
353
+ if (state.lockedUntil < oldestUntil) {
354
+ oldestUntil = state.lockedUntil;
355
+ oldestKey = key;
356
+ }
357
+ }
358
+ if (oldestKey !== undefined) {
359
+ this.accountLocks.delete(oldestKey);
360
+ }
361
+ }
362
+ /**
363
+ * Expose account lock state for testing.
364
+ * Returns a defensive copy so callers cannot mutate internal state.
365
+ * Use the injected nowFn (via UserAuthManager constructor) to advance time in tests.
366
+ */
367
+ getAccountLockState(username) {
368
+ const state = this.accountLocks.get(username);
369
+ if (!state)
370
+ return undefined;
371
+ return { failures: state.failures, lockedUntil: state.lockedUntil };
195
372
  }
196
373
  getUser(username) {
197
374
  const user = this.users.get(username);
@@ -286,7 +463,7 @@ export class UserAuthManager {
286
463
  throw new Error('Password must be at least 8 characters.');
287
464
  const user = {
288
465
  username: normalized,
289
- passwordHash: hashPassword(password),
466
+ passwordHash: hashPassword(password, this.scryptParams),
290
467
  roles: [...new Set(roles.filter(Boolean))],
291
468
  };
292
469
  this.users.set(normalized, user);
@@ -315,7 +492,7 @@ export class UserAuthManager {
315
492
  throw new Error('Password must be at least 8 characters.');
316
493
  this.users.set(normalized, {
317
494
  ...existing,
318
- passwordHash: hashPassword(nextPassword),
495
+ passwordHash: hashPassword(nextPassword, this.scryptParams),
319
496
  });
320
497
  this.revokeSessionsForUser(normalized);
321
498
  this.persist();
@@ -11,6 +11,8 @@ export interface SessionMeta {
11
11
  timestamp: number;
12
12
  titleSource?: ConversationTitleSource | undefined;
13
13
  returnContext?: SessionReturnContextSummary | undefined;
14
+ /** File format version written into the JSONL meta line. Present on files saved after schemaVersion was introduced. Missing on older files (treat as version 0). */
15
+ schemaVersion?: number | undefined;
14
16
  }
15
17
  /**
16
18
  * Summary info for listing saved sessions.
@@ -34,6 +36,13 @@ export interface SessionInfo {
34
36
  * Line 0: { type: 'meta', ...SessionMeta }
35
37
  * Line N: { type: 'message', ...message fields }
36
38
  */
39
+ /**
40
+ * Current schema version written to session files.
41
+ * Increment when the file format changes in a backward-incompatible way.
42
+ * Readers accept: version undefined (legacy, treated as 0), version <= CURRENT, and
43
+ * version > CURRENT (future — logged as a warning, accepted with best-effort parsing).
44
+ */
45
+ export declare const CURRENT_SESSION_SCHEMA_VERSION = 1;
37
46
  export declare class SessionManager {
38
47
  private sessionsDir;
39
48
  constructor(baseDir: string, options?: {
@@ -46,8 +55,16 @@ export declare class SessionManager {
46
55
  */
47
56
  private _cleanupOrphanTempFiles;
48
57
  /**
49
- * Atomically write content to filePath via a temp file + fsync + rename.
50
- * Protects against partial writes on crash.
58
+ * Atomically write content to filePath via a temp file + fsync(file) + rename + fsync(dir).
59
+ * Protects against partial writes and directory-entry reversion on power loss:
60
+ * 1. Write content to a tmp file in the same directory.
61
+ * 2. fsync the tmp file to flush its data to storage.
62
+ * 3. rename the tmp file into place (atomic on POSIX).
63
+ * 4. fsync the parent directory to flush the directory entry — without
64
+ * this step, on power loss after rename the directory entry can
65
+ * revert and the renamed file disappears.
66
+ * Mirrors the reference implementation in platform/security/user-auth.ts
67
+ * (atomicWriteSecretFile), which performs both fsyncs.
51
68
  */
52
69
  private _atomicWrite;
53
70
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../../src/platform/sessions/manager.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,sCAAsC,CAAC;AACxF,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAIvE;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,uBAAuB,GAAG,SAAS,CAAC;IAClD,aAAa,CAAC,EAAE,2BAA2B,GAAG,SAAS,CAAC;CACzD;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,uBAAuB,GAAG,SAAS,CAAC;IAClD,aAAa,CAAC,EAAE,2BAA2B,GAAG,SAAS,CAAC;CACzD;AAED;;;;;;;GAOG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,WAAW,CAAS;gBAEhB,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE;IAM/H;;;OAGG;IACH,OAAO,CAAC,uBAAuB;IA0B/B;;;OAGG;IACH,OAAO,CAAC,YAAY;IAapB;;;;OAIG;IACH,IAAI,CACF,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAAE,EAClB,IAAI,EAAE,WAAW,EACjB,YAAY,CAAC,EAAE,WAAW,EAAE,GAC3B;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE;IAuC9C;;;OAGG;IACH,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG;QAAE,IAAI,EAAE,WAAW,CAAC;QAAC,QAAQ,EAAE,MAAM,EAAE,CAAC;QAAC,YAAY,EAAE,WAAW,EAAE,CAAA;KAAE;IAuD1F;;OAEG;IACH,IAAI,IAAI,WAAW,EAAE;IA6FrB;;;OAGG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI;IA+BzC;;;;;OAKG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IAoB5C;;;OAGG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAY1B;;;OAGG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,OAAO,EAAE,WAAW,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IAuD9F;;;;OAIG;IACH,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;CASnC"}
1
+ {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../../src/platform/sessions/manager.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,sCAAsC,CAAC;AACxF,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAIvE;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,uBAAuB,GAAG,SAAS,CAAC;IAClD,aAAa,CAAC,EAAE,2BAA2B,GAAG,SAAS,CAAC;IACxD,oKAAoK;IACpK,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,uBAAuB,GAAG,SAAS,CAAC;IAClD,aAAa,CAAC,EAAE,2BAA2B,GAAG,SAAS,CAAC;CACzD;AAED;;;;;;;GAOG;AACH;;;;;GAKG;AACH,eAAO,MAAM,8BAA8B,IAAI,CAAC;AAEhD,qBAAa,cAAc;IACzB,OAAO,CAAC,WAAW,CAAS;gBAEhB,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE;IAM/H;;;OAGG;IACH,OAAO,CAAC,uBAAuB;IA0B/B;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,YAAY;IAoBpB;;;;OAIG;IACH,IAAI,CACF,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAAE,EAClB,IAAI,EAAE,WAAW,EACjB,YAAY,CAAC,EAAE,WAAW,EAAE,GAC3B;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE;IAwC9C;;;OAGG;IACH,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG;QAAE,IAAI,EAAE,WAAW,CAAC;QAAC,QAAQ,EAAE,MAAM,EAAE,CAAC;QAAC,YAAY,EAAE,WAAW,EAAE,CAAA;KAAE;IAgE1F;;OAEG;IACH,IAAI,IAAI,WAAW,EAAE;IA+FrB;;;OAGG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI;IAiCzC;;;;;OAKG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IAoB5C;;;OAGG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAY1B;;;OAGG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,OAAO,EAAE,WAAW,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IAuD9F;;;;OAIG;IACH,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;CASnC"}
@@ -11,6 +11,13 @@ import { resolveScopedDirectory } from '../runtime/surface-root.js';
11
11
  * Line 0: { type: 'meta', ...SessionMeta }
12
12
  * Line N: { type: 'message', ...message fields }
13
13
  */
14
+ /**
15
+ * Current schema version written to session files.
16
+ * Increment when the file format changes in a backward-incompatible way.
17
+ * Readers accept: version undefined (legacy, treated as 0), version <= CURRENT, and
18
+ * version > CURRENT (future — logged as a warning, accepted with best-effort parsing).
19
+ */
20
+ export const CURRENT_SESSION_SCHEMA_VERSION = 1;
14
21
  export class SessionManager {
15
22
  sessionsDir;
16
23
  constructor(baseDir, options) {
@@ -51,21 +58,37 @@ export class SessionManager {
51
58
  }
52
59
  }
53
60
  /**
54
- * Atomically write content to filePath via a temp file + fsync + rename.
55
- * Protects against partial writes on crash.
61
+ * Atomically write content to filePath via a temp file + fsync(file) + rename + fsync(dir).
62
+ * Protects against partial writes and directory-entry reversion on power loss:
63
+ * 1. Write content to a tmp file in the same directory.
64
+ * 2. fsync the tmp file to flush its data to storage.
65
+ * 3. rename the tmp file into place (atomic on POSIX).
66
+ * 4. fsync the parent directory to flush the directory entry — without
67
+ * this step, on power loss after rename the directory entry can
68
+ * revert and the renamed file disappears.
69
+ * Mirrors the reference implementation in platform/security/user-auth.ts
70
+ * (atomicWriteSecretFile), which performs both fsyncs.
56
71
  */
57
72
  _atomicWrite(filePath, content) {
58
73
  const tmpPath = join(this.sessionsDir, `.tmp-${process.pid}-${Date.now()}`);
59
74
  writeFileSync(tmpPath, content, 'utf-8');
60
- // fsync to flush OS write buffers before rename
61
- const fd = openSync(tmpPath, 'r+');
75
+ // fsync the file to flush data buffers before rename
76
+ const fileFd = openSync(tmpPath, 'r+');
62
77
  try {
63
- fsyncSync(fd);
78
+ fsyncSync(fileFd);
64
79
  }
65
80
  finally {
66
- closeSync(fd);
81
+ closeSync(fileFd);
67
82
  }
68
83
  renameSync(tmpPath, filePath);
84
+ // fsync the directory to flush the directory entry (makes rename durable)
85
+ const dirFd = openSync(this.sessionsDir, 'r');
86
+ try {
87
+ fsyncSync(dirFd);
88
+ }
89
+ finally {
90
+ closeSync(dirFd);
91
+ }
69
92
  }
70
93
  /**
71
94
  * Save conversation messages to a JSONL session file.
@@ -82,6 +105,7 @@ export class SessionManager {
82
105
  // First line: meta record
83
106
  const metaRecord = {
84
107
  type: 'meta',
108
+ schemaVersion: CURRENT_SESSION_SCHEMA_VERSION,
85
109
  timestamp: meta.timestamp,
86
110
  title: meta.title,
87
111
  model: meta.model,
@@ -135,6 +159,14 @@ export class SessionManager {
135
159
  continue;
136
160
  }
137
161
  if (record.type === 'meta') {
162
+ const fileVersion = typeof record.schemaVersion === 'number' ? record.schemaVersion : 0;
163
+ if (fileVersion > CURRENT_SESSION_SCHEMA_VERSION) {
164
+ logger.warn('SessionManager: session file has a newer schemaVersion — loading with best-effort parsing', {
165
+ name,
166
+ fileVersion,
167
+ currentVersion: CURRENT_SESSION_SCHEMA_VERSION,
168
+ });
169
+ }
138
170
  meta = {
139
171
  title: String(record.title ?? ''),
140
172
  model: String(record.model ?? ''),
@@ -144,6 +176,7 @@ export class SessionManager {
144
176
  returnContext: (record.returnContext && typeof record.returnContext === 'object')
145
177
  ? record.returnContext
146
178
  : undefined,
179
+ schemaVersion: fileVersion,
147
180
  };
148
181
  }
149
182
  else if (record.type === 'message') {
@@ -196,6 +229,7 @@ export class SessionManager {
196
229
  try {
197
230
  const first = JSON.parse(lines[0]);
198
231
  if (first.type === 'meta') {
232
+ const fileVersion = typeof first.schemaVersion === 'number' ? first.schemaVersion : 0;
199
233
  meta = {
200
234
  title: String(first.title ?? ''),
201
235
  model: String(first.model ?? ''),
@@ -205,6 +239,7 @@ export class SessionManager {
205
239
  returnContext: (first.returnContext && typeof first.returnContext === 'object')
206
240
  ? first.returnContext
207
241
  : undefined,
242
+ schemaVersion: fileVersion,
208
243
  };
209
244
  }
210
245
  }
@@ -273,6 +308,7 @@ export class SessionManager {
273
308
  const record = JSON.parse(firstLine);
274
309
  if (record.type !== 'meta')
275
310
  return null;
311
+ const fileVersion = typeof record.schemaVersion === 'number' ? record.schemaVersion : 0;
276
312
  return {
277
313
  title: String(record.title ?? ''),
278
314
  model: String(record.model ?? ''),
@@ -282,6 +318,7 @@ export class SessionManager {
282
318
  returnContext: (record.returnContext && typeof record.returnContext === 'object')
283
319
  ? record.returnContext
284
320
  : undefined,
321
+ schemaVersion: fileVersion,
285
322
  };
286
323
  }
287
324
  catch (err) {
@@ -19,7 +19,7 @@ export declare class MemoryRegistry {
19
19
  rebuildVectorsAsync(): Promise<MemoryVectorStats>;
20
20
  vectorStats(): MemoryVectorStats;
21
21
  doctor(): Promise<MemoryDoctorReport>;
22
- reviewQueue(limit?: number): MemoryRecord[];
22
+ reviewQueue(limit?: number, scope?: MemoryScope): MemoryRecord[];
23
23
  exportBundle(filter?: MemorySearchFilter): MemoryBundle;
24
24
  importBundle(bundle: MemoryBundle): Promise<MemoryImportResult>;
25
25
  get(id: string): MemoryRecord | null;
@@ -1 +1 @@
1
- {"version":3,"file":"memory-registry.d.ts","sourceRoot":"","sources":["../../../src/platform/state/memory-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,kBAAkB,EAAE,UAAU,EAAE,YAAY,EAAE,iBAAiB,EAAE,WAAW,EAAE,kBAAkB,EAAE,0BAA0B,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACnN,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAE5D;;;GAGG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,SAAS,CAAyB;gBAE9B,KAAK,EAAE,WAAW;IAI9B,QAAQ,IAAI,WAAW;IAIvB,SAAS,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI;IAOrC,OAAO,CAAC,MAAM;IAIR,GAAG,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,YAAY,CAAC;IAMxD,MAAM,CAAC,MAAM,GAAE,kBAAuB,GAAG,YAAY,EAAE;IAIvD,cAAc,CAAC,MAAM,GAAE,kBAAuB,GAAG,0BAA0B,EAAE;IAI7E,cAAc,IAAI,iBAAiB;IAI7B,mBAAmB,IAAI,OAAO,CAAC,iBAAiB,CAAC;IAIvD,WAAW,IAAI,iBAAiB;IAI1B,MAAM,IAAI,OAAO,CAAC,kBAAkB,CAAC;IAI3C,WAAW,CAAC,KAAK,SAAK,GAAG,YAAY,EAAE;IAIvC,YAAY,CAAC,MAAM,GAAE,kBAAuB,GAAG,YAAY;IAIrD,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAMrE,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI;IAI9B,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAMtF,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,UAAU,EAAE;IAIlC,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE;QAAE,KAAK,CAAC,EAAE,WAAW,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,GAAG,YAAY,GAAG,IAAI;IAM3H,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,GAAG,YAAY,GAAG,IAAI;IAMjE,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAM3B,MAAM,IAAI,YAAY,EAAE;CAGzB"}
1
+ {"version":3,"file":"memory-registry.d.ts","sourceRoot":"","sources":["../../../src/platform/state/memory-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,kBAAkB,EAAE,UAAU,EAAE,YAAY,EAAE,iBAAiB,EAAE,WAAW,EAAE,kBAAkB,EAAE,0BAA0B,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACnN,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAE5D;;;GAGG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,SAAS,CAAyB;gBAE9B,KAAK,EAAE,WAAW;IAI9B,QAAQ,IAAI,WAAW;IAIvB,SAAS,CAAC,EAAE,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI;IAOrC,OAAO,CAAC,MAAM;IAIR,GAAG,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,YAAY,CAAC;IAMxD,MAAM,CAAC,MAAM,GAAE,kBAAuB,GAAG,YAAY,EAAE;IAIvD,cAAc,CAAC,MAAM,GAAE,kBAAuB,GAAG,0BAA0B,EAAE;IAI7E,cAAc,IAAI,iBAAiB;IAI7B,mBAAmB,IAAI,OAAO,CAAC,iBAAiB,CAAC;IAIvD,WAAW,IAAI,iBAAiB;IAI1B,MAAM,IAAI,OAAO,CAAC,kBAAkB,CAAC;IAI3C,WAAW,CAAC,KAAK,SAAK,EAAE,KAAK,CAAC,EAAE,WAAW,GAAG,YAAY,EAAE;IAI5D,YAAY,CAAC,MAAM,GAAE,kBAAuB,GAAG,YAAY;IAIrD,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAMrE,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI;IAI9B,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAMtF,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,UAAU,EAAE;IAIlC,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE;QAAE,KAAK,CAAC,EAAE,WAAW,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,GAAG,YAAY,GAAG,IAAI;IAM3H,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB,GAAG,YAAY,GAAG,IAAI;IAMjE,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAM3B,MAAM,IAAI,YAAY,EAAE;CAGzB"}
@@ -44,8 +44,8 @@ export class MemoryRegistry {
44
44
  async doctor() {
45
45
  return this.store.doctor();
46
46
  }
47
- reviewQueue(limit = 10) {
48
- return this.store.reviewQueue(limit);
47
+ reviewQueue(limit = 10, scope) {
48
+ return this.store.reviewQueue(limit, scope);
49
49
  }
50
50
  exportBundle(filter = {}) {
51
51
  return this.store.exportBundle(filter);
@@ -148,7 +148,7 @@ export declare class MemoryStore {
148
148
  /** Search records with an optional filter. */
149
149
  search(filter?: MemorySearchFilter): MemoryRecord[];
150
150
  searchSemantic(filter?: MemorySearchFilter): MemorySemanticSearchResult[];
151
- reviewQueue(limit?: number): MemoryRecord[];
151
+ reviewQueue(limit?: number, scope?: MemoryScope): MemoryRecord[];
152
152
  exportBundle(filter?: MemorySearchFilter): MemoryBundle;
153
153
  importBundle(bundle: MemoryBundle): Promise<MemoryImportResult>;
154
154
  /** Create a directed link between two records. */
@@ -1 +1 @@
1
- {"version":3,"file":"memory-store.d.ts","sourceRoot":"","sources":["../../../src/platform/state/memory-store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"memory-store.d.ts","sourceRoot":"","sources":["../../../src/platform/state/memory-store.ts"],"names":[],"mappings":"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"}
@@ -207,8 +207,8 @@ export class MemoryStore {
207
207
  .sort((a, b) => b.score - a.score || a.distance - b.distance || b.record.updatedAt - a.record.updatedAt)
208
208
  .slice(0, requestedLimit);
209
209
  }
210
- reviewQueue(limit = 10) {
211
- const records = this.search({ limit: Math.max(limit * 4, 25) });
210
+ reviewQueue(limit = 10, scope) {
211
+ const records = this.search({ limit: Math.max(limit * 4, 25), ...(scope ? { scope } : {}) });
212
212
  const candidates = records.filter((record) => isReviewCandidate(record));
213
213
  return candidates
214
214
  .sort((a, b) => reviewQueueScore(b) - reviewQueueScore(a) || b.updatedAt - a.updatedAt || b.createdAt - a.createdAt)
@@ -1,6 +1,6 @@
1
1
  import { readFileSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
- let version = '0.33.36';
3
+ let version = '0.33.38';
4
4
  try {
5
5
  const pkg = JSON.parse(readFileSync(join(import.meta.dir, '..', '..', 'package.json'), 'utf-8'));
6
6
  version = pkg.version ?? version;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pellux/goodvibes-sdk",
3
- "version": "0.33.36",
3
+ "version": "0.33.38",
4
4
  "description": "TypeScript SDK for building GoodVibes operator, peer, web, mobile, and daemon-connected apps with typed contracts, auth, realtime events, and transport layers.",
5
5
  "keywords": [
6
6
  "goodvibes",
@@ -453,14 +453,14 @@
453
453
  "sideEffects": false,
454
454
  "type": "module",
455
455
  "dependencies": {
456
- "@pellux/goodvibes-contracts": "0.33.36",
457
- "@pellux/goodvibes-daemon-sdk": "0.33.36",
458
- "@pellux/goodvibes-errors": "0.33.36",
459
- "@pellux/goodvibes-operator-sdk": "0.33.36",
460
- "@pellux/goodvibes-peer-sdk": "0.33.36",
461
- "@pellux/goodvibes-transport-core": "0.33.36",
462
- "@pellux/goodvibes-transport-http": "0.33.36",
463
- "@pellux/goodvibes-transport-realtime": "0.33.36"
456
+ "@pellux/goodvibes-contracts": "0.33.38",
457
+ "@pellux/goodvibes-daemon-sdk": "0.33.38",
458
+ "@pellux/goodvibes-errors": "0.33.38",
459
+ "@pellux/goodvibes-operator-sdk": "0.33.38",
460
+ "@pellux/goodvibes-peer-sdk": "0.33.38",
461
+ "@pellux/goodvibes-transport-core": "0.33.38",
462
+ "@pellux/goodvibes-transport-http": "0.33.38",
463
+ "@pellux/goodvibes-transport-realtime": "0.33.38"
464
464
  },
465
465
  "optionalDependencies": {
466
466
  "@agentclientprotocol/sdk": "^0.21.0",