@pitcher/js-api 1.28.0 → 1.30.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.
- package/js-api.esm.js +477 -44
- package/js-api.esm.js.map +1 -1
- package/js-api.umd.min.js +32 -11
- package/js-api.umd.min.js.map +1 -1
- package/lib/ai-tasks/__tests__/weekly-focus.spec.d.ts +1 -0
- package/lib/ai-tasks/weekly-focus.d.ts +241 -0
- package/lib/apps/browser/appTypes.d.ts +28 -0
- package/lib/apps/browser/stores/api.d.ts +6 -1
- package/lib/apps/browser/stores/app.d.ts +5 -0
- package/lib/apps/browser/stores/app.selectionPruneScope.test.d.ts +1 -0
- package/lib/apps/browser/stores/upload.d.ts +26 -1
- package/lib/apps/browser/stores/upload.types.d.ts +9 -0
- package/lib/apps/browser/util/lifecycle.d.ts +57 -0
- package/lib/apps/browser/util/lifecycle.test.d.ts +1 -0
- package/lib/apps/canvas-builder/components/ui/DynamicContent/useDynamicContent.d.ts +2 -0
- package/lib/apps/canvas-builder/components/ui/Multimedia/Multimedia.ldAsset.spec.d.ts +1 -0
- package/lib/apps/canvas-builder/composables/useCanvas.d.ts +203 -1
- package/lib/apps/canvas-builder/composables/useCanvas.saveEcho.spec.d.ts +1 -0
- package/lib/apps/canvas-builder/composables/useCanvasBlocks.d.ts +36 -0
- package/lib/apps/canvas-builder/composables/useCanvasExit.d.ts +1 -0
- package/lib/apps/canvas-builder/composables/useCanvasHistory.d.ts +36 -0
- package/lib/apps/canvas-builder/composables/useCanvasTheme.d.ts +20 -18
- package/lib/apps/canvas-builder/composables/useContentSelector.d.ts +8 -1
- package/lib/apps/canvas-builder/composables/useInstalledEmbeddables.d.ts +1 -1
- package/lib/apps/canvas-builder/composables/useNavigatorDrawer.d.ts +2 -0
- package/lib/apps/canvas-builder/composables/usePopupApps.d.ts +2 -0
- package/lib/apps/canvas-builder/types/canvas.d.ts +53 -3
- package/lib/apps/canvas-builder/util/canvas.util.d.ts +8 -0
- package/lib/apps/canvas-builder/util/completionWizard.util.d.ts +107 -0
- package/lib/apps/canvas-builder/util/completionWizard.util.test.d.ts +1 -0
- package/lib/apps/canvas-builder/util/fullscreenMode.util.d.ts +7 -0
- package/lib/apps/canvas-builder/util/fullscreenMode.util.test.d.ts +1 -0
- package/lib/apps/canvas-builder/util/lazySections.util.d.ts +35 -0
- package/lib/apps/canvas-builder/util/lazySections.util.test.d.ts +1 -0
- package/lib/apps/collection-player/composables/collection-navigation.util.d.ts +27 -0
- package/lib/apps/collection-player/composables/collection-navigation.util.spec.d.ts +1 -0
- package/lib/apps/collection-player/stores/app.store.d.ts +3 -0
- package/lib/apps/content-selector/components/Assets.uploaderRefresh.spec.d.ts +1 -0
- package/lib/apps/content-selector/stores/app.d.ts +1 -1
- package/lib/components/CFileViewer/pdf/CFileViewer.pdf.annotations.use.d.ts +5 -1
- package/lib/components/CFileViewer/pdf/CFileViewer.pdf.formFields.use.d.ts +8 -0
- package/lib/components/CFileViewer/pdf/CFileViewer.pdf.link.use.d.ts +1 -1
- package/lib/components/CFileViewer/pdf/CFileViewer.pdf.overlayItems.use.d.ts +1 -1
- package/lib/components/CFileViewer/pdf/CFileViewer.pdf.use.d.ts +4 -9
- package/lib/components/CFileViewer/pdf/CFileViewer.pdf.util.d.ts +1 -1
- package/lib/components/CFileViewer/pdf/CFileViewerFullscreen.pdf.use.d.ts +1 -1
- package/lib/components/CFileViewer/pdf/__tests__/CFileViewer.pdf.annotations.text.test.d.ts +1 -0
- package/lib/components/savedCanvases/CSavedCanvasesManagement.use.d.ts +1 -0
- package/lib/composables/useCourseOnlyApps.d.ts +1 -1
- package/lib/composables/usePdfInterpolation.d.ts +2 -2
- package/lib/composables/useWindowEvents.d.ts +1 -1
- package/lib/constants/cdp.const.d.ts +1 -0
- package/lib/constants/translationLanguages.const.d.ts +72 -0
- package/lib/main.lib.d.ts +2 -0
- package/lib/sdk/api/HighLevelApi.d.ts +26 -9
- package/lib/sdk/api/modules/admin/types.admin.d.ts +1 -0
- package/lib/sdk/api/modules/ai.d.ts +43 -1
- package/lib/sdk/api/modules/ai.spec.d.ts +1 -0
- package/lib/sdk/api/modules/canvas.d.ts +8 -0
- package/lib/sdk/api/modules/index.d.ts +2 -1
- package/lib/sdk/api/modules/stt.d.ts +65 -0
- package/lib/sdk/api/modules/stt.spec.d.ts +1 -0
- package/lib/sdk/api/modules/tts.d.ts +41 -0
- package/lib/sdk/api/modules/tts.spec.d.ts +1 -0
- package/lib/sdk/api/modules/ui/types.ui.d.ts +7 -0
- package/lib/sdk/interfaces.d.ts +26 -1
- package/lib/sdk/main.d.ts +35 -6
- package/lib/sdk/payload.types.d.ts +284 -0
- package/lib/types/ai-tasks.const.d.ts +39 -1
- package/lib/types/call.d.ts +14 -0
- package/lib/types/canvases.d.ts +0 -3
- package/lib/types/launchDarkly.types.d.ts +1 -3
- package/lib/types/organizationSettings.types.d.ts +10 -0
- package/lib/types/sfdc.d.ts +15 -0
- package/lib/util/soql.util.d.ts +36 -0
- package/lib/util/user.d.ts +18 -0
- package/lib/util/user.test.d.ts +1 -0
- package/package.json +1 -1
- package/types/openapi/index.d.ts +0 -1
- package/types/openapi/models/DeletedEnvironment.d.ts +0 -1
- package/types/openapi/models/Environment.d.ts +0 -1
- package/types/openapi/models/EnvironmentRequest.d.ts +0 -1
- package/types/openapi/models/File.d.ts +2 -0
- package/types/openapi/models/FileRetrieve.d.ts +2 -0
- package/types/openapi/models/PatchedEnvironmentRequest.d.ts +0 -1
- package/types/openapi/models/PatchedUserRequest.d.ts +1 -2
- package/types/openapi/models/User.d.ts +1 -2
- package/types/openapi/models/UserRequest.d.ts +1 -2
- package/lib/sdk/api/modules/languages.d.ts +0 -8
- package/types/openapi/models/LanguageEnum.d.ts +0 -12
package/js-api.esm.js
CHANGED
|
@@ -2385,6 +2385,261 @@ function resolvePiaSearchMode(config) {
|
|
|
2385
2385
|
return "off";
|
|
2386
2386
|
}
|
|
2387
2387
|
|
|
2388
|
+
const WINDOW_DAYS = 6;
|
|
2389
|
+
const MAX_RANK_MEETINGS = 12;
|
|
2390
|
+
const HERO_MAX_CHARS = 400;
|
|
2391
|
+
const REASON_MAX_CHARS = 140;
|
|
2392
|
+
const DEFAULT_MAX_PICKS = 5;
|
|
2393
|
+
const TITLE_MAX_CHARS = 70;
|
|
2394
|
+
const ACCOUNT_NAME_MAX_CHARS = 60;
|
|
2395
|
+
const SECTION_LABEL_MAX_CHARS = 40;
|
|
2396
|
+
const FIELD_LABEL_MAX_CHARS = 30;
|
|
2397
|
+
const FIELD_VALUE_MAX_CHARS = 60;
|
|
2398
|
+
const WEEKLY_FOCUS_RANKING_RULES = `You rank a sales rep's upcoming meetings by how much they need preparation.
|
|
2399
|
+
|
|
2400
|
+
Below are {{meeting_count}} numbered meetings, each with its account's CRM context (deals, tasks, recent calls).
|
|
2401
|
+
|
|
2402
|
+
Pick the {{max_picks}} most important. Pick fewer only if the list has fewer meetings.
|
|
2403
|
+
|
|
2404
|
+
Rules:
|
|
2405
|
+
- Highest priority first; ties go to the earlier meeting.
|
|
2406
|
+
- Strongest signals: an overdue or due-soon high-priority task, a poorly rated recent interaction, a deal closing within days. Routine meetings on healthy accounts rank last.
|
|
2407
|
+
- {{account_rule}}
|
|
2408
|
+
- Each "reason": ONE sentence, max {{reason_max_chars}} characters, only facts from the data — name the deal, task, rating, or date that makes it matter.
|
|
2409
|
+
- "hero": 1-2 sentences, max {{hero_max_chars}} characters, summarizing the week in plain business language.
|
|
2410
|
+
- Never invent facts. Never repeat field names, codes, or IDs.`;
|
|
2411
|
+
const ONE_PER_ACCOUNT_RULE = "Pick at most ONE meeting per account: when an account has several meetings, pick only its most important one.";
|
|
2412
|
+
const MULTI_PER_ACCOUNT_RULE = "Prefer spreading picks across different accounts, but you may pick two meetings on the same account when each is genuinely high priority on its own.";
|
|
2413
|
+
function effectiveMaxPicks(maxPicks) {
|
|
2414
|
+
if (typeof maxPicks !== "number" || !Number.isFinite(maxPicks)) return DEFAULT_MAX_PICKS;
|
|
2415
|
+
return Math.min(8, Math.max(1, Math.floor(maxPicks)));
|
|
2416
|
+
}
|
|
2417
|
+
function clamp(value, max) {
|
|
2418
|
+
const trimmed = value.trim();
|
|
2419
|
+
return trimmed.length > max ? trimmed.slice(0, max - 1).trimEnd() + "…" : trimmed;
|
|
2420
|
+
}
|
|
2421
|
+
function fill(template2, tokens) {
|
|
2422
|
+
return template2.replace(
|
|
2423
|
+
/\{\{(\w+)\}\}/g,
|
|
2424
|
+
(match, key) => Object.prototype.hasOwnProperty.call(tokens, key) ? tokens[key] : match
|
|
2425
|
+
);
|
|
2426
|
+
}
|
|
2427
|
+
const DATE_ONLY = /^(\d{4})-(\d{2})-(\d{2})$/;
|
|
2428
|
+
function startOfDayUtcMs(date, timeZone) {
|
|
2429
|
+
if (!timeZone) return Date.UTC(date.getFullYear(), date.getMonth(), date.getDate());
|
|
2430
|
+
const parts = new Intl.DateTimeFormat("en-US", {
|
|
2431
|
+
timeZone,
|
|
2432
|
+
year: "numeric",
|
|
2433
|
+
month: "2-digit",
|
|
2434
|
+
day: "2-digit"
|
|
2435
|
+
}).formatToParts(date);
|
|
2436
|
+
const get = (type) => Number(parts.find((p) => p.type === type)?.value);
|
|
2437
|
+
return Date.UTC(get("year"), get("month") - 1, get("day"));
|
|
2438
|
+
}
|
|
2439
|
+
function daysAgo(iso, options) {
|
|
2440
|
+
const now = options?.now ?? /* @__PURE__ */ new Date();
|
|
2441
|
+
const todayMs = startOfDayUtcMs(now, options?.timeZone);
|
|
2442
|
+
const dateOnly = DATE_ONLY.exec(iso);
|
|
2443
|
+
if (dateOnly) {
|
|
2444
|
+
const [y, m, d] = [+dateOnly[1], +dateOnly[2] - 1, +dateOnly[3]];
|
|
2445
|
+
const thenMs = Date.UTC(y, m, d);
|
|
2446
|
+
const back = new Date(thenMs);
|
|
2447
|
+
if (back.getUTCFullYear() !== y || back.getUTCMonth() !== m || back.getUTCDate() !== d) return null;
|
|
2448
|
+
return Math.round((todayMs - thenMs) / 864e5);
|
|
2449
|
+
}
|
|
2450
|
+
const then = new Date(iso);
|
|
2451
|
+
if (Number.isNaN(then.getTime())) return null;
|
|
2452
|
+
return Math.round((todayMs - startOfDayUtcMs(then, options?.timeZone)) / 864e5);
|
|
2453
|
+
}
|
|
2454
|
+
function weeklyWindowMeetings(meetings, options) {
|
|
2455
|
+
const now = options?.now ?? /* @__PURE__ */ new Date();
|
|
2456
|
+
const nowMs = now.getTime();
|
|
2457
|
+
return meetings.filter((meeting) => {
|
|
2458
|
+
const days = daysAgo(meeting.meeting_time, { now, timeZone: options?.timeZone });
|
|
2459
|
+
if (days === null) return false;
|
|
2460
|
+
if (!DATE_ONLY.test(meeting.meeting_time)) {
|
|
2461
|
+
const t = new Date(meeting.meeting_time).getTime();
|
|
2462
|
+
if (Number.isNaN(t) || t <= nowMs) return false;
|
|
2463
|
+
}
|
|
2464
|
+
const offset = -days;
|
|
2465
|
+
return offset >= 0 && offset <= WINDOW_DAYS;
|
|
2466
|
+
}).sort((a, b) => sortableTime(a.meeting_time) - sortableTime(b.meeting_time));
|
|
2467
|
+
}
|
|
2468
|
+
function sortableTime(iso) {
|
|
2469
|
+
const t = Date.parse(iso);
|
|
2470
|
+
return Number.isNaN(t) ? Number.POSITIVE_INFINITY : t;
|
|
2471
|
+
}
|
|
2472
|
+
function relativeDays(days) {
|
|
2473
|
+
if (days === 0) return "today";
|
|
2474
|
+
if (days === 1) return "yesterday";
|
|
2475
|
+
if (days < 0) return days === -1 ? "tomorrow" : `in ${-days} days`;
|
|
2476
|
+
return `${days} days ago`;
|
|
2477
|
+
}
|
|
2478
|
+
function formatWhen(iso, timeZone) {
|
|
2479
|
+
if (DATE_ONLY.test(iso)) return iso;
|
|
2480
|
+
const date = new Date(iso);
|
|
2481
|
+
if (Number.isNaN(date.getTime())) return iso;
|
|
2482
|
+
const day = date.toLocaleDateString("en-US", {
|
|
2483
|
+
weekday: "short",
|
|
2484
|
+
month: "short",
|
|
2485
|
+
day: "numeric",
|
|
2486
|
+
...timeZone && { timeZone }
|
|
2487
|
+
});
|
|
2488
|
+
const time = date.toLocaleTimeString("en-US", {
|
|
2489
|
+
hour: "numeric",
|
|
2490
|
+
minute: "2-digit",
|
|
2491
|
+
...timeZone && { timeZone }
|
|
2492
|
+
});
|
|
2493
|
+
return `${day} ${time}`.trim();
|
|
2494
|
+
}
|
|
2495
|
+
function meetingBlock(index, meeting, context, options) {
|
|
2496
|
+
const when = formatWhen(meeting.meeting_time, options.timeZone);
|
|
2497
|
+
const offset = -(daysAgo(meeting.meeting_time, options) ?? 0);
|
|
2498
|
+
const day = offset === 0 ? "today" : offset === 1 ? "tomorrow" : `in ${offset} days`;
|
|
2499
|
+
const attendees = meeting.attendee_count ?? 0;
|
|
2500
|
+
const head = `${index}. "${clamp(meeting.meeting_subject, TITLE_MAX_CHARS)}" — ${clamp(meeting.account_name, ACCOUNT_NAME_MAX_CHARS)} — ${when} (${day})` + (attendees > 0 ? ` — ${attendees} attendee${attendees === 1 ? "" : "s"}` : "");
|
|
2501
|
+
const lines = [head];
|
|
2502
|
+
for (const section of context?.sections ?? []) {
|
|
2503
|
+
if (section.items.length === 0) continue;
|
|
2504
|
+
const first = section.items[0];
|
|
2505
|
+
const fieldList = first.fields ?? [];
|
|
2506
|
+
const fields = section.fields_curated && fieldList.length > 0 ? " · " + fieldList.slice(0, 3).map((f) => `${clamp(f.label, FIELD_LABEL_MAX_CHARS)}: ${clamp(f.value, FIELD_VALUE_MAX_CHARS)}`).join(" · ") : "";
|
|
2507
|
+
const age = first.days_ago != null ? ` (${relativeDays(first.days_ago)})` : "";
|
|
2508
|
+
const count = section.items.length > 1 ? `${section.items.length} records; key: ` : "";
|
|
2509
|
+
lines.push(
|
|
2510
|
+
` ${clamp(section.label, SECTION_LABEL_MAX_CHARS)}: ${count}${clamp(first.title, TITLE_MAX_CHARS)}${fields}${age}`
|
|
2511
|
+
);
|
|
2512
|
+
}
|
|
2513
|
+
return lines.join("\n");
|
|
2514
|
+
}
|
|
2515
|
+
function template(variables) {
|
|
2516
|
+
const meetings = variables.meetings;
|
|
2517
|
+
const context = variables.context ?? {};
|
|
2518
|
+
const maxPicks = effectiveMaxPicks(variables.max_picks);
|
|
2519
|
+
const onePerAccount = variables.one_per_account !== false;
|
|
2520
|
+
const options = { now: variables.now, timeZone: variables.time_zone };
|
|
2521
|
+
const blocks = meetings.map((m, i) => meetingBlock(i + 1, m, context[m.account_id], options)).join("\n");
|
|
2522
|
+
const rules = fill(variables.ranking_rules || WEEKLY_FOCUS_RANKING_RULES, {
|
|
2523
|
+
meeting_count: String(meetings.length),
|
|
2524
|
+
max_picks: String(maxPicks),
|
|
2525
|
+
reason_max_chars: String(REASON_MAX_CHARS),
|
|
2526
|
+
hero_max_chars: String(HERO_MAX_CHARS),
|
|
2527
|
+
account_rule: onePerAccount ? ONE_PER_ACCOUNT_RULE : MULTI_PER_ACCOUNT_RULE
|
|
2528
|
+
});
|
|
2529
|
+
return `${rules}
|
|
2530
|
+
|
|
2531
|
+
Answer with JSON ONLY — no other text. The object has exactly two keys:
|
|
2532
|
+
"hero": the summary as one string.
|
|
2533
|
+
"top": an array with one entry per picked meeting, highest priority first. Each entry is an object with "m" (the picked meeting's number from the list below, as an integer) and "reason" (one sentence as a string).
|
|
2534
|
+
|
|
2535
|
+
MEETINGS:
|
|
2536
|
+
${blocks}`;
|
|
2537
|
+
}
|
|
2538
|
+
function tryParse(slice) {
|
|
2539
|
+
try {
|
|
2540
|
+
return JSON.parse(slice);
|
|
2541
|
+
} catch {
|
|
2542
|
+
const repaired = slice.replace(/[“”]/g, '"').replace(/[‘’]/g, "'").replace(/,\s*([}\]])/g, "$1");
|
|
2543
|
+
try {
|
|
2544
|
+
return JSON.parse(repaired);
|
|
2545
|
+
} catch {
|
|
2546
|
+
return null;
|
|
2547
|
+
}
|
|
2548
|
+
}
|
|
2549
|
+
}
|
|
2550
|
+
function looksLikeResponse(o) {
|
|
2551
|
+
return Array.isArray(o.top);
|
|
2552
|
+
}
|
|
2553
|
+
function extractJson(raw) {
|
|
2554
|
+
let fallback = null;
|
|
2555
|
+
for (let start = raw.indexOf("{"); start !== -1; start = raw.indexOf("{", start + 1)) {
|
|
2556
|
+
let depth = 0;
|
|
2557
|
+
let inString = false;
|
|
2558
|
+
let escaped = false;
|
|
2559
|
+
for (let i = start; i < raw.length; i++) {
|
|
2560
|
+
const ch = raw[i];
|
|
2561
|
+
if (inString) {
|
|
2562
|
+
if (escaped) escaped = false;
|
|
2563
|
+
else if (ch === "\\") escaped = true;
|
|
2564
|
+
else if (ch === '"') inString = false;
|
|
2565
|
+
} else if (ch === '"') {
|
|
2566
|
+
inString = true;
|
|
2567
|
+
} else if (ch === "{") {
|
|
2568
|
+
depth += 1;
|
|
2569
|
+
} else if (ch === "}") {
|
|
2570
|
+
depth -= 1;
|
|
2571
|
+
if (depth === 0) {
|
|
2572
|
+
const parsed = tryParse(raw.slice(start, i + 1));
|
|
2573
|
+
if (parsed) {
|
|
2574
|
+
if (looksLikeResponse(parsed)) return parsed;
|
|
2575
|
+
if (!fallback) fallback = parsed;
|
|
2576
|
+
}
|
|
2577
|
+
break;
|
|
2578
|
+
}
|
|
2579
|
+
}
|
|
2580
|
+
}
|
|
2581
|
+
}
|
|
2582
|
+
return fallback;
|
|
2583
|
+
}
|
|
2584
|
+
function normalizeWeeklyFocus(parsed) {
|
|
2585
|
+
const raw = parsed ?? {};
|
|
2586
|
+
const top = [];
|
|
2587
|
+
if (Array.isArray(raw.top)) {
|
|
2588
|
+
for (const pick of raw.top) {
|
|
2589
|
+
const m = typeof pick?.m === "number" ? pick.m : Number(pick?.m);
|
|
2590
|
+
const reason = typeof pick?.reason === "string" ? pick.reason.trim() : "";
|
|
2591
|
+
if (!Number.isInteger(m) || !reason) continue;
|
|
2592
|
+
top.push({ m, reason: clamp(reason, REASON_MAX_CHARS) });
|
|
2593
|
+
}
|
|
2594
|
+
}
|
|
2595
|
+
const hero = typeof raw.hero === "string" ? clamp(raw.hero, HERO_MAX_CHARS) : "";
|
|
2596
|
+
return { hero, top };
|
|
2597
|
+
}
|
|
2598
|
+
function extractWeeklyFocus(rawText) {
|
|
2599
|
+
const parsed = extractJson(rawText ?? "");
|
|
2600
|
+
if (!parsed) return { hero: "", top: [] };
|
|
2601
|
+
return normalizeWeeklyFocus(parsed);
|
|
2602
|
+
}
|
|
2603
|
+
function resolveWeeklyFocusPicks(output, rankedMeetings, options = {}) {
|
|
2604
|
+
const maxPicks = effectiveMaxPicks(options.maxPicks);
|
|
2605
|
+
const onePerAccount = options.onePerAccount !== false;
|
|
2606
|
+
const windowCount = options.windowCount ?? rankedMeetings.length;
|
|
2607
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2608
|
+
const pickedAccounts = /* @__PURE__ */ new Set();
|
|
2609
|
+
const top = [];
|
|
2610
|
+
let dropped = 0;
|
|
2611
|
+
for (const pick of output.top ?? []) {
|
|
2612
|
+
const meeting = Number.isInteger(pick.m) ? rankedMeetings[pick.m - 1] : void 0;
|
|
2613
|
+
const reason = typeof pick.reason === "string" ? pick.reason.trim() : "";
|
|
2614
|
+
if (!meeting || !reason || seen.has(meeting.event_id)) {
|
|
2615
|
+
dropped += 1;
|
|
2616
|
+
continue;
|
|
2617
|
+
}
|
|
2618
|
+
if (onePerAccount && pickedAccounts.has(meeting.account_id)) {
|
|
2619
|
+
dropped += 1;
|
|
2620
|
+
continue;
|
|
2621
|
+
}
|
|
2622
|
+
seen.add(meeting.event_id);
|
|
2623
|
+
pickedAccounts.add(meeting.account_id);
|
|
2624
|
+
top.push({ event_id: meeting.event_id, rank: top.length + 1, reason: clamp(reason, REASON_MAX_CHARS) });
|
|
2625
|
+
if (top.length >= maxPicks) break;
|
|
2626
|
+
}
|
|
2627
|
+
return { top, collapsed_count: Math.max(0, windowCount - top.length), dropped };
|
|
2628
|
+
}
|
|
2629
|
+
function weeklyFocusTarget(rankedMeetings, options = {}) {
|
|
2630
|
+
const onePerAccount = options.onePerAccount !== false;
|
|
2631
|
+
const ceiling = onePerAccount ? new Set(rankedMeetings.map((m) => m.account_id)).size : rankedMeetings.length;
|
|
2632
|
+
return Math.min(effectiveMaxPicks(options.maxPicks), ceiling);
|
|
2633
|
+
}
|
|
2634
|
+
function prepareWeeklyFocusMeetings(meetings, options) {
|
|
2635
|
+
const windowMeetings = weeklyWindowMeetings(meetings, options);
|
|
2636
|
+
return {
|
|
2637
|
+
windowMeetings,
|
|
2638
|
+
rankedMeetings: windowMeetings.slice(0, MAX_RANK_MEETINGS),
|
|
2639
|
+
windowCount: windowMeetings.length
|
|
2640
|
+
};
|
|
2641
|
+
}
|
|
2642
|
+
|
|
2388
2643
|
const PIA_SEARCH_ROUTE = "/core/api/protected/pia-prepare/pia-search";
|
|
2389
2644
|
const CAPABILITIES_TIMEOUT_MS = 3e3;
|
|
2390
2645
|
const ON_DEVICE_MAX_TOKENS = 512;
|
|
@@ -2419,6 +2674,32 @@ function apiOriginFromEnv(env) {
|
|
|
2419
2674
|
if (typeof orgDomain === "string" && orgDomain.length > 0) return normalizeOrigin(orgDomain);
|
|
2420
2675
|
throw new Error("piaSearchAnswer: no customer domain found in env (token_claims / organization metadata)");
|
|
2421
2676
|
}
|
|
2677
|
+
async function postJsonWithTimeout(url, body, accessToken, timeoutMs, label) {
|
|
2678
|
+
const controller = new AbortController();
|
|
2679
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
2680
|
+
try {
|
|
2681
|
+
const response = await fetch(url, {
|
|
2682
|
+
method: "POST",
|
|
2683
|
+
signal: controller.signal,
|
|
2684
|
+
headers: {
|
|
2685
|
+
"Content-Type": "application/json",
|
|
2686
|
+
Authorization: `Bearer ${accessToken}`
|
|
2687
|
+
},
|
|
2688
|
+
body: JSON.stringify(body)
|
|
2689
|
+
});
|
|
2690
|
+
if (!response.ok) {
|
|
2691
|
+
throw new Error(`${label} ${response.status}`);
|
|
2692
|
+
}
|
|
2693
|
+
return await response.json();
|
|
2694
|
+
} catch (error) {
|
|
2695
|
+
if (error?.name === "AbortError") {
|
|
2696
|
+
throw new Error(`${label} online request timed out after ${timeoutMs}ms`);
|
|
2697
|
+
}
|
|
2698
|
+
throw error;
|
|
2699
|
+
} finally {
|
|
2700
|
+
clearTimeout(timer);
|
|
2701
|
+
}
|
|
2702
|
+
}
|
|
2422
2703
|
function buildOnDevicePrompt(query, contextText) {
|
|
2423
2704
|
const context = typeof contextText === "string" && contextText.trim().length > 0 ? contextText.trim() : "No content context was provided.";
|
|
2424
2705
|
return `You are PIA, a sales content assistant. Answer the rep's question using ONLY the CONTEXT below — do not invent content that is not in it.
|
|
@@ -2428,7 +2709,7 @@ ${context}
|
|
|
2428
2709
|
|
|
2429
2710
|
QUESTION: ${query}
|
|
2430
2711
|
|
|
2431
|
-
Answer concisely in plain text. If the context does not contain the answer, say you could not find it in the available content.`;
|
|
2712
|
+
Answer concisely in plain text. If the context does not contain the answer, say you could not find it in the available content. Refer to content by its name only — do not include any file ids or codes in your answer.`;
|
|
2432
2713
|
}
|
|
2433
2714
|
async function aiGetCapabilities() {
|
|
2434
2715
|
const env = await highLevelApi.API.request("get_env");
|
|
@@ -2474,41 +2755,126 @@ async function piaSearchAnswer(payload) {
|
|
|
2474
2755
|
if (!instanceId) {
|
|
2475
2756
|
throw new Error("piaSearchAnswer: instance_id could not be resolved from the payload or env");
|
|
2476
2757
|
}
|
|
2477
|
-
const
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
}
|
|
2505
|
-
|
|
2506
|
-
|
|
2758
|
+
const data = await postJsonWithTimeout(
|
|
2759
|
+
`${apiOriginFromEnv(env)}${PIA_SEARCH_ROUTE}`,
|
|
2760
|
+
{
|
|
2761
|
+
query: payload.query,
|
|
2762
|
+
instance_id: instanceId,
|
|
2763
|
+
...Array.isArray(payload.file_ids) && payload.file_ids.length > 0 && { file_ids: payload.file_ids },
|
|
2764
|
+
...payload.include_document_text === true && { include_document_text: true },
|
|
2765
|
+
...Array.isArray(payload.expansion_terms) && payload.expansion_terms.length > 0 && { expansion_terms: payload.expansion_terms }
|
|
2766
|
+
},
|
|
2767
|
+
env.pitcher.access_token,
|
|
2768
|
+
ONLINE_TIMEOUT_MS,
|
|
2769
|
+
"pia_search"
|
|
2770
|
+
);
|
|
2771
|
+
const output = data?.pia_search ?? {};
|
|
2772
|
+
return {
|
|
2773
|
+
answer: typeof output.answer === "string" ? output.answer : "",
|
|
2774
|
+
cited_file_ids: Array.isArray(output.cited_file_ids) ? output.cited_file_ids : [],
|
|
2775
|
+
...output.confidence && { confidence: output.confidence },
|
|
2776
|
+
source: "online"
|
|
2777
|
+
};
|
|
2778
|
+
}
|
|
2779
|
+
const WEEKLY_FOCUS_ROUTE = "/core/api/protected/pia-prepare/weekly-focus";
|
|
2780
|
+
const weeklyFocusOutputReserve = (maxPicks) => 220 + 60 * maxPicks;
|
|
2781
|
+
const ON_DEVICE_COMPLETE_TIMEOUT_MS = 12e4;
|
|
2782
|
+
async function weeklyFocusRank(payload) {
|
|
2783
|
+
if (!payload || !Array.isArray(payload.meetings)) {
|
|
2784
|
+
return Promise.reject(new Error("meetings is required and must be an array"));
|
|
2785
|
+
}
|
|
2786
|
+
const env = await highLevelApi.API.request("get_env");
|
|
2787
|
+
const timeZone = payload.time_zone || Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
2788
|
+
const { windowMeetings, rankedMeetings, windowCount } = prepareWeeklyFocusMeetings(payload.meetings, { timeZone });
|
|
2789
|
+
if (rankedMeetings.length === 0) {
|
|
2790
|
+
return { hero: "", top: [], collapsed_count: 0, source: env?.mode === "IOS" ? "on_device" : "online" };
|
|
2791
|
+
}
|
|
2792
|
+
const maxPicks = effectiveMaxPicks(payload.max_picks);
|
|
2793
|
+
const resolveOptions = {
|
|
2794
|
+
maxPicks,
|
|
2795
|
+
onePerAccount: payload.one_per_account,
|
|
2796
|
+
windowCount
|
|
2797
|
+
};
|
|
2798
|
+
if (env?.mode === "IOS") {
|
|
2799
|
+
try {
|
|
2800
|
+
const capabilities = await withTimeout(
|
|
2801
|
+
highLevelApi.API.request("ai.get_capabilities"),
|
|
2802
|
+
CAPABILITIES_TIMEOUT_MS,
|
|
2803
|
+
"ai.get_capabilities"
|
|
2804
|
+
);
|
|
2805
|
+
if (capabilities?.available) {
|
|
2806
|
+
const prompt = template({
|
|
2807
|
+
meetings: rankedMeetings,
|
|
2808
|
+
context: payload.context,
|
|
2809
|
+
max_picks: maxPicks,
|
|
2810
|
+
one_per_account: payload.one_per_account,
|
|
2811
|
+
time_zone: timeZone
|
|
2812
|
+
});
|
|
2813
|
+
const runOnce = async () => {
|
|
2814
|
+
const completion = await withTimeout(
|
|
2815
|
+
aiComplete({ prompt, max_tokens: weeklyFocusOutputReserve(maxPicks) }),
|
|
2816
|
+
ON_DEVICE_COMPLETE_TIMEOUT_MS,
|
|
2817
|
+
"ai.complete"
|
|
2818
|
+
);
|
|
2819
|
+
const parsed2 = extractWeeklyFocus(completion.text);
|
|
2820
|
+
return { parsed: parsed2, resolved: resolveWeeklyFocusPicks(parsed2, rankedMeetings, resolveOptions) };
|
|
2821
|
+
};
|
|
2822
|
+
let attempt = await runOnce();
|
|
2823
|
+
if (attempt.resolved.top.length < weeklyFocusTarget(rankedMeetings, resolveOptions)) {
|
|
2824
|
+
try {
|
|
2825
|
+
const second = await runOnce();
|
|
2826
|
+
if (second.resolved.top.length > attempt.resolved.top.length) attempt = second;
|
|
2827
|
+
} catch (error) {
|
|
2828
|
+
console.warn("[weeklyFocusRank] on-device retry failed, keeping the first attempt:", error);
|
|
2829
|
+
}
|
|
2830
|
+
}
|
|
2831
|
+
const { parsed, resolved } = attempt;
|
|
2832
|
+
if (resolved.top.length > 0 || parsed.hero) {
|
|
2833
|
+
return {
|
|
2834
|
+
hero: parsed.hero,
|
|
2835
|
+
top: resolved.top,
|
|
2836
|
+
collapsed_count: resolved.collapsed_count,
|
|
2837
|
+
source: "on_device"
|
|
2838
|
+
};
|
|
2839
|
+
}
|
|
2840
|
+
}
|
|
2841
|
+
} catch (error) {
|
|
2842
|
+
console.warn("[weeklyFocusRank] on-device path unavailable, falling back to online:", error);
|
|
2507
2843
|
}
|
|
2508
|
-
throw error;
|
|
2509
|
-
} finally {
|
|
2510
|
-
clearTimeout(timer);
|
|
2511
2844
|
}
|
|
2845
|
+
const instanceId = payload.instance_id || env?.pitcher?.instance?.id;
|
|
2846
|
+
if (!instanceId) {
|
|
2847
|
+
throw new Error("weeklyFocusRank: instance_id could not be resolved from the payload or env");
|
|
2848
|
+
}
|
|
2849
|
+
const data = await postJsonWithTimeout(
|
|
2850
|
+
`${apiOriginFromEnv(env)}${WEEKLY_FOCUS_ROUTE}`,
|
|
2851
|
+
{
|
|
2852
|
+
instance_id: instanceId,
|
|
2853
|
+
// Send the WINDOW, not the caller's whole calendar. The route re-windows
|
|
2854
|
+
// anyway (idempotent — same rule, same zone), so this only drops meetings
|
|
2855
|
+
// it would have dropped, while keeping `collapsed_count` correct: the route
|
|
2856
|
+
// derives the window size from what it receives, so sending the
|
|
2857
|
+
// already-capped 12 would understate it. It also keeps a caller with a long
|
|
2858
|
+
// history from tripping the route's 500-meeting body cap over meetings
|
|
2859
|
+
// outside the week entirely.
|
|
2860
|
+
meetings: windowMeetings,
|
|
2861
|
+
time_zone: timeZone,
|
|
2862
|
+
...payload.context && { context: payload.context },
|
|
2863
|
+
// Clamped, not raw: the route 400s on out-of-range values.
|
|
2864
|
+
...payload.max_picks !== void 0 && { max_picks: maxPicks },
|
|
2865
|
+
...payload.one_per_account !== void 0 && { one_per_account: payload.one_per_account }
|
|
2866
|
+
},
|
|
2867
|
+
env.pitcher.access_token,
|
|
2868
|
+
ONLINE_TIMEOUT_MS,
|
|
2869
|
+
"weekly_focus"
|
|
2870
|
+
);
|
|
2871
|
+
const output = data?.weekly_focus ?? {};
|
|
2872
|
+
return {
|
|
2873
|
+
hero: typeof output.hero === "string" ? output.hero : "",
|
|
2874
|
+
top: Array.isArray(output.top) ? output.top : [],
|
|
2875
|
+
collapsed_count: typeof output.collapsed_count === "number" ? output.collapsed_count : 0,
|
|
2876
|
+
source: "online"
|
|
2877
|
+
};
|
|
2512
2878
|
}
|
|
2513
2879
|
|
|
2514
2880
|
const APPS_DB_BASE_PATH = "/core/api/protected/appsdb";
|
|
@@ -2773,10 +3139,6 @@ function isOffline() {
|
|
|
2773
3139
|
return this.API.request("is_offline");
|
|
2774
3140
|
}
|
|
2775
3141
|
|
|
2776
|
-
function getLanguages() {
|
|
2777
|
-
return this.API.request("get_languages");
|
|
2778
|
-
}
|
|
2779
|
-
|
|
2780
3142
|
function getCanvases$1(payload) {
|
|
2781
3143
|
return this.API.request("get_canvases", payload);
|
|
2782
3144
|
}
|
|
@@ -3760,6 +4122,65 @@ function assignCanvasTheme(payload) {
|
|
|
3760
4122
|
return this.API.request("assign_canvas_theme", payload);
|
|
3761
4123
|
}
|
|
3762
4124
|
|
|
4125
|
+
const STT_ERROR_CODES = [
|
|
4126
|
+
"STT_DISABLED",
|
|
4127
|
+
"STT_DEVICE_UNSUPPORTED",
|
|
4128
|
+
"STT_MODEL_NOT_READY",
|
|
4129
|
+
"STT_BUSY",
|
|
4130
|
+
"STT_MIC_PERMISSION_DENIED",
|
|
4131
|
+
"STT_NOT_RECORDING",
|
|
4132
|
+
"STT_CAPTURE_FAILED",
|
|
4133
|
+
"STT_ERROR"
|
|
4134
|
+
];
|
|
4135
|
+
function asSttUnsupportedError(error) {
|
|
4136
|
+
const marker = "requestTypeDoesNotExists";
|
|
4137
|
+
const e = error;
|
|
4138
|
+
const isUnsupported = e?.error_code === marker || e?.errorCode === marker || e?.code === marker || typeof e?.reason === "string" && e.reason.includes(marker) || typeof e?.message === "string" && e.message.includes(marker);
|
|
4139
|
+
return isUnsupported ? new Error("STT_DEVICE_UNSUPPORTED: dictation is not supported by this app version") : error;
|
|
4140
|
+
}
|
|
4141
|
+
function sttStart(payload) {
|
|
4142
|
+
return this.API.request("stt.start", payload).catch((error) => {
|
|
4143
|
+
throw asSttUnsupportedError(error);
|
|
4144
|
+
});
|
|
4145
|
+
}
|
|
4146
|
+
function sttAvailability() {
|
|
4147
|
+
return this.API.request("stt.availability");
|
|
4148
|
+
}
|
|
4149
|
+
function sttWarmup() {
|
|
4150
|
+
return this.API.request("stt.warmup");
|
|
4151
|
+
}
|
|
4152
|
+
function sttStop(payload) {
|
|
4153
|
+
return this.API.request("stt.stop", payload).catch((error) => {
|
|
4154
|
+
throw asSttUnsupportedError(error);
|
|
4155
|
+
});
|
|
4156
|
+
}
|
|
4157
|
+
function sttErrorCode(error) {
|
|
4158
|
+
const e = error;
|
|
4159
|
+
const direct = e?.code ?? e?.error_code ?? e?.errorCode;
|
|
4160
|
+
if (typeof direct === "string" && STT_ERROR_CODES.includes(direct)) {
|
|
4161
|
+
return direct;
|
|
4162
|
+
}
|
|
4163
|
+
const text = typeof e?.reason === "string" ? e.reason : typeof e?.message === "string" ? e.message : "";
|
|
4164
|
+
return STT_ERROR_CODES.find((code) => text.includes(code));
|
|
4165
|
+
}
|
|
4166
|
+
|
|
4167
|
+
const TTS_ERROR_CODES = ["TTS_EMPTY_TEXT", "TTS_VOICE_UNAVAILABLE", "TTS_ERROR"];
|
|
4168
|
+
function ttsSpeak(payload) {
|
|
4169
|
+
return this.API.request("tts.speak", payload);
|
|
4170
|
+
}
|
|
4171
|
+
function ttsStop() {
|
|
4172
|
+
return this.API.request("tts.stop");
|
|
4173
|
+
}
|
|
4174
|
+
function ttsErrorCode(error) {
|
|
4175
|
+
const e = error;
|
|
4176
|
+
const direct = e?.code ?? e?.error_code ?? e?.errorCode;
|
|
4177
|
+
if (typeof direct === "string" && TTS_ERROR_CODES.includes(direct)) {
|
|
4178
|
+
return direct;
|
|
4179
|
+
}
|
|
4180
|
+
const text = typeof e?.reason === "string" ? e.reason : typeof e?.message === "string" ? e.message : "";
|
|
4181
|
+
return TTS_ERROR_CODES.find((code) => text.includes(code));
|
|
4182
|
+
}
|
|
4183
|
+
|
|
3763
4184
|
const modules = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
|
|
3764
4185
|
__proto__: null,
|
|
3765
4186
|
aiComplete,
|
|
@@ -3805,7 +4226,6 @@ const modules = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
|
|
|
3805
4226
|
getFolder,
|
|
3806
4227
|
getFolders,
|
|
3807
4228
|
getInstanceMetadataTemplates: getInstanceMetadataTemplates$1,
|
|
3808
|
-
getLanguages,
|
|
3809
4229
|
getSectionsByIds,
|
|
3810
4230
|
getThemes,
|
|
3811
4231
|
getUsers: getUsers$1,
|
|
@@ -3817,6 +4237,7 @@ const modules = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
|
|
|
3817
4237
|
openWebViewAlwaysOnTop,
|
|
3818
4238
|
patchCoreFolder,
|
|
3819
4239
|
piaSearchAnswer,
|
|
4240
|
+
postJsonWithTimeout,
|
|
3820
4241
|
query,
|
|
3821
4242
|
refreshAccessToken,
|
|
3822
4243
|
refreshServiceToken,
|
|
@@ -3827,16 +4248,25 @@ const modules = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
|
|
|
3827
4248
|
shareCanvas,
|
|
3828
4249
|
showPeerSession,
|
|
3829
4250
|
showSyncbox,
|
|
4251
|
+
sttAvailability,
|
|
4252
|
+
sttErrorCode,
|
|
4253
|
+
sttStart,
|
|
4254
|
+
sttStop,
|
|
4255
|
+
sttWarmup,
|
|
3830
4256
|
submitUserFeedback,
|
|
3831
4257
|
toast: toast$2,
|
|
3832
4258
|
track,
|
|
3833
4259
|
triggerNonFilesSync,
|
|
4260
|
+
ttsErrorCode,
|
|
4261
|
+
ttsSpeak,
|
|
4262
|
+
ttsStop,
|
|
3834
4263
|
unassignCanvasTheme,
|
|
3835
4264
|
updateCanvas: updateCanvas$2,
|
|
3836
4265
|
updateCanvasIndicators,
|
|
3837
4266
|
updateFile,
|
|
3838
4267
|
updateFolder,
|
|
3839
|
-
updateMyUser
|
|
4268
|
+
updateMyUser,
|
|
4269
|
+
weeklyFocusRank
|
|
3840
4270
|
}, Symbol.toStringTag, { value: 'Module' }));
|
|
3841
4271
|
|
|
3842
4272
|
class EventEmitter {
|
|
@@ -3947,6 +4377,8 @@ var PitcherEventName = /* @__PURE__ */ ((PitcherEventName2) => {
|
|
|
3947
4377
|
PitcherEventName2["SYNC_BADGE_VALUE_REPORTED"] = "sync_badge_value_reported";
|
|
3948
4378
|
PitcherEventName2["SYNC_BOX_RESOLVE_ERROR_TAPPED"] = "sync_box_resolve_error_tapped";
|
|
3949
4379
|
PitcherEventName2["PEER_CONNECTIVITY_EVENT"] = "peer_connectivity_event";
|
|
4380
|
+
PitcherEventName2["STT_PARTIAL"] = "stt.partial";
|
|
4381
|
+
PitcherEventName2["STT_INTERRUPTED"] = "stt.interrupted";
|
|
3950
4382
|
return PitcherEventName2;
|
|
3951
4383
|
})(PitcherEventName || {});
|
|
3952
4384
|
var PitcherBroadcastedEventName = /* @__PURE__ */ ((PitcherBroadcastedEventName2) => {
|
|
@@ -4316,16 +4748,17 @@ const onlineRestApiHandlers = {
|
|
|
4316
4748
|
},
|
|
4317
4749
|
update_canvas: async (params) => {
|
|
4318
4750
|
check();
|
|
4319
|
-
const { id, fields, ...canvasData } = params;
|
|
4751
|
+
const { id, fields, lazy_sections, ...canvasData } = params;
|
|
4752
|
+
const lazyQs = lazy_sections === true ? "lazy_sections=true" : "";
|
|
4320
4753
|
const useCoreEndpoint = env?.launch_darkly?.enable_canvas_core_patch === true;
|
|
4321
4754
|
if (useCoreEndpoint) {
|
|
4322
4755
|
const origin = env.pitcher.token_claims["https://pitcher.com/claims/urls"].custom_domain;
|
|
4323
|
-
const
|
|
4324
|
-
const url = `${origin}/core/api/protected/v1/canvases/${id}${
|
|
4756
|
+
const qs = [fields ? `fields=${encodeURIComponent(fields)}` : "", lazyQs].filter(Boolean).join("&");
|
|
4757
|
+
const url = `${origin}/core/api/protected/v1/canvases/${id}${qs ? `?${qs}` : ""}`;
|
|
4325
4758
|
const res2 = await http.patchUrl(url, canvasData);
|
|
4326
4759
|
return res2;
|
|
4327
4760
|
}
|
|
4328
|
-
const res = await http.patch(`/canvases/${id}`, { fields, ...canvasData });
|
|
4761
|
+
const res = await http.patch(`/canvases/${id}${lazyQs ? `?${lazyQs}` : ""}`, { fields, ...canvasData });
|
|
4329
4762
|
return res;
|
|
4330
4763
|
},
|
|
4331
4764
|
delete_canvas: async (params) => {
|