backend-manager 5.2.6 → 5.2.7
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/CHANGELOG.md
CHANGED
|
@@ -14,6 +14,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
|
14
14
|
- `Fixed` for any bug fixes.
|
|
15
15
|
- `Security` in case of vulnerabilities.
|
|
16
16
|
|
|
17
|
+
# [5.2.7] - 2026-05-24
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- **`inferContact` silent-failure logging.** When the AI inference returned an empty result (either the AI call failed and returned null, or `gpt-5-mini` returned a parsed response with all-empty fields, or the response shape was missing `firstName`), the whole flow silently swallowed the failure and the signup's `user.personal.name` got written as `null`/`null`. Confirmed live on Somiibo: signed up `ian.wiedenman.business@gmail.com` twice on the same backend — first signup inferred nothing (empty name written), second signup correctly inferred "Ian Wiedenman". Same email, same code, transient AI hiccup, zero log trail. Added three unconditional diagnostic logs to `src/manager/libraries/infer-contact.js` (AI returned null, AI parsed response had all fields empty, AI response missing firstName) plus a log in `src/manager/routes/user/signup/post.js#inferUserContact` when the helper returns null. Next silent failure will at least leave a breadcrumb.
|
|
22
|
+
|
|
17
23
|
# [5.2.6] - 2026-05-24
|
|
18
24
|
|
|
19
25
|
### Added
|
package/package.json
CHANGED
|
@@ -26,6 +26,9 @@ async function inferContact(email, assistant) {
|
|
|
26
26
|
if (aiResult) {
|
|
27
27
|
return aiResult;
|
|
28
28
|
}
|
|
29
|
+
assistant.log(`inferContact: AI returned null for ${email} — falling back to empty result`);
|
|
30
|
+
} else {
|
|
31
|
+
assistant.log(`inferContact: BACKEND_MANAGER_OPENAI_API_KEY not set — skipping AI inference for ${email}`);
|
|
29
32
|
}
|
|
30
33
|
|
|
31
34
|
return { firstName: '', lastName: '', company: '', confidence: 0, method: 'none' };
|
|
@@ -57,18 +60,22 @@ async function inferContactWithAI(email, assistant) {
|
|
|
57
60
|
|
|
58
61
|
const parsed = result?.content;
|
|
59
62
|
if (parsed?.firstName !== undefined) {
|
|
60
|
-
|
|
63
|
+
const inferred = {
|
|
61
64
|
firstName: capitalize(parsed.firstName || ''),
|
|
62
65
|
lastName: capitalize(parsed.lastName || ''),
|
|
63
66
|
company: capitalize(parsed.company || ''),
|
|
64
67
|
confidence: typeof parsed.confidence === 'number' ? parsed.confidence : 0.5,
|
|
65
68
|
method: 'ai',
|
|
66
69
|
};
|
|
70
|
+
if (!inferred.firstName && !inferred.lastName && !inferred.company) {
|
|
71
|
+
assistant.log(`inferContactWithAI: AI parsed response had ALL fields empty for ${email}. Raw:`, parsed);
|
|
72
|
+
}
|
|
73
|
+
return inferred;
|
|
67
74
|
}
|
|
75
|
+
|
|
76
|
+
assistant.log(`inferContactWithAI: AI response missing firstName for ${email}. Raw result:`, result);
|
|
68
77
|
} catch (e) {
|
|
69
|
-
|
|
70
|
-
assistant.error('inferContactWithAI: Failed:', e);
|
|
71
|
-
}
|
|
78
|
+
assistant.error('inferContactWithAI: Failed:', e);
|
|
72
79
|
}
|
|
73
80
|
|
|
74
81
|
return null;
|
|
@@ -229,6 +229,7 @@ async function inferUserContact(assistant, email) {
|
|
|
229
229
|
const inferred = await inferContact(email, assistant);
|
|
230
230
|
|
|
231
231
|
if (!inferred?.firstName && !inferred?.lastName && !inferred?.company) {
|
|
232
|
+
assistant.log(`signup(): inferUserContact returned empty result for ${email} (method=${inferred?.method || 'unknown'})`);
|
|
232
233
|
return null;
|
|
233
234
|
}
|
|
234
235
|
|
|
@@ -239,6 +239,10 @@ module.exports = {
|
|
|
239
239
|
// Test 9: Cron resets daily counters for authenticated users
|
|
240
240
|
{
|
|
241
241
|
name: 'cron-resets-daily-counters',
|
|
242
|
+
// bm_cronDaily runs all daily jobs serially; reset-usage is at the end of
|
|
243
|
+
// the alphabetical sequence. In EXTENDED mode the real-API jobs ahead of
|
|
244
|
+
// it can take ~50s combined — override the suite's 30s default.
|
|
245
|
+
timeout: 75000,
|
|
242
246
|
async run({ assert, firestore, state, accounts, waitFor, pubsub }) {
|
|
243
247
|
// Verify daily counter is > 0 before cron
|
|
244
248
|
const beforeDoc = await firestore.get(`users/${accounts.basic.uid}`);
|
|
@@ -251,19 +255,24 @@ module.exports = {
|
|
|
251
255
|
// Trigger cron via PubSub
|
|
252
256
|
await pubsub.trigger('bm_cronDaily');
|
|
253
257
|
|
|
254
|
-
// Wait for cron to reset daily counter
|
|
258
|
+
// Wait for cron to reset daily counter.
|
|
259
|
+
// bm_cronDaily executes every registered daily job sequentially. In EXTENDED
|
|
260
|
+
// mode the real-API jobs (marketing-newsletter-generate, expire-paypal-cancellations,
|
|
261
|
+
// ghostii-auto-publisher, etc.) can take 40-50s combined before reset-usage
|
|
262
|
+
// (alphabetical tail) gets its turn. 70s gives that the headroom it needs;
|
|
263
|
+
// the per-test `timeout` below matches.
|
|
255
264
|
try {
|
|
256
265
|
await waitFor(
|
|
257
266
|
async () => {
|
|
258
267
|
const doc = await firestore.get(`users/${accounts.basic.uid}`);
|
|
259
268
|
return doc?.usage?.requests?.daily === 0;
|
|
260
269
|
},
|
|
261
|
-
|
|
270
|
+
70000,
|
|
262
271
|
500
|
|
263
272
|
);
|
|
264
273
|
assert.ok(true, 'Daily counter was reset to 0 by cron');
|
|
265
274
|
} catch (error) {
|
|
266
|
-
assert.fail('Daily counter should be reset to 0 within
|
|
275
|
+
assert.fail('Daily counter should be reset to 0 within 70s');
|
|
267
276
|
}
|
|
268
277
|
},
|
|
269
278
|
},
|